Go · 8050 bytes Raw Blame History
1 package llm
2
3 import (
4 "regexp"
5 "strings"
6 )
7
8 // ErrorCategory represents the type of error that occurred
9 type ErrorCategory string
10
11 const (
12 ErrorPermission ErrorCategory = "permission"
13 ErrorSyntax ErrorCategory = "syntax"
14 ErrorNetwork ErrorCategory = "network"
15 ErrorDependency ErrorCategory = "dependency"
16 ErrorConfiguration ErrorCategory = "config"
17 ErrorMergeConflict ErrorCategory = "merge_conflict"
18 ErrorTestFailure ErrorCategory = "test_failure"
19 ErrorBuildFailure ErrorCategory = "build_failure"
20 ErrorTimeout ErrorCategory = "timeout"
21 ErrorNotFound ErrorCategory = "not_found"
22 ErrorAuthentication ErrorCategory = "authentication"
23 ErrorDiskSpace ErrorCategory = "disk_space"
24 ErrorMemory ErrorCategory = "memory"
25 ErrorSegfault ErrorCategory = "segfault"
26 ErrorRaceCondition ErrorCategory = "race_condition"
27 ErrorDeprecated ErrorCategory = "deprecated"
28 ErrorLinting ErrorCategory = "linting"
29 ErrorTypeMismatch ErrorCategory = "type_mismatch"
30 ErrorNullPointer ErrorCategory = "null_pointer"
31 ErrorInfiniteLoop ErrorCategory = "infinite_loop"
32 ErrorGeneric ErrorCategory = "generic"
33 )
34
35 // ErrorClassifier analyzes commands and exit codes to determine error types
36 type ErrorClassifier struct {
37 exitCodePatterns map[int][]ErrorCategory
38 commandPatterns map[*regexp.Regexp]ErrorCategory
39 keywordPatterns map[string]ErrorCategory
40 }
41
42 // NewErrorClassifier creates a new error classifier
43 func NewErrorClassifier() *ErrorClassifier {
44 ec := &ErrorClassifier{
45 exitCodePatterns: make(map[int][]ErrorCategory),
46 commandPatterns: make(map[*regexp.Regexp]ErrorCategory),
47 keywordPatterns: make(map[string]ErrorCategory),
48 }
49 ec.initializePatterns()
50 return ec
51 }
52
53 func (ec *ErrorClassifier) initializePatterns() {
54 // Exit code mappings (common Unix exit codes)
55 ec.exitCodePatterns[1] = []ErrorCategory{ErrorGeneric}
56 ec.exitCodePatterns[2] = []ErrorCategory{ErrorSyntax}
57 ec.exitCodePatterns[126] = []ErrorCategory{ErrorPermission}
58 ec.exitCodePatterns[127] = []ErrorCategory{ErrorNotFound}
59 ec.exitCodePatterns[128] = []ErrorCategory{ErrorSegfault}
60 ec.exitCodePatterns[130] = []ErrorCategory{ErrorTimeout}
61 ec.exitCodePatterns[137] = []ErrorCategory{ErrorMemory} // SIGKILL
62 ec.exitCodePatterns[139] = []ErrorCategory{ErrorSegfault} // SIGSEGV
63 ec.exitCodePatterns[143] = []ErrorCategory{ErrorTimeout} // SIGTERM
64
65 // Command pattern mappings
66 ec.commandPatterns[regexp.MustCompile(`chmod|chown|sudo|permission`)] = ErrorPermission
67 ec.commandPatterns[regexp.MustCompile(`git\s+(merge|rebase|cherry-pick)`)] = ErrorMergeConflict
68 ec.commandPatterns[regexp.MustCompile(`(npm|yarn|pip|cargo|go)\s+(install|add)`)] = ErrorDependency
69 ec.commandPatterns[regexp.MustCompile(`(pytest|jest|cargo test|go test|npm test)`)] = ErrorTestFailure
70 ec.commandPatterns[regexp.MustCompile(`(make|cargo build|npm run build|go build|mvn compile)`)] = ErrorBuildFailure
71 ec.commandPatterns[regexp.MustCompile(`curl|wget|fetch|ping|ssh|scp`)] = ErrorNetwork
72 ec.commandPatterns[regexp.MustCompile(`docker login|git push.*http|auth|token`)] = ErrorAuthentication
73 ec.commandPatterns[regexp.MustCompile(`eslint|pylint|clippy|golint|rubocop`)] = ErrorLinting
74
75 // Keyword patterns (for error messages/command text analysis)
76 ec.keywordPatterns["permission denied"] = ErrorPermission
77 ec.keywordPatterns["eacces"] = ErrorPermission
78 ec.keywordPatterns["eperm"] = ErrorPermission
79 ec.keywordPatterns["access denied"] = ErrorPermission
80 ec.keywordPatterns["forbidden"] = ErrorPermission
81
82 ec.keywordPatterns["syntax error"] = ErrorSyntax
83 ec.keywordPatterns["parse error"] = ErrorSyntax
84 ec.keywordPatterns["unexpected token"] = ErrorSyntax
85 ec.keywordPatterns["invalid syntax"] = ErrorSyntax
86
87 ec.keywordPatterns["connection refused"] = ErrorNetwork
88 ec.keywordPatterns["timeout"] = ErrorTimeout
89 ec.keywordPatterns["connection timed out"] = ErrorTimeout
90 ec.keywordPatterns["network unreachable"] = ErrorNetwork
91 ec.keywordPatterns["could not resolve host"] = ErrorNetwork
92 ec.keywordPatterns["econnrefused"] = ErrorNetwork
93
94 ec.keywordPatterns["module not found"] = ErrorDependency
95 ec.keywordPatterns["package not found"] = ErrorDependency
96 ec.keywordPatterns["cannot find module"] = ErrorDependency
97 ec.keywordPatterns["no such file or directory"] = ErrorNotFound
98 ec.keywordPatterns["command not found"] = ErrorNotFound
99
100 ec.keywordPatterns["merge conflict"] = ErrorMergeConflict
101 ec.keywordPatterns["conflict (content)"] = ErrorMergeConflict
102 ec.keywordPatterns["both modified"] = ErrorMergeConflict
103
104 ec.keywordPatterns["authentication failed"] = ErrorAuthentication
105 ec.keywordPatterns["401"] = ErrorAuthentication
106 ec.keywordPatterns["403"] = ErrorPermission
107 ec.keywordPatterns["unauthorized"] = ErrorAuthentication
108
109 ec.keywordPatterns["no space left"] = ErrorDiskSpace
110 ec.keywordPatterns["disk full"] = ErrorDiskSpace
111 ec.keywordPatterns["enospc"] = ErrorDiskSpace
112
113 ec.keywordPatterns["out of memory"] = ErrorMemory
114 ec.keywordPatterns["enomem"] = ErrorMemory
115 ec.keywordPatterns["killed"] = ErrorMemory
116
117 ec.keywordPatterns["segmentation fault"] = ErrorSegfault
118 ec.keywordPatterns["sigsegv"] = ErrorSegfault
119 ec.keywordPatterns["core dumped"] = ErrorSegfault
120
121 ec.keywordPatterns["race detected"] = ErrorRaceCondition
122 ec.keywordPatterns["data race"] = ErrorRaceCondition
123
124 ec.keywordPatterns["deprecated"] = ErrorDeprecated
125 ec.keywordPatterns["no longer supported"] = ErrorDeprecated
126
127 ec.keywordPatterns["test failed"] = ErrorTestFailure
128 ec.keywordPatterns["assertion failed"] = ErrorTestFailure
129 ec.keywordPatterns["expected"] = ErrorTestFailure
130
131 ec.keywordPatterns["type error"] = ErrorTypeMismatch
132 ec.keywordPatterns["type mismatch"] = ErrorTypeMismatch
133 ec.keywordPatterns["cannot convert"] = ErrorTypeMismatch
134
135 ec.keywordPatterns["null pointer"] = ErrorNullPointer
136 ec.keywordPatterns["nil pointer"] = ErrorNullPointer
137 ec.keywordPatterns["nullptr"] = ErrorNullPointer
138 }
139
140 // ClassifyError analyzes the command and exit code to determine error categories
141 func (ec *ErrorClassifier) ClassifyError(command string, exitCode int, errorOutput string) []ErrorCategory {
142 categories := make(map[ErrorCategory]bool)
143
144 // Check exit code patterns
145 if exitCategories, exists := ec.exitCodePatterns[exitCode]; exists {
146 for _, cat := range exitCategories {
147 categories[cat] = true
148 }
149 }
150
151 // Check command patterns
152 commandLower := strings.ToLower(command)
153 for pattern, category := range ec.commandPatterns {
154 if pattern.MatchString(commandLower) {
155 categories[category] = true
156 }
157 }
158
159 // Check keyword patterns in command and error output
160 combinedText := strings.ToLower(command + " " + errorOutput)
161 for keyword, category := range ec.keywordPatterns {
162 if strings.Contains(combinedText, keyword) {
163 categories[category] = true
164 }
165 }
166
167 // Convert map to slice
168 result := make([]ErrorCategory, 0, len(categories))
169 for cat := range categories {
170 result = append(result, cat)
171 }
172
173 // If no specific category found, return generic
174 if len(result) == 0 {
175 result = append(result, ErrorGeneric)
176 }
177
178 return result
179 }
180
181 // GetPrimaryError returns the most specific error category
182 func (ec *ErrorClassifier) GetPrimaryError(command string, exitCode int, errorOutput string) ErrorCategory {
183 categories := ec.ClassifyError(command, exitCode, errorOutput)
184
185 // Priority order: specific errors first, generic last
186 priority := []ErrorCategory{
187 ErrorSegfault,
188 ErrorMergeConflict,
189 ErrorRaceCondition,
190 ErrorPermission,
191 ErrorAuthentication,
192 ErrorDiskSpace,
193 ErrorMemory,
194 ErrorTimeout,
195 ErrorNetwork,
196 ErrorDependency,
197 ErrorTestFailure,
198 ErrorBuildFailure,
199 ErrorLinting,
200 ErrorSyntax,
201 ErrorTypeMismatch,
202 ErrorNullPointer,
203 ErrorConfiguration,
204 ErrorDeprecated,
205 ErrorNotFound,
206 ErrorInfiniteLoop,
207 ErrorGeneric,
208 }
209
210 for _, prioCategory := range priority {
211 for _, foundCategory := range categories {
212 if foundCategory == prioCategory {
213 return prioCategory
214 }
215 }
216 }
217
218 return ErrorGeneric
219 }
220