@aiready/context-analyzer 0.21.5 → 0.21.6

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 (149) 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-test.log +41 -119
  9. package/dist/__tests__/analyzer.test.js +55 -14
  10. package/dist/__tests__/analyzer.test.js.map +1 -1
  11. package/dist/__tests__/cluster-detector.test.d.ts +2 -0
  12. package/dist/__tests__/cluster-detector.test.d.ts.map +1 -0
  13. package/dist/__tests__/cluster-detector.test.js +121 -0
  14. package/dist/__tests__/cluster-detector.test.js.map +1 -0
  15. package/dist/__tests__/contract.test.d.ts +2 -0
  16. package/dist/__tests__/contract.test.d.ts.map +1 -0
  17. package/dist/__tests__/contract.test.js +59 -0
  18. package/dist/__tests__/contract.test.js.map +1 -0
  19. package/dist/__tests__/enhanced-cohesion.test.js +12 -2
  20. package/dist/__tests__/enhanced-cohesion.test.js.map +1 -1
  21. package/dist/__tests__/file-classification.test.d.ts +2 -0
  22. package/dist/__tests__/file-classification.test.d.ts.map +1 -0
  23. package/dist/__tests__/file-classification.test.js +749 -0
  24. package/dist/__tests__/file-classification.test.js.map +1 -0
  25. package/dist/__tests__/fragmentation-advanced.test.js +2 -8
  26. package/dist/__tests__/fragmentation-advanced.test.js.map +1 -1
  27. package/dist/__tests__/fragmentation-coupling.test.js +2 -2
  28. package/dist/__tests__/fragmentation-coupling.test.js.map +1 -1
  29. package/dist/__tests__/fragmentation-log.test.js +3 -7
  30. package/dist/__tests__/fragmentation-log.test.js.map +1 -1
  31. package/dist/__tests__/provider.test.d.ts +2 -0
  32. package/dist/__tests__/provider.test.d.ts.map +1 -0
  33. package/dist/__tests__/provider.test.js +72 -0
  34. package/dist/__tests__/provider.test.js.map +1 -0
  35. package/dist/__tests__/remediation.test.d.ts +2 -0
  36. package/dist/__tests__/remediation.test.d.ts.map +1 -0
  37. package/dist/__tests__/remediation.test.js +61 -0
  38. package/dist/__tests__/remediation.test.js.map +1 -0
  39. package/dist/__tests__/scoring.test.js +196 -16
  40. package/dist/__tests__/scoring.test.js.map +1 -1
  41. package/dist/__tests__/structural-cohesion.test.js +8 -2
  42. package/dist/__tests__/structural-cohesion.test.js.map +1 -1
  43. package/dist/analyzer.d.ts +31 -94
  44. package/dist/analyzer.d.ts.map +1 -1
  45. package/dist/analyzer.js +260 -678
  46. package/dist/analyzer.js.map +1 -1
  47. package/dist/analyzers/python-context.d.ts.map +1 -1
  48. package/dist/analyzers/python-context.js +10 -8
  49. package/dist/analyzers/python-context.js.map +1 -1
  50. package/dist/ast-utils.d.ts +16 -0
  51. package/dist/ast-utils.d.ts.map +1 -0
  52. package/dist/ast-utils.js +81 -0
  53. package/dist/ast-utils.js.map +1 -0
  54. package/dist/chunk-64U3PNO3.mjs +94 -0
  55. package/dist/chunk-CDIVYADN.mjs +2110 -0
  56. package/dist/chunk-D3SIHB2V.mjs +2118 -0
  57. package/dist/chunk-FNPSK3CG.mjs +1760 -0
  58. package/dist/chunk-GXTGOLZT.mjs +92 -0
  59. package/dist/chunk-LERPI33Y.mjs +2060 -0
  60. package/dist/chunk-MZP3G7TF.mjs +2118 -0
  61. package/dist/chunk-NOHK5DLU.mjs +2173 -0
  62. package/dist/chunk-ORLC5Y4J.mjs +1787 -0
  63. package/dist/chunk-OTCQL7DY.mjs +2045 -0
  64. package/dist/chunk-SFK6XTJE.mjs +2110 -0
  65. package/dist/chunk-U5R2FTCR.mjs +1803 -0
  66. package/dist/chunk-UU4HZ7ZT.mjs +1849 -0
  67. package/dist/chunk-WKOZOHOU.mjs +2060 -0
  68. package/dist/chunk-XIXAWCMS.mjs +1760 -0
  69. package/dist/classifier.d.ts +114 -0
  70. package/dist/classifier.d.ts.map +1 -0
  71. package/dist/classifier.js +439 -0
  72. package/dist/classifier.js.map +1 -0
  73. package/dist/cli.js +590 -1071
  74. package/dist/cli.js.map +1 -1
  75. package/dist/cli.mjs +63 -533
  76. package/dist/cluster-detector.d.ts +8 -0
  77. package/dist/cluster-detector.d.ts.map +1 -0
  78. package/dist/cluster-detector.js +70 -0
  79. package/dist/cluster-detector.js.map +1 -0
  80. package/dist/defaults.d.ts +7 -0
  81. package/dist/defaults.d.ts.map +1 -0
  82. package/dist/defaults.js +54 -0
  83. package/dist/defaults.js.map +1 -0
  84. package/dist/graph-builder.d.ts +33 -0
  85. package/dist/graph-builder.d.ts.map +1 -0
  86. package/dist/graph-builder.js +225 -0
  87. package/dist/graph-builder.js.map +1 -0
  88. package/dist/index.d.mts +24 -31
  89. package/dist/index.d.ts +24 -31
  90. package/dist/index.d.ts.map +1 -1
  91. package/dist/index.js +788 -569
  92. package/dist/index.js.map +1 -1
  93. package/dist/index.mjs +265 -8
  94. package/dist/metrics.d.ts +34 -0
  95. package/dist/metrics.d.ts.map +1 -0
  96. package/dist/metrics.js +170 -0
  97. package/dist/metrics.js.map +1 -0
  98. package/dist/provider.d.ts +6 -0
  99. package/dist/provider.d.ts.map +1 -0
  100. package/dist/provider.js +48 -0
  101. package/dist/provider.js.map +1 -0
  102. package/dist/python-context-3GZKN3LR.mjs +162 -0
  103. package/dist/python-context-O2EN3M6Z.mjs +162 -0
  104. package/dist/remediation.d.ts +25 -0
  105. package/dist/remediation.d.ts.map +1 -0
  106. package/dist/remediation.js +98 -0
  107. package/dist/remediation.js.map +1 -0
  108. package/dist/scoring.d.ts +3 -7
  109. package/dist/scoring.d.ts.map +1 -1
  110. package/dist/scoring.js +57 -48
  111. package/dist/scoring.js.map +1 -1
  112. package/dist/semantic-analysis.d.ts +12 -23
  113. package/dist/semantic-analysis.d.ts.map +1 -1
  114. package/dist/semantic-analysis.js +172 -110
  115. package/dist/semantic-analysis.js.map +1 -1
  116. package/dist/summary.d.ts +6 -0
  117. package/dist/summary.d.ts.map +1 -0
  118. package/dist/summary.js +92 -0
  119. package/dist/summary.js.map +1 -0
  120. package/dist/types.d.ts +9 -2
  121. package/dist/types.d.ts.map +1 -1
  122. package/dist/utils/output-formatter.d.ts +14 -0
  123. package/dist/utils/output-formatter.d.ts.map +1 -0
  124. package/dist/utils/output-formatter.js +338 -0
  125. package/dist/utils/output-formatter.js.map +1 -0
  126. package/package.json +2 -2
  127. package/src/__tests__/analyzer.test.ts +1 -1
  128. package/src/__tests__/auto-detection.test.ts +1 -1
  129. package/src/__tests__/contract.test.ts +1 -1
  130. package/src/__tests__/enhanced-cohesion.test.ts +1 -1
  131. package/src/__tests__/file-classification.test.ts +1 -1
  132. package/src/__tests__/fragmentation-advanced.test.ts +1 -1
  133. package/src/__tests__/fragmentation-coupling.test.ts +1 -1
  134. package/src/__tests__/fragmentation-log.test.ts +1 -1
  135. package/src/__tests__/provider.test.ts +1 -1
  136. package/src/__tests__/structural-cohesion.test.ts +1 -1
  137. package/src/analyzer.ts +96 -309
  138. package/src/analyzers/python-context.ts +7 -76
  139. package/src/cli-action.ts +103 -0
  140. package/src/cli.ts +12 -693
  141. package/src/cluster-detector.ts +1 -1
  142. package/src/graph-builder.ts +9 -85
  143. package/src/index.ts +6 -0
  144. package/src/issue-analyzer.ts +143 -0
  145. package/src/semantic-analysis.ts +1 -14
  146. package/src/summary.ts +62 -106
  147. package/src/utils/dependency-graph-utils.ts +126 -0
  148. package/src/utils/output-formatter.ts +411 -0
  149. package/src/utils/string-utils.ts +17 -0
package/src/cli.ts CHANGED
@@ -1,10 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { Command } from 'commander';
4
- import { analyzeContext, generateSummary } from './index';
4
+ import { analyzeContext } from './analyzer';
5
+ import { generateSummary } from './summary';
6
+ import {
7
+ displayConsoleReport,
8
+ generateHTMLReport,
9
+ runInteractiveSetup,
10
+ } from './utils/output-formatter';
5
11
  import chalk from 'chalk';
6
- import { writeFileSync, existsSync, readFileSync, mkdirSync } from 'fs';
7
- import { join, dirname } from 'path';
12
+ import { writeFileSync } from 'fs';
8
13
  import {
9
14
  loadMergedConfig,
10
15
  handleJSONOutput,
@@ -12,7 +17,8 @@ import {
12
17
  getElapsedTime,
13
18
  resolveOutputPath,
14
19
  } from '@aiready/core';
15
- import prompts from 'prompts';
20
+
21
+ import { contextActionHandler } from './cli-action';
16
22
 
17
23
  const program = new Command();
18
24
 
@@ -56,693 +62,6 @@ program
56
62
  '--interactive',
57
63
  'Run interactive setup to suggest excludes and focus areas'
58
64
  )
59
- .action(async (directory, options) => {
60
- console.log(chalk.blue('🔍 Analyzing context window costs...\n'));
61
-
62
- const startTime = Date.now();
63
-
64
- try {
65
- // Define defaults
66
- const defaults = {
67
- maxDepth: 5,
68
- maxContextBudget: 10000,
69
- minCohesion: 0.6,
70
- maxFragmentation: 0.5,
71
- focus: 'all',
72
- includeNodeModules: false,
73
- include: undefined,
74
- exclude: undefined,
75
- maxResults: 10,
76
- };
77
-
78
- // Load and merge config with CLI options
79
- let finalOptions = (await loadMergedConfig(directory, defaults, {
80
- maxDepth: options.maxDepth ? parseInt(options.maxDepth) : undefined,
81
- maxContextBudget: options.maxContext
82
- ? parseInt(options.maxContext)
83
- : undefined,
84
- minCohesion: options.minCohesion
85
- ? parseFloat(options.minCohesion)
86
- : undefined,
87
- maxFragmentation: options.maxFragmentation
88
- ? parseFloat(options.maxFragmentation)
89
- : undefined,
90
- focus:
91
- (options.focus as 'fragmentation' | 'cohesion' | 'depth' | 'all') ||
92
- undefined,
93
- includeNodeModules: options.includeNodeModules,
94
- include: options.include?.split(','),
95
- exclude: options.exclude?.split(','),
96
- maxResults: options.maxResults
97
- ? parseInt(options.maxResults)
98
- : undefined,
99
- })) as any;
100
-
101
- // Optional: interactive setup to refine options for first-time users
102
- if (options.interactive) {
103
- finalOptions = await runInteractiveSetup(directory, finalOptions);
104
- }
105
-
106
- const results = await analyzeContext(finalOptions);
107
-
108
- const elapsedTime = getElapsedTime(startTime);
109
- const summary = generateSummary(results);
110
-
111
- if (options.output === 'json') {
112
- const jsonOutput = {
113
- summary,
114
- results,
115
- timestamp: new Date().toISOString(),
116
- analysisTime: elapsedTime,
117
- };
118
-
119
- const outputPath = resolveOutputPath(
120
- options.outputFile,
121
- `context-report-${new Date().toISOString().split('T')[0]}.json`,
122
- directory
123
- );
124
-
125
- handleJSONOutput(
126
- jsonOutput,
127
- outputPath,
128
- `\n✓ JSON report saved to ${outputPath}`
129
- );
130
- return;
131
- }
132
-
133
- if (options.output === 'html') {
134
- const html = generateHTMLReport(summary, results);
135
- const outputPath = resolveOutputPath(
136
- options.outputFile,
137
- `context-report-${new Date().toISOString().split('T')[0]}.html`,
138
- directory
139
- );
140
-
141
- const dir = dirname(outputPath);
142
- if (!existsSync(dir)) {
143
- mkdirSync(dir, { recursive: true });
144
- }
145
-
146
- writeFileSync(outputPath, html);
147
- console.log(chalk.green(`\n✓ HTML report saved to ${outputPath}`));
148
- return;
149
- }
150
-
151
- // Console output
152
- displayConsoleReport(
153
- summary,
154
- results,
155
- elapsedTime,
156
- finalOptions.maxResults
157
- );
158
-
159
- // Show tuning guidance after results
160
- displayTuningGuidance(results, finalOptions);
161
- } catch (error) {
162
- handleCLIError(error, 'Analysis');
163
- }
164
- });
165
-
166
- program.parse();
167
-
168
- /**
169
- * Display tuning guidance to help users adjust thresholds
170
- */
171
- function displayTuningGuidance(
172
- results: Awaited<ReturnType<typeof analyzeContext>>,
173
- options: any
174
- ): void {
175
- const issueCount = results.filter((r) => r.severity !== 'info').length;
176
-
177
- if (issueCount === 0) {
178
- console.log(
179
- chalk.green(
180
- '\n✨ No optimization opportunities found! Your code is well-structured for AI context usage.\n'
181
- )
182
- );
183
- return;
184
- }
185
-
186
- console.log(chalk.cyan('\n━'.repeat(60)));
187
- console.log(chalk.bold.white(' TUNING GUIDANCE'));
188
- console.log(chalk.cyan('━'.repeat(60) + '\n'));
189
-
190
- if (issueCount < 5) {
191
- console.log(
192
- chalk.yellow(
193
- '📊 Showing few optimization opportunities. To find more areas to improve:\n'
194
- )
195
- );
196
- console.log(
197
- chalk.dim(
198
- ' • Lower --max-depth (currently: ' +
199
- options.maxDepth +
200
- ') to catch shallower import chains'
201
- )
202
- );
203
- console.log(
204
- chalk.dim(
205
- ' • Lower --max-context (currently: ' +
206
- options.maxContextBudget.toLocaleString() +
207
- ') to catch smaller files'
208
- )
209
- );
210
- console.log(
211
- chalk.dim(
212
- ' • Raise --min-cohesion (currently: ' +
213
- (options.minCohesion * 100).toFixed(0) +
214
- '%) to be stricter about mixed concerns'
215
- )
216
- );
217
- console.log(
218
- chalk.dim(
219
- ' • Lower --max-fragmentation (currently: ' +
220
- (options.maxFragmentation * 100).toFixed(0) +
221
- '%) to catch scattered code\n'
222
- )
223
- );
224
- } else if (issueCount > 20) {
225
- console.log(
226
- chalk.yellow(
227
- '📊 Showing many opportunities. To focus on highest-impact areas:\n'
228
- )
229
- );
230
- console.log(
231
- chalk.dim(
232
- ' • Raise --max-depth (currently: ' +
233
- options.maxDepth +
234
- ') to only catch very deep chains'
235
- )
236
- );
237
- console.log(
238
- chalk.dim(
239
- ' • Raise --max-context (currently: ' +
240
- options.maxContextBudget.toLocaleString() +
241
- ') to focus on largest files'
242
- )
243
- );
244
- console.log(
245
- chalk.dim(
246
- ' • Lower --min-cohesion (currently: ' +
247
- (options.minCohesion * 100).toFixed(0) +
248
- '%) to only flag severe mixed concerns'
249
- )
250
- );
251
- console.log(
252
- chalk.dim(
253
- ' • Raise --max-fragmentation (currently: ' +
254
- (options.maxFragmentation * 100).toFixed(0) +
255
- '%) to only flag highly scattered code\n'
256
- )
257
- );
258
- } else {
259
- console.log(
260
- chalk.green(
261
- '📊 Good balance of optimization opportunities (showing ' +
262
- issueCount +
263
- ' areas)\n'
264
- )
265
- );
266
- console.log(chalk.dim(' 💡 Tip: Adjust thresholds if needed:'));
267
- console.log(
268
- chalk.dim(
269
- ' aiready-context . --max-depth N --max-context N --min-cohesion 0.X\n'
270
- )
271
- );
272
- }
273
-
274
- console.log(chalk.dim(' 📖 See README for detailed tuning guide\n'));
275
- }
276
-
277
- /**
278
- * Display formatted console report
279
- */
280
- function displayConsoleReport(
281
- summary: ReturnType<typeof generateSummary>,
282
- results: Awaited<ReturnType<typeof analyzeContext>>,
283
- elapsedTime: string,
284
- maxResults: number = 10
285
- ) {
286
- const terminalWidth = process.stdout.columns || 80;
287
- const dividerWidth = Math.min(60, terminalWidth - 2);
288
- const divider = '━'.repeat(dividerWidth);
289
-
290
- console.log(chalk.cyan(divider));
291
- console.log(chalk.bold.white(' CONTEXT ANALYSIS SUMMARY'));
292
- console.log(chalk.cyan(divider) + '\n');
293
-
294
- // Overview
295
- console.log(
296
- chalk.white(`📁 Files analyzed: ${chalk.bold(summary.totalFiles)}`)
297
- );
298
- console.log(
299
- chalk.white(
300
- `📊 Total tokens: ${chalk.bold(summary.totalTokens.toLocaleString())}`
301
- )
302
- );
303
- console.log(
304
- chalk.yellow(
305
- `💰 Avg context budget: ${chalk.bold(summary.avgContextBudget.toFixed(0))} tokens/file`
306
- )
307
- );
308
- console.log(
309
- chalk.white(`⏱ Analysis time: ${chalk.bold(elapsedTime + 's')}\n`)
310
- );
311
-
312
- // Issues summary
313
- const totalIssues =
314
- summary.criticalIssues + summary.majorIssues + summary.minorIssues;
315
- if (totalIssues > 0) {
316
- console.log(chalk.bold('⚠️ Issues Found:\n'));
317
- if (summary.criticalIssues > 0) {
318
- console.log(
319
- chalk.red(` 🔴 Critical: ${chalk.bold(summary.criticalIssues)}`)
320
- );
321
- }
322
- if (summary.majorIssues > 0) {
323
- console.log(
324
- chalk.yellow(` 🟡 Major: ${chalk.bold(summary.majorIssues)}`)
325
- );
326
- }
327
- if (summary.minorIssues > 0) {
328
- console.log(
329
- chalk.blue(` 🔵 Minor: ${chalk.bold(summary.minorIssues)}`)
330
- );
331
- }
332
- console.log(
333
- chalk.green(
334
- `\n 💡 Potential savings: ${chalk.bold(summary.totalPotentialSavings.toLocaleString())} tokens\n`
335
- )
336
- );
337
- } else {
338
- console.log(chalk.green('✅ No significant issues found!\n'));
339
- }
340
-
341
- // Import depth analysis
342
- if (summary.deepFiles.length > 0) {
343
- console.log(chalk.bold('📏 Deep Import Chains:\n'));
344
- console.log(
345
- chalk.gray(` Average depth: ${summary.avgImportDepth.toFixed(1)}`)
346
- );
347
- console.log(chalk.gray(` Maximum depth: ${summary.maxImportDepth}\n`));
348
-
349
- summary.deepFiles.slice(0, maxResults).forEach((item) => {
350
- const fileName = item.file.split('/').slice(-2).join('/');
351
- console.log(
352
- ` ${chalk.cyan('→')} ${chalk.white(fileName)} ${chalk.dim(`(depth: ${item.depth})`)}`
353
- );
354
- });
355
- console.log();
356
- }
357
-
358
- // Fragmentation analysis
359
- if (summary.fragmentedModules.length > 0) {
360
- console.log(chalk.bold('🧩 Fragmented Modules:\n'));
361
- console.log(
362
- chalk.gray(
363
- ` Average fragmentation: ${(summary.avgFragmentation * 100).toFixed(0)}%\n`
364
- )
365
- );
366
-
367
- summary.fragmentedModules.slice(0, maxResults).forEach((module) => {
368
- console.log(
369
- ` ${chalk.yellow('●')} ${chalk.white(module.domain)} - ${chalk.dim(`${module.files.length} files, ${(module.fragmentationScore * 100).toFixed(0)}% scattered`)}`
370
- );
371
- console.log(
372
- chalk.dim(
373
- ` Token cost: ${module.totalTokens.toLocaleString()}, Cohesion: ${(module.avgCohesion * 100).toFixed(0)}%`
374
- )
375
- );
376
- });
377
- console.log();
378
- }
379
-
380
- // Low cohesion files
381
- if (summary.lowCohesionFiles.length > 0) {
382
- console.log(chalk.bold('🔀 Low Cohesion Files:\n'));
383
- console.log(
384
- chalk.gray(
385
- ` Average cohesion: ${(summary.avgCohesion * 100).toFixed(0)}%\n`
386
- )
387
- );
388
-
389
- summary.lowCohesionFiles.slice(0, maxResults).forEach((item) => {
390
- const fileName = item.file.split('/').slice(-2).join('/');
391
- const scorePercent = (item.score * 100).toFixed(0);
392
- const color = item.score < 0.4 ? chalk.red : chalk.yellow;
393
- console.log(
394
- ` ${color('○')} ${chalk.white(fileName)} ${chalk.dim(`(${scorePercent}% cohesion)`)}`
395
- );
396
- });
397
- console.log();
398
- }
399
-
400
- // Top expensive files
401
- if (summary.topExpensiveFiles.length > 0) {
402
- console.log(chalk.bold('💸 Most Expensive Files (Context Budget):\n'));
403
-
404
- summary.topExpensiveFiles.slice(0, maxResults).forEach((item) => {
405
- const fileName = item.file.split('/').slice(-2).join('/');
406
- const severityColor =
407
- item.severity === 'critical'
408
- ? chalk.red
409
- : item.severity === 'major'
410
- ? chalk.yellow
411
- : chalk.blue;
412
-
413
- console.log(
414
- ` ${severityColor('●')} ${chalk.white(fileName)} ${chalk.dim(`- ${item.contextBudget.toLocaleString()} tokens`)}`
415
- );
416
- });
417
- console.log();
418
- }
419
-
420
- // Recommendations
421
- if (totalIssues > 0) {
422
- console.log(chalk.bold('💡 Top Recommendations:\n'));
423
-
424
- const topFiles = results
425
- .filter((r) => r.severity === 'critical' || r.severity === 'major')
426
- .slice(0, 3);
427
-
428
- topFiles.forEach((result, index) => {
429
- const fileName = result.file.split('/').slice(-2).join('/');
430
- console.log(chalk.cyan(` ${index + 1}. ${fileName}`));
431
- result.recommendations.slice(0, 2).forEach((rec) => {
432
- console.log(chalk.dim(` • ${rec}`));
433
- });
434
- });
435
- console.log();
436
- }
437
-
438
- // Footer
439
- console.log(chalk.cyan(divider));
440
- console.log(
441
- chalk.dim(
442
- '\n⭐ Like aiready? Star us on GitHub: https://github.com/caopengau/aiready-context-analyzer'
443
- )
444
- );
445
- console.log(
446
- chalk.dim(
447
- '🐛 Found a bug? Report it: https://github.com/caopengau/aiready-context-analyzer/issues\n'
448
- )
449
- );
450
- }
451
-
452
- /**
453
- * Generate HTML report
454
- */
455
- function generateHTMLReport(
456
- summary: ReturnType<typeof generateSummary>,
457
- results: Awaited<ReturnType<typeof analyzeContext>>
458
- ): string {
459
- const totalIssues =
460
- summary.criticalIssues + summary.majorIssues + summary.minorIssues;
461
-
462
- // 'results' may be used in templates later; reference to avoid lint warnings
463
- void results;
464
-
465
- return `<!DOCTYPE html>
466
- <html lang="en">
467
- <head>
468
- <meta charset="UTF-8">
469
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
470
- <title>aiready Context Analysis Report</title>
471
- <style>
472
- body {
473
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
474
- line-height: 1.6;
475
- color: #333;
476
- max-width: 1200px;
477
- margin: 0 auto;
478
- padding: 20px;
479
- background-color: #f5f5f5;
480
- }
481
- h1, h2, h3 { color: #2c3e50; }
482
- .header {
483
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
484
- color: white;
485
- padding: 30px;
486
- border-radius: 8px;
487
- margin-bottom: 30px;
488
- }
489
- .summary {
490
- display: grid;
491
- grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
492
- gap: 20px;
493
- margin-bottom: 30px;
494
- }
495
- .card {
496
- background: white;
497
- padding: 20px;
498
- border-radius: 8px;
499
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
500
- }
501
- .metric {
502
- font-size: 2em;
503
- font-weight: bold;
504
- color: #667eea;
505
- }
506
- .label {
507
- color: #666;
508
- font-size: 0.9em;
509
- margin-top: 5px;
510
- }
511
- .issue-critical { color: #e74c3c; }
512
- .issue-major { color: #f39c12; }
513
- .issue-minor { color: #3498db; }
514
- table {
515
- width: 100%;
516
- border-collapse: collapse;
517
- background: white;
518
- border-radius: 8px;
519
- overflow: hidden;
520
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
521
- }
522
- th, td {
523
- padding: 12px;
524
- text-align: left;
525
- border-bottom: 1px solid #eee;
526
- }
527
- th {
528
- background-color: #667eea;
529
- color: white;
530
- font-weight: 600;
531
- }
532
- tr:hover { background-color: #f8f9fa; }
533
- .footer {
534
- text-align: center;
535
- margin-top: 40px;
536
- padding: 20px;
537
- color: #666;
538
- font-size: 0.9em;
539
- }
540
- </style>
541
- </head>
542
- <body>
543
- <div class="header">
544
- <h1>🔍 AIReady Context Analysis Report</h1>
545
- <p>Generated on ${new Date().toLocaleString()}</p>
546
- </div>
547
-
548
- <div class="summary">
549
- <div class="card">
550
- <div class="metric">${summary.totalFiles}</div>
551
- <div class="label">Files Analyzed</div>
552
- </div>
553
- <div class="card">
554
- <div class="metric">${summary.totalTokens.toLocaleString()}</div>
555
- <div class="label">Total Tokens</div>
556
- </div>
557
- <div class="card">
558
- <div class="metric">${summary.avgContextBudget.toFixed(0)}</div>
559
- <div class="label">Avg Context Budget</div>
560
- </div>
561
- <div class="card">
562
- <div class="metric ${totalIssues > 0 ? 'issue-major' : ''}">${totalIssues}</div>
563
- <div class="label">Total Issues</div>
564
- </div>
565
- </div>
566
-
567
- ${
568
- totalIssues > 0
569
- ? `
570
- <div class="card" style="margin-bottom: 30px;">
571
- <h2>⚠️ Issues Summary</h2>
572
- <p>
573
- <span class="issue-critical">🔴 Critical: ${summary.criticalIssues}</span> &nbsp;
574
- <span class="issue-major">🟡 Major: ${summary.majorIssues}</span> &nbsp;
575
- <span class="issue-minor">🔵 Minor: ${summary.minorIssues}</span>
576
- </p>
577
- <p><strong>Potential Savings:</strong> ${summary.totalPotentialSavings.toLocaleString()} tokens</p>
578
- </div>
579
- `
580
- : ''
581
- }
582
-
583
- ${
584
- summary.fragmentedModules.length > 0
585
- ? `
586
- <div class="card" style="margin-bottom: 30px;">
587
- <h2>🧩 Fragmented Modules</h2>
588
- <table>
589
- <thead>
590
- <tr>
591
- <th>Domain</th>
592
- <th>Files</th>
593
- <th>Fragmentation</th>
594
- <th>Token Cost</th>
595
- </tr>
596
- </thead>
597
- <tbody>
598
- ${summary.fragmentedModules
599
- .map(
600
- (m) => `
601
- <tr>
602
- <td>${m.domain}</td>
603
- <td>${m.files.length}</td>
604
- <td>${(m.fragmentationScore * 100).toFixed(0)}%</td>
605
- <td>${m.totalTokens.toLocaleString()}</td>
606
- </tr>
607
- `
608
- )
609
- .join('')}
610
- </tbody>
611
- </table>
612
- </div>
613
- `
614
- : ''
615
- }
616
-
617
- ${
618
- summary.topExpensiveFiles.length > 0
619
- ? `
620
- <div class="card" style="margin-bottom: 30px;">
621
- <h2>💸 Most Expensive Files</h2>
622
- <table>
623
- <thead>
624
- <tr>
625
- <th>File</th>
626
- <th>Context Budget</th>
627
- <th>Severity</th>
628
- </tr>
629
- </thead>
630
- <tbody>
631
- ${summary.topExpensiveFiles
632
- .map(
633
- (f) => `
634
- <tr>
635
- <td>${f.file}</td>
636
- <td>${f.contextBudget.toLocaleString()} tokens</td>
637
- <td class="issue-${f.severity}">${f.severity.toUpperCase()}</td>
638
- </tr>
639
- `
640
- )
641
- .join('')}
642
- </tbody>
643
- </table>
644
- </div>
645
- `
646
- : ''
647
- }
648
-
649
- <div class="footer">
650
- <p>Generated by <strong>@aiready/context-analyzer</strong></p>
651
- <p>Like AIReady? <a href="https://github.com/caopengau/aiready-context-analyzer">Star us on GitHub</a></p>
652
- <p>Found a bug? <a href="https://github.com/caopengau/aiready-context-analyzer/issues">Report it here</a></p>
653
- </div>
654
- </body>
655
- </html>`;
656
- }
657
-
658
- /**
659
- * Interactive setup: detect common frameworks and suggest excludes & focus areas
660
- */
661
- async function runInteractiveSetup(
662
- directory: string,
663
- current: any
664
- ): Promise<any> {
665
- console.log(chalk.yellow('🧭 Interactive mode: let’s tailor the analysis.'));
666
-
667
- const pkgPath = join(directory, 'package.json');
668
- let deps: Record<string, string> = {};
669
- if (existsSync(pkgPath)) {
670
- try {
671
- const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
672
- deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
673
- } catch (e) {
674
- void e;
675
- // Ignore parse errors, use empty deps
676
- }
677
- }
678
-
679
- const hasNextJs = existsSync(join(directory, '.next')) || !!deps['next'];
680
- const hasCDK =
681
- existsSync(join(directory, 'cdk.out')) ||
682
- !!deps['aws-cdk-lib'] ||
683
- Object.keys(deps).some((d) => d.startsWith('@aws-cdk/'));
684
-
685
- const recommendedExcludes = new Set<string>(current.exclude || []);
686
- if (
687
- hasNextJs &&
688
- !Array.from(recommendedExcludes).some((p) => p.includes('.next'))
689
- ) {
690
- recommendedExcludes.add('**/.next/**');
691
- }
692
- if (
693
- hasCDK &&
694
- !Array.from(recommendedExcludes).some((p) => p.includes('cdk.out'))
695
- ) {
696
- recommendedExcludes.add('**/cdk.out/**');
697
- }
698
-
699
- const { applyExcludes } = await prompts({
700
- type: 'toggle',
701
- name: 'applyExcludes',
702
- message: `Detected ${hasNextJs ? 'Next.js ' : ''}${hasCDK ? 'AWS CDK ' : ''}frameworks. Apply recommended excludes?`,
703
- initial: true,
704
- active: 'yes',
705
- inactive: 'no',
706
- });
707
-
708
- const nextOptions = { ...current };
709
- if (applyExcludes) {
710
- nextOptions.exclude = Array.from(recommendedExcludes);
711
- }
712
-
713
- const { focusArea } = await prompts({
714
- type: 'select',
715
- name: 'focusArea',
716
- message: 'Which areas to focus?',
717
- choices: [
718
- { title: 'Frontend (web app)', value: 'frontend' },
719
- { title: 'Backend (API/infra)', value: 'backend' },
720
- { title: 'Both', value: 'both' },
721
- ],
722
- initial: 2,
723
- });
724
-
725
- if (focusArea === 'frontend') {
726
- nextOptions.include = ['**/*.{ts,tsx,js,jsx}'];
727
- nextOptions.exclude = Array.from(
728
- new Set([
729
- ...(nextOptions.exclude || []),
730
- '**/cdk.out/**',
731
- '**/infra/**',
732
- '**/server/**',
733
- '**/backend/**',
734
- ])
735
- );
736
- } else if (focusArea === 'backend') {
737
- nextOptions.include = [
738
- '**/api/**',
739
- '**/server/**',
740
- '**/backend/**',
741
- '**/infra/**',
742
- '**/*.{ts,js,py,java}',
743
- ];
744
- }
65
+ .action(contextActionHandler);
745
66
 
746
- console.log(chalk.green('✓ Interactive configuration applied.'));
747
- return nextOptions;
748
- }
67
+ program.parse(process.argv);