@gotza02/sequential-thinking 10000.1.6 → 10000.1.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.
@@ -0,0 +1,779 @@
1
+ /**
2
+ * INTELLIGENT CODE MODULE
3
+ * AI-powered code analysis, semantic search, and smart refactoring
4
+ */
5
+ import * as fs from 'fs/promises';
6
+ import * as path from 'path';
7
+ import { exec } from 'child_process';
8
+ import { promisify } from 'util';
9
+ import ts from 'typescript';
10
+ import { validatePath } from './utils.js';
11
+ const execAsync = promisify(exec);
12
+ // ============= Intelligent Code Analyzer =============
13
+ export class IntelligentCodeAnalyzer {
14
+ graph;
15
+ program;
16
+ typeChecker;
17
+ constructor(graph) {
18
+ this.graph = graph;
19
+ }
20
+ /**
21
+ * Comprehensive code analysis with metrics and quality scoring
22
+ */
23
+ async analyzeFile(filePath) {
24
+ const absolutePath = validatePath(filePath);
25
+ const content = await fs.readFile(absolutePath, 'utf-8');
26
+ // Parse AST
27
+ const sourceFile = ts.createSourceFile(absolutePath, content, ts.ScriptTarget.Latest, true);
28
+ // Calculate metrics
29
+ const metrics = this.calculateMetrics(sourceFile, content);
30
+ // Analyze quality
31
+ const quality = await this.analyzeQuality(sourceFile, content, metrics);
32
+ // Extract symbols with analysis
33
+ const symbols = this.analyzeSymbols(sourceFile);
34
+ return { metrics, quality, ast: sourceFile, symbols };
35
+ }
36
+ calculateMetrics(sourceFile, content) {
37
+ let complexity = 0;
38
+ let functionCount = 0;
39
+ let maxNestingDepth = 0;
40
+ let currentDepth = 0;
41
+ const visit = (node) => {
42
+ // Cyclomatic complexity
43
+ if (ts.isIfStatement(node) ||
44
+ ts.isConditionalExpression(node) ||
45
+ ts.isWhileStatement(node) ||
46
+ ts.isForStatement(node) ||
47
+ ts.isForInStatement(node) ||
48
+ ts.isForOfStatement(node) ||
49
+ ts.isCaseClause(node) ||
50
+ (ts.isBinaryExpression(node) &&
51
+ (node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken ||
52
+ node.operatorToken.kind === ts.SyntaxKind.BarBarToken))) {
53
+ complexity++;
54
+ }
55
+ // Function count
56
+ if (ts.isFunctionDeclaration(node) ||
57
+ ts.isArrowFunction(node) ||
58
+ ts.isMethodDeclaration(node)) {
59
+ functionCount++;
60
+ }
61
+ // Nesting depth
62
+ if (ts.isBlock(node) ||
63
+ ts.isIfStatement(node) ||
64
+ ts.isForStatement(node) ||
65
+ ts.isWhileStatement(node)) {
66
+ currentDepth++;
67
+ maxNestingDepth = Math.max(maxNestingDepth, currentDepth);
68
+ }
69
+ ts.forEachChild(node, visit);
70
+ if (ts.isBlock(node) ||
71
+ ts.isIfStatement(node) ||
72
+ ts.isForStatement(node) ||
73
+ ts.isWhileStatement(node)) {
74
+ currentDepth--;
75
+ }
76
+ };
77
+ visit(sourceFile);
78
+ const lines = content.split('\n');
79
+ const codeLines = lines.filter(l => l.trim().length > 0 && !l.trim().startsWith('//'));
80
+ const commentLines = lines.filter(l => l.trim().startsWith('//') || l.trim().startsWith('/*'));
81
+ return {
82
+ complexity: complexity + 1, // Base complexity is 1
83
+ linesOfCode: codeLines.length,
84
+ commentRatio: codeLines.length > 0 ? commentLines.length / codeLines.length : 0,
85
+ functionCount,
86
+ maxNestingDepth,
87
+ duplicateCode: 0, // Would need cross-file analysis
88
+ };
89
+ }
90
+ async analyzeQuality(sourceFile, content, metrics) {
91
+ const issues = [];
92
+ // Complexity checks
93
+ if (metrics.complexity > 20) {
94
+ issues.push({
95
+ type: 'warning',
96
+ category: 'complexity',
97
+ message: `High cyclomatic complexity (${metrics.complexity}). Consider refactoring.`,
98
+ severity: 7,
99
+ suggestion: 'Extract smaller functions or simplify conditional logic',
100
+ autoFixable: false,
101
+ });
102
+ }
103
+ if (metrics.maxNestingDepth > 4) {
104
+ issues.push({
105
+ type: 'warning',
106
+ category: 'maintainability',
107
+ message: `Deep nesting detected (${metrics.maxNestingDepth} levels)`,
108
+ severity: 6,
109
+ suggestion: 'Use early returns or extract nested logic into functions',
110
+ autoFixable: false,
111
+ });
112
+ }
113
+ // Comment ratio
114
+ if (metrics.commentRatio < 0.05 && metrics.linesOfCode > 50) {
115
+ issues.push({
116
+ type: 'info',
117
+ category: 'maintainability',
118
+ message: 'Low comment ratio. Consider adding documentation.',
119
+ severity: 3,
120
+ suggestion: 'Add JSDoc comments for public APIs',
121
+ autoFixable: false,
122
+ });
123
+ }
124
+ // Function length
125
+ const visit = (node) => {
126
+ if (ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node)) {
127
+ const start = node.getStart(sourceFile);
128
+ const end = node.getEnd();
129
+ const functionLines = content.substring(start, end).split('\n').length;
130
+ if (functionLines > 50) {
131
+ issues.push({
132
+ type: 'warning',
133
+ category: 'maintainability',
134
+ message: `Long function detected (${functionLines} lines)`,
135
+ line: ts.getLineAndCharacterOfPosition(sourceFile, node.getStart()).line + 1,
136
+ severity: 6,
137
+ suggestion: 'Extract helper functions to reduce function length',
138
+ autoFixable: false,
139
+ });
140
+ }
141
+ }
142
+ ts.forEachChild(node, visit);
143
+ };
144
+ visit(sourceFile);
145
+ // Check for any types
146
+ const anyTypeRegex = /:\s*any\b/g;
147
+ const anyMatches = content.match(anyTypeRegex);
148
+ if (anyMatches && anyMatches.length > 5) {
149
+ issues.push({
150
+ type: 'warning',
151
+ category: 'maintainability',
152
+ message: `Excessive use of 'any' type (${anyMatches.length} occurrences)`,
153
+ severity: 5,
154
+ suggestion: 'Replace with proper types or use unknown with type guards',
155
+ autoFixable: false,
156
+ });
157
+ }
158
+ // Calculate scores
159
+ const maintainability = Math.max(0, 100 - metrics.complexity * 2 - metrics.maxNestingDepth * 5);
160
+ const reliability = Math.max(0, 100 - (anyMatches?.length || 0) * 5);
161
+ const security = 100; // Would need security-specific checks
162
+ const performance = 100; // Would need performance analysis
163
+ const issuePenalty = issues.reduce((sum, i) => sum + i.severity * 2, 0);
164
+ const overall = Math.max(0, Math.min(100, (maintainability + reliability + security + performance) / 4 - issuePenalty));
165
+ return {
166
+ overall: Math.round(overall),
167
+ maintainability: Math.round(maintainability),
168
+ reliability: Math.round(reliability),
169
+ security,
170
+ performance,
171
+ issues: issues.sort((a, b) => b.severity - a.severity),
172
+ };
173
+ }
174
+ analyzeSymbols(sourceFile) {
175
+ const symbols = [];
176
+ const visit = (node) => {
177
+ if (ts.isFunctionDeclaration(node) && node.name) {
178
+ const symbol = this.analyzeFunction(node, sourceFile);
179
+ if (symbol)
180
+ symbols.push(symbol);
181
+ }
182
+ else if (ts.isClassDeclaration(node) && node.name) {
183
+ const symbol = this.analyzeClass(node, sourceFile);
184
+ if (symbol)
185
+ symbols.push(symbol);
186
+ }
187
+ else if (ts.isInterfaceDeclaration(node)) {
188
+ const symbol = this.analyzeInterface(node, sourceFile);
189
+ if (symbol)
190
+ symbols.push(symbol);
191
+ }
192
+ ts.forEachChild(node, visit);
193
+ };
194
+ visit(sourceFile);
195
+ return symbols;
196
+ }
197
+ analyzeFunction(node, sourceFile) {
198
+ if (!node.name)
199
+ return null;
200
+ const params = node.parameters.map(p => ({
201
+ name: p.name.getText(sourceFile),
202
+ type: p.type?.getText(sourceFile) || 'unknown',
203
+ optional: !!p.questionToken,
204
+ }));
205
+ const returnType = node.type?.getText(sourceFile) || 'inferred';
206
+ // Check for async
207
+ const isAsync = node.modifiers?.some(m => m.kind === ts.SyntaxKind.AsyncKeyword);
208
+ // Calculate function complexity
209
+ let complexity = 1;
210
+ const countComplexity = (n) => {
211
+ if (ts.isIfStatement(n) ||
212
+ ts.isWhileStatement(n) ||
213
+ ts.isForStatement(n) ||
214
+ ts.isConditionalExpression(n)) {
215
+ complexity++;
216
+ }
217
+ ts.forEachChild(n, countComplexity);
218
+ };
219
+ countComplexity(node);
220
+ return {
221
+ name: node.name.text,
222
+ kind: 'function',
223
+ params,
224
+ returnType,
225
+ isAsync,
226
+ complexity,
227
+ isExported: node.modifiers?.some(m => m.kind === ts.SyntaxKind.ExportKeyword) || false,
228
+ documentation: this.extractJSDoc(node, sourceFile),
229
+ };
230
+ }
231
+ analyzeClass(node, sourceFile) {
232
+ if (!node.name)
233
+ return null;
234
+ const methods = [];
235
+ const properties = [];
236
+ node.members.forEach(member => {
237
+ if (ts.isMethodDeclaration(member) && member.name) {
238
+ methods.push({
239
+ name: member.name.getText(sourceFile),
240
+ kind: 'method',
241
+ params: member.parameters.map(p => ({
242
+ name: p.name.getText(sourceFile),
243
+ type: p.type?.getText(sourceFile) || 'unknown',
244
+ })),
245
+ returnType: member.type?.getText(sourceFile) || 'void',
246
+ isAsync: member.modifiers?.some(m => m.kind === ts.SyntaxKind.AsyncKeyword) || false,
247
+ complexity: 1,
248
+ isExported: false,
249
+ });
250
+ }
251
+ else if (ts.isPropertyDeclaration(member) && member.name) {
252
+ properties.push({
253
+ name: member.name.getText(sourceFile),
254
+ type: member.type?.getText(sourceFile) || 'unknown',
255
+ });
256
+ }
257
+ });
258
+ return {
259
+ name: node.name.text,
260
+ kind: 'class',
261
+ methods,
262
+ properties,
263
+ isExported: node.modifiers?.some(m => m.kind === ts.SyntaxKind.ExportKeyword) || false,
264
+ documentation: this.extractJSDoc(node, sourceFile),
265
+ };
266
+ }
267
+ analyzeInterface(node, sourceFile) {
268
+ const properties = node.members.map(member => {
269
+ if (ts.isPropertySignature(member) && member.name) {
270
+ return {
271
+ name: member.name.getText(sourceFile),
272
+ type: member.type?.getText(sourceFile) || 'unknown',
273
+ optional: !!member.questionToken,
274
+ };
275
+ }
276
+ return null;
277
+ }).filter((p) => p !== null);
278
+ return {
279
+ name: node.name.text,
280
+ kind: 'interface',
281
+ properties,
282
+ isExported: node.modifiers?.some(m => m.kind === ts.SyntaxKind.ExportKeyword) || false,
283
+ documentation: this.extractJSDoc(node, sourceFile),
284
+ };
285
+ }
286
+ extractJSDoc(node, sourceFile) {
287
+ const jsDoc = node.jsDoc;
288
+ if (jsDoc && jsDoc.length > 0) {
289
+ return jsDoc[0].comment?.toString();
290
+ }
291
+ return undefined;
292
+ }
293
+ /**
294
+ * Smart impact analysis - predicts what will be affected by changes
295
+ */
296
+ async analyzeImpact(filePath, changeDescription) {
297
+ const absolutePath = validatePath(filePath);
298
+ // Get graph relationships
299
+ const context = this.graph.getDeepContext(filePath);
300
+ if (!context) {
301
+ await this.graph.build(process.cwd());
302
+ }
303
+ const relationships = this.graph.getRelationships(filePath);
304
+ if (!relationships) {
305
+ return {
306
+ filePath,
307
+ directImpacts: [],
308
+ indirectImpacts: [],
309
+ testFiles: [],
310
+ riskScore: 0,
311
+ breakingChanges: false,
312
+ estimatedReviewTime: 0,
313
+ };
314
+ }
315
+ // Direct impacts: files that import this file
316
+ const directImpacts = relationships.importedBy;
317
+ // Find indirect impacts (files that import the direct impacts)
318
+ const indirectImpacts = [];
319
+ for (const directFile of directImpacts) {
320
+ const directRel = this.graph.getRelationships(directFile);
321
+ if (directRel) {
322
+ for (const indirect of directRel.importedBy) {
323
+ if (indirect !== filePath && !directImpacts.includes(indirect)) {
324
+ indirectImpacts.push(indirect);
325
+ }
326
+ }
327
+ }
328
+ }
329
+ // Find related test files
330
+ const testFiles = await this.findRelatedTests(filePath);
331
+ // Calculate risk score
332
+ let riskScore = 0;
333
+ // Base risk from number of dependents
334
+ riskScore += directImpacts.length * 10;
335
+ riskScore += indirectImpacts.length * 5;
336
+ // Higher risk if changing public API
337
+ if (relationships.symbols.some(s => s.includes('export'))) {
338
+ riskScore += 20;
339
+ }
340
+ // Check change description for risky keywords
341
+ if (changeDescription) {
342
+ const riskyKeywords = ['delete', 'remove', 'rename', 'breaking', 'api'];
343
+ if (riskyKeywords.some(k => changeDescription.toLowerCase().includes(k))) {
344
+ riskScore += 15;
345
+ }
346
+ }
347
+ const breakingChanges = riskScore > 50;
348
+ const estimatedReviewTime = Math.min(120, 5 + directImpacts.length * 2 + indirectImpacts.length + testFiles.length * 3);
349
+ return {
350
+ filePath,
351
+ directImpacts,
352
+ indirectImpacts: [...new Set(indirectImpacts)],
353
+ testFiles,
354
+ riskScore: Math.min(100, riskScore),
355
+ breakingChanges,
356
+ estimatedReviewTime,
357
+ };
358
+ }
359
+ async findRelatedTests(filePath) {
360
+ const testFiles = [];
361
+ const baseName = path.basename(filePath, path.extname(filePath));
362
+ const dir = path.dirname(filePath);
363
+ // Common test file patterns
364
+ const patterns = [
365
+ `${baseName}.test.ts`,
366
+ `${baseName}.spec.ts`,
367
+ `${baseName}.test.js`,
368
+ `${baseName}.spec.js`,
369
+ ];
370
+ for (const pattern of patterns) {
371
+ const testPath = path.join(dir, pattern);
372
+ try {
373
+ await fs.access(testPath);
374
+ testFiles.push(testPath);
375
+ }
376
+ catch {
377
+ // File doesn't exist
378
+ }
379
+ }
380
+ // Check for __tests__ directory
381
+ const testsDir = path.join(dir, '__tests__');
382
+ try {
383
+ const files = await fs.readdir(testsDir);
384
+ files.forEach(f => {
385
+ if (f.includes(baseName) && (f.endsWith('.test.ts') || f.endsWith('.spec.ts'))) {
386
+ testFiles.push(path.join(testsDir, f));
387
+ }
388
+ });
389
+ }
390
+ catch {
391
+ // Directory doesn't exist
392
+ }
393
+ return testFiles;
394
+ }
395
+ /**
396
+ * Generate intelligent refactoring suggestions
397
+ */
398
+ async suggestRefactoring(filePath) {
399
+ const { ast, metrics, quality } = await this.analyzeFile(filePath);
400
+ if (!ast)
401
+ return [];
402
+ const suggestions = [];
403
+ // Suggest extracting long functions
404
+ if (metrics.complexity > 10) {
405
+ const longFunctions = this.findLongFunctions(ast);
406
+ for (const func of longFunctions) {
407
+ suggestions.push({
408
+ type: 'extract-method',
409
+ description: `Extract ${func.name} into smaller functions`,
410
+ currentCode: func.code,
411
+ suggestedCode: `// Suggested: Split into ${Math.ceil(func.lines / 20)} helper functions`,
412
+ confidence: 85,
413
+ impact: 'medium',
414
+ effort: 'moderate',
415
+ benefits: ['Reduced complexity', 'Better testability', 'Easier maintenance'],
416
+ });
417
+ }
418
+ }
419
+ // Simplify nested conditionals
420
+ const nestedConditionals = this.findNestedConditionals(ast);
421
+ for (const nested of nestedConditionals) {
422
+ suggestions.push({
423
+ type: 'simplify',
424
+ description: 'Simplify deeply nested conditionals',
425
+ currentCode: nested.code,
426
+ suggestedCode: '// Use early returns or extract into guard clauses',
427
+ confidence: 90,
428
+ impact: 'low',
429
+ effort: 'quick',
430
+ benefits: ['Improved readability', 'Reduced cognitive load'],
431
+ });
432
+ }
433
+ // Add types for any
434
+ const anyUsages = this.findAnyUsages(ast);
435
+ if (anyUsages.length > 0) {
436
+ suggestions.push({
437
+ type: 'add-types',
438
+ description: `Replace ${anyUsages.length} 'any' types with proper types`,
439
+ currentCode: anyUsages.map(a => a.code).join('\n'),
440
+ suggestedCode: '// Define proper interfaces or use unknown with type guards',
441
+ confidence: 80,
442
+ impact: 'medium',
443
+ effort: 'moderate',
444
+ benefits: ['Type safety', 'Better IDE support', 'Fewer runtime errors'],
445
+ });
446
+ }
447
+ return suggestions.sort((a, b) => b.confidence - a.confidence);
448
+ }
449
+ findLongFunctions(sourceFile) {
450
+ const functions = [];
451
+ const content = sourceFile.getFullText();
452
+ const visit = (node) => {
453
+ if (ts.isFunctionDeclaration(node) && node.name) {
454
+ const start = node.getStart(sourceFile);
455
+ const end = node.getEnd();
456
+ const code = content.substring(start, end);
457
+ const lines = code.split('\n').length;
458
+ if (lines > 30) {
459
+ functions.push({
460
+ name: node.name.text,
461
+ lines,
462
+ code: code.substring(0, 200) + '...',
463
+ });
464
+ }
465
+ }
466
+ ts.forEachChild(node, visit);
467
+ };
468
+ visit(sourceFile);
469
+ return functions;
470
+ }
471
+ findNestedConditionals(sourceFile) {
472
+ const nested = [];
473
+ const content = sourceFile.getFullText();
474
+ const visit = (node, depth = 0) => {
475
+ if (ts.isIfStatement(node)) {
476
+ if (depth > 3) {
477
+ const code = content.substring(node.getStart(sourceFile), node.getEnd());
478
+ nested.push({ depth, code: code.substring(0, 150) + '...' });
479
+ }
480
+ ts.forEachChild(node, n => visit(n, depth + 1));
481
+ }
482
+ else {
483
+ ts.forEachChild(node, n => visit(n, depth));
484
+ }
485
+ };
486
+ visit(sourceFile);
487
+ return nested;
488
+ }
489
+ findAnyUsages(sourceFile) {
490
+ const usages = [];
491
+ const content = sourceFile.getFullText();
492
+ const visit = (node) => {
493
+ if (ts.isTypeReferenceNode(node) && node.typeName.getText(sourceFile) === 'any') {
494
+ usages.push({
495
+ code: content.substring(node.getStart(sourceFile), node.getEnd()),
496
+ });
497
+ }
498
+ ts.forEachChild(node, visit);
499
+ };
500
+ visit(sourceFile);
501
+ return usages;
502
+ }
503
+ /**
504
+ * Semantic code search - find code by concept, not just text
505
+ */
506
+ async semanticSearch(query, options = {}) {
507
+ const { filePattern = '*', maxResults = 10 } = options;
508
+ // Build query concepts
509
+ const concepts = this.extractConcepts(query);
510
+ // Search through codebase
511
+ const files = await this.getMatchingFiles(filePattern);
512
+ const results = [];
513
+ for (const filePath of files) {
514
+ try {
515
+ const content = await fs.readFile(filePath, 'utf-8');
516
+ const relevance = this.calculateRelevance(content, concepts, query);
517
+ if (relevance > 0.3) {
518
+ const snippet = this.extractRelevantSnippet(content, concepts);
519
+ results.push({
520
+ filePath,
521
+ relevance,
522
+ context: this.extractContext(content, snippet),
523
+ matchedConcepts: concepts.filter(c => content.toLowerCase().includes(c.toLowerCase())),
524
+ codeSnippet: snippet,
525
+ });
526
+ }
527
+ }
528
+ catch {
529
+ // Skip files that can't be read
530
+ }
531
+ }
532
+ return results
533
+ .sort((a, b) => b.relevance - a.relevance)
534
+ .slice(0, maxResults);
535
+ }
536
+ extractConcepts(query) {
537
+ // Extract key concepts from query
538
+ const stopWords = new Set(['the', 'a', 'an', 'in', 'on', 'at', 'to', 'for', 'of', 'and', 'or']);
539
+ return query
540
+ .toLowerCase()
541
+ .split(/\s+/)
542
+ .filter(w => w.length > 2 && !stopWords.has(w))
543
+ .map(w => w.replace(/[^a-z0-9]/g, ''));
544
+ }
545
+ calculateRelevance(content, concepts, originalQuery) {
546
+ const lowerContent = content.toLowerCase();
547
+ let score = 0;
548
+ // Concept matching
549
+ for (const concept of concepts) {
550
+ const count = (lowerContent.match(new RegExp(concept, 'g')) || []).length;
551
+ score += Math.min(count * 0.1, 0.5);
552
+ }
553
+ // Check for function/variable names matching query
554
+ const camelCaseQuery = originalQuery.replace(/\s+(.)/g, (_, c) => c.toUpperCase());
555
+ const snakeCaseQuery = originalQuery.replace(/\s+/g, '_');
556
+ if (content.includes(camelCaseQuery) || content.includes(snakeCaseQuery)) {
557
+ score += 0.3;
558
+ }
559
+ // Boost for TypeScript/JavaScript files
560
+ if (content.includes('function') || content.includes('const') || content.includes('export')) {
561
+ score += 0.1;
562
+ }
563
+ return Math.min(1, score);
564
+ }
565
+ extractRelevantSnippet(content, concepts) {
566
+ const lines = content.split('\n');
567
+ let bestStart = 0;
568
+ let bestScore = 0;
569
+ // Find window with highest concept density
570
+ for (let i = 0; i < lines.length - 10; i++) {
571
+ const window = lines.slice(i, i + 10).join('\n').toLowerCase();
572
+ const score = concepts.filter(c => window.includes(c)).length;
573
+ if (score > bestScore) {
574
+ bestScore = score;
575
+ bestStart = i;
576
+ }
577
+ }
578
+ return lines.slice(bestStart, bestStart + 15).join('\n');
579
+ }
580
+ extractContext(content, snippet) {
581
+ const lines = content.split('\n');
582
+ const snippetLines = snippet.split('\n');
583
+ const firstLine = snippetLines[0];
584
+ // Try to find class/function name
585
+ for (let i = 0; i < lines.length; i++) {
586
+ if (lines[i] === firstLine) {
587
+ // Look backwards for function/class declaration
588
+ for (let j = Math.max(0, i - 5); j < i; j++) {
589
+ const match = lines[j].match(/(?:export\s+)?(?:class|function|interface)\s+(\w+)/);
590
+ if (match) {
591
+ return `${match[0]} (line ${j + 1})`;
592
+ }
593
+ }
594
+ }
595
+ }
596
+ return 'Global scope';
597
+ }
598
+ async getMatchingFiles(pattern) {
599
+ // Simple glob-like matching
600
+ const files = [];
601
+ try {
602
+ const { stdout } = await execAsync(`find . -type f -name "${pattern}" ! -path "*/node_modules/*" ! -path "*/.git/*" | head -100`, { cwd: process.cwd() });
603
+ return stdout.trim().split('\n').filter(Boolean);
604
+ }
605
+ catch {
606
+ return [];
607
+ }
608
+ }
609
+ /**
610
+ * Auto-fix common code issues
611
+ */
612
+ async autoFix(filePath, options = {}) {
613
+ const absolutePath = validatePath(filePath);
614
+ const content = await fs.readFile(absolutePath, 'utf-8');
615
+ const lines = content.split('\n');
616
+ const fixes = [];
617
+ let modifiedContent = content;
618
+ // Fix 1: Remove trailing whitespace
619
+ if (options.fixTrailingWhitespace !== false) {
620
+ for (let i = 0; i < lines.length; i++) {
621
+ const line = lines[i];
622
+ const trimmed = line.replace(/\s+$/, '');
623
+ if (line !== trimmed) {
624
+ fixes.push({
625
+ type: 'trailing-whitespace',
626
+ line: i + 1,
627
+ original: line,
628
+ fixed: trimmed,
629
+ });
630
+ lines[i] = trimmed;
631
+ }
632
+ }
633
+ modifiedContent = lines.join('\n');
634
+ }
635
+ // Fix 2: Add missing semicolons (simple heuristic)
636
+ if (options.fixMissingSemicolons) {
637
+ const semicolonPattern = /^(\s*)(.+?)(?<!;)(\s*)$/;
638
+ for (let i = 0; i < lines.length; i++) {
639
+ const line = lines[i];
640
+ // Skip comments, strings, imports, exports, function declarations, etc.
641
+ if (line.trim().startsWith('//') ||
642
+ line.trim().startsWith('/*') ||
643
+ line.trim().startsWith('*') ||
644
+ line.trim().startsWith('import') ||
645
+ line.trim().startsWith('export') ||
646
+ line.trim().startsWith('function') ||
647
+ line.trim().startsWith('async function') ||
648
+ line.trim().startsWith('class') ||
649
+ line.trim().startsWith('interface') ||
650
+ line.trim().startsWith('type') ||
651
+ line.trim().startsWith('if') ||
652
+ line.trim().startsWith('for') ||
653
+ line.trim().startsWith('while') ||
654
+ line.trim().startsWith('switch') ||
655
+ line.trim().startsWith('try') ||
656
+ line.trim().startsWith('catch') ||
657
+ line.trim().startsWith('finally') ||
658
+ line.trim().startsWith('{') ||
659
+ line.trim().startsWith('}') ||
660
+ line.trim().endsWith('{') ||
661
+ line.trim().endsWith('}') ||
662
+ line.trim().endsWith(';') ||
663
+ line.trim() === '') {
664
+ continue;
665
+ }
666
+ // Add semicolon if line looks like a statement
667
+ if (/[a-zA-Z0-9_)\]]\s*$/.test(line) && !line.trim().endsWith(';')) {
668
+ fixes.push({
669
+ type: 'missing-semicolon',
670
+ line: i + 1,
671
+ original: line,
672
+ fixed: line + ';',
673
+ });
674
+ lines[i] = line + ';';
675
+ }
676
+ }
677
+ modifiedContent = lines.join('\n');
678
+ }
679
+ // Fix 3: Optimize imports
680
+ if (options.optimizeImports) {
681
+ const importRegex = /^(import\s+(?:{[^}]*}|\*\s+as\s+\w+|\w+)\s+from\s+['"][^'"]+['"];?)$/gm;
682
+ const imports = [];
683
+ let match;
684
+ while ((match = importRegex.exec(content)) !== null) {
685
+ imports.push(match[1]);
686
+ }
687
+ if (imports.length > 0) {
688
+ // Sort imports
689
+ imports.sort((a, b) => {
690
+ const aIsRelative = a.includes("from '.'") || a.includes("from '..");
691
+ const bIsRelative = b.includes("from '.'") || b.includes("from '..");
692
+ if (aIsRelative && !bIsRelative)
693
+ return 1;
694
+ if (!aIsRelative && bIsRelative)
695
+ return -1;
696
+ return a.localeCompare(b);
697
+ });
698
+ // Remove duplicates
699
+ const uniqueImports = [...new Set(imports)];
700
+ if (uniqueImports.length !== imports.length || imports.join('\n') !== uniqueImports.join('\n')) {
701
+ fixes.push({
702
+ type: 'optimize-imports',
703
+ line: 1,
704
+ original: `Found ${imports.length} imports`,
705
+ fixed: `Optimized to ${uniqueImports.length} imports`,
706
+ });
707
+ }
708
+ }
709
+ }
710
+ // Apply fixes if not dry run
711
+ const applied = !options.dryRun && fixes.length > 0;
712
+ if (applied) {
713
+ await fs.writeFile(absolutePath, modifiedContent, 'utf-8');
714
+ }
715
+ return { fixes, applied };
716
+ }
717
+ /**
718
+ * Detect code patterns and anti-patterns
719
+ */
720
+ detectPatterns(filePath, sourceFile) {
721
+ const patterns = [];
722
+ const content = sourceFile.getFullText();
723
+ // Pattern: Singleton
724
+ const singletonPattern = /class\s+(\w+).*?private\s+static\s+instance|getInstance/s;
725
+ if (singletonPattern.test(content)) {
726
+ patterns.push({
727
+ name: 'Singleton Pattern',
728
+ description: 'Ensures a class has only one instance',
729
+ examples: ['Database connection', 'Logger', 'Configuration'],
730
+ antiPattern: 'Can make testing difficult, consider dependency injection',
731
+ solution: 'Use factory functions or dependency injection instead',
732
+ confidence: 85,
733
+ });
734
+ }
735
+ // Pattern: Factory
736
+ const factoryPattern = /create[A-Z]\w+\s*\(|make[A-Z]\w+\s*\(/;
737
+ if (factoryPattern.test(content)) {
738
+ patterns.push({
739
+ name: 'Factory Pattern',
740
+ description: 'Creates objects without specifying exact class',
741
+ examples: ['createUser()', 'makeRequest()'],
742
+ solution: 'Use dependency injection container or factory pattern',
743
+ confidence: 70,
744
+ });
745
+ }
746
+ // Anti-pattern: Callback hell
747
+ const callbackHell = /\)\s*\{[\s\S]*?\)\s*\{[\s\S]*?\)\s*\{/;
748
+ if (callbackHell.test(content)) {
749
+ patterns.push({
750
+ name: 'Callback Hell (Anti-pattern)',
751
+ description: 'Deeply nested callbacks',
752
+ examples: [],
753
+ antiPattern: 'Makes code hard to read and maintain',
754
+ solution: 'Use async/await or Promise chains',
755
+ confidence: 90,
756
+ });
757
+ }
758
+ // Pattern: Dependency Injection
759
+ const diPattern = /constructor\s*\([^)]*(?:private|public|protected)[^)]*\)/;
760
+ if (diPattern.test(content)) {
761
+ patterns.push({
762
+ name: 'Dependency Injection',
763
+ description: 'Dependencies provided through constructor',
764
+ examples: ['constructor(private db: Database)'],
765
+ solution: 'Good practice for testability and modularity',
766
+ confidence: 80,
767
+ });
768
+ }
769
+ return patterns;
770
+ }
771
+ }
772
+ // ============= Export singleton =============
773
+ let analyzerInstance = null;
774
+ export function getIntelligentAnalyzer(graph) {
775
+ if (!analyzerInstance) {
776
+ analyzerInstance = new IntelligentCodeAnalyzer(graph);
777
+ }
778
+ return analyzerInstance;
779
+ }