| 1 | package llm |
| 2 | |
| 3 | import ( |
| 4 | "regexp" |
| 5 | "strings" |
| 6 | "time" |
| 7 | ) |
| 8 | |
| 9 | // CommandIntent represents what the user was trying to accomplish |
| 10 | type CommandIntent struct { |
| 11 | PrimaryIntent string // Main action: push, build, test, deploy, etc. |
| 12 | SecondaryIntents []string // Additional actions detected |
| 13 | Targets []string // What they were acting on: files, branches, containers |
| 14 | Complexity string // simple, moderate, complex |
| 15 | RiskLevel string // low, medium, high (based on destructiveness) |
| 16 | } |
| 17 | |
| 18 | // IntentParser extracts semantic meaning from commands |
| 19 | type IntentParser struct { |
| 20 | intentPatterns map[string]*regexp.Regexp |
| 21 | complexityIndicators []string |
| 22 | highRiskPatterns []*regexp.Regexp |
| 23 | } |
| 24 | |
| 25 | // NewIntentParser creates a new intent parser |
| 26 | func NewIntentParser() *IntentParser { |
| 27 | ip := &IntentParser{ |
| 28 | intentPatterns: make(map[string]*regexp.Regexp), |
| 29 | complexityIndicators: []string{ |
| 30 | "|", "&&", "||", "xargs", "awk", "sed", |
| 31 | }, |
| 32 | } |
| 33 | ip.initializePatterns() |
| 34 | return ip |
| 35 | } |
| 36 | |
| 37 | func (ip *IntentParser) initializePatterns() { |
| 38 | // Intent patterns |
| 39 | ip.intentPatterns["push"] = regexp.MustCompile(`(git\s+push|docker\s+push|npm\s+publish)`) |
| 40 | ip.intentPatterns["pull"] = regexp.MustCompile(`(git\s+pull|docker\s+pull)`) |
| 41 | ip.intentPatterns["commit"] = regexp.MustCompile(`git\s+commit`) |
| 42 | ip.intentPatterns["merge"] = regexp.MustCompile(`git\s+(merge|rebase)`) |
| 43 | ip.intentPatterns["build"] = regexp.MustCompile(`(make|cargo\s+build|npm\s+run\s+build|go\s+build|mvn\s+compile|gradle\s+build|docker\s+build)`) |
| 44 | ip.intentPatterns["test"] = regexp.MustCompile(`(test|jest|pytest|cargo\s+test|go\s+test|npm\s+test|mvn\s+test)`) |
| 45 | ip.intentPatterns["deploy"] = regexp.MustCompile(`(deploy|kubectl\s+apply|helm\s+install|terraform\s+apply)`) |
| 46 | ip.intentPatterns["install"] = regexp.MustCompile(`(install|npm\s+i|pip\s+install|cargo\s+add|go\s+get|apt\s+install|brew\s+install)`) |
| 47 | ip.intentPatterns["configure"] = regexp.MustCompile(`(config|configure|setup|init)`) |
| 48 | ip.intentPatterns["debug"] = regexp.MustCompile(`(debug|gdb|lldb|strace|lsof)`) |
| 49 | ip.intentPatterns["refactor"] = regexp.MustCompile(`(refactor|rename|move\s+.*\.(js|ts|py|rs|go))`) |
| 50 | ip.intentPatterns["lint"] = regexp.MustCompile(`(lint|eslint|pylint|clippy|golint|rubocop|prettier)`) |
| 51 | ip.intentPatterns["format"] = regexp.MustCompile(`(format|prettier|black|rustfmt|gofmt)`) |
| 52 | ip.intentPatterns["start"] = regexp.MustCompile(`(start|run|serve|up)`) |
| 53 | ip.intentPatterns["stop"] = regexp.MustCompile(`(stop|kill|down)`) |
| 54 | ip.intentPatterns["clean"] = regexp.MustCompile(`(clean|prune|rm|remove)`) |
| 55 | ip.intentPatterns["revert"] = regexp.MustCompile(`git\s+(revert|reset|checkout)`) |
| 56 | |
| 57 | // High risk patterns |
| 58 | ip.highRiskPatterns = []*regexp.Regexp{ |
| 59 | regexp.MustCompile(`--force`), |
| 60 | regexp.MustCompile(`-f\s`), |
| 61 | regexp.MustCompile(`rm\s+-rf`), |
| 62 | regexp.MustCompile(`git\s+reset\s+--hard`), |
| 63 | regexp.MustCompile(`drop\s+(database|table)`), |
| 64 | regexp.MustCompile(`kubectl\s+delete`), |
| 65 | regexp.MustCompile(`terraform\s+destroy`), |
| 66 | regexp.MustCompile(`docker\s+system\s+prune`), |
| 67 | regexp.MustCompile(`sudo\s+rm`), |
| 68 | regexp.MustCompile(`chmod\s+777`), |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | // ParseIntent analyzes a command to extract intent |
| 73 | func (ip *IntentParser) ParseIntent(command string) CommandIntent { |
| 74 | intent := CommandIntent{ |
| 75 | PrimaryIntent: "unknown", |
| 76 | SecondaryIntents: []string{}, |
| 77 | Targets: []string{}, |
| 78 | Complexity: "simple", |
| 79 | RiskLevel: "low", |
| 80 | } |
| 81 | |
| 82 | commandLower := strings.ToLower(command) |
| 83 | |
| 84 | // Detect intents |
| 85 | intentsFound := make(map[string]bool) |
| 86 | for intentName, pattern := range ip.intentPatterns { |
| 87 | if pattern.MatchString(commandLower) { |
| 88 | intentsFound[intentName] = true |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | // Set primary and secondary intents |
| 93 | if len(intentsFound) > 0 { |
| 94 | // Priority order for primary intent |
| 95 | priorityOrder := []string{ |
| 96 | "deploy", "push", "build", "test", "merge", |
| 97 | "commit", "install", "configure", "start", |
| 98 | "stop", "clean", "revert", "debug", |
| 99 | } |
| 100 | |
| 101 | for _, priority := range priorityOrder { |
| 102 | if intentsFound[priority] { |
| 103 | intent.PrimaryIntent = priority |
| 104 | delete(intentsFound, priority) |
| 105 | break |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | // Remaining are secondary |
| 110 | for secondary := range intentsFound { |
| 111 | intent.SecondaryIntents = append(intent.SecondaryIntents, secondary) |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | // Extract targets (file names, branch names, etc.) |
| 116 | intent.Targets = ip.extractTargets(command) |
| 117 | |
| 118 | // Determine complexity |
| 119 | intent.Complexity = ip.assessComplexity(command) |
| 120 | |
| 121 | // Determine risk level |
| 122 | intent.RiskLevel = ip.assessRisk(command) |
| 123 | |
| 124 | return intent |
| 125 | } |
| 126 | |
| 127 | func (ip *IntentParser) extractTargets(command string) []string { |
| 128 | targets := []string{} |
| 129 | |
| 130 | // Extract git branches |
| 131 | branchPattern := regexp.MustCompile(`(origin/|refs/heads/)?(main|master|develop|feature/[\w-]+|bugfix/[\w-]+)`) |
| 132 | if matches := branchPattern.FindAllString(command, -1); len(matches) > 0 { |
| 133 | targets = append(targets, matches...) |
| 134 | } |
| 135 | |
| 136 | // Extract file paths |
| 137 | filePattern := regexp.MustCompile(`[\w/.-]+\.(js|ts|py|rs|go|java|cpp|c|rb|php|json|yaml|yml|toml|md)`) |
| 138 | if matches := filePattern.FindAllString(command, -1); len(matches) > 0 { |
| 139 | targets = append(targets, matches...) |
| 140 | } |
| 141 | |
| 142 | // Extract container/image names |
| 143 | containerPattern := regexp.MustCompile(`[\w.-]+:[\w.-]+`) |
| 144 | if matches := containerPattern.FindAllString(command, -1); len(matches) > 0 { |
| 145 | targets = append(targets, matches...) |
| 146 | } |
| 147 | |
| 148 | return targets |
| 149 | } |
| 150 | |
| 151 | func (ip *IntentParser) assessComplexity(command string) string { |
| 152 | // Check for complexity indicators |
| 153 | complexityScore := 0 |
| 154 | |
| 155 | for _, indicator := range ip.complexityIndicators { |
| 156 | if strings.Contains(command, indicator) { |
| 157 | complexityScore++ |
| 158 | } |
| 159 | } |
| 160 | |
| 161 | // Length also indicates complexity |
| 162 | if len(command) > 100 { |
| 163 | complexityScore++ |
| 164 | } |
| 165 | |
| 166 | // Multiple commands |
| 167 | if strings.Contains(command, "&&") || strings.Contains(command, ";") { |
| 168 | complexityScore++ |
| 169 | } |
| 170 | |
| 171 | if complexityScore == 0 { |
| 172 | return "simple" |
| 173 | } else if complexityScore <= 2 { |
| 174 | return "moderate" |
| 175 | } |
| 176 | return "complex" |
| 177 | } |
| 178 | |
| 179 | func (ip *IntentParser) assessRisk(command string) string { |
| 180 | for _, pattern := range ip.highRiskPatterns { |
| 181 | if pattern.MatchString(command) { |
| 182 | return "high" |
| 183 | } |
| 184 | } |
| 185 | |
| 186 | // Moderate risk patterns |
| 187 | moderateRiskKeywords := []string{ |
| 188 | "delete", "remove", "drop", "destroy", |
| 189 | "force", "hard", "production", "master", "main", |
| 190 | } |
| 191 | |
| 192 | commandLower := strings.ToLower(command) |
| 193 | for _, keyword := range moderateRiskKeywords { |
| 194 | if strings.Contains(commandLower, keyword) { |
| 195 | return "medium" |
| 196 | } |
| 197 | } |
| 198 | |
| 199 | return "low" |
| 200 | } |
| 201 | |
| 202 | // ContextualTags generates semantic tags based on context |
| 203 | func ContextualTags(ctx *SmartFallbackContext, intent CommandIntent) []InsultTag { |
| 204 | tags := []InsultTag{} |
| 205 | |
| 206 | // Add error-based tags |
| 207 | if ctx.ErrorPattern != "" { |
| 208 | switch ctx.ErrorPattern { |
| 209 | case "permission_denied": |
| 210 | tags = append(tags, TagPermission) |
| 211 | case "merge_conflict": |
| 212 | tags = append(tags, TagMergeConflict) |
| 213 | case "syntax_error": |
| 214 | tags = append(tags, TagSyntax) |
| 215 | case "network_error": |
| 216 | tags = append(tags, TagNetwork) |
| 217 | case "timeout": |
| 218 | tags = append(tags, TagTimeout) |
| 219 | } |
| 220 | } |
| 221 | |
| 222 | // Add command type tags |
| 223 | switch ctx.CommandType { |
| 224 | case "git": |
| 225 | tags = append(tags, TagGit) |
| 226 | case "docker": |
| 227 | tags = append(tags, TagDocker) |
| 228 | case "kubernetes": |
| 229 | tags = append(tags, TagKubernetes) |
| 230 | case "nodejs": |
| 231 | tags = append(tags, TagNode) |
| 232 | case "python": |
| 233 | tags = append(tags, TagPython) |
| 234 | case "rust": |
| 235 | tags = append(tags, TagRust) |
| 236 | case "golang": |
| 237 | tags = append(tags, TagGolang) |
| 238 | } |
| 239 | |
| 240 | // Add intent tags |
| 241 | switch intent.PrimaryIntent { |
| 242 | case "push": |
| 243 | tags = append(tags, TagPush) |
| 244 | case "pull": |
| 245 | tags = append(tags, TagPull) |
| 246 | case "commit": |
| 247 | tags = append(tags, TagCommit) |
| 248 | case "build": |
| 249 | tags = append(tags, TagBuild) |
| 250 | case "test": |
| 251 | tags = append(tags, TagTest) |
| 252 | case "deploy": |
| 253 | tags = append(tags, TagDeploy) |
| 254 | case "install": |
| 255 | tags = append(tags, TagInstall) |
| 256 | case "revert": |
| 257 | tags = append(tags, TagRevert) |
| 258 | } |
| 259 | |
| 260 | // Add time-based tags |
| 261 | hour := ctx.TimeOfDay |
| 262 | if hour >= 22 || hour <= 4 { |
| 263 | tags = append(tags, TagLateNight) |
| 264 | } else if hour >= 5 && hour <= 7 { |
| 265 | tags = append(tags, TagEarlyMorning) |
| 266 | } |
| 267 | |
| 268 | // Weekend check |
| 269 | if time.Now().Weekday() == time.Saturday || time.Now().Weekday() == time.Sunday { |
| 270 | tags = append(tags, TagWeekend) |
| 271 | } |
| 272 | |
| 273 | // Add context tags |
| 274 | if ctx.IsCI { |
| 275 | tags = append(tags, TagCI) |
| 276 | } |
| 277 | |
| 278 | if ctx.GitBranch == "main" || ctx.GitBranch == "master" { |
| 279 | tags = append(tags, TagMainBranch) |
| 280 | } |
| 281 | |
| 282 | if ctx.IsRepeatedFailure { |
| 283 | tags = append(tags, TagRepeated) |
| 284 | } |
| 285 | |
| 286 | // Complexity tags |
| 287 | switch intent.Complexity { |
| 288 | case "simple": |
| 289 | tags = append(tags, TagSimple) |
| 290 | case "complex": |
| 291 | tags = append(tags, TagComplex) |
| 292 | } |
| 293 | |
| 294 | // Risk tags |
| 295 | if intent.RiskLevel == "high" { |
| 296 | tags = append(tags, TagProduction) |
| 297 | } |
| 298 | |
| 299 | return tags |
| 300 | } |
| 301 |