@aiready/context-analyzer 0.9.34 → 0.9.36

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.
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Python Context Analyzer
3
- *
3
+ *
4
4
  * Analyzes Python code for:
5
5
  * - Import chain depth
6
6
  * - Context budget (tokens needed)
@@ -54,34 +54,52 @@ export async function analyzePythonContext(
54
54
  return results;
55
55
  }
56
56
 
57
- const pythonFiles = files.filter(f => f.toLowerCase().endsWith('.py'));
57
+ const pythonFiles = files.filter((f) => f.toLowerCase().endsWith('.py'));
58
+
59
+ // Some path helpers are imported for future use; reference them to avoid lint warnings
60
+ void relative;
61
+ void join;
58
62
 
59
63
  // Build dependency graph first
60
- const dependencyGraph = await buildPythonDependencyGraph(pythonFiles, rootDir);
64
+ const dependencyGraph = await buildPythonDependencyGraph(
65
+ pythonFiles,
66
+ rootDir
67
+ );
61
68
 
62
69
  for (const file of pythonFiles) {
63
70
  try {
64
71
  const code = await fs.promises.readFile(file, 'utf-8');
65
72
  const result = parser.parse(code, file);
66
73
 
67
- const imports: PythonImportInfo[] = result.imports.map(imp => ({
74
+ const imports: PythonImportInfo[] = result.imports.map((imp) => ({
68
75
  source: imp.source,
69
76
  specifiers: imp.specifiers,
70
77
  isRelative: imp.source.startsWith('.'),
71
78
  resolvedPath: resolvePythonImport(file, imp.source, rootDir),
72
79
  }));
73
80
 
74
- const exports: PythonExportInfo[] = result.exports.map(exp => ({
81
+ const exports: PythonExportInfo[] = result.exports.map((exp) => ({
75
82
  name: exp.name,
76
83
  type: exp.type,
77
84
  }));
78
85
 
79
86
  // Calculate metrics
80
87
  const linesOfCode = code.split('\n').length;
81
- const importDepth = await calculatePythonImportDepth(file, dependencyGraph, new Set());
82
- const contextBudget = estimateContextBudget(code, imports, dependencyGraph);
88
+ const importDepth = await calculatePythonImportDepth(
89
+ file,
90
+ dependencyGraph,
91
+ new Set()
92
+ );
93
+ const contextBudget = estimateContextBudget(
94
+ code,
95
+ imports,
96
+ dependencyGraph
97
+ );
83
98
  const cohesion = calculatePythonCohesion(exports, imports);
84
- const circularDependencies = detectCircularDependencies(file, dependencyGraph);
99
+ const circularDependencies = detectCircularDependencies(
100
+ file,
101
+ dependencyGraph
102
+ );
85
103
 
86
104
  results.push({
87
105
  file,
@@ -133,6 +151,7 @@ async function buildPythonDependencyGraph(
133
151
 
134
152
  graph.set(file, dependencies);
135
153
  } catch (error) {
154
+ void error;
136
155
  // Skip files with errors
137
156
  }
138
157
  }
@@ -143,7 +162,11 @@ async function buildPythonDependencyGraph(
143
162
  /**
144
163
  * Resolve Python import to file path
145
164
  */
146
- function resolvePythonImport(fromFile: string, importPath: string, rootDir: string): string | undefined {
165
+ function resolvePythonImport(
166
+ fromFile: string,
167
+ importPath: string,
168
+ rootDir: string
169
+ ): string | undefined {
147
170
  const dir = dirname(fromFile);
148
171
 
149
172
  // Handle relative imports
@@ -244,7 +267,7 @@ function estimateContextBudget(
244
267
 
245
268
  /**
246
269
  * Calculate cohesion for a Python module
247
- *
270
+ *
248
271
  * Cohesion = How related are the exports to each other?
249
272
  * Higher cohesion = better (single responsibility)
250
273
  */
package/src/cli.ts CHANGED
@@ -5,7 +5,13 @@ import { analyzeContext, generateSummary } from './index';
5
5
  import chalk from 'chalk';
6
6
  import { writeFileSync, existsSync, readFileSync, mkdirSync } from 'fs';
7
7
  import { join, dirname } from 'path';
8
- import { loadMergedConfig, handleJSONOutput, handleCLIError, getElapsedTime, resolveOutputPath } from '@aiready/core';
8
+ import {
9
+ loadMergedConfig,
10
+ handleJSONOutput,
11
+ handleCLIError,
12
+ getElapsedTime,
13
+ resolveOutputPath,
14
+ } from '@aiready/core';
9
15
  import prompts from 'prompts';
10
16
 
11
17
  const program = new Command();
@@ -14,7 +20,10 @@ program
14
20
  .name('aiready-context')
15
21
  .description('Analyze AI context window cost and code structure')
16
22
  .version('0.1.0')
17
- .addHelpText('after', '\nCONFIGURATION:\n Supports config files: aiready.json, aiready.config.json, .aiready.json, .aireadyrc.json, aiready.config.js, .aireadyrc.js\n CLI options override config file settings')
23
+ .addHelpText(
24
+ 'after',
25
+ '\nCONFIGURATION:\n Supports config files: aiready.json, aiready.config.json, .aiready.json, .aireadyrc.json, aiready.config.js, .aireadyrc.js\n CLI options override config file settings'
26
+ )
18
27
  .argument('<directory>', 'Directory to analyze')
19
28
  .option('--max-depth <number>', 'Maximum acceptable import depth')
20
29
  .option(
@@ -33,14 +42,20 @@ program
33
42
  .option('--include-node-modules', 'Include node_modules in analysis')
34
43
  .option('--include <patterns>', 'File patterns to include (comma-separated)')
35
44
  .option('--exclude <patterns>', 'File patterns to exclude (comma-separated)')
36
- .option('--max-results <number>', 'Maximum number of results to show in console output')
45
+ .option(
46
+ '--max-results <number>',
47
+ 'Maximum number of results to show in console output'
48
+ )
37
49
  .option(
38
50
  '-o, --output <format>',
39
51
  'Output format: console, json, html',
40
52
  'console'
41
53
  )
42
54
  .option('--output-file <path>', 'Output file path (for json/html)')
43
- .option('--interactive', 'Run interactive setup to suggest excludes and focus areas')
55
+ .option(
56
+ '--interactive',
57
+ 'Run interactive setup to suggest excludes and focus areas'
58
+ )
44
59
  .action(async (directory, options) => {
45
60
  console.log(chalk.blue('🔍 Analyzing context window costs...\n'));
46
61
 
@@ -61,17 +76,27 @@ program
61
76
  };
62
77
 
63
78
  // Load and merge config with CLI options
64
- let finalOptions = await loadMergedConfig(directory, defaults, {
79
+ let finalOptions = (await loadMergedConfig(directory, defaults, {
65
80
  maxDepth: options.maxDepth ? parseInt(options.maxDepth) : undefined,
66
- maxContextBudget: options.maxContext ? parseInt(options.maxContext) : undefined,
67
- minCohesion: options.minCohesion ? parseFloat(options.minCohesion) : undefined,
68
- maxFragmentation: options.maxFragmentation ? parseFloat(options.maxFragmentation) : undefined,
69
- focus: (options.focus as 'fragmentation' | 'cohesion' | 'depth' | 'all') || 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,
70
93
  includeNodeModules: options.includeNodeModules,
71
94
  include: options.include?.split(','),
72
95
  exclude: options.exclude?.split(','),
73
- maxResults: options.maxResults ? parseInt(options.maxResults) : undefined,
74
- }) as any;
96
+ maxResults: options.maxResults
97
+ ? parseInt(options.maxResults)
98
+ : undefined,
99
+ })) as any;
75
100
 
76
101
  // Optional: interactive setup to refine options for first-time users
77
102
  if (options.interactive) {
@@ -96,8 +121,12 @@ program
96
121
  `context-report-${new Date().toISOString().split('T')[0]}.json`,
97
122
  directory
98
123
  );
99
-
100
- handleJSONOutput(jsonOutput, outputPath, `\n✓ JSON report saved to ${outputPath}`);
124
+
125
+ handleJSONOutput(
126
+ jsonOutput,
127
+ outputPath,
128
+ `\n✓ JSON report saved to ${outputPath}`
129
+ );
101
130
  return;
102
131
  }
103
132
 
@@ -108,20 +137,25 @@ program
108
137
  `context-report-${new Date().toISOString().split('T')[0]}.html`,
109
138
  directory
110
139
  );
111
-
140
+
112
141
  const dir = dirname(outputPath);
113
142
  if (!existsSync(dir)) {
114
143
  mkdirSync(dir, { recursive: true });
115
144
  }
116
-
145
+
117
146
  writeFileSync(outputPath, html);
118
147
  console.log(chalk.green(`\n✓ HTML report saved to ${outputPath}`));
119
148
  return;
120
149
  }
121
150
 
122
151
  // Console output
123
- displayConsoleReport(summary, results, elapsedTime, finalOptions.maxResults);
124
-
152
+ displayConsoleReport(
153
+ summary,
154
+ results,
155
+ elapsedTime,
156
+ finalOptions.maxResults
157
+ );
158
+
125
159
  // Show tuning guidance after results
126
160
  displayTuningGuidance(results, finalOptions);
127
161
  } catch (error) {
@@ -138,35 +172,105 @@ function displayTuningGuidance(
138
172
  results: Awaited<ReturnType<typeof analyzeContext>>,
139
173
  options: any
140
174
  ): void {
141
- const issueCount = results.filter(r => r.severity !== 'info').length;
142
-
175
+ const issueCount = results.filter((r) => r.severity !== 'info').length;
176
+
143
177
  if (issueCount === 0) {
144
- console.log(chalk.green('\n✨ No optimization opportunities found! Your code is well-structured for AI context usage.\n'));
178
+ console.log(
179
+ chalk.green(
180
+ '\n✨ No optimization opportunities found! Your code is well-structured for AI context usage.\n'
181
+ )
182
+ );
145
183
  return;
146
184
  }
147
-
185
+
148
186
  console.log(chalk.cyan('\n━'.repeat(60)));
149
187
  console.log(chalk.bold.white(' TUNING GUIDANCE'));
150
188
  console.log(chalk.cyan('━'.repeat(60) + '\n'));
151
-
189
+
152
190
  if (issueCount < 5) {
153
- console.log(chalk.yellow('📊 Showing few optimization opportunities. To find more areas to improve:\n'));
154
- console.log(chalk.dim(' • Lower --max-depth (currently: ' + options.maxDepth + ') to catch shallower import chains'));
155
- console.log(chalk.dim(' Lower --max-context (currently: ' + options.maxContextBudget.toLocaleString() + ') to catch smaller files'));
156
- console.log(chalk.dim(' • Raise --min-cohesion (currently: ' + (options.minCohesion * 100).toFixed(0) + '%) to be stricter about mixed concerns'));
157
- console.log(chalk.dim(' • Lower --max-fragmentation (currently: ' + (options.maxFragmentation * 100).toFixed(0) + '%) to catch scattered code\n'));
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
+ );
158
224
  } else if (issueCount > 20) {
159
- console.log(chalk.yellow('📊 Showing many opportunities. To focus on highest-impact areas:\n'));
160
- console.log(chalk.dim(' • Raise --max-depth (currently: ' + options.maxDepth + ') to only catch very deep chains'));
161
- console.log(chalk.dim(' Raise --max-context (currently: ' + options.maxContextBudget.toLocaleString() + ') to focus on largest files'));
162
- console.log(chalk.dim(' • Lower --min-cohesion (currently: ' + (options.minCohesion * 100).toFixed(0) + '%) to only flag severe mixed concerns'));
163
- console.log(chalk.dim(' • Raise --max-fragmentation (currently: ' + (options.maxFragmentation * 100).toFixed(0) + '%) to only flag highly scattered code\n'));
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
+ );
164
258
  } else {
165
- console.log(chalk.green('📊 Good balance of optimization opportunities (showing ' + issueCount + ' areas)\n'));
259
+ console.log(
260
+ chalk.green(
261
+ '📊 Good balance of optimization opportunities (showing ' +
262
+ issueCount +
263
+ ' areas)\n'
264
+ )
265
+ );
166
266
  console.log(chalk.dim(' 💡 Tip: Adjust thresholds if needed:'));
167
- console.log(chalk.dim(' aiready-context . --max-depth N --max-context N --min-cohesion 0.X\n'));
267
+ console.log(
268
+ chalk.dim(
269
+ ' aiready-context . --max-depth N --max-context N --min-cohesion 0.X\n'
270
+ )
271
+ );
168
272
  }
169
-
273
+
170
274
  console.log(chalk.dim(' 📖 See README for detailed tuning guide\n'));
171
275
  }
172
276
 
@@ -188,9 +292,13 @@ function displayConsoleReport(
188
292
  console.log(chalk.cyan(divider) + '\n');
189
293
 
190
294
  // Overview
191
- console.log(chalk.white(`📁 Files analyzed: ${chalk.bold(summary.totalFiles)}`));
192
295
  console.log(
193
- chalk.white(`📊 Total tokens: ${chalk.bold(summary.totalTokens.toLocaleString())}`)
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
+ )
194
302
  );
195
303
  console.log(
196
304
  chalk.yellow(
@@ -217,7 +325,9 @@ function displayConsoleReport(
217
325
  );
218
326
  }
219
327
  if (summary.minorIssues > 0) {
220
- console.log(chalk.blue(` 🔵 Minor: ${chalk.bold(summary.minorIssues)}`));
328
+ console.log(
329
+ chalk.blue(` 🔵 Minor: ${chalk.bold(summary.minorIssues)}`)
330
+ );
221
331
  }
222
332
  console.log(
223
333
  chalk.green(
@@ -297,8 +407,8 @@ function displayConsoleReport(
297
407
  item.severity === 'critical'
298
408
  ? chalk.red
299
409
  : item.severity === 'major'
300
- ? chalk.yellow
301
- : chalk.blue;
410
+ ? chalk.yellow
411
+ : chalk.blue;
302
412
 
303
413
  console.log(
304
414
  ` ${severityColor('●')} ${chalk.white(fileName)} ${chalk.dim(`- ${item.contextBudget.toLocaleString()} tokens`)}`
@@ -333,7 +443,9 @@ function displayConsoleReport(
333
443
  )
334
444
  );
335
445
  console.log(
336
- chalk.dim('🐛 Found a bug? Report it: https://github.com/caopengau/aiready-context-analyzer/issues\n')
446
+ chalk.dim(
447
+ '🐛 Found a bug? Report it: https://github.com/caopengau/aiready-context-analyzer/issues\n'
448
+ )
337
449
  );
338
450
  }
339
451
 
@@ -347,6 +459,9 @@ function generateHTMLReport(
347
459
  const totalIssues =
348
460
  summary.criticalIssues + summary.majorIssues + summary.minorIssues;
349
461
 
462
+ // 'results' may be used in templates later; reference to avoid lint warnings
463
+ void results;
464
+
350
465
  return `<!DOCTYPE html>
351
466
  <html lang="en">
352
467
  <head>
@@ -449,7 +564,9 @@ function generateHTMLReport(
449
564
  </div>
450
565
  </div>
451
566
 
452
- ${totalIssues > 0 ? `
567
+ ${
568
+ totalIssues > 0
569
+ ? `
453
570
  <div class="card" style="margin-bottom: 30px;">
454
571
  <h2>⚠️ Issues Summary</h2>
455
572
  <p>
@@ -459,9 +576,13 @@ function generateHTMLReport(
459
576
  </p>
460
577
  <p><strong>Potential Savings:</strong> ${summary.totalPotentialSavings.toLocaleString()} tokens</p>
461
578
  </div>
462
- ` : ''}
579
+ `
580
+ : ''
581
+ }
463
582
 
464
- ${summary.fragmentedModules.length > 0 ? `
583
+ ${
584
+ summary.fragmentedModules.length > 0
585
+ ? `
465
586
  <div class="card" style="margin-bottom: 30px;">
466
587
  <h2>🧩 Fragmented Modules</h2>
467
588
  <table>
@@ -474,20 +595,28 @@ function generateHTMLReport(
474
595
  </tr>
475
596
  </thead>
476
597
  <tbody>
477
- ${summary.fragmentedModules.map(m => `
598
+ ${summary.fragmentedModules
599
+ .map(
600
+ (m) => `
478
601
  <tr>
479
602
  <td>${m.domain}</td>
480
603
  <td>${m.files.length}</td>
481
604
  <td>${(m.fragmentationScore * 100).toFixed(0)}%</td>
482
605
  <td>${m.totalTokens.toLocaleString()}</td>
483
606
  </tr>
484
- `).join('')}
607
+ `
608
+ )
609
+ .join('')}
485
610
  </tbody>
486
611
  </table>
487
612
  </div>
488
- ` : ''}
613
+ `
614
+ : ''
615
+ }
489
616
 
490
- ${summary.topExpensiveFiles.length > 0 ? `
617
+ ${
618
+ summary.topExpensiveFiles.length > 0
619
+ ? `
491
620
  <div class="card" style="margin-bottom: 30px;">
492
621
  <h2>💸 Most Expensive Files</h2>
493
622
  <table>
@@ -499,17 +628,23 @@ function generateHTMLReport(
499
628
  </tr>
500
629
  </thead>
501
630
  <tbody>
502
- ${summary.topExpensiveFiles.map(f => `
631
+ ${summary.topExpensiveFiles
632
+ .map(
633
+ (f) => `
503
634
  <tr>
504
635
  <td>${f.file}</td>
505
636
  <td>${f.contextBudget.toLocaleString()} tokens</td>
506
637
  <td class="issue-${f.severity}">${f.severity.toUpperCase()}</td>
507
638
  </tr>
508
- `).join('')}
639
+ `
640
+ )
641
+ .join('')}
509
642
  </tbody>
510
643
  </table>
511
644
  </div>
512
- ` : ''}
645
+ `
646
+ : ''
647
+ }
513
648
 
514
649
  <div class="footer">
515
650
  <p>Generated by <strong>@aiready/context-analyzer</strong></p>
@@ -523,7 +658,10 @@ function generateHTMLReport(
523
658
  /**
524
659
  * Interactive setup: detect common frameworks and suggest excludes & focus areas
525
660
  */
526
- async function runInteractiveSetup(directory: string, current: any): Promise<any> {
661
+ async function runInteractiveSetup(
662
+ directory: string,
663
+ current: any
664
+ ): Promise<any> {
527
665
  console.log(chalk.yellow('🧭 Interactive mode: let’s tailor the analysis.'));
528
666
 
529
667
  const pkgPath = join(directory, 'package.json');
@@ -532,17 +670,29 @@ async function runInteractiveSetup(directory: string, current: any): Promise<any
532
670
  try {
533
671
  const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
534
672
  deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
535
- } catch {}
673
+ } catch (e) {
674
+ void e;
675
+ // Ignore parse errors, use empty deps
676
+ }
536
677
  }
537
678
 
538
679
  const hasNextJs = existsSync(join(directory, '.next')) || !!deps['next'];
539
- const hasCDK = existsSync(join(directory, 'cdk.out')) || !!deps['aws-cdk-lib'] || Object.keys(deps).some(d => d.startsWith('@aws-cdk/'));
680
+ const hasCDK =
681
+ existsSync(join(directory, 'cdk.out')) ||
682
+ !!deps['aws-cdk-lib'] ||
683
+ Object.keys(deps).some((d) => d.startsWith('@aws-cdk/'));
540
684
 
541
685
  const recommendedExcludes = new Set<string>(current.exclude || []);
542
- if (hasNextJs && !Array.from(recommendedExcludes).some((p) => p.includes('.next'))) {
686
+ if (
687
+ hasNextJs &&
688
+ !Array.from(recommendedExcludes).some((p) => p.includes('.next'))
689
+ ) {
543
690
  recommendedExcludes.add('**/.next/**');
544
691
  }
545
- if (hasCDK && !Array.from(recommendedExcludes).some((p) => p.includes('cdk.out'))) {
692
+ if (
693
+ hasCDK &&
694
+ !Array.from(recommendedExcludes).some((p) => p.includes('cdk.out'))
695
+ ) {
546
696
  recommendedExcludes.add('**/cdk.out/**');
547
697
  }
548
698
 
@@ -555,7 +705,7 @@ async function runInteractiveSetup(directory: string, current: any): Promise<any
555
705
  inactive: 'no',
556
706
  });
557
707
 
558
- let nextOptions = { ...current };
708
+ const nextOptions = { ...current };
559
709
  if (applyExcludes) {
560
710
  nextOptions.exclude = Array.from(recommendedExcludes);
561
711
  }
@@ -574,9 +724,23 @@ async function runInteractiveSetup(directory: string, current: any): Promise<any
574
724
 
575
725
  if (focusArea === 'frontend') {
576
726
  nextOptions.include = ['**/*.{ts,tsx,js,jsx}'];
577
- nextOptions.exclude = Array.from(new Set([...(nextOptions.exclude || []), '**/cdk.out/**', '**/infra/**', '**/server/**', '**/backend/**']));
727
+ nextOptions.exclude = Array.from(
728
+ new Set([
729
+ ...(nextOptions.exclude || []),
730
+ '**/cdk.out/**',
731
+ '**/infra/**',
732
+ '**/server/**',
733
+ '**/backend/**',
734
+ ])
735
+ );
578
736
  } else if (focusArea === 'backend') {
579
- nextOptions.include = ['**/api/**', '**/server/**', '**/backend/**', '**/infra/**', '**/*.{ts,js,py,java}'];
737
+ nextOptions.include = [
738
+ '**/api/**',
739
+ '**/server/**',
740
+ '**/backend/**',
741
+ '**/infra/**',
742
+ '**/*.{ts,js,py,java}',
743
+ ];
580
744
  }
581
745
 
582
746
  console.log(chalk.green('✓ Interactive configuration applied.'));