@@ -153,6 +153,16 @@ func (a *Analyzer) AnalyzeChanges(totalAdded, totalRemoved int, branchName strin
153153 }
154154 }
155155
156+ // NEW: Monitoring Dependency Changes (Dependency Watcher)
157+ newDeps := a .detectNewDependencies ()
158+ if len (newDeps ) > 0 {
159+ commitMessage .Action = "chore"
160+ commitMessage .Scope = "deps"
161+ commitMessage .Item = strings .Join (newDeps , ", " )
162+ commitMessage .Purpose = "update dependencies"
163+ return commitMessage // Priority return for dependency updates
164+ }
165+
156166 // NEW: Learning from recent commit history (Commit History Consistency)
157167 if historyScope := a .analyzeHistoryScopes (); historyScope != "" {
158168 // Only override if scope is empty or "core"
@@ -762,113 +772,32 @@ func (a *Analyzer) detectFunctions(diff string) []string {
762772 var functions []string
763773 scanner := bufio .NewScanner (strings .NewReader (diff ))
764774
775+ // Regex registry for functions
776+ patterns := map [string ]* regexp.Regexp {
777+ "go" : regexp .MustCompile (`func\s+(?:\([^)]*\)\s+)?([A-Z][A-Za-z0-9]*)` ),
778+ "ts" : regexp .MustCompile (`(?:function\s+([a-zA-Z0-9]*)|const\s+([a-zA-Z0-9]*)\s*=\s*(?:\([^)]*\)|[a-zA-Z0-9]*)\s*=>)` ),
779+ "js" : regexp .MustCompile (`(?:function\s+([a-zA-Z0-9]*)|const\s+([a-zA-Z0-9]*)\s*=\s*(?:\([^)]*\)|[a-zA-Z0-9]*)\s*=>)` ),
780+ "python" : regexp .MustCompile (`def\s+([a-zA-Z0-9_]+)\s*\(` ),
781+ "java" : regexp .MustCompile (`(?:public|private|protected|static)\s+(?:[\w<>[\]]+\s+)+([a-zA-Z0-9_]+)\s*\(` ),
782+ }
783+
765784 for scanner .Scan () {
766785 line := scanner .Text ()
767-
768- // Only look at added lines
769- if ! strings .HasPrefix (line , "+" ) {
786+ if ! strings .HasPrefix (line , "+" ) || strings .HasPrefix (line , "+++" ) {
770787 continue
771788 }
772789
773- cleanLine := strings .TrimSpace (strings .TrimPrefix (line , "+" ))
774-
775- // Go functions
776- if strings .HasPrefix (cleanLine , "func " ) {
777- // Extract function name: func FunctionName( or func (receiver) MethodName(
778- if strings .Contains (cleanLine , "(" ) {
779- // Check for method receiver
780- if cleanLine [5 ] == '(' {
781- // Method: func (r Receiver) MethodName
782- parts := strings .SplitN (cleanLine [5 :], ")" , 2 )
783- if len (parts ) == 2 {
784- methodPart := strings .TrimSpace (parts [1 ])
785- if idx := strings .Index (methodPart , "(" ); idx > 0 {
786- methodName := strings .TrimSpace (methodPart [:idx ])
787- if methodName != "" {
788- functions = append (functions , methodName )
789- }
790- }
791- }
792- } else {
793- // Regular function: func FunctionName(
794- parts := strings .Fields (cleanLine )
795- if len (parts ) >= 2 {
796- funcName := strings .Split (parts [1 ], "(" )[0 ]
797- if funcName != "" {
798- functions = append (functions , funcName )
799- }
800- }
801- }
802- }
803- }
804-
805- // JavaScript/TypeScript functions
806- if strings .Contains (cleanLine , "function " ) {
807- // function functionName( or function(
808- idx := strings .Index (cleanLine , "function " )
809- if idx >= 0 {
810- remaining := cleanLine [idx + 9 :]
811- if parenIdx := strings .Index (remaining , "(" ); parenIdx > 0 {
812- funcName := strings .TrimSpace (remaining [:parenIdx ])
813- if funcName != "" && funcName != "function" {
814- functions = append (functions , funcName )
815- }
816- }
817- }
818- }
819-
820- // Arrow functions: const funcName = () =>
821- if strings .Contains (cleanLine , "=>" ) && (strings .Contains (cleanLine , "const " ) || strings .Contains (cleanLine , "let " ) || strings .Contains (cleanLine , "var " )) {
822- // Extract: const funcName = ...
823- for _ , prefix := range []string {"const " , "let " , "var " } {
824- if strings .Contains (cleanLine , prefix ) {
825- idx := strings .Index (cleanLine , prefix )
826- remaining := cleanLine [idx + len (prefix ):]
827- if eqIdx := strings .Index (remaining , "=" ); eqIdx > 0 {
828- funcName := strings .TrimSpace (remaining [:eqIdx ])
829- if funcName != "" {
830- functions = append (functions , funcName )
831- }
832- }
833- break
834- }
835- }
836- }
790+ cleanLine := strings .TrimPrefix (line , "+" )
837791
838- // Python functions
839- if strings .HasPrefix (cleanLine , "def " ) || strings .HasPrefix (cleanLine , "async def " ) {
840- // Extract: def function_name( or async def function_name(
841- var remaining string
842- if strings .HasPrefix (cleanLine , "async def " ) {
843- remaining = cleanLine [10 :]
844- } else {
845- remaining = cleanLine [4 :]
846- }
847- if parenIdx := strings .Index (remaining , "(" ); parenIdx > 0 {
848- funcName := strings .TrimSpace (remaining [:parenIdx ])
849- if funcName != "" {
850- functions = append (functions , funcName )
851- }
852- }
853- }
854-
855- // Java/C/C++ methods
856- // Pattern: public/private/protected Type methodName(
857- if strings .Contains (cleanLine , "(" ) {
858- for _ , modifier := range []string {"public " , "private " , "protected " , "static " } {
859- if strings .Contains (cleanLine , modifier ) {
860- parts := strings .Fields (cleanLine )
861- // Find the part before (
862- for _ , part := range parts {
863- if strings .Contains (part , "(" ) {
864- funcName := strings .Split (part , "(" )[0 ]
865- if funcName != "" && funcName != "if" && funcName != "for" && funcName != "while" && funcName != "switch" {
866- functions = append (functions , funcName )
867- break
868- }
869- }
792+ for _ , re := range patterns {
793+ matches := re .FindStringSubmatch (cleanLine )
794+ if len (matches ) > 0 {
795+ // The first captured group (that is not empty) is the function name
796+ for i := 1 ; i < len (matches ); i ++ {
797+ if matches [i ] != "" {
798+ functions = append (functions , matches [i ])
799+ break
870800 }
871- break
872801 }
873802 }
874803 }
@@ -881,87 +810,27 @@ func (a *Analyzer) detectStructs(diff string) []string {
881810 var structs []string
882811 scanner := bufio .NewScanner (strings .NewReader (diff ))
883812
813+ // Regex registry for structs/classes
814+ patterns := map [string ]* regexp.Regexp {
815+ "go" : regexp .MustCompile (`type\s+([A-Z][A-Za-z0-9]*)\s+(?:struct|interface)` ),
816+ "ts" : regexp .MustCompile (`class\s+([a-zA-Z0-9]*)` ),
817+ "js" : regexp .MustCompile (`class\s+([a-zA-Z0-9]*)` ),
818+ "python" : regexp .MustCompile (`class\s+([a-zA-Z0-9_]+)\s*(?:\(|:)` ),
819+ "java" : regexp .MustCompile (`(?:public|private|protected|abstract)?\s*class\s+([a-zA-Z0-9_]+)` ),
820+ }
821+
884822 for scanner .Scan () {
885823 line := scanner .Text ()
886-
887- // Only look at added lines
888- if ! strings .HasPrefix (line , "+" ) {
824+ if ! strings .HasPrefix (line , "+" ) || strings .HasPrefix (line , "+++" ) {
889825 continue
890826 }
891827
892- cleanLine := strings .TrimSpace ( strings . TrimPrefix (line , "+" ) )
828+ cleanLine := strings .TrimPrefix (line , "+" )
893829
894- // Go structs and interfaces
895- if strings .HasPrefix (cleanLine , "type " ) && (strings .Contains (cleanLine , "struct" ) || strings .Contains (cleanLine , "interface" )) {
896- parts := strings .Fields (cleanLine )
897- if len (parts ) >= 2 {
898- structName := parts [1 ]
899- if structName != "" {
900- structs = append (structs , structName )
901- }
902- }
903- }
904-
905- // JavaScript/TypeScript classes
906- if strings .HasPrefix (cleanLine , "class " ) || strings .HasPrefix (cleanLine , "export class " ) {
907- var remaining string
908- if strings .HasPrefix (cleanLine , "export class " ) {
909- remaining = cleanLine [13 :]
910- } else {
911- remaining = cleanLine [6 :]
912- }
913-
914- // Extract class name (before space, { or extends)
915- className := remaining
916- for _ , delimiter := range []string {" " , "{" , "extends" } {
917- if idx := strings .Index (className , delimiter ); idx > 0 {
918- className = className [:idx ]
919- break
920- }
921- }
922- className = strings .TrimSpace (className )
923- if className != "" {
924- structs = append (structs , className )
925- }
926- }
927-
928- // Python classes
929- if strings .HasPrefix (cleanLine , "class " ) {
930- remaining := cleanLine [6 :]
931- // Extract class name (before ( or :)
932- className := remaining
933- for _ , delimiter := range []string {"(" , ":" } {
934- if idx := strings .Index (className , delimiter ); idx > 0 {
935- className = className [:idx ]
936- break
937- }
938- }
939- className = strings .TrimSpace (className )
940- if className != "" {
941- structs = append (structs , className )
942- }
943- }
944-
945- // Java classes
946- if strings .Contains (cleanLine , "class " ) {
947- for _ , modifier := range []string {"public class " , "private class " , "protected class " , "abstract class " } {
948- if strings .Contains (cleanLine , modifier ) {
949- idx := strings .Index (cleanLine , modifier )
950- remaining := cleanLine [idx + len (modifier ):]
951- // Extract class name (before space, { or extends/implements)
952- className := remaining
953- for _ , delimiter := range []string {" " , "{" , "extends" , "implements" } {
954- if idx := strings .Index (className , delimiter ); idx > 0 {
955- className = className [:idx ]
956- break
957- }
958- }
959- className = strings .TrimSpace (className )
960- if className != "" {
961- structs = append (structs , className )
962- }
963- break
964- }
830+ for _ , re := range patterns {
831+ matches := re .FindStringSubmatch (cleanLine )
832+ if len (matches ) > 1 && matches [1 ] != "" {
833+ structs = append (structs , matches [1 ])
965834 }
966835 }
967836 }
@@ -1141,37 +1010,63 @@ func (a *Analyzer) analyzeDiffStat(totalAdded, totalRemoved int) string {
11411010 return ""
11421011 }
11431012
1144- deletedRatio := float64 ( totalRemoved ) / float64 ( total )
1145- addedRatio := float64 (totalAdded ) / float64 (total )
1013+ // Structural Ratio Calculation
1014+ ratio := float64 (totalAdded ) / float64 (total )
11461015
11471016 threshold := a .config .DiffStatThreshold
11481017 if threshold == 0 {
11491018 threshold = 0.5
11501019 }
11511020
1152- // If deleted lines dominate, suggest cleanup or refactor
1153- if deletedRatio > threshold + 0.2 { // More than 70% deletions
1021+ // If deletions heavily dominate (Ratio < 0.2)
1022+ if ratio < 0.2 {
11541023 return "refactor"
11551024 }
11561025
1157- // If a large number of lines are added with minimal deletions, suggest feat
1158- if addedRatio > threshold + 0.2 && totalAdded > 50 {
1159- // Check if it's a new file addition
1160- for _ , change := range a .changes {
1161- if change .Action == "A" && change .Added > 30 {
1162- return "feat"
1163- }
1026+ // If additions heavily dominate (Ratio > 0.8)
1027+ if ratio > 0.8 {
1028+ // If many lines added, likely a feature
1029+ if totalAdded > 30 {
1030+ return "feat"
11641031 }
11651032 }
11661033
11671034 // Balanced changes often indicate modifications or fixes
1168- if deletedRatio > 0.3 && addedRatio > 0.3 {
1035+ if ratio >= 0.3 && ratio <= 0.7 {
11691036 return "refactor"
11701037 }
11711038
11721039 return ""
11731040}
11741041
1042+ // detectNewDependencies identifies newly added libraries in package management files
1043+ func (a * Analyzer ) detectNewDependencies () []string {
1044+ var newDeps []string
1045+ depFiles := map [string ]* regexp.Regexp {
1046+ "go.mod" : regexp .MustCompile (`^\+\s+([^\s]+)\s+v` ),
1047+ "package.json" : regexp .MustCompile (`^\+\s+"([^"]+)":` ),
1048+ "requirements.txt" : regexp .MustCompile (`^\+([a-zA-Z0-9\-_]+)==` ),
1049+ "Cargo.toml" : regexp .MustCompile (`^\+([a-zA-Z0-9\-_]+)\s+=` ),
1050+ }
1051+
1052+ for _ , change := range a .changes {
1053+ fileName := filepath .Base (change .File )
1054+ if re , ok := depFiles [fileName ]; ok {
1055+ scanner := bufio .NewScanner (strings .NewReader (change .Diff ))
1056+ for scanner .Scan () {
1057+ line := scanner .Text ()
1058+ if strings .HasPrefix (line , "+" ) && ! strings .HasPrefix (line , "+++" ) {
1059+ matches := re .FindStringSubmatch (line )
1060+ if len (matches ) > 1 {
1061+ newDeps = append (newDeps , matches [1 ])
1062+ }
1063+ }
1064+ }
1065+ }
1066+ }
1067+ return uniqueStrings (newDeps )
1068+ }
1069+
11751070// parseBranchName extracts type and scope from branch name
11761071func (a * Analyzer ) parseBranchName (branch string ) (string , string ) {
11771072 // Patterns like feature/auth-login or bugfix/fix-memleak
0 commit comments