Go · 50905 bytes Raw Blame History
1 package llm
2
3 import (
4 "os"
5 "path/filepath"
6 "regexp"
7 "strconv"
8 "strings"
9 "sync"
10 "time"
11 )
12
13 // SmartFallbackContext contains all context needed for intelligent fallback generation
14 type SmartFallbackContext struct {
15 Command string
16 CommandType string
17 Subcommand string
18 Arguments []string
19 ExitCode int
20 FullCommand string
21
22 // Tier 1 Intelligence
23 Environment map[string]string
24 WorkingDir string
25 TimeOfDay int // Hour 0-23
26 FileExtensions []string
27 CommandLength int
28 NumericArgs []int
29 Shell string
30 HasPipes bool
31 HasChaining bool
32
33 // Tier 2 Intelligence
34 GitBranch string
35 ProjectType string // "node", "rust", "go", "python", "java", etc.
36 ProjectFiles []string // List of project files found
37
38 // Tier 3 Intelligence
39 IsCI bool // Running in CI/CD environment
40 CIProvider string // "github", "gitlab", "jenkins", "circle", etc.
41 HasDockerfile bool
42 HasMakefile bool
43 DependencyCount int // Rough estimate of dependencies
44 ErrorPattern string // Detected error pattern
45 IsRepeatedFailure bool // Same command failed recently
46 }
47
48 // ParseCommandContext extracts context from a command for intelligent fallback
49 func ParseCommandContext(command string, commandType string, exitCode string) SmartFallbackContext {
50 ctx := SmartFallbackContext{
51 FullCommand: command,
52 CommandType: commandType,
53 Environment: make(map[string]string),
54 }
55
56 // Parse exit code
57 if ec, err := strconv.Atoi(exitCode); err == nil {
58 ctx.ExitCode = ec
59 }
60
61 // Parse command into parts
62 parts := strings.Fields(command)
63 if len(parts) == 0 {
64 return ctx
65 }
66
67 ctx.Command = parts[0]
68 if len(parts) > 1 {
69 ctx.Subcommand = parts[1]
70 ctx.Arguments = parts[2:]
71 }
72
73 // Tier 1 Intelligence Gathering
74 ctx.CommandLength = len(command)
75 ctx.TimeOfDay = time.Now().Hour()
76 ctx.HasPipes = strings.Contains(command, "|")
77 ctx.HasChaining = strings.Contains(command, "&&") || strings.Contains(command, "||")
78
79 // Get working directory
80 if wd, err := os.Getwd(); err == nil {
81 ctx.WorkingDir = wd
82 }
83
84 // Get shell type
85 ctx.Shell = filepath.Base(os.Getenv("SHELL"))
86
87 // Collect interesting environment variables
88 envVars := []string{"CI", "DEBUG", "NODE_ENV", "PROD", "PRODUCTION", "STAGING", "DEVELOPMENT",
89 "GITHUB_ACTIONS", "GITLAB_CI", "JENKINS_HOME", "CIRCLECI", "TRAVIS"}
90 for _, envVar := range envVars {
91 if val := os.Getenv(envVar); val != "" {
92 ctx.Environment[envVar] = val
93 }
94 }
95
96 // Extract file extensions from command
97 extRegex := regexp.MustCompile(`\.\w{1,6}\b`)
98 exts := extRegex.FindAllString(command, -1)
99 ctx.FileExtensions = exts
100
101 // Extract numeric arguments (ports, chmod values, PIDs, etc.)
102 numRegex := regexp.MustCompile(`\b\d+\b`)
103 nums := numRegex.FindAllString(command, -1)
104 for _, num := range nums {
105 if n, err := strconv.Atoi(num); err == nil {
106 ctx.NumericArgs = append(ctx.NumericArgs, n)
107 }
108 }
109
110 // Tier 2 Intelligence Gathering
111
112 // Detect git branch if in a git repo
113 ctx.GitBranch = detectGitBranch()
114
115 // Detect project type by checking for common project files
116 ctx.ProjectType, ctx.ProjectFiles = detectProjectType()
117
118 // Tier 3 Intelligence Gathering
119
120 // Detect CI/CD environment
121 ctx.IsCI, ctx.CIProvider = detectCIEnvironment(ctx.Environment)
122
123 // Detect Docker and build files
124 ctx.HasDockerfile = fileExists("Dockerfile") || fileExists("docker-compose.yml")
125 ctx.HasMakefile = fileExists("Makefile")
126
127 // Estimate dependency count
128 ctx.DependencyCount = estimateDependencyCount(ctx.ProjectType)
129
130 // Detect common error patterns from exit code
131 ctx.ErrorPattern = detectErrorPattern(ctx.ExitCode, command)
132
133 // Track repeated failures (simple in-process tracking)
134 ctx.IsRepeatedFailure = trackFailure(command)
135
136 return ctx
137 }
138
139 // Global insult scorer and database (initialized once)
140 var (
141 insultDB *InsultDatabase
142 insultScorer *InsultScorer
143 insultHist *InsultHistory
144 ensembleSystem *EnsembleSystem
145
146 // TIER 6 INTELLIGENCE - Novel ML-inspired systems
147 contextualGraph *ContextualMemoryGraph
148 adversarialGen *AdversarialInsultGenerator
149 editDistanceMatcher *EditDistanceMatcher
150 embeddingEngine *EmbeddingEngine
151 )
152
153 func init() {
154 // Initialize ML-inspired components on first use
155 insultDB = NewInsultDatabase()
156 insultScorer = NewInsultScorer(insultDB)
157 insultHist = NewInsultHistory(20) // Track last 20 insults
158
159 // Initialize the ensemble system (combines TF-IDF, Markov, and tag-based scoring)
160 ensembleSystem = NewEnsembleSystem(insultDB, insultScorer, insultHist)
161
162 // Train the ensemble system on startup (async to avoid blocking)
163 go ensembleSystem.Train()
164
165 // TIER 6 INTELLIGENCE - Initialize novel systems
166 contextualGraph = NewContextualMemoryGraph()
167 adversarialGen = NewAdversarialInsultGenerator(insultDB, ensembleSystem.markovGen)
168 editDistanceMatcher = NewEditDistanceMatcher()
169 embeddingEngine = NewEmbeddingEngine()
170
171 // Periodically apply decay to keep system fresh
172 go func() {
173 ticker := time.NewTicker(1 * time.Hour)
174 defer ticker.Stop()
175 for range ticker.C {
176 contextualGraph.ApplyDecay()
177 }
178 }()
179 }
180
181 // GenerateSmartFallback generates a context-aware insult
182 func GenerateSmartFallback(ctx SmartFallbackContext) string {
183 // Load user history for Tier 4 intelligence
184 history := LoadUserHistory()
185
186 // Record this failure
187 if history != nil {
188 history.RecordFailure(ctx)
189 }
190
191 // TIER 6 INTELLIGENCE - Novel ML-Inspired Systems (HIGHEST PRIORITY)
192 // These are cutting-edge techniques never before seen in CLI tools
193
194 // 1. Contextual Memory Graph - Track failure relationships
195 contextID := contextualGraph.RecordContext(&ctx)
196
197 // Check for transition-specific insults (failure sequences)
198 if transitionInsult := contextualGraph.GetTransitionInsult(contextualGraph.previousContext, contextID); transitionInsult != "" {
199 // Record use for RL
200 contextualGraph.RecordInsultUse(contextID, transitionInsult)
201 editDistanceMatcher.RecordCommand(&ctx, transitionInsult, 0.8)
202 return transitionInsult
203 }
204
205 // 2. Adversarial Generation System (GAN-inspired)
206 // Generator vs. Critic competing for best insult
207 personality := "sarcastic"
208 if config := LoadConfig(); config != nil && config.General.Personality != "" {
209 personality = config.General.Personality
210 }
211
212 if adversarialInsult := adversarialGen.Generate(&ctx, personality); adversarialInsult != "" {
213 // Record for learning
214 contextualGraph.AddInsultToPool(contextID, adversarialInsult, 0.9)
215 contextualGraph.RecordInsultUse(contextID, adversarialInsult)
216 editDistanceMatcher.RecordCommand(&ctx, adversarialInsult, 0.75)
217 return adversarialInsult
218 }
219
220 // 3. Edit Distance Matcher - Adapt insults from similar past failures
221 if adaptedInsult := editDistanceMatcher.GetAdaptedInsult(&ctx); adaptedInsult != "" {
222 contextualGraph.AddInsultToPool(contextID, adaptedInsult, 0.85)
223 contextualGraph.RecordInsultUse(contextID, adaptedInsult)
224 return adaptedInsult
225 }
226
227 // 4. Contextual Graph Memory - Use specialized insult pool
228 if graphInsult := contextualGraph.GetContextualInsult(contextID); graphInsult != "" {
229 contextualGraph.RecordInsultUse(contextID, graphInsult)
230 return graphInsult
231 }
232
233 // TIER 5 INTELLIGENCE - ML-Inspired Semantic Matching
234 // Uses error classification, intent parsing, and multi-factor scoring
235 if insult := generateMLInsult(ctx); insult != "" {
236 // Feed back to Tier 6 systems for learning
237 contextualGraph.AddInsultToPool(contextID, insult, 0.7)
238 editDistanceMatcher.RecordCommand(&ctx, insult, 0.7)
239 return insult
240 }
241
242 // TIER 4 INTELLIGENCE - Historical Learning & Dynamic Generation
243
244 // 1. Failure streak escalation (gets more brutal over time)
245 if history != nil && history.CurrentStreak >= 2 {
246 if insult := GenerateStreakEscalation(history.CurrentStreak, ctx); insult != "" {
247 return insult
248 }
249 }
250
251 // 2. Historical pattern-based insults (learns from past failures)
252 if history != nil && history.TotalFailures > 0 {
253 if insult := GenerateHistoricalInsult(history, ctx); insult != "" {
254 return insult
255 }
256 }
257
258 // 3. Dynamic template-based generation (infinite combinations)
259 if insult := GenerateDynamicInsult(ctx, history); insult != "" {
260 return insult
261 }
262
263 // 4. Savage mode (brutal, devastating insults)
264 // Activate savage mode for: high streaks, high total failures, or specific contexts
265 if history != nil && (history.CurrentStreak >= 5 || history.TotalFailures >= 50) {
266 if insult := GetAnySavageInsult(ctx.FullCommand); insult != "" {
267 return insult
268 }
269 }
270
271 // Tier 3 Intelligence - LLM-like awareness
272
273 // 5. Repeated failure escalation
274 if insult := getRepeatedFailureInsult(ctx); insult != "" {
275 return insult
276 }
277
278 // 6. CI/CD environment detection
279 if insult := getCIInsult(ctx); insult != "" {
280 return insult
281 }
282
283 // 7. Error pattern recognition
284 if insult := getErrorPatternInsult(ctx); insult != "" {
285 return insult
286 }
287
288 // 8. Docker/Build system awareness
289 if insult := getBuildSystemInsult(ctx); insult != "" {
290 return insult
291 }
292
293 // 9. Dependency complexity awareness
294 if insult := getDependencyInsult(ctx); insult != "" {
295 return insult
296 }
297
298 // Tier 2 Intelligence
299
300 // 6. Git branch awareness
301 if insult := getGitBranchInsult(ctx); insult != "" {
302 return insult
303 }
304
305 // 7. Project type detection
306 if insult := getProjectTypeInsult(ctx); insult != "" {
307 return insult
308 }
309
310 // Tier 1 Intelligence - Priority Order
311
312 // 3. Environment-specific insults
313 if insult := getEnvironmentInsult(ctx); insult != "" {
314 return insult
315 }
316
317 // 2. Time-sensitive insults
318 if insult := getTimeOfDayInsult(ctx); insult != "" {
319 return insult
320 }
321
322 // 3. Working directory insults
323 if insult := getWorkingDirInsult(ctx); insult != "" {
324 return insult
325 }
326
327 // 4. File extension insults
328 if insult := getFileExtensionInsult(ctx); insult != "" {
329 return insult
330 }
331
332 // 5. Numeric argument insults
333 if insult := getNumericArgumentInsult(ctx); insult != "" {
334 return insult
335 }
336
337 // 6. Shell-specific insults
338 if insult := getShellInsult(ctx); insult != "" {
339 return insult
340 }
341
342 // 7. Command complexity insults
343 if insult := getCommandComplexityInsult(ctx); insult != "" {
344 return insult
345 }
346
347 // Existing intelligence layers
348
349 // 8. Exit code specific insults
350 if insult := getExitCodeInsult(ctx); insult != "" {
351 return insult
352 }
353
354 // 9. Command-specific patterns
355 if insult := getCommandPatternInsult(ctx); insult != "" {
356 return insult
357 }
358
359 // 10. Argument-aware insults
360 if insult := getArgumentAwareInsult(ctx); insult != "" {
361 return insult
362 }
363
364 // 11. Fall back to expanded database
365 return GetExpandedFallback(ctx.CommandType, ctx.FullCommand)
366 }
367
368 // getExitCodeInsult returns insults specific to common exit codes
369 func getExitCodeInsult(ctx SmartFallbackContext) string {
370 exitCodeInsults := map[int][]string{
371 1: {
372 "Exit code 1: One failure, infinite disappointment.",
373 "Generic error for a generic developer.",
374 "Exit 1: First step to unemployment.",
375 "Error level 1: Your competence level 0.",
376 "Failed with distinction: Exit code 1.",
377 },
378 2: {
379 "Exit 2: Misuse of command. Misuse of developer title.",
380 "Built-in syntax error: You're a built-in failure.",
381 "Wrong arguments. Wrong career.",
382 "Command misuse detected. Life misuse detected.",
383 "Exit 2: Two brain cells, neither working.",
384 },
385 126: {
386 "Permission denied: Can't execute what you wrote anyway.",
387 "Not executable: Neither are your plans.",
388 "126: Command found, competence not found.",
389 "Can't execute: Your logic isn't executable either.",
390 },
391 127: {
392 "Command not found: Neither is your skill.",
393 "127: Path to success not found.",
394 "Command doesn't exist. Your competence doesn't exist.",
395 "Not in PATH: You're not on the path to success.",
396 "Command not found: Story of your career.",
397 },
398 128: {
399 "Invalid exit argument. Invalid life argument.",
400 "Exit 128: One-two-eight steps to failure.",
401 "Invalid signal: Your brain sends invalid signals.",
402 },
403 130: {
404 "Ctrl+C? Can't escape your mistakes that easily.",
405 "Interrupted: Like your thought process.",
406 "SIGINT: Significance? Interrupted.",
407 "Killed by keyboard: At least something stopped you.",
408 },
409 137: {
410 "SIGKILL: Something had to stop you forcefully.",
411 "Exit 137: Murdered by the system. Deservedly.",
412 "Killed with prejudice: OOMKiller knows best.",
413 "137: You were killed. Your code was mercy-killed.",
414 },
415 139: {
416 "Segmentation fault: Your logic segfaulted first.",
417 "139: Memory violation. Logic violation. Everything violation.",
418 "Segfault: Your brain segfaulted at compile time.",
419 "Core dumped: Your core competencies dumped earlier.",
420 },
421 143: {
422 "SIGTERM: Terminated for being terrible.",
423 "Graceful termination: More graceful than your code.",
424 "143: Terminated by common sense.",
425 },
426 255: {
427 "Exit 255: Overflow of failure.",
428 "Maximum exit code: Maximum incompetence.",
429 "255: You maxed out the failure counter.",
430 "Exit code overflow: Like your error overflow.",
431 },
432 }
433
434 if insults, exists := exitCodeInsults[ctx.ExitCode]; exists {
435 return selectInsult(insults, ctx.FullCommand)
436 }
437
438 return ""
439 }
440
441 // getCommandPatternInsult returns insults based on command + subcommand patterns
442 func getCommandPatternInsult(ctx SmartFallbackContext) string {
443 pattern := ctx.Command + " " + ctx.Subcommand
444 pattern = strings.TrimSpace(pattern)
445
446 commandPatterns := map[string][]string{
447 // Git operations
448 "git push": {
449 "Push rejected: The remote has standards.",
450 "Git push failed: Even version control rejects you.",
451 "Rejected by remote: Story of your life.",
452 "Push denied: Your code is unpushable.",
453 "Remote said no: Listen to the remote.",
454 "Can't push incompetence to production.",
455 "Git push failed: Your career trajectory in command form.",
456 },
457 "git pull": {
458 "Pull failed: Can't pull competence from thin air.",
459 "Merge conflicts incoming: Your code vs. reality.",
460 "Pull rejected: Your branch diverged from sanity.",
461 "Can't pull: You're already pulling everyone down.",
462 "Fetch failed: Can't fetch what doesn't exist.",
463 },
464 "git commit": {
465 "Commit failed: Even git won't commit to your code.",
466 "Nothing to commit: Nothing worth committing.",
467 "Pre-commit hook failed: Your code failed harder.",
468 "Can't commit disaster: Wait, git tried and failed.",
469 "Commit message empty: Like your understanding.",
470 },
471 "git merge": {
472 "Merge failed: Can't merge competence into chaos.",
473 "Conflict resolution required: Start with your career.",
474 "Merge aborted: Smart choice by git.",
475 "Auto-merge failed: Manual merge won't help you either.",
476 },
477 "git clone": {
478 "Clone failed: Repository running away from you.",
479 "Can't clone competence: It doesn't exist to clone.",
480 "Permission denied: Even public repos protect themselves.",
481 "Clone timeout: Repository chose death over your attention.",
482 },
483 "git rebase": {
484 "Rebase failed: Can't rebase on a foundation of failure.",
485 "Interactive rebase: Interactively watching you fail.",
486 "Rebase conflict: Your existence conflicts with success.",
487 "Can't rewrite history to hide your incompetence.",
488 },
489 "git checkout": {
490 "Checkout failed: Can't check out from reality.",
491 "Branch not found: Neither is your competence.",
492 "Detached HEAD: Matches your detachment from reality.",
493 "Already on that branch: Already on the failure branch.",
494 },
495 "git reset": {
496 "Reset failed: Can't reset your mistakes that easily.",
497 "Hard reset won't fix soft skills.",
498 "Resetting to a previous commit won't fix current you.",
499 },
500 "git stash": {
501 "Stash failed: Can't stash your incompetence away.",
502 "Nothing to stash: Nothing worth saving.",
503 "Stash apply failed: Your problems can't be applied away.",
504 },
505 "git branch": {
506 "Branch creation failed: Branching into more failure.",
507 "Can't create branch: Too many failure branches already.",
508 "Branch diverged: You diverged from competence long ago.",
509 },
510 "git fetch": {
511 "Fetch failed: Can't fetch common sense.",
512 "Nothing to fetch: Nothing to learn from you either.",
513 "Remote unreachable: Like your career goals.",
514 },
515
516 // Docker operations
517 "docker build": {
518 "Build failed: Can't dockerize disaster.",
519 "Dockerfile syntax error: Your syntax is always wrong.",
520 "Build context too large: Your mistakes are infinite.",
521 "Layer failed: All your layers are failures.",
522 "FROM scratch: You are scratch.",
523 "Build arg undefined: Like your competence.",
524 },
525 "docker run": {
526 "Container exited immediately: Smart container.",
527 "Run failed: Nothing wants to run for you.",
528 "Port binding failed: Can't bind success to you.",
529 "Volume mount error: Can't mount your chaos.",
530 "Container crashed on startup: Your code in container form.",
531 },
532 "docker push": {
533 "Push denied: Registry has standards.",
534 "Authentication failed: You're not authenticated as competent.",
535 "Image push rejected: Your image is not production-ready.",
536 "Manifest invalid: Your competence manifest is invalid.",
537 },
538 "docker pull": {
539 "Pull failed: Can't pull what doesn't work.",
540 "Image not found: Your skill image doesn't exist.",
541 "Digest invalid: Can't digest your code.",
542 },
543 "docker-compose up": {
544 "Compose failed: Can't compose order from chaos.",
545 "Service unhealthy: You're the unhealthy service.",
546 "Network creation failed: Your networking is broken too.",
547 "Volume error: Can't volume-ize your mistakes.",
548 },
549 "docker exec": {
550 "Exec failed: Can't exec into disaster.",
551 "Container not running: Your competence isn't running either.",
552 "No such container: No such developer.",
553 },
554
555 // NPM operations
556 "npm install": {
557 "Install failed: NPM refuses to install for you.",
558 "Dependency hell: You're the dependency from hell.",
559 "Package not found: Neither is your talent.",
560 "ERESOLVE: Can't resolve your incompetence.",
561 "Peer dependency conflict: Your existence is a conflict.",
562 "Funding request: Fund your education first.",
563 },
564 "npm start": {
565 "Start script failed: Can't start what's broken.",
566 "Port already in use: By someone competent.",
567 "Module not found: Neither is your ability.",
568 },
569 "npm run": {
570 "Script not found: Neither is your skill.",
571 "Build failed: Can't build on a foundation of failure.",
572 "Test failed: Your code is the test, reality failed you.",
573 },
574 "npm test": {
575 "Tests failed: 0% passing, 100% crying.",
576 "Test suite disaster: Every assertion asserts your failure.",
577 "Coverage 0%: Covered in incompetence though.",
578 },
579
580 // Python operations
581 "python": {
582 "Python execution failed: The snake bit back.",
583 "ModuleNotFoundError: Module 'brain' not found.",
584 "SyntaxError: Invalid syntax, invalid developer.",
585 "IndentationError: Your career is misaligned too.",
586 },
587 "pip install": {
588 "Pip install failed: Package manager managing disappointment.",
589 "Requirements not met: Competence requirement not met.",
590 "Dependency resolution impossible: Like resolving to make you competent.",
591 },
592
593 // Rust operations
594 "cargo build": {
595 "Build failed: Rust compiles. You don't.",
596 "Borrow checker says no: You can't borrow competence.",
597 "Lifetime error: Your career lifetime is expiring.",
598 "Type mismatch: Expected developer, found disaster.",
599 },
600 "cargo run": {
601 "Run failed: Panic in main thread.",
602 "Binary execution failed: Your execution is always flawed.",
603 },
604
605 // Database operations
606 "mysql": {
607 "MySQL error: My SQL, your hell.",
608 "Connection refused: Database has self-respect.",
609 "Access denied: Denied access to success.",
610 },
611 "psql": {
612 "Postgres error: Post-gres, pre-disaster.",
613 "Connection failed: Can't connect competence to you.",
614 },
615
616 // Make/Build operations
617 "make": {
618 "Make failed: Make better choices.",
619 "Target not found: Your target of competence not found.",
620 "Recipe failed: Recipe for disaster succeeded though.",
621 },
622 "cmake": {
623 "CMake error: Can't make sense of you.",
624 "Configuration failed: You're misconfigured.",
625 },
626
627 // SSH operations
628 "ssh": {
629 "Connection refused: Server protecting itself.",
630 "Permission denied: Your credentials are insufficient.",
631 "Host key verification failed: Host doesn't trust you.",
632 "Timeout: Server chose silence over your presence.",
633 },
634 }
635
636 if insults, exists := commandPatterns[pattern]; exists {
637 return selectInsult(insults, ctx.FullCommand)
638 }
639
640 // Try just the command without subcommand
641 if insults, exists := commandPatterns[ctx.Command]; exists {
642 return selectInsult(insults, ctx.FullCommand)
643 }
644
645 return ""
646 }
647
648 // getArgumentAwareInsult returns insults based on command arguments
649 func getArgumentAwareInsult(ctx SmartFallbackContext) string {
650 // Check for specific argument patterns
651 fullCmd := strings.ToLower(ctx.FullCommand)
652
653 // Force operations
654 if strings.Contains(fullCmd, " -f") || strings.Contains(fullCmd, "--force") {
655 return selectInsult([]string{
656 "Force flag detected: Forcing failure down everyone's throat.",
657 "--force won't force competence into you.",
658 "Force push to production: Force push to unemployment.",
659 "Forcing it won't make it work. Like your career.",
660 }, ctx.FullCommand)
661 }
662
663 // Sudo operations
664 if strings.HasPrefix(fullCmd, "sudo") {
665 return selectInsult([]string{
666 "Sudo failed: Superuser can't grant super-competence.",
667 "Even root privileges can't fix your code.",
668 "Sudo make me a developer: Permission denied.",
669 "With great power comes great responsibility. You have neither.",
670 }, ctx.FullCommand)
671 }
672
673 // Recursive operations
674 if strings.Contains(fullCmd, " -r") || strings.Contains(fullCmd, "--recursive") {
675 return selectInsult([]string{
676 "Recursive fail: Failing recursively at every level.",
677 "-r flag: Recursively destroying everything.",
678 "Recursion depth exceeded: By your incompetence.",
679 }, ctx.FullCommand)
680 }
681
682 // Verbose operations
683 if strings.Contains(fullCmd, " -v") || strings.Contains(fullCmd, "--verbose") {
684 return selectInsult([]string{
685 "Verbose mode: More output, same failure.",
686 "--verbose showing verbose failure details.",
687 "Verbose mode: Because watching you fail in detail is entertaining.",
688 }, ctx.FullCommand)
689 }
690
691 // Help flags
692 if strings.Contains(fullCmd, " --help") || strings.Contains(fullCmd, " -h") {
693 return selectInsult([]string{
694 "Reading help? That ship sailed long ago.",
695 "--help can't help you now.",
696 "Even the help documentation gave up on you.",
697 "RTFM: Read The Failed Manual you just failed.",
698 }, ctx.FullCommand)
699 }
700
701 // Version checks
702 if strings.Contains(fullCmd, "--version") || strings.Contains(fullCmd, "-v") {
703 return selectInsult([]string{
704 "Checking version: Your version is deprecated.",
705 "--version: v0.0.0-incompetent",
706 "Software version: Current. Developer version: Obsolete.",
707 }, ctx.FullCommand)
708 }
709
710 return ""
711 }
712
713 // Tier 1 Intelligence Detection Functions
714
715 // getEnvironmentInsult returns insults based on environment variables
716 func getEnvironmentInsult(ctx SmartFallbackContext) string {
717 envInsults := map[string][]string{
718 "CI": {
719 "Breaking CI? Breaking everyone's day.",
720 "CI failure: Continuous Incompetence detected.",
721 "Failed in CI: Failing Continuously and Immediately.",
722 "CI pipeline broken: Your career pipeline next.",
723 },
724 "GITHUB_ACTIONS": {
725 "GitHub Actions failed: Your actions speak louder than words.",
726 "Actions workflow broken: Action item: Find new career.",
727 "GitHub runner quit: Running from your code.",
728 },
729 "GITLAB_CI": {
730 "GitLab CI failed: Lab results show terminal incompetence.",
731 "Pipeline failed: Pipe down, you're done.",
732 },
733 "JENKINS_HOME": {
734 "Jenkins build failed: Job security failed too.",
735 "Jenkins says no: Automated rejection system working.",
736 },
737 "DEBUG": {
738 "Debug mode active: Can't debug your brain.",
739 "Debugging? You ARE the bug.",
740 },
741 "PRODUCTION": {
742 "Production error: Producing only failures.",
743 "PROD failure: Professional Regression Of Development.",
744 "Testing in production? Testing everyone's patience.",
745 },
746 "NODE_ENV": {
747 "Node environment error: Environment of incompetence.",
748 },
749 }
750
751 for env, val := range ctx.Environment {
752 if insults, exists := envInsults[env]; exists && val != "" {
753 return selectInsult(insults, ctx.FullCommand)
754 }
755 }
756
757 return ""
758 }
759
760 // getTimeOfDayInsult returns insults based on time of day
761 func getTimeOfDayInsult(ctx SmartFallbackContext) string {
762 hour := ctx.TimeOfDay
763
764 switch {
765 case hour >= 0 && hour < 6:
766 return selectInsult([]string{
767 "3 AM debugging? Tomorrow won't fix today's code.",
768 "Coding at 3 AM? Your code is as tired as you.",
769 "Late night failure: Sleep won't fix this.",
770 "Midnight coding: Both your code and judgment are impaired.",
771 }, ctx.FullCommand)
772 case hour >= 6 && hour < 9:
773 return selectInsult([]string{
774 "Morning failure sets the tone for the day.",
775 "Failed before breakfast: Hungry for failure.",
776 "Early bird gets the worm: Early coder gets the bugs.",
777 }, ctx.FullCommand)
778 case hour >= 17 && hour < 20:
779 return selectInsult([]string{
780 "Evening failure: Overtime making more bugs.",
781 "5 PM deploy failed: Weekend ruined.",
782 "After hours coding: After competence hours too.",
783 }, ctx.FullCommand)
784 case hour >= 20 && hour < 24:
785 return selectInsult([]string{
786 "Late night commit: Commit to quitting instead.",
787 "Coding past 8 PM? Desperation detected.",
788 "Night owl? More like night fail.",
789 }, ctx.FullCommand)
790 }
791
792 return ""
793 }
794
795 // getWorkingDirInsult returns insults based on working directory
796 func getWorkingDirInsult(ctx SmartFallbackContext) string {
797 wd := strings.ToLower(ctx.WorkingDir)
798
799 dirPatterns := map[string][]string{
800 "/tmp": {
801 "Coding in /tmp? That's where your code belongs: temporary.",
802 "Temp directory for temp solution for temp developer.",
803 "/tmp: Temporary directory, permanent failure.",
804 },
805 "downloads": {
806 "Coding in Downloads? Your career is downloading too.",
807 "Downloads folder: Downloaded failure.",
808 },
809 "/var/log": {
810 "Working in logs? You belong in error logs.",
811 "/var/log: Logging your mistakes for posterity.",
812 },
813 "/root": {
814 "Running as root? Root of all problems.",
815 "Root directory? Rooted in incompetence.",
816 },
817 "desktop": {
818 "Desktop coding? Desktop disaster.",
819 "Desktop folder: Where careers go to die.",
820 },
821 "/opt": {
822 "Optional directory for optional competence.",
823 "/opt: Opted out of skill.",
824 },
825 }
826
827 for pattern, insults := range dirPatterns {
828 if strings.Contains(wd, pattern) {
829 return selectInsult(insults, ctx.FullCommand)
830 }
831 }
832
833 return ""
834 }
835
836 // getFileExtensionInsult returns insults based on file extensions in command
837 func getFileExtensionInsult(ctx SmartFallbackContext) string {
838 if len(ctx.FileExtensions) == 0 {
839 return ""
840 }
841
842 ext := strings.ToLower(ctx.FileExtensions[0])
843
844 extInsults := map[string][]string{
845 ".rs": {
846 "Rust file failed: Rust in peace, code.",
847 ".rs: Rust? Your skills are corroded.",
848 "Rust compile failed: Oxidized incompetence.",
849 },
850 ".go": {
851 "Go file failed: Stop. Don't go. Just don't.",
852 ".go error: Should've Go-ne into another career.",
853 "Go build failed: Go away.",
854 },
855 ".java": {
856 "Java failed: Needs more than coffee beans.",
857 ".java compile error: Java the Hutt-level bloat.",
858 "Java exception: You're the exception to competence.",
859 },
860 ".cpp": {
861 "C++ failed: C++ you later, career.",
862 ".cpp segfault: C++ more like C-- --.",
863 "C++ error: Can't ++ your skill level.",
864 },
865 ".c": {
866 "C compilation failed: C you don't understand C.",
867 ".c file error: C-riously incompetent.",
868 },
869 ".py": {
870 "Python failed: Snake bit back.",
871 ".py error: Python crying from your code.",
872 "Python IndentationError: Career misaligned too.",
873 },
874 ".js": {
875 "JavaScript failed: Just awful Script.",
876 ".js error: Java-Script? Neither Java nor scripted competence.",
877 },
878 ".ts": {
879 "TypeScript failed: Type: Disaster.",
880 ".ts error: TypeScript can't type your chaos.",
881 },
882 ".sh": {
883 "Shell script failed: Script kiddie confirmed.",
884 ".sh error: Shell-shocked by incompetence.",
885 },
886 ".rb": {
887 "Ruby failed: More like Rub-y wounds in codebase.",
888 ".rb error: Ruby gem? Cubic zirconia skill.",
889 },
890 ".php": {
891 "PHP failed: Probably Horrible Programming.",
892 ".php error: PHP stands for Please Help Professional.",
893 },
894 }
895
896 if insults, exists := extInsults[ext]; exists {
897 return selectInsult(insults, ctx.FullCommand)
898 }
899
900 return ""
901 }
902
903 // getNumericArgumentInsult returns insults based on numeric arguments
904 func getNumericArgumentInsult(ctx SmartFallbackContext) string {
905 if len(ctx.NumericArgs) == 0 {
906 return ""
907 }
908
909 num := ctx.NumericArgs[0]
910
911 // Port numbers
912 if num >= 1 && num <= 65535 {
913 portInsults := map[int][]string{
914 22: {
915 "Port 22: 22 ways to fail at SSH.",
916 "SSH on port 22: Access denied to competence.",
917 },
918 80: {
919 "Port 80: HTTP status 500 Internal User Error.",
920 "Port 80: Gateway to failure.",
921 },
922 443: {
923 "Port 443: HTTPS - Hyper Text Tragic Protocol Stupidity.",
924 "Port 443: Secure connection to incompetence.",
925 },
926 3000: {
927 "Port 3000: Three thousand problems detected.",
928 "Port 3000: Development port for underdeveloped skills.",
929 },
930 8080: {
931 "Port 8080: Eight-zero-eight-zero errors found.",
932 "Port 8080: Alternative HTTP, alternative competence (zero).",
933 },
934 5432: {
935 "Port 5432: PostgreSQL rejecting your queries and you.",
936 "Port 5432: Postgres? More like Post-regrets.",
937 },
938 3306: {
939 "Port 3306: MySQL - My Structured Query: Why are you coding?",
940 "Port 3306: MySQL rejecting your SQL and existence.",
941 },
942 27017: {
943 "Port 27017: MongoDB - More like MongoDON'T.",
944 "Port 27017: NoSQL? No skill either.",
945 },
946 }
947
948 if insults, exists := portInsults[num]; exists {
949 return selectInsult(insults, ctx.FullCommand)
950 }
951 }
952
953 // Chmod values
954 if num == 777 {
955 return selectInsult([]string{
956 "chmod 777: Maximum permissions, minimum security, zero brains.",
957 "777: Jackpot of incompetence.",
958 "chmod 777: Triple seven, triple failure.",
959 }, ctx.FullCommand)
960 }
961 if num == 666 {
962 return "chmod 666: Devil's permission for devilish code."
963 }
964 if num == 000 || num == 0 {
965 return "chmod 000: Like your access to competence."
966 }
967
968 // Kill signals
969 if num == 9 {
970 return selectInsult([]string{
971 "kill -9: Killing process. Can't kill your incompetence.",
972 "SIGKILL sent: Signal your career is over.",
973 }, ctx.FullCommand)
974 }
975
976 return ""
977 }
978
979 // getShellInsult returns insults based on shell type
980 func getShellInsult(ctx SmartFallbackContext) string {
981 shell := strings.ToLower(ctx.Shell)
982
983 shellInsults := map[string][]string{
984 "bash": {
985 "Bash error: Bash your head against keyboard, same result.",
986 "Bourne Again Shell: Borne to fail again.",
987 },
988 "zsh": {
989 "Z shell: Z for Zero competence level.",
990 "Zsh failure: Last shell of the alphabet, last in skill.",
991 },
992 "fish": {
993 "Fish shell: You're swimming in failure.",
994 "Fish error: Fishing for competence, caught nothing.",
995 },
996 "sh": {
997 "Bourne shell: Born to fail.",
998 "sh: Should've stayed in the shell.",
999 },
1000 "ksh": {
1001 "Korn shell: Your code is corny.",
1002 },
1003 "csh": {
1004 "C shell: See? Shell of incompetence.",
1005 },
1006 }
1007
1008 if insults, exists := shellInsults[shell]; exists {
1009 return selectInsult(insults, ctx.FullCommand)
1010 }
1011
1012 return ""
1013 }
1014
1015 // getCommandComplexityInsult returns insults based on command complexity
1016 func getCommandComplexityInsult(ctx SmartFallbackContext) string {
1017 length := ctx.CommandLength
1018
1019 if length < 10 {
1020 return selectInsult([]string{
1021 "Short command, short career.",
1022 "Simple command failed: Simply incompetent.",
1023 }, ctx.FullCommand)
1024 }
1025
1026 if length > 100 {
1027 return selectInsult([]string{
1028 "Command longer than your employment prospects.",
1029 "100+ characters: Complexity hiding incompetence.",
1030 "Long command: Compensating for short skills.",
1031 }, ctx.FullCommand)
1032 }
1033
1034 if ctx.HasPipes && ctx.HasChaining {
1035 return selectInsult([]string{
1036 "Pipes AND chaining? Piping chained disasters together.",
1037 "Complex piped chain: Complexly incompetent.",
1038 }, ctx.FullCommand)
1039 }
1040
1041 if ctx.HasPipes {
1042 return selectInsult([]string{
1043 "Pipe fail: Piping garbage to garbage.",
1044 "Pipeline broken: Like your career pipeline.",
1045 }, ctx.FullCommand)
1046 }
1047
1048 if ctx.HasChaining {
1049 return selectInsult([]string{
1050 "Chaining commands: Chaining failures sequentially.",
1051 "&& operator: AND you're terrible AND incompetent.",
1052 }, ctx.FullCommand)
1053 }
1054
1055 return ""
1056 }
1057
1058 // Tier 2 Intelligence Detection Functions
1059
1060 // detectGitBranch detects the current git branch
1061 func detectGitBranch() string {
1062 // Check if we're in a git repo first
1063 if _, err := os.Stat(".git"); os.IsNotExist(err) {
1064 return ""
1065 }
1066
1067 // Try to get current branch
1068 if data, err := os.ReadFile(".git/HEAD"); err == nil {
1069 head := string(data)
1070 // Format: "ref: refs/heads/branch-name"
1071 if strings.HasPrefix(head, "ref: refs/heads/") {
1072 branch := strings.TrimPrefix(head, "ref: refs/heads/")
1073 return strings.TrimSpace(branch)
1074 }
1075 }
1076
1077 return ""
1078 }
1079
1080 // detectProjectType detects project type by checking for common project files
1081 func detectProjectType() (string, []string) {
1082 projectFiles := []string{
1083 "package.json", // Node.js
1084 "Cargo.toml", // Rust
1085 "go.mod", // Go
1086 "requirements.txt", // Python
1087 "Pipfile", // Python (pipenv)
1088 "pyproject.toml", // Python (poetry)
1089 "pom.xml", // Java (Maven)
1090 "build.gradle", // Java (Gradle)
1091 "Gemfile", // Ruby
1092 "composer.json", // PHP
1093 "Makefile", // C/C++
1094 "CMakeLists.txt", // C/C++ (CMake)
1095 "package.swift", // Swift
1096 "mix.exs", // Elixir
1097 "Dockerfile", // Docker
1098 "docker-compose.yml", // Docker Compose
1099 }
1100
1101 var foundFiles []string
1102 for _, file := range projectFiles {
1103 if _, err := os.Stat(file); err == nil {
1104 foundFiles = append(foundFiles, file)
1105 }
1106 }
1107
1108 // Determine project type from found files
1109 if len(foundFiles) == 0 {
1110 return "", nil
1111 }
1112
1113 // Priority order for type detection
1114 typeMap := map[string]string{
1115 "package.json": "node",
1116 "Cargo.toml": "rust",
1117 "go.mod": "go",
1118 "requirements.txt": "python",
1119 "Pipfile": "python",
1120 "pyproject.toml": "python",
1121 "pom.xml": "java",
1122 "build.gradle": "java",
1123 "Gemfile": "ruby",
1124 "composer.json": "php",
1125 "package.swift": "swift",
1126 "mix.exs": "elixir",
1127 }
1128
1129 for _, file := range foundFiles {
1130 if projectType, exists := typeMap[file]; exists {
1131 return projectType, foundFiles
1132 }
1133 }
1134
1135 return "generic", foundFiles
1136 }
1137
1138 // getGitBranchInsult returns insults based on git branch
1139 func getGitBranchInsult(ctx SmartFallbackContext) string {
1140 if ctx.GitBranch == "" {
1141 return ""
1142 }
1143
1144 branch := strings.ToLower(ctx.GitBranch)
1145
1146 branchInsults := map[string][]string{
1147 "main": {
1148 "Breaking main? Breaking everyone's day.",
1149 "Main branch failure: Main character of disasters.",
1150 "Failed on main: Mainly incompetent.",
1151 "Main branch disaster: You're the main problem.",
1152 },
1153 "master": {
1154 "Master branch failure: Master of disasters.",
1155 "Breaking master: Mastering incompetence.",
1156 "Master branch error: You've mastered failure.",
1157 },
1158 "develop": {
1159 "Develop branch failed: Develop your skills first.",
1160 "Development branch: Under-developed skills.",
1161 "Develop? More like devolve.",
1162 },
1163 "dev": {
1164 "Dev branch failed: Dev-astating incompetence.",
1165 "Dev environment: Environment of failure.",
1166 },
1167 "staging": {
1168 "Staging failure: Staging your resignation.",
1169 "Staging branch: Staging area for disaster.",
1170 },
1171 "production": {
1172 "Production branch failed: Producing unemployment.",
1173 "Prod branch error: Professionally regressive.",
1174 },
1175 }
1176
1177 // Check exact matches first
1178 if insults, exists := branchInsults[branch]; exists {
1179 return selectInsult(insults, ctx.FullCommand)
1180 }
1181
1182 // Check for patterns
1183 if strings.Contains(branch, "feature") || strings.HasPrefix(branch, "feat/") {
1184 return selectInsult([]string{
1185 "Feature branch: Featured failure.",
1186 "New feature: Newly incompetent.",
1187 "Feature branch failed: Feature: Broken. Developer: Broken.",
1188 }, ctx.FullCommand)
1189 }
1190
1191 if strings.Contains(branch, "hotfix") || strings.HasPrefix(branch, "fix/") {
1192 return selectInsult([]string{
1193 "Hotfix branch: You ARE the bug.",
1194 "Hotfix failed: Can't fix what's fundamentally broken: You.",
1195 "Hotfix? More like hot mess.",
1196 }, ctx.FullCommand)
1197 }
1198
1199 if strings.Contains(branch, "bugfix") || strings.Contains(branch, "bug/") {
1200 return selectInsult([]string{
1201 "Bugfix branch: The bug is you.",
1202 "Fixing bugs? You ARE the bug.",
1203 }, ctx.FullCommand)
1204 }
1205
1206 if strings.Contains(branch, "release") {
1207 return selectInsult([]string{
1208 "Release branch failed: Release your grip on keyboard.",
1209 "Release branch: Releasing disaster into the world.",
1210 }, ctx.FullCommand)
1211 }
1212
1213 if strings.Contains(branch, "test") {
1214 return selectInsult([]string{
1215 "Test branch failed: You're the test, reality failed you.",
1216 "Testing branch: Test results: FAIL.",
1217 }, ctx.FullCommand)
1218 }
1219
1220 return ""
1221 }
1222
1223 // getProjectTypeInsult returns insults based on detected project type
1224 func getProjectTypeInsult(ctx SmartFallbackContext) string {
1225 if ctx.ProjectType == "" {
1226 return ""
1227 }
1228
1229 projectInsults := map[string][]string{
1230 "node": {
1231 "Node project detected. Dependencies: Many. Skills: None.",
1232 "package.json found: Package of failures.",
1233 "Node.js project: Node your way out of this one.",
1234 "npm detected: Node Package Misery.",
1235 },
1236 "rust": {
1237 "Cargo.toml found: Can't cargo your incompetence.",
1238 "Rust project detected: Rust in peace, code.",
1239 "Cargo workspace: Working on failure.",
1240 },
1241 "go": {
1242 "go.mod found: Go away.",
1243 "Go project detected: Should've gone into another career.",
1244 "Go modules: Modular incompetence.",
1245 },
1246 "python": {
1247 "requirements.txt found: Requirement for skill: UNMET.",
1248 "Python project detected: Snake bit you back.",
1249 "Python dependencies: Depending on incompetence.",
1250 "Virtual environment detected: Can't isolate stupidity.",
1251 },
1252 "java": {
1253 "pom.xml found: Maven project: Mav-un successful.",
1254 "Java project detected: Needs more than coffee.",
1255 "Gradle detected: Grade: F. Project: Failed.",
1256 },
1257 "ruby": {
1258 "Gemfile found: Cubic zirconia skills.",
1259 "Ruby project: Ruby red with embarrassment.",
1260 "Bundler detected: Bundle of incompetence.",
1261 },
1262 "php": {
1263 "composer.json found: Can't compose competence.",
1264 "PHP project: Probably Horrible Programming detected.",
1265 },
1266 "swift": {
1267 "Swift project: Swift path to unemployment.",
1268 "Package.swift found: Swiftly failing.",
1269 },
1270 "elixir": {
1271 "Elixir project: No elixir can cure this.",
1272 "mix.exs found: Mixed results: All bad.",
1273 },
1274 }
1275
1276 if insults, exists := projectInsults[ctx.ProjectType]; exists {
1277 return selectInsult(insults, ctx.FullCommand)
1278 }
1279
1280 return ""
1281 }
1282
1283 // selectInsult picks an insult using pseudo-random selection with time-based entropy
1284 // This adds variety over time while maintaining some determinism within short timeframes
1285 func selectInsult(insults []string, seed string) string {
1286 if len(insults) == 0 {
1287 return ""
1288 }
1289
1290 // Add time-based entropy: same command gets different insults over time
1291 // Using 10-second buckets means same command within ~10 seconds gets same insult,
1292 // but after that, the insult changes. This provides both consistency and variety.
1293 timeBucket := time.Now().Unix() / 10 // 10-second buckets
1294
1295 hash := 0
1296 // Hash the seed (command)
1297 for _, char := range seed {
1298 hash = hash*31 + int(char)
1299 }
1300 // Mix in time-based entropy
1301 hash = hash*31 + int(timeBucket)
1302
1303 if hash < 0 {
1304 hash = -hash
1305 }
1306
1307 return insults[hash%len(insults)]
1308 }
1309
1310 // ============================================================================
1311 // TIER 3 INTELLIGENCE - Advanced LLM-like Context Awareness
1312 // ============================================================================
1313
1314 // fileExists checks if a file exists
1315 func fileExists(filename string) bool {
1316 _, err := os.Stat(filename)
1317 return err == nil
1318 }
1319
1320 // detectCIEnvironment detects if running in CI/CD
1321 func detectCIEnvironment(env map[string]string) (bool, string) {
1322 ciChecks := map[string]string{
1323 "GITHUB_ACTIONS": "github",
1324 "GITLAB_CI": "gitlab",
1325 "JENKINS_HOME": "jenkins",
1326 "CIRCLECI": "circle",
1327 "TRAVIS": "travis",
1328 "CI": "generic",
1329 }
1330
1331 for envVar, provider := range ciChecks {
1332 if _, exists := env[envVar]; exists {
1333 return true, provider
1334 }
1335 }
1336
1337 return false, ""
1338 }
1339
1340 // estimateDependencyCount estimates project complexity
1341 func estimateDependencyCount(projectType string) int {
1342 switch projectType {
1343 case "node":
1344 // Check package.json for rough count
1345 if data, err := os.ReadFile("package.json"); err == nil {
1346 // Rough heuristic: count dependencies and devDependencies
1347 count := strings.Count(string(data), `"dependencies"`)
1348 count += strings.Count(string(data), `"devDependencies"`)
1349 if count > 0 {
1350 // Estimate ~20-100 deps if sections exist
1351 return 50
1352 }
1353 }
1354 case "rust":
1355 if data, err := os.ReadFile("Cargo.toml"); err == nil {
1356 // Count [dependencies] entries
1357 deps := strings.Count(string(data), "\n") / 3
1358 return deps
1359 }
1360 case "go":
1361 if data, err := os.ReadFile("go.mod"); err == nil {
1362 // Count require statements
1363 deps := strings.Count(string(data), "require")
1364 return deps
1365 }
1366 case "python":
1367 if data, err := os.ReadFile("requirements.txt"); err == nil {
1368 // Count lines
1369 deps := strings.Count(string(data), "\n")
1370 return deps
1371 }
1372 }
1373 return 0
1374 }
1375
1376 // detectErrorPattern recognizes common error patterns
1377 func detectErrorPattern(exitCode int, command string) string {
1378 // Map exit codes to error patterns
1379 patterns := map[int]string{
1380 1: "general_error",
1381 2: "misuse",
1382 126: "permission_denied",
1383 127: "command_not_found",
1384 128: "invalid_exit",
1385 130: "ctrl_c",
1386 137: "killed",
1387 139: "segfault",
1388 143: "sigterm",
1389 255: "network_error",
1390 }
1391
1392 if pattern, exists := patterns[exitCode]; exists {
1393 return pattern
1394 }
1395
1396 // Pattern detection from command content
1397 cmdLower := strings.ToLower(command)
1398 if strings.Contains(cmdLower, "permission") || strings.Contains(cmdLower, "denied") {
1399 return "permission_denied"
1400 }
1401 if strings.Contains(cmdLower, "connection") || strings.Contains(cmdLower, "refused") {
1402 return "connection_refused"
1403 }
1404 if strings.Contains(cmdLower, "timeout") {
1405 return "timeout"
1406 }
1407 if strings.Contains(cmdLower, "not found") || strings.Contains(cmdLower, "404") {
1408 return "not_found"
1409 }
1410
1411 return ""
1412 }
1413
1414 // failureTracker stores recent command failures (simple in-process tracking)
1415 var (
1416 failureTracker = make(map[string]int)
1417 failureTrackerMutex sync.Mutex
1418 )
1419
1420 // trackFailure tracks command failures and detects repeats
1421 func trackFailure(command string) bool {
1422 // Create a simple hash of the command
1423 hash := 0
1424 for _, char := range command {
1425 hash = hash*31 + int(char)
1426 }
1427 cmdHash := strconv.Itoa(hash)
1428
1429 // Thread-safe map access
1430 failureTrackerMutex.Lock()
1431 defer failureTrackerMutex.Unlock()
1432
1433 // Increment failure count
1434 failureTracker[cmdHash]++
1435
1436 // Consider it repeated if failed 2+ times
1437 return failureTracker[cmdHash] >= 2
1438 }
1439
1440 // getRepeatedFailureInsult returns escalated insults for repeated failures
1441 func getRepeatedFailureInsult(ctx SmartFallbackContext) string {
1442 if !ctx.IsRepeatedFailure {
1443 return ""
1444 }
1445
1446 insults := []string{
1447 "Same command, same failure. Definition of insanity.",
1448 "Trying again? Einstein called: That's insanity.",
1449 "Repeated failure detected. Time to try reading docs.",
1450 "Still failing? Maybe it's not the computer.",
1451 "Second time's the charm? Not for you.",
1452 "Failure #2: The sequel nobody asked for.",
1453 "Doing it again won't make it work. Won't make you smarter either.",
1454 "Another attempt, another disaster. Predictable.",
1455 "Groundhog Day: The Failure Edition.",
1456 "Learning from mistakes requires learning.",
1457 "Try, fail, repeat. The three-step program to nowhere.",
1458 "Persistence is admirable. Persistent incompetence isn't.",
1459 "You're very consistent. Consistently wrong.",
1460 "Same command, different hour, identical disaster.",
1461 "Repeating mistakes: The one skill you've mastered.",
1462 }
1463
1464 return selectInsult(insults, ctx.FullCommand)
1465 }
1466
1467 // getCIInsult returns CI/CD-specific insults
1468 func getCIInsult(ctx SmartFallbackContext) string {
1469 if !ctx.IsCI {
1470 return ""
1471 }
1472
1473 ciInsults := map[string][]string{
1474 "github": {
1475 "Breaking GitHub Actions? Breaking everyone's sprint.",
1476 "GitHub Actions failed: Your actions speak volumes.",
1477 "GH Actions: Great Humiliation Actions.",
1478 "Failed in CI: Continuous Integration of Disaster.",
1479 "GitHub Actions: Git blame yourself.",
1480 },
1481 "gitlab": {
1482 "GitLab CI failed: Commit to unemployment.",
1483 "Pipeline broken: Your career too.",
1484 "GitLab runner failed: Run from development.",
1485 "CI/CD failed: Career Imminent Cancellation Detected.",
1486 },
1487 "jenkins": {
1488 "Jenkins job failed: Job security failed.",
1489 "Build #404: Competence not found.",
1490 "Jenkins says: Build career, not this garbage.",
1491 "Red build: Your face should match.",
1492 },
1493 "circle": {
1494 "CircleCI failed: Circle of failure complete.",
1495 "Workflow failed: Work towards new career flow.",
1496 "Circle CI: Circular logic detected.",
1497 },
1498 "travis": {
1499 "Travis CI broken: Travis-ty of errors.",
1500 "Build failed: Career build deprecated.",
1501 },
1502 "generic": {
1503 "CI failed: Continuous Integration? Continuous Incompetence.",
1504 "Breaking the build: Breaking your team's trust.",
1505 "Failed in CI: Everyone can see your shame.",
1506 "Pipeline blocked: By your incompetence.",
1507 },
1508 }
1509
1510 if insults, exists := ciInsults[ctx.CIProvider]; exists {
1511 return selectInsult(insults, ctx.FullCommand)
1512 }
1513
1514 return ""
1515 }
1516
1517 // getErrorPatternInsult returns pattern-specific insults
1518 func getErrorPatternInsult(ctx SmartFallbackContext) string {
1519 if ctx.ErrorPattern == "" {
1520 return ""
1521 }
1522
1523 patternInsults := map[string][]string{
1524 "permission_denied": {
1525 "Permission denied: Denied access to competence too.",
1526 "No permission: No skill either.",
1527 "sudo won't fix this level of incompetence.",
1528 "Access denied: Reality denying you success.",
1529 "Insufficient permissions: Insufficient everything.",
1530 },
1531 "command_not_found": {
1532 "Command not found: Competence not found.",
1533 "404: Command missing. Also: Your skill.",
1534 "Command not found: Try 'find-new-career'.",
1535 "PATH searched: Competence not in PATH.",
1536 },
1537 "connection_refused": {
1538 "Connection refused: Server refused your incompetence.",
1539 "Connection denied: Denied by reality.",
1540 "Can't connect: Reality disconnecting from you.",
1541 "Connection refused: Even localhost rejects you.",
1542 },
1543 "timeout": {
1544 "Timeout: Even the computer gave up waiting.",
1545 "Request timed out: So did patience.",
1546 "Timeout: Time to consider new profession.",
1547 "Connection timeout: Career timeout imminent.",
1548 },
1549 "segfault": {
1550 "Segmentation fault: Segmented from reality.",
1551 "Segfault: Memory accessed. Memory of competence: Not found.",
1552 "Core dumped: Core competence: Also dumped.",
1553 "Segfault: Your code violated everything.",
1554 },
1555 "killed": {
1556 "Process killed: Career should be too.",
1557 "Killed by signal: Signaling incompetence.",
1558 "SIGKILL received: Kill switch on career recommended.",
1559 },
1560 "not_found": {
1561 "Not found: Your skill is also 404.",
1562 "404: Not found. Unlike your incompetence: Always 200 OK.",
1563 "Resource not found: Resources for learning: Also not found.",
1564 },
1565 }
1566
1567 if insults, exists := patternInsults[ctx.ErrorPattern]; exists {
1568 return selectInsult(insults, ctx.FullCommand)
1569 }
1570
1571 return ""
1572 }
1573
1574 // getBuildSystemInsult returns build tool specific insults
1575 func getBuildSystemInsult(ctx SmartFallbackContext) string {
1576 insults := []string{}
1577
1578 if ctx.HasDockerfile {
1579 insults = append(insults,
1580 "Dockerfile detected: Can't containerize competence.",
1581 "Docker build failed: Image of failure created.",
1582 "Container crashed: Can't contain your mistakes.",
1583 "Dockerfile present: Should've dockerized your career away.",
1584 )
1585 }
1586
1587 if ctx.HasMakefile {
1588 insults = append(insults,
1589 "Makefile found: Make disasters, not software.",
1590 "make failed: Made a mistake becoming a developer.",
1591 "Build system detected: System detected you.",
1592 )
1593 }
1594
1595 if len(insults) > 0 {
1596 return selectInsult(insults, ctx.FullCommand)
1597 }
1598
1599 return ""
1600 }
1601
1602 // getDependencyInsult returns complexity-aware insults
1603 func getDependencyInsult(ctx SmartFallbackContext) string {
1604 if ctx.DependencyCount == 0 {
1605 return ""
1606 }
1607
1608 var insults []string
1609
1610 if ctx.DependencyCount > 100 {
1611 insults = []string{
1612 "100+ dependencies: Depending on others for your failures.",
1613 "Dependency hell: You're everyone's least favorite dependency.",
1614 "Massive dependency tree: Tree of incompetence.",
1615 "Dependencies: Hundreds. Competence: Zero.",
1616 }
1617 } else if ctx.DependencyCount > 20 {
1618 insults = []string{
1619 "Dependencies detected: You depend on failure.",
1620 "Package.json bloated: Like your ego, unlike your skill.",
1621 "Dependency count high: Skill count low.",
1622 "Many dependencies: None can fix this.",
1623 }
1624 }
1625
1626 if len(insults) > 0 {
1627 return selectInsult(insults, ctx.FullCommand)
1628 }
1629
1630 return ""
1631 }
1632
1633 // ============================================================================
1634 // TIER 5 INTELLIGENCE - Hybrid Ensemble ML System
1635 // ============================================================================
1636 // Combines TF-IDF semantic similarity, Markov chain generation, tag-based
1637 // scoring, and historical pattern matching with ensemble voting.
1638
1639 // generateMLInsult uses the hybrid ensemble system to select/generate the best insult
1640 func generateMLInsult(ctx SmartFallbackContext) string {
1641 // Determine personality from config (default to sarcastic)
1642 personality := "sarcastic"
1643 if config := LoadConfig(); config != nil && config.General.Personality != "" {
1644 personality = config.General.Personality
1645 }
1646
1647 // Use the powerful ensemble system that combines:
1648 // - TF-IDF semantic similarity (cosine similarity)
1649 // - Tag-based matching (existing system)
1650 // - Markov chain generation (novel insults)
1651 // - Historical pattern learning
1652 // - Novelty scoring (avoid repetition)
1653 // - Weighted ensemble voting
1654 insult := ensembleSystem.GenerateInsult(&ctx, personality)
1655
1656 if insult == "" {
1657 return "" // Fall through to other tiers
1658 }
1659
1660 return insult
1661 }
1662
1663 // LoadConfig loads the parrot configuration (stub - integrate with actual config)
1664 func LoadConfig() *Config {
1665 // This should integrate with the actual config loader
1666 // For now, return nil to use defaults
1667 return nil
1668 }
1669
1670 // Config represents parrot configuration
1671 type Config struct {
1672 General GeneralConfig
1673 }
1674
1675 // GeneralConfig represents general configuration
1676 type GeneralConfig struct {
1677 Personality string
1678 }
1679