@entro314labs/ai-changelog-generator 3.2.1 → 3.6.0

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 (36) hide show
  1. package/CHANGELOG.md +42 -2
  2. package/README.md +21 -1
  3. package/ai-changelog-mcp.sh +0 -0
  4. package/ai-changelog.sh +0 -0
  5. package/bin/ai-changelog-dxt.js +6 -3
  6. package/manifest.json +177 -0
  7. package/package.json +76 -81
  8. package/src/ai-changelog-generator.js +5 -4
  9. package/src/application/orchestrators/changelog.orchestrator.js +19 -203
  10. package/src/cli.js +16 -5
  11. package/src/domains/ai/ai-analysis.service.js +2 -0
  12. package/src/domains/analysis/analysis.engine.js +714 -37
  13. package/src/domains/changelog/changelog.service.js +623 -32
  14. package/src/domains/changelog/workspace-changelog.service.js +445 -622
  15. package/src/domains/git/commit-tagger.js +552 -0
  16. package/src/domains/git/git-manager.js +357 -0
  17. package/src/domains/git/git.service.js +865 -16
  18. package/src/infrastructure/cli/cli.controller.js +14 -9
  19. package/src/infrastructure/config/configuration.manager.js +25 -11
  20. package/src/infrastructure/interactive/interactive-workflow.service.js +8 -1
  21. package/src/infrastructure/mcp/mcp-server.service.js +105 -32
  22. package/src/infrastructure/providers/core/base-provider.js +1 -1
  23. package/src/infrastructure/providers/implementations/anthropic.js +16 -173
  24. package/src/infrastructure/providers/implementations/azure.js +16 -63
  25. package/src/infrastructure/providers/implementations/dummy.js +13 -16
  26. package/src/infrastructure/providers/implementations/mock.js +13 -26
  27. package/src/infrastructure/providers/implementations/ollama.js +12 -4
  28. package/src/infrastructure/providers/implementations/openai.js +13 -165
  29. package/src/infrastructure/providers/provider-management.service.js +126 -412
  30. package/src/infrastructure/providers/utils/base-provider-helpers.js +11 -0
  31. package/src/shared/utils/cli-ui.js +8 -10
  32. package/src/shared/utils/diff-processor.js +21 -19
  33. package/src/shared/utils/error-classes.js +33 -0
  34. package/src/shared/utils/utils.js +83 -63
  35. package/types/index.d.ts +61 -68
  36. package/src/domains/git/git-repository.analyzer.js +0 -678
@@ -24,11 +24,8 @@ export class AnalysisEngine {
24
24
 
25
25
  async _ensureGitAnalyzer() {
26
26
  if (!this.gitRepoAnalyzer && this.gitService?.gitManager) {
27
- const { GitRepositoryAnalyzer } = await import('../git/git-repository.analyzer.js')
28
- this.gitRepoAnalyzer = new GitRepositoryAnalyzer(
29
- this.gitService.gitManager,
30
- this.aiAnalysisService
31
- )
27
+ // Use the existing GitService instead of separate analyzer
28
+ this.gitRepoAnalyzer = this.gitService
32
29
  }
33
30
  return this.gitRepoAnalyzer
34
31
  }
@@ -503,70 +500,750 @@ export class AnalysisEngine {
503
500
  return { health: 'unknown', analysis: 'Git analyzer not available' }
504
501
  }
505
502
 
506
- // Missing methods expected by tests
503
+ // Advanced analysis methods
507
504
  analyzeCommitPatterns(commits) {
508
- return {
509
- patterns: ['conventional', 'semantic'],
510
- compliance: 85,
511
- suggestions: []
505
+ try {
506
+ if (!commits || commits.length === 0) {
507
+ return {
508
+ patterns: [],
509
+ compliance: 0,
510
+ suggestions: ['No commits provided for analysis'],
511
+ }
512
+ }
513
+
514
+ const patterns = []
515
+ let conventionalCount = 0
516
+ let semanticCount = 0
517
+
518
+ const suggestions = []
519
+
520
+ for (const commit of commits) {
521
+ const message = commit.subject || commit.message || ''
522
+
523
+ // Check for conventional commit format
524
+ if (/^(feat|fix|docs|style|refactor|test|chore|perf|build|ci)(\(.+\))?!?:/.test(message)) {
525
+ conventionalCount++
526
+ patterns.push('conventional')
527
+ }
528
+
529
+ // Check for semantic patterns
530
+ if (/\b(add|remove|update|fix|improve|refactor)\b/i.test(message)) {
531
+ semanticCount++
532
+ patterns.push('semantic')
533
+ }
534
+ }
535
+
536
+ const compliance = Math.round((conventionalCount / commits.length) * 100)
537
+
538
+ if (compliance < 50) {
539
+ suggestions.push(
540
+ 'Consider adopting conventional commit format for better changelog generation'
541
+ )
542
+ }
543
+ if (conventionalCount === 0) {
544
+ suggestions.push('No conventional commits found. See https://www.conventionalcommits.org/')
545
+ }
546
+
547
+ return {
548
+ patterns: [...new Set(patterns)],
549
+ compliance,
550
+ conventionalCommits: conventionalCount,
551
+ semanticCommits: semanticCount,
552
+ totalCommits: commits.length,
553
+ suggestions,
554
+ }
555
+ } catch (error) {
556
+ console.warn(`Commit pattern analysis failed: ${error.message}`)
557
+ return {
558
+ patterns: [],
559
+ compliance: 0,
560
+ suggestions: ['Analysis failed - ensure valid commits provided'],
561
+ }
512
562
  }
513
563
  }
514
564
 
515
565
  detectChangeTypes(changes) {
516
- return {
517
- types: ['feat', 'fix', 'docs'],
518
- confidence: 'high'
566
+ try {
567
+ if (!changes || changes.length === 0) {
568
+ return {
569
+ types: [],
570
+ confidence: 'low',
571
+ details: 'No changes provided',
572
+ }
573
+ }
574
+
575
+ const detectedTypes = new Set()
576
+ const typeIndicators = {
577
+ feat: ['feat', 'feature', 'add', 'new', 'implement', 'create'],
578
+ fix: ['fix', 'bug', 'issue', 'resolve', 'correct', 'repair'],
579
+ docs: ['doc', 'readme', 'comment', 'documentation', 'guide'],
580
+ test: ['test', 'spec', 'coverage', 'unit', 'integration'],
581
+ refactor: ['refactor', 'cleanup', 'restructure', 'reorganize'],
582
+ style: ['style', 'format', 'lint', 'prettier', 'formatting'],
583
+ perf: ['perf', 'performance', 'optimize', 'speed', 'efficient'],
584
+ build: ['build', 'compile', 'bundle', 'package', 'deploy'],
585
+ chore: ['chore', 'maintenance', 'update', 'upgrade', 'bump'],
586
+ }
587
+
588
+ // Analyze file paths and change descriptions
589
+ for (const change of changes) {
590
+ const searchText = [
591
+ change.filePath || change.path || '',
592
+ change.diff || '',
593
+ change.description || '',
594
+ ]
595
+ .join(' ')
596
+ .toLowerCase()
597
+
598
+ for (const [type, indicators] of Object.entries(typeIndicators)) {
599
+ if (indicators.some((indicator) => searchText.includes(indicator))) {
600
+ detectedTypes.add(type)
601
+ }
602
+ }
603
+
604
+ // File-based type detection
605
+ const filePath = change.filePath || change.path || ''
606
+ if (filePath.includes('test') || filePath.includes('spec')) {
607
+ detectedTypes.add('test')
608
+ } else if (filePath.includes('doc') || filePath.endsWith('.md')) {
609
+ detectedTypes.add('docs')
610
+ } else if (filePath.includes('config') || filePath.includes('build')) {
611
+ detectedTypes.add('build')
612
+ }
613
+ }
614
+
615
+ const confidence =
616
+ detectedTypes.size > 0 ? (detectedTypes.size === 1 ? 'high' : 'medium') : 'low'
617
+
618
+ return {
619
+ types: Array.from(detectedTypes),
620
+ confidence,
621
+ totalChanges: changes.length,
622
+ details: `Detected ${detectedTypes.size} change types from ${changes.length} changes`,
623
+ }
624
+ } catch (error) {
625
+ console.warn(`Change type detection failed: ${error.message}`)
626
+ return {
627
+ types: [],
628
+ confidence: 'low',
629
+ details: `Analysis failed: ${error.message}`,
630
+ }
519
631
  }
520
632
  }
521
633
 
522
634
  assessCodeQuality(files) {
523
- return {
524
- score: 8.5,
525
- issues: [],
526
- recommendations: []
635
+ try {
636
+ if (!files || files.length === 0) {
637
+ return {
638
+ score: 0,
639
+ issues: ['No files provided for analysis'],
640
+ recommendations: [],
641
+ }
642
+ }
643
+
644
+ let score = 10.0
645
+ const issues = []
646
+ const recommendations = []
647
+
648
+ // Analyze each file
649
+ for (const file of files) {
650
+ const filePath = file.filePath || file.path || ''
651
+ const diff = file.diff || ''
652
+
653
+ // Check for code quality indicators
654
+ if (diff.includes('TODO') || diff.includes('FIXME')) {
655
+ issues.push(`${filePath}: Contains TODO/FIXME comments`)
656
+ score -= 0.5
657
+ }
658
+
659
+ if (diff.includes('console.log') && !filePath.includes('test')) {
660
+ issues.push(`${filePath}: Contains console.log statements`)
661
+ score -= 0.3
662
+ }
663
+
664
+ if (diff.includes('debugger')) {
665
+ issues.push(`${filePath}: Contains debugger statements`)
666
+ score -= 0.5
667
+ }
668
+
669
+ // Check for large functions (very basic heuristic)
670
+ const functionMatches = diff.match(/function\s+\w+|const\s+\w+\s*=/g)
671
+ if (functionMatches && functionMatches.length > 5) {
672
+ issues.push(`${filePath}: May contain large or complex functions`)
673
+ score -= 0.2
674
+ }
675
+
676
+ // Check for proper error handling
677
+ if (diff.includes('try') && !diff.includes('catch')) {
678
+ issues.push(`${filePath}: Incomplete error handling detected`)
679
+ score -= 0.3
680
+ }
681
+
682
+ // Check for documentation
683
+ if (!(diff.includes('/**') || diff.includes('//')) && filePath.endsWith('.js')) {
684
+ issues.push(`${filePath}: Lacks documentation comments`)
685
+ score -= 0.2
686
+ }
687
+ }
688
+
689
+ // Generate recommendations based on issues
690
+ if (issues.some((issue) => issue.includes('TODO'))) {
691
+ recommendations.push('Address TODO comments before releasing')
692
+ }
693
+ if (issues.some((issue) => issue.includes('console.log'))) {
694
+ recommendations.push('Remove debug console.log statements')
695
+ }
696
+ if (issues.some((issue) => issue.includes('documentation'))) {
697
+ recommendations.push('Add documentation comments to improve maintainability')
698
+ }
699
+ if (score < 7) {
700
+ recommendations.push('Consider code review and refactoring for better quality')
701
+ }
702
+
703
+ return {
704
+ score: Math.max(0, Math.min(10, score)),
705
+ issues: issues.slice(0, 10), // Limit to top 10 issues
706
+ recommendations,
707
+ filesAnalyzed: files.length,
708
+ }
709
+ } catch (error) {
710
+ console.warn(`Code quality assessment failed: ${error.message}`)
711
+ return {
712
+ score: 0,
713
+ issues: [`Assessment failed: ${error.message}`],
714
+ recommendations: ['Ensure valid file data is provided'],
715
+ }
527
716
  }
528
717
  }
529
718
 
530
719
  identifyDependencies(changes) {
531
- return {
532
- added: [],
533
- removed: [],
534
- updated: []
720
+ try {
721
+ const dependencies = {
722
+ added: [],
723
+ removed: [],
724
+ updated: [],
725
+ details: [],
726
+ }
727
+
728
+ if (!changes || changes.length === 0) {
729
+ return dependencies
730
+ }
731
+
732
+ // Look for package.json changes
733
+ const packageJsonChanges = changes.filter((change) =>
734
+ (change.filePath || change.path || '').includes('package.json')
735
+ )
736
+
737
+ for (const change of packageJsonChanges) {
738
+ const diff = change.diff || ''
739
+
740
+ // Parse dependency changes from diff
741
+ const addedDeps = diff.match(/\+\s*"([^"]+)":\s*"([^"]+)"/g) || []
742
+ const removedDeps = diff.match(/-\s*"([^"]+)":\s*"([^"]+)"/g) || []
743
+
744
+ for (const match of addedDeps) {
745
+ const [, name, version] = match.match(/\+\s*"([^"]+)":\s*"([^"]+)"/) || []
746
+ if (name && version) {
747
+ dependencies.added.push({ name, version, file: change.filePath })
748
+ }
749
+ }
750
+
751
+ for (const match of removedDeps) {
752
+ const [, name, version] = match.match(/-\s*"([^"]+)":\s*"([^"]+)"/) || []
753
+ if (name && version) {
754
+ dependencies.removed.push({ name, version, file: change.filePath })
755
+ }
756
+ }
757
+ }
758
+
759
+ // Look for other dependency files
760
+ const depFiles = changes.filter((change) => {
761
+ const path = change.filePath || change.path || ''
762
+ return (
763
+ path.includes('requirements.txt') ||
764
+ path.includes('Gemfile') ||
765
+ path.includes('go.mod') ||
766
+ path.includes('Cargo.toml')
767
+ )
768
+ })
769
+
770
+ dependencies.details = [
771
+ `Found ${packageJsonChanges.length} package.json changes`,
772
+ `Found ${depFiles.length} other dependency file changes`,
773
+ `Total: ${dependencies.added.length} added, ${dependencies.removed.length} removed`,
774
+ ]
775
+
776
+ return dependencies
777
+ } catch (error) {
778
+ console.warn(`Dependency analysis failed: ${error.message}`)
779
+ return {
780
+ added: [],
781
+ removed: [],
782
+ updated: [],
783
+ details: [`Analysis failed: ${error.message}`],
784
+ }
535
785
  }
536
786
  }
537
787
 
538
788
  evaluatePerformanceImpact(changes) {
539
- return {
540
- impact: 'low',
541
- metrics: {}
789
+ try {
790
+ const impact = {
791
+ impact: 'low',
792
+ metrics: {},
793
+ concerns: [],
794
+ improvements: [],
795
+ }
796
+
797
+ if (!changes || changes.length === 0) {
798
+ return impact
799
+ }
800
+
801
+ // Performance-related patterns
802
+ const performancePatterns = {
803
+ high: [
804
+ /database.*query/i,
805
+ /n\+1/i,
806
+ /memory.*leak/i,
807
+ /infinite.*loop/i,
808
+ /synchronous.*call/i,
809
+ ],
810
+ medium: [/algorithm/i, /cache/i, /optimization/i, /performance/i, /async/i, /await/i],
811
+ improvements: [
812
+ /optimize/i,
813
+ /faster/i,
814
+ /efficient/i,
815
+ /reduce.*time/i,
816
+ /improve.*performance/i,
817
+ ],
818
+ }
819
+
820
+ let riskScore = 0
821
+ let improvementScore = 0
822
+
823
+ for (const change of changes) {
824
+ const content = [
825
+ change.filePath || change.path || '',
826
+ change.diff || '',
827
+ change.description || '',
828
+ ].join(' ')
829
+
830
+ // Check for performance risks
831
+ for (const pattern of performancePatterns.high) {
832
+ if (pattern.test(content)) {
833
+ riskScore += 3
834
+ impact.concerns.push(`High-risk pattern detected in ${change.filePath}`)
835
+ }
836
+ }
837
+
838
+ for (const pattern of performancePatterns.medium) {
839
+ if (pattern.test(content)) {
840
+ riskScore += 1
841
+ }
842
+ }
843
+
844
+ for (const pattern of performancePatterns.improvements) {
845
+ if (pattern.test(content)) {
846
+ improvementScore += 2
847
+ impact.improvements.push(`Performance improvement in ${change.filePath}`)
848
+ }
849
+ }
850
+ }
851
+
852
+ // Determine overall impact
853
+ if (riskScore > 5) {
854
+ impact.impact = 'high'
855
+ } else if (riskScore > 2 || improvementScore > 3) {
856
+ impact.impact = 'medium'
857
+ }
858
+
859
+ impact.metrics = {
860
+ riskScore,
861
+ improvementScore,
862
+ filesAnalyzed: changes.length,
863
+ concernsFound: impact.concerns.length,
864
+ improvementsFound: impact.improvements.length,
865
+ }
866
+
867
+ return impact
868
+ } catch (error) {
869
+ console.warn(`Performance impact evaluation failed: ${error.message}`)
870
+ return {
871
+ impact: 'unknown',
872
+ metrics: { error: error.message },
873
+ concerns: [],
874
+ improvements: [],
875
+ }
542
876
  }
543
877
  }
544
878
 
545
879
  checkSecurityImplications(changes) {
546
- return {
547
- issues: [],
548
- score: 'safe'
880
+ try {
881
+ const security = {
882
+ issues: [],
883
+ score: 'safe',
884
+ warnings: [],
885
+ recommendations: [],
886
+ }
887
+
888
+ if (!changes || changes.length === 0) {
889
+ return security
890
+ }
891
+
892
+ // Security-related patterns
893
+ const securityPatterns = {
894
+ critical: [
895
+ /password.*=.*['"][^'"]+['"]/i,
896
+ /api[_-]?key.*=.*['"][^'"]+['"]/i,
897
+ /secret.*=.*['"][^'"]+['"]/i,
898
+ /token.*=.*['"][^'"]+['"]/i,
899
+ /eval\s*\(/i,
900
+ /innerHTML\s*=/i,
901
+ /document\.write/i,
902
+ ],
903
+ warning: [
904
+ /http:\/\//i,
905
+ /\.execute\(/i,
906
+ /shell_exec/i,
907
+ /system\(/i,
908
+ /exec\(/i,
909
+ /sudo/i,
910
+ /chmod\s+777/i,
911
+ ],
912
+ auth: [/auth/i, /login/i, /permission/i, /role/i, /access/i],
913
+ }
914
+
915
+ let riskLevel = 0
916
+
917
+ for (const change of changes) {
918
+ const content = [
919
+ change.filePath || change.path || '',
920
+ change.diff || '',
921
+ change.description || '',
922
+ ].join(' ')
923
+
924
+ // Check for critical security issues
925
+ for (const pattern of securityPatterns.critical) {
926
+ if (pattern.test(content)) {
927
+ security.issues.push(`Critical: Potential security issue in ${change.filePath}`)
928
+ riskLevel += 10
929
+ }
930
+ }
931
+
932
+ // Check for warnings
933
+ for (const pattern of securityPatterns.warning) {
934
+ if (pattern.test(content)) {
935
+ security.warnings.push(`Warning: Security concern in ${change.filePath}`)
936
+ riskLevel += 3
937
+ }
938
+ }
939
+
940
+ // Check for authentication changes
941
+ for (const pattern of securityPatterns.auth) {
942
+ if (pattern.test(content)) {
943
+ security.warnings.push(`Authentication changes detected in ${change.filePath}`)
944
+ riskLevel += 1
945
+ }
946
+ }
947
+ }
948
+
949
+ // Determine security score
950
+ if (riskLevel >= 10) {
951
+ security.score = 'critical'
952
+ security.recommendations.push('Immediate security review required')
953
+ } else if (riskLevel >= 5) {
954
+ security.score = 'warning'
955
+ security.recommendations.push('Security review recommended')
956
+ } else if (riskLevel > 0) {
957
+ security.score = 'caution'
958
+ security.recommendations.push('Monitor for security implications')
959
+ }
960
+
961
+ if (security.issues.length > 0) {
962
+ security.recommendations.push('Remove hardcoded credentials immediately')
963
+ }
964
+
965
+ return security
966
+ } catch (error) {
967
+ console.warn(`Security analysis failed: ${error.message}`)
968
+ return {
969
+ issues: [`Analysis failed: ${error.message}`],
970
+ score: 'unknown',
971
+ warnings: [],
972
+ recommendations: [],
973
+ }
549
974
  }
550
975
  }
551
976
 
552
977
  analyzeDocumentationChanges(changes) {
553
- return {
554
- coverage: 'good',
555
- changes: []
978
+ try {
979
+ const documentation = {
980
+ coverage: 'unknown',
981
+ changes: [],
982
+ statistics: {},
983
+ recommendations: [],
984
+ }
985
+
986
+ if (!changes || changes.length === 0) {
987
+ return documentation
988
+ }
989
+
990
+ let docFiles = 0
991
+ let codeFiles = 0
992
+ let docChanges = 0
993
+
994
+ for (const change of changes) {
995
+ const filePath = change.filePath || change.path || ''
996
+ const diff = change.diff || ''
997
+
998
+ if (filePath.endsWith('.md') || filePath.includes('doc') || filePath.includes('readme')) {
999
+ docFiles++
1000
+ docChanges++
1001
+ documentation.changes.push({
1002
+ file: filePath,
1003
+ type: 'documentation',
1004
+ status: change.status,
1005
+ })
1006
+ } else if (
1007
+ filePath.endsWith('.js') ||
1008
+ filePath.endsWith('.ts') ||
1009
+ filePath.endsWith('.py')
1010
+ ) {
1011
+ codeFiles++
1012
+
1013
+ // Check for comment changes
1014
+ if (diff.includes('/**') || diff.includes('//') || diff.includes('#')) {
1015
+ documentation.changes.push({
1016
+ file: filePath,
1017
+ type: 'code-comments',
1018
+ status: change.status,
1019
+ })
1020
+ }
1021
+ }
1022
+ }
1023
+
1024
+ // Calculate coverage assessment
1025
+ const totalFiles = docFiles + codeFiles
1026
+ if (totalFiles === 0) {
1027
+ documentation.coverage = 'unknown'
1028
+ } else {
1029
+ const docRatio = docFiles / totalFiles
1030
+ if (docRatio > 0.3) {
1031
+ documentation.coverage = 'good'
1032
+ } else if (docRatio > 0.1) {
1033
+ documentation.coverage = 'fair'
1034
+ } else {
1035
+ documentation.coverage = 'poor'
1036
+ }
1037
+ }
1038
+
1039
+ documentation.statistics = {
1040
+ documentationFiles: docFiles,
1041
+ codeFiles,
1042
+ totalChanges: documentation.changes.length,
1043
+ documentationRatio: totalFiles > 0 ? Math.round((docFiles / totalFiles) * 100) : 0,
1044
+ }
1045
+
1046
+ // Generate recommendations
1047
+ if (documentation.coverage === 'poor') {
1048
+ documentation.recommendations.push('Consider adding more documentation')
1049
+ }
1050
+ if (codeFiles > 0 && docChanges === 0) {
1051
+ documentation.recommendations.push('Code changes detected without documentation updates')
1052
+ }
1053
+
1054
+ return documentation
1055
+ } catch (error) {
1056
+ console.warn(`Documentation analysis failed: ${error.message}`)
1057
+ return {
1058
+ coverage: 'unknown',
1059
+ changes: [],
1060
+ statistics: { error: error.message },
1061
+ recommendations: [],
1062
+ }
556
1063
  }
557
1064
  }
558
1065
 
559
1066
  assessTestCoverage(changes) {
560
- return {
561
- coverage: 85,
562
- missing: []
1067
+ try {
1068
+ const coverage = {
1069
+ coverage: 0,
1070
+ missing: [],
1071
+ statistics: {},
1072
+ recommendations: [],
1073
+ }
1074
+
1075
+ if (!changes || changes.length === 0) {
1076
+ return coverage
1077
+ }
1078
+
1079
+ let testFiles = 0
1080
+ let codeFiles = 0
1081
+ const missingTests = []
1082
+
1083
+ for (const change of changes) {
1084
+ const filePath = change.filePath || change.path || ''
1085
+
1086
+ if (
1087
+ filePath.includes('test') ||
1088
+ filePath.includes('spec') ||
1089
+ filePath.endsWith('.test.js') ||
1090
+ filePath.endsWith('.spec.js')
1091
+ ) {
1092
+ testFiles++
1093
+ } else if (
1094
+ filePath.endsWith('.js') ||
1095
+ filePath.endsWith('.ts') ||
1096
+ filePath.endsWith('.py')
1097
+ ) {
1098
+ codeFiles++
1099
+
1100
+ // Check if corresponding test file exists (simplified check)
1101
+ const hasTest = changes.some((c) => {
1102
+ const testPath = c.filePath || c.path || ''
1103
+ return (
1104
+ testPath.includes(filePath.replace(/\.(js|ts|py)$/, '')) &&
1105
+ (testPath.includes('test') || testPath.includes('spec'))
1106
+ )
1107
+ })
1108
+
1109
+ if (!hasTest) {
1110
+ missingTests.push(filePath)
1111
+ }
1112
+ }
1113
+ }
1114
+
1115
+ // Calculate coverage estimate
1116
+ const totalFiles = testFiles + codeFiles
1117
+ if (totalFiles > 0) {
1118
+ coverage.coverage = Math.round((testFiles / totalFiles) * 100)
1119
+ }
1120
+
1121
+ coverage.missing = missingTests.slice(0, 10) // Limit to top 10
1122
+ coverage.statistics = {
1123
+ testFiles,
1124
+ codeFiles,
1125
+ totalFiles,
1126
+ estimatedCoverage: coverage.coverage,
1127
+ }
1128
+
1129
+ // Generate recommendations
1130
+ if (coverage.coverage < 50) {
1131
+ coverage.recommendations.push('Low test coverage detected - consider adding more tests')
1132
+ }
1133
+ if (missingTests.length > 0) {
1134
+ coverage.recommendations.push(
1135
+ `${missingTests.length} code files may lack corresponding tests`
1136
+ )
1137
+ }
1138
+
1139
+ return coverage
1140
+ } catch (error) {
1141
+ console.warn(`Test coverage assessment failed: ${error.message}`)
1142
+ return {
1143
+ coverage: 0,
1144
+ missing: [],
1145
+ statistics: { error: error.message },
1146
+ recommendations: [],
1147
+ }
563
1148
  }
564
1149
  }
565
1150
 
566
1151
  evaluateArchitecturalChanges(changes) {
567
- return {
568
- impact: 'minimal',
569
- changes: []
1152
+ try {
1153
+ const architecture = {
1154
+ impact: 'minimal',
1155
+ changes: [],
1156
+ patterns: [],
1157
+ recommendations: [],
1158
+ }
1159
+
1160
+ if (!changes || changes.length === 0) {
1161
+ return architecture
1162
+ }
1163
+
1164
+ // Architectural patterns to detect
1165
+ const architecturalPatterns = {
1166
+ 'database-schema': /migration|schema|table|index/i,
1167
+ 'api-changes': /api|endpoint|route|controller/i,
1168
+ 'dependency-injection': /inject|provider|service|factory/i,
1169
+ configuration: /config|setting|environment|env/i,
1170
+ security: /auth|security|permission|role/i,
1171
+ infrastructure: /docker|kubernetes|deploy|infra/i,
1172
+ }
1173
+
1174
+ let impactScore = 0
1175
+ const detectedPatterns = new Set()
1176
+
1177
+ for (const change of changes) {
1178
+ const content = [
1179
+ change.filePath || change.path || '',
1180
+ change.diff || '',
1181
+ change.description || '',
1182
+ ].join(' ')
1183
+
1184
+ // Check for architectural patterns
1185
+ for (const [pattern, regex] of Object.entries(architecturalPatterns)) {
1186
+ if (regex.test(content)) {
1187
+ detectedPatterns.add(pattern)
1188
+ impactScore += 2
1189
+
1190
+ architecture.changes.push({
1191
+ file: change.filePath || change.path,
1192
+ pattern,
1193
+ type: change.status,
1194
+ })
1195
+ }
1196
+ }
1197
+
1198
+ // Check for directory structure changes
1199
+ const filePath = change.filePath || change.path || ''
1200
+ if (filePath.includes('/') && (change.status === 'A' || change.status === 'D')) {
1201
+ impactScore += 1
1202
+ architecture.changes.push({
1203
+ file: filePath,
1204
+ pattern: 'structure-change',
1205
+ type: change.status,
1206
+ })
1207
+ }
1208
+ }
1209
+
1210
+ // Determine impact level
1211
+ if (impactScore > 10) {
1212
+ architecture.impact = 'major'
1213
+ } else if (impactScore > 5) {
1214
+ architecture.impact = 'moderate'
1215
+ } else if (impactScore > 0) {
1216
+ architecture.impact = 'minor'
1217
+ }
1218
+
1219
+ architecture.patterns = Array.from(detectedPatterns)
1220
+
1221
+ // Generate recommendations
1222
+ if (detectedPatterns.has('database-schema')) {
1223
+ architecture.recommendations.push(
1224
+ 'Database schema changes detected - ensure migration scripts are tested'
1225
+ )
1226
+ }
1227
+ if (detectedPatterns.has('api-changes')) {
1228
+ architecture.recommendations.push(
1229
+ 'API changes detected - update documentation and versioning'
1230
+ )
1231
+ }
1232
+ if (architecture.impact === 'major') {
1233
+ architecture.recommendations.push(
1234
+ 'Major architectural changes - consider architectural review'
1235
+ )
1236
+ }
1237
+
1238
+ return architecture
1239
+ } catch (error) {
1240
+ console.warn(`Architectural analysis failed: ${error.message}`)
1241
+ return {
1242
+ impact: 'unknown',
1243
+ changes: [],
1244
+ patterns: [],
1245
+ recommendations: [`Analysis failed: ${error.message}`],
1246
+ }
570
1247
  }
571
1248
  }
572
1249
 
@@ -575,7 +1252,7 @@ export class AnalysisEngine {
575
1252
  return {
576
1253
  analysisCount: 0,
577
1254
  averageTime: 0,
578
- successRate: 100
1255
+ successRate: 100,
579
1256
  }
580
1257
  }
581
1258
  }