@sun-asterisk/sunlint 1.3.2 โ†’ 1.3.3

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 (59) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +5 -3
  3. package/config/rules/enhanced-rules-registry.json +144 -33
  4. package/core/analysis-orchestrator.js +167 -42
  5. package/core/auto-performance-manager.js +243 -0
  6. package/core/cli-action-handler.js +9 -1
  7. package/core/cli-program.js +19 -5
  8. package/core/constants/defaults.js +56 -0
  9. package/core/performance-optimizer.js +271 -0
  10. package/docs/FILE_LIMITS_COMPLETION_REPORT.md +151 -0
  11. package/docs/FILE_LIMITS_EXPLANATION.md +190 -0
  12. package/docs/PERFORMANCE.md +311 -0
  13. package/docs/PERFORMANCE_MIGRATION_GUIDE.md +368 -0
  14. package/docs/PERFORMANCE_OPTIMIZATION_PLAN.md +255 -0
  15. package/docs/QUICK_FILE_LIMITS.md +64 -0
  16. package/docs/SIMPLIFIED_USAGE_GUIDE.md +208 -0
  17. package/engines/heuristic-engine.js +182 -5
  18. package/package.json +2 -1
  19. package/rules/common/C048_no_bypass_architectural_layers/analyzer.js +180 -0
  20. package/rules/common/C048_no_bypass_architectural_layers/config.json +50 -0
  21. package/rules/common/C048_no_bypass_architectural_layers/symbol-based-analyzer.js +235 -0
  22. package/rules/common/C052_parsing_or_data_transformation/analyzer.js +180 -0
  23. package/rules/common/C052_parsing_or_data_transformation/config.json +50 -0
  24. package/rules/common/C052_parsing_or_data_transformation/symbol-based-analyzer.js +132 -0
  25. package/rules/index.js +2 -0
  26. package/rules/security/S017_use_parameterized_queries/README.md +128 -0
  27. package/rules/security/S017_use_parameterized_queries/analyzer.js +286 -0
  28. package/rules/security/S017_use_parameterized_queries/config.json +109 -0
  29. package/rules/security/S017_use_parameterized_queries/regex-based-analyzer.js +541 -0
  30. package/rules/security/S017_use_parameterized_queries/symbol-based-analyzer.js +777 -0
  31. package/rules/security/S031_secure_session_cookies/README.md +127 -0
  32. package/rules/security/S031_secure_session_cookies/analyzer.js +245 -0
  33. package/rules/security/S031_secure_session_cookies/config.json +86 -0
  34. package/rules/security/S031_secure_session_cookies/regex-based-analyzer.js +196 -0
  35. package/rules/security/S031_secure_session_cookies/symbol-based-analyzer.js +1084 -0
  36. package/rules/security/S032_httponly_session_cookies/FRAMEWORK_SUPPORT.md +209 -0
  37. package/rules/security/S032_httponly_session_cookies/README.md +184 -0
  38. package/rules/security/S032_httponly_session_cookies/analyzer.js +282 -0
  39. package/rules/security/S032_httponly_session_cookies/config.json +96 -0
  40. package/rules/security/S032_httponly_session_cookies/regex-based-analyzer.js +715 -0
  41. package/rules/security/S032_httponly_session_cookies/symbol-based-analyzer.js +1348 -0
  42. package/rules/security/S033_samesite_session_cookies/README.md +227 -0
  43. package/rules/security/S033_samesite_session_cookies/analyzer.js +242 -0
  44. package/rules/security/S033_samesite_session_cookies/config.json +87 -0
  45. package/rules/security/S033_samesite_session_cookies/regex-based-analyzer.js +703 -0
  46. package/rules/security/S033_samesite_session_cookies/symbol-based-analyzer.js +732 -0
  47. package/rules/security/S034_host_prefix_session_cookies/README.md +204 -0
  48. package/rules/security/S034_host_prefix_session_cookies/analyzer.js +290 -0
  49. package/rules/security/S034_host_prefix_session_cookies/config.json +62 -0
  50. package/rules/security/S034_host_prefix_session_cookies/regex-based-analyzer.js +478 -0
  51. package/rules/security/S034_host_prefix_session_cookies/symbol-based-analyzer.js +277 -0
  52. package/rules/security/S035_path_session_cookies/README.md +257 -0
  53. package/rules/security/S035_path_session_cookies/analyzer.js +316 -0
  54. package/rules/security/S035_path_session_cookies/config.json +99 -0
  55. package/rules/security/S035_path_session_cookies/regex-based-analyzer.js +724 -0
  56. package/rules/security/S035_path_session_cookies/symbol-based-analyzer.js +373 -0
  57. package/scripts/batch-processing-demo.js +334 -0
  58. package/scripts/performance-test.js +541 -0
  59. package/scripts/quick-performance-test.js +108 -0
package/CHANGELOG.md CHANGED
@@ -2,6 +2,44 @@
2
2
 
3
3
  ---
4
4
 
5
+ ## ๐Ÿš€ **v1.3.3 - Performance & File Limits Optimization (September 4, 2025)**
6
+
7
+ **Release Date**: September 4, 2025
8
+ **Type**: Performance Enhancement & User Experience
9
+
10
+ ### โšก **Performance Engineering**
11
+ - **ENHANCED**: Heuristic Engine v4.0 with integrated performance optimizations
12
+ - **Smart file limits**: Auto-detection prevents memory issues
13
+ - **Batch processing**: Optimized rule execution for large projects
14
+ - **Memory management**: Symbol table limits for TypeScript projects
15
+ - **Timeout protection**: Graceful handling of long-running analysis
16
+
17
+ ### ๐ŸŽ›๏ธ **CLI Enhancement & Clarity**
18
+ - **CLARIFIED**: File limit options with comprehensive documentation
19
+ - **`--max-files`**: Controls total analysis workload (performance)
20
+ - **`--max-semantic-files`**: Controls TypeScript symbol table memory
21
+ - **Auto-detection**: Smart defaults for 90% of use cases
22
+ - **Manual tuning**: Fine control for enterprise projects
23
+
24
+ ### ๐Ÿ“š **Documentation Expansion**
25
+ - **NEW**: [FILE_LIMITS_EXPLANATION.md](./docs/FILE_LIMITS_EXPLANATION.md) - Comprehensive guide (5.7KB)
26
+ - **NEW**: [QUICK_FILE_LIMITS.md](./docs/QUICK_FILE_LIMITS.md) - Quick reference (1.8KB)
27
+ - **ENHANCED**: CLI help with clear usage examples
28
+ - **INTEGRATED**: Performance docs in README.md
29
+
30
+ ### ๐Ÿง  **Architecture Improvements**
31
+ - **INTEGRATED**: Performance logic into heuristic engine (no separate files)
32
+ - **ENHANCED**: Auto-performance-manager for intelligent limit calculation
33
+ - **OPTIMIZED**: Memory usage patterns for large codebases
34
+ - **TESTED**: GitHub Actions compatibility with resource constraints
35
+
36
+ ### ๐ŸŽฏ **User Experience**
37
+ - **90/10 Rule**: Auto-detection works for most cases, manual tuning available
38
+ - **Progressive disclosure**: Quick ref โ†’ detailed guide โ†’ implementation details
39
+ - **CI/CD Ready**: Optimized for memory-constrained environments
40
+
41
+ ---
42
+
5
43
  ## ๐Ÿ† **v1.3.2 - Precision Engineering & Rule Maturity (August 21, 2025)**
6
44
 
7
45
  **Release Date**: August 21, 2025
package/README.md CHANGED
@@ -9,12 +9,13 @@ Sun Lint is a universal coding standards checker providing comprehensive code qu
9
9
  ### **โœจ Key Features**
10
10
  - โœ… **256+ Coding Rules**: Quality (161), Security (70), Performance (25)
11
11
  - โœ… **Unified Architecture**: Same adapter pattern for CLI and VSCode extension
12
- - โœ… **Multi-Engine Support**: Heuristic (244 rules) + ESLint (17 rules) + AI (256 rules)
12
+ - โœ… **Multi-Engine Support**: Heuristic v4.0 (244 rules) + ESLint (17 rules) + AI (256 rules)
13
+ - โœ… **Performance Optimized**: Auto file limits, memory management, GitHub Actions ready
13
14
  - โœ… **Built-in AST Analysis**: JavaScript/TypeScript parsing out of the box
14
15
  - โœ… **Git Integration**: `--changed-files`, `--staged-files`, `--pr-mode`
15
- - โœ… **TypeScript Support**: Native TypeScript 5.8+ analysis
16
+ - โœ… **TypeScript Support**: Native TypeScript 5.8+ analysis with smart memory limits
16
17
  - โœ… **Zero Config**: Works immediately after `npm install`
17
- - โœ… **CI/CD Ready**: Baseline comparison, fail-on-new-violations
18
+ - โœ… **CI/CD Ready**: Baseline comparison, fail-on-new-violations, timeout protection
18
19
  - โœ… **Advanced File Targeting**: Include/exclude patterns, language filtering
19
20
 
20
21
  ### **๐Ÿ—๏ธ Architecture**
@@ -376,6 +377,7 @@ sunlint --validate-config .sunlint.json
376
377
  ## ๐Ÿ“š **Documentation**
377
378
 
378
379
  - **[Configuration Guide](./docs/CONFIGURATION.md)** - Complete config options with examples
380
+ - **[Performance & File Limits](./docs/FILE_LIMITS_EXPLANATION.md)** - Understanding `--max-files` vs `--max-semantic-files`
379
381
  - [ESLint Integration Guide](./docs/ESLINT_INTEGRATION.md)
380
382
  - [CI/CD Guide](./docs/CI-CD-GUIDE.md)
381
383
  - [Architecture](./docs/ARCHITECTURE.md)
@@ -27,14 +27,8 @@
27
27
  "status": "stable",
28
28
  "tags": ["logging", "error-handling", "severity"],
29
29
  "engineMappings": {
30
- "eslint": [
31
- "no-console",
32
- "no-alert",
33
- "no-debugger"
34
- ],
35
- "heuristic": [
36
- "rules/common/C019_log_level_usage/analyzer.js"
37
- ]
30
+ "eslint": ["no-console", "no-alert", "no-debugger"],
31
+ "heuristic": ["rules/common/C019_log_level_usage/analyzer.js"]
38
32
  }
39
33
  },
40
34
  "C006": {
@@ -635,8 +629,8 @@
635
629
  "category": "security",
636
630
  "severity": "error",
637
631
  "languages": ["typescript", "javascript"],
638
- "analyzer": "eslint",
639
- "eslintRule": "custom/typescript_s017",
632
+ "analyzer": "./rules/security/S017_use_parameterized_queries/analyzer.js",
633
+ "config": "./rules/security/S017_use_parameterized_queries/config.json",
640
634
  "version": "1.0.0",
641
635
  "status": "stable",
642
636
  "tags": ["security", "sql-injection", "database"]
@@ -769,41 +763,122 @@
769
763
  "status": "stable",
770
764
  "tags": ["security", "directory-browsing", "information-disclosure"]
771
765
  },
766
+ "S031": {
767
+ "name": "Set Secure flag for Session Cookies",
768
+ "description": "Set Secure flag for Session Cookies to protect via HTTPS. This ensures cookies are only transmitted over secure connections, preventing interception.",
769
+ "category": "security",
770
+ "severity": "error",
771
+ "languages": ["typescript", "javascript"],
772
+ "analyzer": "./rules/security/S031_secure_session_cookies/analyzer.js",
773
+ "config": "./rules/security/S031_secure_session_cookies/config.json",
774
+ "version": "1.0.0",
775
+ "status": "stable",
776
+ "tags": ["security", "cookies", "session", "https", "secure"],
777
+ "strategy": {
778
+ "preferred": "ast",
779
+ "fallbacks": ["ast", "regex"],
780
+ "accuracy": {
781
+ "ast": 95,
782
+ "regex": 85
783
+ }
784
+ },
785
+ "engineMappings": {
786
+ "heuristic": ["rules/security/S031_secure_session_cookies/analyzer.js"]
787
+ }
788
+ },
789
+ "S032": {
790
+ "name": "Set HttpOnly attribute for Session Cookies",
791
+ "description": "Set HttpOnly attribute for Session Cookies to prevent JavaScript access. This protects against XSS attacks by preventing client-side script access to sensitive cookies.",
792
+ "category": "security",
793
+ "severity": "error",
794
+ "languages": ["typescript", "javascript"],
795
+ "analyzer": "./rules/security/S032_httponly_session_cookies/analyzer.js",
796
+ "config": "./rules/security/S032_httponly_session_cookies/config.json",
797
+ "version": "1.0.0",
798
+ "status": "stable",
799
+ "tags": ["security", "cookies", "session", "httponly", "xss"],
800
+ "strategy": {
801
+ "preferred": "ast",
802
+ "fallbacks": ["ast", "regex"],
803
+ "accuracy": {
804
+ "ast": 95,
805
+ "regex": 85
806
+ }
807
+ },
808
+ "engineMappings": {
809
+ "heuristic": [
810
+ "rules/security/S032_httponly_session_cookies/analyzer.js"
811
+ ]
812
+ }
813
+ },
772
814
  "S033": {
773
- "name": "Require SameSite Cookie",
774
- "description": "Require SameSite attribute for cookies",
815
+ "name": "Set SameSite attribute for Session Cookies",
816
+ "description": "Set SameSite attribute for Session Cookies to reduce CSRF risk. This prevents the browser from sending cookies along with cross-site requests, mitigating CSRF attacks.",
775
817
  "category": "security",
776
818
  "severity": "error",
777
819
  "languages": ["typescript", "javascript"],
778
- "analyzer": "eslint",
779
- "eslintRule": "custom/typescript_s033",
820
+ "analyzer": "./rules/security/S033_samesite_session_cookies/analyzer.js",
821
+ "config": "./rules/security/S033_samesite_session_cookies/config.json",
780
822
  "version": "1.0.0",
781
823
  "status": "stable",
782
- "tags": ["security", "cookies", "samesite"]
824
+ "tags": ["security", "cookies", "session", "samesite", "csrf"],
825
+ "strategy": {
826
+ "preferred": "ast",
827
+ "fallbacks": ["ast", "regex"],
828
+ "accuracy": {
829
+ "ast": 95,
830
+ "regex": 85
831
+ }
832
+ },
833
+ "engineMappings": {
834
+ "heuristic": [
835
+ "rules/security/S033_samesite_session_cookies/analyzer.js"
836
+ ]
837
+ }
783
838
  },
784
839
  "S034": {
785
- "name": "Require Host Cookie Prefix",
786
- "description": "Require __Host- prefix for secure cookies",
840
+ "name": "Use __Host- prefix for Session Cookies",
841
+ "description": "Use __Host- prefix for Session Cookies to prevent subdomain sharing. The __Host- prefix ensures cookies are only sent to the exact domain that set them, preventing subdomain cookie sharing attacks.",
787
842
  "category": "security",
788
- "severity": "error",
843
+ "severity": "warning",
789
844
  "languages": ["typescript", "javascript"],
790
- "analyzer": "eslint",
791
- "eslintRule": "custom/typescript_s034",
845
+ "analyzer": "./rules/security/S034_host_prefix_session_cookies/analyzer.js",
846
+ "config": "./rules/security/S034_host_prefix_session_cookies/config.json",
792
847
  "version": "1.0.0",
793
848
  "status": "stable",
794
- "tags": ["security", "cookies", "host-prefix"]
849
+ "tags": ["security", "cookies", "session", "host-prefix", "subdomain"],
850
+ "strategy": {
851
+ "preferred": "ast",
852
+ "fallbacks": ["ast", "regex"],
853
+ "accuracy": {
854
+ "ast": 95,
855
+ "regex": 85
856
+ }
857
+ },
858
+ "engineMappings": {
859
+ "heuristic": [
860
+ "rules/security/S034_host_prefix_session_cookies/analyzer.js"
861
+ ]
862
+ }
795
863
  },
796
864
  "S035": {
797
- "name": "Cookie Specific Path",
798
- "description": "Require specific path for sensitive cookies",
865
+ "name": "Set Path attribute for Session Cookies",
866
+ "description": "Set Path attribute for Session Cookies to limit access scope",
799
867
  "category": "security",
800
- "severity": "error",
868
+ "severity": "warning",
801
869
  "languages": ["typescript", "javascript"],
802
- "analyzer": "eslint",
803
- "eslintRule": "custom/typescript_s035",
870
+ "analyzer": "heuristic",
804
871
  "version": "1.0.0",
805
872
  "status": "stable",
806
- "tags": ["security", "cookies", "path"]
873
+ "tags": ["security", "cookies", "path"],
874
+ "strategy": {
875
+ "defaultEngine": "heuristic",
876
+ "engineMappings": {
877
+ "heuristic": ["rules/security/S035_path_session_cookies/analyzer.js"]
878
+ }
879
+ },
880
+ "configPath": "rules/security/S035_path_session_cookies/config.json",
881
+ "analyzerPath": ["rules/security/S035_path_session_cookies/analyzer.js"]
807
882
  },
808
883
  "S036": {
809
884
  "name": "No Unsafe File Include",
@@ -1196,6 +1271,36 @@
1196
1271
  "accuracy": {}
1197
1272
  }
1198
1273
  },
1274
+ "C048": {
1275
+ "name": "Do not bypass architectural layers (controller/service/repository)",
1276
+ "description": "Maintain a clear layered architecture, ensuring logic and data flow are well-structured and maintainable.",
1277
+ "category": "naming",
1278
+ "severity": "warning",
1279
+ "languages": ["typescript", "javascript", "dart", "kotlin"],
1280
+ "analyzer": "./rules/common/C048_no_bypass_architectural_layers/analyzer.js",
1281
+ "config": "./rules/common/C048_no_bypass_architectural_layers/config.json",
1282
+ "version": "1.0.0",
1283
+ "status": "stable",
1284
+ "tags": ["naming", "domain", "readability"],
1285
+ "engineMappings": {
1286
+ "eslint": ["@typescript-eslint/naming-convention", "camelcase"]
1287
+ }
1288
+ },
1289
+ "C052": {
1290
+ "name": "Parsing or data transformation logic must be separated from controllers",
1291
+ "description": "Enforce separation of concerns โ€” controllers should only handle requests and delegate processing, improving testability, maintainability, and reuse.",
1292
+ "category": "naming",
1293
+ "severity": "warning",
1294
+ "languages": ["typescript", "javascript", "dart", "kotlin"],
1295
+ "analyzer": "./rules/common/C052_parsing_or_data_transformation/analyzer.js",
1296
+ "config": "./rules/common/C052_parsing_or_data_transformation/config.json",
1297
+ "version": "1.0.0",
1298
+ "status": "stable",
1299
+ "tags": ["naming", "domain", "readability"],
1300
+ "engineMappings": {
1301
+ "eslint": ["@typescript-eslint/naming-convention", "camelcase"]
1302
+ }
1303
+ },
1199
1304
  "C072": {
1200
1305
  "id": "C072",
1201
1306
  "name": "Single Test Behavior",
@@ -1612,6 +1717,7 @@
1612
1717
  "C017",
1613
1718
  "C018",
1614
1719
  "C023",
1720
+ "C024",
1615
1721
  "C029",
1616
1722
  "C030",
1617
1723
  "C035",
@@ -1619,6 +1725,8 @@
1619
1725
  "C042",
1620
1726
  "C043",
1621
1727
  "C047",
1728
+ "C048",
1729
+ "C052",
1622
1730
  "C072",
1623
1731
  "C075",
1624
1732
  "T002",
@@ -1668,6 +1776,8 @@
1668
1776
  "S027",
1669
1777
  "S029",
1670
1778
  "S030",
1779
+ "S031",
1780
+ "S032",
1671
1781
  "S033",
1672
1782
  "S034",
1673
1783
  "S035",
@@ -1777,12 +1887,12 @@
1777
1887
  }
1778
1888
  },
1779
1889
  "metadata": {
1780
- "version": "1.1.6",
1781
- "lastUpdated": "2025-08-19",
1782
- "totalRules": 95,
1890
+ "version": "1.1.7",
1891
+ "lastUpdated": "2025-08-25",
1892
+ "totalRules": 97,
1783
1893
  "qualityRules": 33,
1784
- "securityRules": 47,
1785
- "stableRules": 43,
1894
+ "securityRules": 49,
1895
+ "stableRules": 45,
1786
1896
  "experimentalRules": 1,
1787
1897
  "supportedLanguages": 4,
1788
1898
  "features": [
@@ -1791,7 +1901,8 @@
1791
1901
  "Dynamic rule configuration",
1792
1902
  "ESLint 9.x integration",
1793
1903
  "React rules integration",
1794
- "Memory leak fixes"
1904
+ "Memory leak fixes",
1905
+ "S032 HttpOnly session cookies"
1795
1906
  ],
1796
1907
  "consolidatedFrom": "/Users/bach.ngoc.hoai/Docs/ee/coding-quality/extensions/sunlint/config/rules/rules-registry.json"
1797
1908
  }
@@ -10,6 +10,7 @@ const path = require('path');
10
10
  const fs = require('fs');
11
11
  const AnalysisEngineInterface = require('./interfaces/analysis-engine.interface');
12
12
  const SunlintRuleAdapter = require('./adapters/sunlint-rule-adapter');
13
+ const PerformanceOptimizer = require('./performance-optimizer');
13
14
 
14
15
  class AnalysisOrchestrator {
15
16
  constructor() {
@@ -18,6 +19,7 @@ class AnalysisOrchestrator {
18
19
  this.defaultTimeout = 30000; // 30 seconds default timeout
19
20
  this.ruleAdapter = SunlintRuleAdapter.getInstance();
20
21
  this.enginesConfigPath = path.join(__dirname, '..', 'config', 'engines', 'engines.json');
22
+ this.performanceOptimizer = new PerformanceOptimizer();
21
23
  }
22
24
 
23
25
  /**
@@ -171,14 +173,35 @@ class AnalysisOrchestrator {
171
173
  throw new Error('No analysis engines registered');
172
174
  }
173
175
 
176
+ // Initialize performance optimizer
177
+ await this.performanceOptimizer.initialize(config);
178
+
179
+ // Apply performance optimizations to files and rules
180
+ const { optimizedFiles, optimizedRules, performanceMetrics } =
181
+ await this.performanceOptimizer.optimizeAnalysis(
182
+ options.files || [options.input],
183
+ rulesToRun,
184
+ config
185
+ );
186
+
187
+ if (!options.quiet) {
188
+ console.log(chalk.cyan(`๐Ÿ” Analyzing ${optimizedRules.length} rules on ${optimizedFiles.length} files...`));
189
+ if (performanceMetrics.filteredFiles > 0) {
190
+ console.log(chalk.gray(` ๐Ÿ“ฆ Filtered ${performanceMetrics.filteredFiles} files for performance`));
191
+ }
192
+ if (performanceMetrics.ruleBatches > 1) {
193
+ console.log(chalk.gray(` ๐Ÿ”„ Using ${performanceMetrics.ruleBatches} rule batches`));
194
+ }
195
+ }
196
+
174
197
  // Group rules by their preferred engines
175
- const engineGroups = this.groupRulesByEngine(rulesToRun, config);
198
+ const engineGroups = this.groupRulesByEngine(optimizedRules, config);
176
199
 
177
200
  if (!options.quiet) {
178
- console.log(chalk.cyan(`๐Ÿ” Analyzing ${rulesToRun.length} rules across ${engineGroups.size} engines...`));
201
+ console.log(chalk.cyan(`๐Ÿš€ Running analysis across ${engineGroups.size} engines...`));
179
202
  }
180
203
 
181
- // Run analysis on each engine
204
+ // Run analysis on each engine with batching
182
205
  const results = [];
183
206
  for (const [engineName, rules] of engineGroups) {
184
207
  const engine = this.engines.get(engineName);
@@ -187,35 +210,64 @@ class AnalysisOrchestrator {
187
210
  continue;
188
211
  }
189
212
 
190
- if (!options.quiet) {
191
- console.log(chalk.blue(`โš™๏ธ Running ${rules.length} rules on ${engineName} engine...`));
192
- }
193
-
194
- try {
195
- const engineResult = await this.runEngineWithTimeout(
196
- engine,
197
- options.files || [options.input],
198
- rules,
199
- options
200
- );
201
-
202
- results.push({
203
- engine: engineName,
204
- rules: rules.map(r => r.id),
205
- ...engineResult
206
- });
213
+ // Process rules in batches for performance
214
+ const ruleBatches = this.performanceOptimizer.createRuleBatches(rules, config);
215
+
216
+ for (let i = 0; i < ruleBatches.length; i++) {
217
+ const batch = ruleBatches[i];
218
+ const batchNumber = i + 1;
207
219
 
208
- if (!options.quiet) {
209
- const violationCount = this.countViolations(engineResult);
210
- console.log(chalk.blue(`โœ… ${engineName}: ${violationCount} violations found`));
220
+ if (!options.quiet && ruleBatches.length > 1) {
221
+ console.log(chalk.blue(`โš™๏ธ ${engineName} - Batch ${batchNumber}/${ruleBatches.length}: ${batch.length} rules`));
222
+ } else if (!options.quiet) {
223
+ console.log(chalk.blue(`โš™๏ธ Running ${batch.length} rules on ${engineName} engine...`));
224
+ }
225
+
226
+ try {
227
+ const engineResult = await this.runEngineWithOptimizations(
228
+ engine,
229
+ optimizedFiles,
230
+ batch,
231
+ options,
232
+ { batchNumber, totalBatches: ruleBatches.length }
233
+ );
234
+
235
+ results.push({
236
+ engine: engineName,
237
+ batch: batchNumber,
238
+ rules: batch.map(r => r.id),
239
+ ...engineResult
240
+ });
241
+
242
+ if (!options.quiet) {
243
+ const violationCount = this.countViolations(engineResult);
244
+ console.log(chalk.blue(`โœ… ${engineName} batch ${batchNumber}: ${violationCount} violations found`));
245
+ }
246
+ } catch (error) {
247
+ // Enhanced error recovery with batch context
248
+ const errorInfo = this.performanceOptimizer.handleAnalysisError(error, {
249
+ engine: engineName,
250
+ batch: batchNumber,
251
+ rules: batch.map(r => r.id),
252
+ files: optimizedFiles.length
253
+ });
254
+
255
+ console.error(chalk.red(`โŒ Engine ${engineName} batch ${batchNumber} failed:`), errorInfo.message);
256
+
257
+ if (errorInfo.shouldRetry && !options.noRetry) {
258
+ console.log(chalk.yellow(`๐Ÿ”„ Retrying with reduced batch size...`));
259
+ // Split batch and retry - implement recursive retry logic here
260
+ }
261
+ // Continue with other batches instead of failing completely
211
262
  }
212
- } catch (error) {
213
- console.error(chalk.red(`โŒ Engine ${engineName} failed:`), error.message);
214
- // Continue with other engines instead of failing completely
215
263
  }
216
264
  }
217
265
 
218
- return this.mergeEngineResults(results, options);
266
+ // Merge results and add performance metrics
267
+ const mergedResults = this.mergeEngineResults(results, options);
268
+ mergedResults.performance = performanceMetrics;
269
+
270
+ return mergedResults;
219
271
 
220
272
  } catch (error) {
221
273
  console.error(chalk.red('โŒ Analysis orchestration failed:'), error.message);
@@ -393,23 +445,68 @@ class AnalysisOrchestrator {
393
445
  }
394
446
 
395
447
  /**
396
- * Run engine analysis with timeout protection
448
+ * Run engine analysis with timeout protection and performance optimizations
397
449
  * Following Rule C006: Verb-noun naming
398
450
  * @param {AnalysisEngineInterface} engine - Engine to run
399
451
  * @param {string[]} files - Files to analyze
400
452
  * @param {Object[]} rules - Rules to apply
401
453
  * @param {Object} options - Analysis options
454
+ * @param {Object} batchInfo - Batch context information
402
455
  * @returns {Promise<Object>} Engine results
403
456
  */
404
- async runEngineWithTimeout(engine, files, rules, options) {
405
- const timeout = options.timeout || this.defaultTimeout;
457
+ async runEngineWithOptimizations(engine, files, rules, options, batchInfo = {}) {
458
+ // Dynamic timeout based on file count and rules
459
+ const adaptiveTimeout = this.performanceOptimizer.calculateAdaptiveTimeout(
460
+ files.length,
461
+ rules.length,
462
+ options.timeout || this.defaultTimeout
463
+ );
406
464
 
407
- return Promise.race([
408
- engine.analyze(files, rules, options),
409
- new Promise((_, reject) =>
410
- setTimeout(() => reject(new Error(`Engine ${engine.name} timed out after ${timeout}ms`)), timeout)
411
- )
412
- ]);
465
+ const enhancedOptions = {
466
+ ...options,
467
+ timeout: adaptiveTimeout,
468
+ batchInfo
469
+ };
470
+
471
+ try {
472
+ return await Promise.race([
473
+ engine.analyze(files, rules, enhancedOptions),
474
+ new Promise((_, reject) =>
475
+ setTimeout(() => reject(new Error(
476
+ `Engine ${engine.name} batch ${batchInfo.batchNumber || 1} timed out after ${adaptiveTimeout}ms`
477
+ )), adaptiveTimeout)
478
+ )
479
+ ]);
480
+ } catch (error) {
481
+ // Enhanced error context for debugging
482
+ const errorContext = {
483
+ engine: engine.name,
484
+ filesCount: files.length,
485
+ rulesCount: rules.length,
486
+ timeout: adaptiveTimeout,
487
+ batch: batchInfo
488
+ };
489
+
490
+ // Wrap error with context
491
+ const enhancedError = new Error(`${error.message} (Context: ${JSON.stringify(errorContext)})`);
492
+ enhancedError.originalError = error;
493
+ enhancedError.context = errorContext;
494
+
495
+ throw enhancedError;
496
+ }
497
+ }
498
+
499
+ /**
500
+ * Run engine analysis with timeout protection (legacy method for backward compatibility)
501
+ * Following Rule C006: Verb-noun naming
502
+ * @param {AnalysisEngineInterface} engine - Engine to run
503
+ * @param {string[]} files - Files to analyze
504
+ * @param {Object[]} rules - Rules to apply
505
+ * @param {Object} options - Analysis options
506
+ * @returns {Promise<Object>} Engine results
507
+ */
508
+ async runEngineWithTimeout(engine, files, rules, options) {
509
+ return this.runEngineWithOptimizations(engine, files, rules, options);
413
510
  }
414
511
 
415
512
  /**
@@ -425,6 +522,7 @@ class AnalysisOrchestrator {
425
522
  results: [],
426
523
  summary: {
427
524
  totalEngines: engineResults.length,
525
+ totalBatches: engineResults.length,
428
526
  totalViolations: 0,
429
527
  totalFiles: 0,
430
528
  engines: {}
@@ -436,8 +534,13 @@ class AnalysisOrchestrator {
436
534
  }
437
535
  };
438
536
 
537
+ // Track unique engines for summary
538
+ const uniqueEngines = new Set();
539
+
439
540
  // Combine results from all engines
440
541
  for (const engineResult of engineResults) {
542
+ uniqueEngines.add(engineResult.engine);
543
+
441
544
  // Add engine-specific results
442
545
  if (engineResult.results) {
443
546
  mergedResults.results.push(...engineResult.results);
@@ -445,16 +548,30 @@ class AnalysisOrchestrator {
445
548
 
446
549
  // Track engine statistics
447
550
  const violationCount = this.countViolations(engineResult);
448
- mergedResults.summary.engines[engineResult.engine] = {
449
- rules: engineResult.rules || [],
450
- violations: violationCount,
451
- files: engineResult.filesAnalyzed || 0
452
- };
551
+ const engineName = engineResult.engine;
552
+
553
+ if (!mergedResults.summary.engines[engineName]) {
554
+ mergedResults.summary.engines[engineName] = {
555
+ rules: [],
556
+ violations: 0,
557
+ files: 0,
558
+ batches: 0
559
+ };
560
+ }
561
+
562
+ // Accumulate engine statistics across batches
563
+ mergedResults.summary.engines[engineName].rules.push(...(engineResult.rules || []));
564
+ mergedResults.summary.engines[engineName].violations += violationCount;
565
+ mergedResults.summary.engines[engineName].files += engineResult.filesAnalyzed || 0;
566
+ mergedResults.summary.engines[engineName].batches += 1;
453
567
 
454
568
  mergedResults.summary.totalViolations += violationCount;
455
569
  mergedResults.summary.totalFiles += engineResult.filesAnalyzed || 0;
456
570
  }
457
571
 
572
+ // Update unique engine count
573
+ mergedResults.summary.totalEngines = uniqueEngines.size;
574
+
458
575
  return mergedResults;
459
576
  }
460
577
 
@@ -517,7 +634,7 @@ class AnalysisOrchestrator {
517
634
  }
518
635
 
519
636
  /**
520
- * Cleanup all engines
637
+ * Cleanup all engines and performance optimizer
521
638
  * Following Rule C006: Verb-noun naming
522
639
  * @returns {Promise<void>}
523
640
  */
@@ -529,6 +646,14 @@ class AnalysisOrchestrator {
529
646
  console.warn(chalk.yellow(`โš ๏ธ Failed to cleanup engine ${engine.id}:`), error.message);
530
647
  }
531
648
  }
649
+
650
+ // Cleanup performance optimizer
651
+ try {
652
+ await this.performanceOptimizer.cleanup();
653
+ } catch (error) {
654
+ console.warn(chalk.yellow(`โš ๏ธ Failed to cleanup performance optimizer:`), error.message);
655
+ }
656
+
532
657
  this.initialized = false;
533
658
  console.log(chalk.blue('๐Ÿงน Engine cleanup completed'));
534
659
  }