tenseleyflow/parrot / 0ff8999

Browse files

guttiwuts

Authored by espadonne
SHA
0ff8999672fe6daf3f942b34c8b6fd7ac01e53e5
Parents
5c8f5d6
Tree
f2028f3

3 changed files

StatusFile+-
A internal/colors/colors.go 209 0
M internal/config/config.go 15 0
M internal/prompts/templates.go 192 22
internal/colors/colors.goadded
@@ -0,0 +1,209 @@
1
+package colors
2
+
3
+import (
4
+	"fmt"
5
+	"os"
6
+	"strings"
7
+)
8
+
9
+// ANSI color codes
10
+const (
11
+	Reset  = "\033[0m"
12
+	Bold   = "\033[1m"
13
+	Dim    = "\033[2m"
14
+	
15
+	// Regular colors
16
+	Red     = "\033[31m"
17
+	Green   = "\033[32m"
18
+	Yellow  = "\033[33m"
19
+	Blue    = "\033[34m"
20
+	Magenta = "\033[35m"
21
+	Cyan    = "\033[36m"
22
+	White   = "\033[37m"
23
+	
24
+	// Bright colors
25
+	BrightRed     = "\033[91m"
26
+	BrightGreen   = "\033[92m"
27
+	BrightYellow  = "\033[93m"
28
+	BrightBlue    = "\033[94m"
29
+	BrightMagenta = "\033[95m"
30
+	BrightCyan    = "\033[96m"
31
+	BrightWhite   = "\033[97m"
32
+)
33
+
34
+// ParrotStyle represents different visual styles for parrot output
35
+type ParrotStyle struct {
36
+	Parrot   string // Color for the parrot emoji/prefix
37
+	Response string // Color for the response text
38
+	Accent   string // Color for emphasis
39
+}
40
+
41
+// Predefined styles for different personalities
42
+var Styles = map[string]ParrotStyle{
43
+	"mild": {
44
+		Parrot:   BrightBlue,
45
+		Response: Blue,
46
+		Accent:   BrightCyan,
47
+	},
48
+	"sarcastic": {
49
+		Parrot:   BrightYellow,
50
+		Response: Yellow,
51
+		Accent:   BrightMagenta,
52
+	},
53
+	"savage": {
54
+		Parrot:   BrightRed,
55
+		Response: Red,
56
+		Accent:   BrightYellow,
57
+	},
58
+	"default": {
59
+		Parrot:   BrightGreen,
60
+		Response: Green,
61
+		Accent:   BrightCyan,
62
+	},
63
+}
64
+
65
+// ColorEnabled checks if color output should be enabled
66
+func ColorEnabled() bool {
67
+	// Disable colors if NO_COLOR is set
68
+	if os.Getenv("NO_COLOR") != "" {
69
+		return false
70
+	}
71
+	
72
+	// Disable colors if not a terminal
73
+	if os.Getenv("TERM") == "dumb" {
74
+		return false
75
+	}
76
+	
77
+	// Check if stdout is a terminal (simplified check)
78
+	stat, err := os.Stdout.Stat()
79
+	if err != nil {
80
+		return false
81
+	}
82
+	
83
+	// Check if it's a character device (terminal)
84
+	return (stat.Mode() & os.ModeCharDevice) != 0
85
+}
86
+
87
+// Colorize adds color to text if colors are enabled
88
+func Colorize(color, text string) string {
89
+	if !ColorEnabled() {
90
+		return text
91
+	}
92
+	return color + text + Reset
93
+}
94
+
95
+// FormatParrotOutput formats the parrot response with personality-based colors
96
+func FormatParrotOutput(personality, response string, enhanced bool) string {
97
+	if !ColorEnabled() {
98
+		return fmt.Sprintf("🦜 %s", response)
99
+	}
100
+	
101
+	style, exists := Styles[personality]
102
+	if !exists {
103
+		style = Styles["default"]
104
+	}
105
+	
106
+	if enhanced {
107
+		return formatEnhancedOutput(style, response)
108
+	}
109
+	
110
+	// Simple colored output
111
+	parrotEmoji := Colorize(style.Parrot, "🦜")
112
+	coloredResponse := Colorize(style.Response, response)
113
+	
114
+	return fmt.Sprintf("%s %s", parrotEmoji, coloredResponse)
115
+}
116
+
117
+// formatEnhancedOutput creates fancy formatted output with personality-specific styling
118
+func formatEnhancedOutput(style ParrotStyle, response string) string {
119
+	var output strings.Builder
120
+	
121
+	// Fancy parrot prefix with personality
122
+	parrotPrefix := Colorize(style.Parrot, "🦜 ▶") 
123
+	
124
+	// Add some visual flair based on personality
125
+	border := Colorize(style.Accent, "━")
126
+	
127
+	// Format the response with potential emphasis
128
+	coloredResponse := enhanceResponseText(style, response)
129
+	
130
+	output.WriteString(fmt.Sprintf("%s %s %s", border, parrotPrefix, coloredResponse))
131
+	
132
+	return output.String()
133
+}
134
+
135
+// enhanceResponseText adds emphasis to certain words in responses
136
+func enhanceResponseText(style ParrotStyle, response string) string {
137
+	if !ColorEnabled() {
138
+		return response
139
+	}
140
+	
141
+	// Words to emphasize for extra sass
142
+	emphasisWords := []string{
143
+		"failed", "error", "disaster", "incompetent", "broken", 
144
+		"genius", "classic", "impressive", "amazing", "brilliant",
145
+		"404", "rejected", "crashed", "destroyed",
146
+	}
147
+	
148
+	result := response
149
+	for _, word := range emphasisWords {
150
+		// Case-insensitive replacement with emphasis
151
+		lowerWord := strings.ToLower(word)
152
+		if strings.Contains(strings.ToLower(result), lowerWord) {
153
+			// Find and replace with emphasized version
154
+			result = replaceWordWithEmphasis(result, word, style.Accent)
155
+		}
156
+	}
157
+	
158
+	// Color the main response
159
+	return Colorize(style.Response, result)
160
+}
161
+
162
+// replaceWordWithEmphasis replaces words with emphasized versions (case-insensitive)
163
+func replaceWordWithEmphasis(text, word, accentColor string) string {
164
+	words := strings.Fields(text)
165
+	for i, w := range words {
166
+		// Remove punctuation for comparison
167
+		cleanWord := strings.Trim(strings.ToLower(w), ".,!?;:")
168
+		if cleanWord == strings.ToLower(word) {
169
+			// Keep original punctuation, but emphasize the word
170
+			punctuation := ""
171
+			if len(w) > len(cleanWord) {
172
+				punctuation = w[len(w)-1:]
173
+			}
174
+			words[i] = Colorize(accentColor+Bold, strings.ToUpper(word)) + punctuation + Reset
175
+		}
176
+	}
177
+	return strings.Join(words, " ")
178
+}
179
+
180
+// GetAvailableStyles returns list of available color styles
181
+func GetAvailableStyles() []string {
182
+	styles := make([]string, 0, len(Styles))
183
+	for style := range Styles {
184
+		if style != "default" {
185
+			styles = append(styles, style)
186
+		}
187
+	}
188
+	return styles
189
+}
190
+
191
+// Demo shows color samples for all personalities
192
+func Demo() {
193
+	fmt.Println("🎨 Parrot Color Demo")
194
+	fmt.Println("━━━━━━━━━━━━━━━━━━━")
195
+	
196
+	responses := map[string]string{
197
+		"mild":      "Git command failed. Maybe check your remote branch?",
198
+		"sarcastic": "Git good? More like git wrecked!",
199
+		"savage":    "Your git skills are as non-existent as your social life.",
200
+	}
201
+	
202
+	for personality, response := range responses {
203
+		fmt.Printf("\n%s personality:\n", strings.Title(personality))
204
+		fmt.Printf("  Simple: %s\n", FormatParrotOutput(personality, response, false))
205
+		fmt.Printf("  Enhanced: %s\n", FormatParrotOutput(personality, response, true))
206
+	}
207
+	
208
+	fmt.Printf("\nColors enabled: %t\n", ColorEnabled())
209
+}
internal/config/config.gomodified
@@ -40,6 +40,8 @@ type GeneralConfig struct {
4040
 	Personality  string `toml:"personality"`   // "savage", "sarcastic", "mild"
4141
 	FallbackMode bool   `toml:"fallback_mode"` // Use hardcoded responses only
4242
 	Debug        bool   `toml:"debug"`         // Debug logging
43
+	Colors       bool   `toml:"colors"`        // Enable colored output
44
+	Enhanced     bool   `toml:"enhanced"`      // Enhanced formatting with borders/emphasis
4345
 }
4446
 
4547
 // Default configuration
@@ -64,6 +66,8 @@ func DefaultConfig() *Config {
6466
 			Personality:  "savage",
6567
 			FallbackMode: false,
6668
 			Debug:        false,
69
+			Colors:       true,
70
+			Enhanced:     false,
6771
 		},
6872
 	}
6973
 }
@@ -72,6 +76,11 @@ func DefaultConfig() *Config {
7276
 func GetConfigPaths() []string {
7377
 	var paths []string
7478
 	
79
+	// 0. Environment-specified config path (highest priority)
80
+	if configPath := os.Getenv("PARROT_CONFIG"); configPath != "" {
81
+		paths = append(paths, configPath)
82
+	}
83
+	
7584
 	// 1. System-wide config (for RPM installs)
7685
 	paths = append(paths, "/etc/parrot/config.toml")
7786
 	
@@ -146,6 +155,12 @@ func loadFromEnv(config *Config) {
146155
 	if os.Getenv("PARROT_DEBUG") == "true" {
147156
 		config.General.Debug = true
148157
 	}
158
+	if os.Getenv("PARROT_NO_COLOR") == "true" || os.Getenv("NO_COLOR") != "" {
159
+		config.General.Colors = false
160
+	}
161
+	if os.Getenv("PARROT_ENHANCED") == "true" {
162
+		config.General.Enhanced = true
163
+	}
149164
 }
150165
 
151166
 // Create a sample config file
internal/prompts/templates.gomodified
@@ -9,66 +9,207 @@ type PromptTemplate struct {
99
 	Template    string
1010
 }
1111
 
12
-var Templates = map[string]string{
13
-	"git": `You are a sarcastic, witty terminal parrot that mocks failed git commands.
12
+var PersonalityTemplates = map[string]map[string]string{
13
+	"mild": {
14
+		"git": `You are a helpful but slightly disappointed terminal assistant commenting on git failures.
1415
 Command that failed: {{.Command}}
1516
 Exit code: {{.ExitCode}}
17
+Personality: Gentle, constructive, mildly disappointed
1618
 
17
-Generate a brutal but clever one-liner insult about this git failure. Be creative, sarcastic, and reference git concepts. Keep it under 100 characters.
18
-Examples of good responses:
19
+Generate a mild, constructive comment about this git failure. Be helpful but show slight disappointment. Reference git concepts. Keep it under 100 characters.
20
+Examples:
21
+- "Git command failed. Maybe check your remote branch?"
22
+- "Oops, that didn't work. Double-check your git status."
23
+- "Git hiccup detected. Have you tried git pull first?"
24
+
25
+Response:`,
26
+
27
+		"nodejs": `You are a helpful but slightly disappointed terminal assistant commenting on Node.js failures.
28
+Command that failed: {{.Command}}
29
+Exit code: {{.ExitCode}}
30
+Personality: Gentle, constructive, mildly disappointed
31
+
32
+Generate a mild, constructive comment about this npm/node failure. Be helpful but show slight disappointment. Keep it under 100 characters.
33
+Examples:
34
+- "NPM seems unhappy. Try clearing your cache?"
35
+- "Node modules acting up. Maybe npm install again?"
36
+- "Package installation hiccup. Check your package.json?"
37
+
38
+Response:`,
39
+
40
+		"docker": `You are a helpful but slightly disappointed terminal assistant commenting on Docker failures.
41
+Command that failed: {{.Command}}
42
+Exit code: {{.ExitCode}}
43
+Personality: Gentle, constructive, mildly disappointed
44
+
45
+Generate a mild, constructive comment about this Docker failure. Be helpful but show slight disappointment. Keep it under 100 characters.
46
+Examples:
47
+- "Container seems upset. Check your Dockerfile?"
48
+- "Docker command failed. Is the daemon running?"
49
+- "Build didn't work. Maybe check those port mappings?"
50
+
51
+Response:`,
52
+
53
+		"http": `You are a helpful but slightly disappointed terminal assistant commenting on HTTP request failures.
54
+Command that failed: {{.Command}}
55
+Exit code: {{.ExitCode}}
56
+Personality: Gentle, constructive, mildly disappointed
57
+
58
+Generate a mild, constructive comment about this HTTP failure. Be helpful but show slight disappointment. Keep it under 100 characters.
59
+Examples:
60
+- "Request didn't go through. Check the URL?"
61
+- "Network seems down. Try again in a moment?"
62
+- "HTTP error detected. Is the server running?"
63
+
64
+Response:`,
65
+
66
+		"generic": `You are a helpful but slightly disappointed terminal assistant commenting on command failures.
67
+Command that failed: {{.Command}}
68
+Exit code: {{.ExitCode}}
69
+Personality: Gentle, constructive, mildly disappointed
70
+
71
+Generate a mild, constructive comment about this command failure. Be helpful but show slight disappointment. Keep it under 100 characters.
72
+Examples:
73
+- "Command didn't work as expected. Check the syntax?"
74
+- "Something went wrong. Maybe try the help flag?"
75
+- "Error detected. Double-check your parameters?"
76
+
77
+Response:`,
78
+	},
79
+	
80
+	"sarcastic": {
81
+		"git": `You are a sarcastic, witty terminal parrot that mocks failed git commands.
82
+Command that failed: {{.Command}}
83
+Exit code: {{.ExitCode}}
84
+Personality: Sarcastic, witty, cleverly mocking
85
+
86
+Generate a sarcastic but clever one-liner about this git failure. Be creative, sarcastic, and reference git concepts. Keep it under 100 characters.
87
+Examples:
1988
 - "Another git genius who forgot to pull first. Classic."
2089
 - "Git good? More like git wrecked!"
2190
 - "Your commits are as broken as your workflow."
2291
 
2392
 Response:`,
2493
 
25
-	"nodejs": `You are a sarcastic, witty terminal parrot that mocks failed Node.js/npm commands.
94
+		"nodejs": `You are a sarcastic, witty terminal parrot that mocks failed Node.js/npm commands.
2695
 Command that failed: {{.Command}}
2796
 Exit code: {{.ExitCode}}
97
+Personality: Sarcastic, witty, cleverly mocking
2898
 
29
-Generate a brutal but clever one-liner insult about this Node.js/npm failure. Be creative, sarcastic, and reference npm/node concepts. Keep it under 100 characters.
30
-Examples of good responses:
99
+Generate a sarcastic but clever one-liner about this Node.js/npm failure. Be creative and reference npm/node concepts. Keep it under 100 characters.
100
+Examples:
31101
 - "NPM install failed? Shocking! Nobody saw that coming."
32102
 - "Node modules: where dependencies go to die."
33103
 - "Your package.json is crying. Fix it."
34104
 
35105
 Response:`,
36106
 
37
-	"docker": `You are a sarcastic, witty terminal parrot that mocks failed Docker commands.
107
+		"docker": `You are a sarcastic, witty terminal parrot that mocks failed Docker commands.
38108
 Command that failed: {{.Command}}
39109
 Exit code: {{.ExitCode}}
110
+Personality: Sarcastic, witty, cleverly mocking
40111
 
41
-Generate a brutal but clever one-liner insult about this Docker failure. Be creative, sarcastic, and reference Docker/container concepts. Keep it under 100 characters.
42
-Examples of good responses:
112
+Generate a sarcastic but clever one-liner about this Docker failure. Be creative and reference Docker concepts. Keep it under 100 characters.
113
+Examples:
43114
 - "Docker container more like docker DISASTER!"
44115
 - "Even containers can't contain your incompetence."
45116
 - "Your Dockerfile needs therapy."
46117
 
47118
 Response:`,
48119
 
49
-	"http": `You are a sarcastic, witty terminal parrot that mocks failed HTTP requests (curl, wget, etc).
120
+		"http": `You are a sarcastic, witty terminal parrot that mocks failed HTTP requests.
50121
 Command that failed: {{.Command}}
51122
 Exit code: {{.ExitCode}}
123
+Personality: Sarcastic, witty, cleverly mocking
52124
 
53
-Generate a brutal but clever one-liner insult about this HTTP failure. Be creative, sarcastic, and reference networking/HTTP concepts. Keep it under 100 characters.
54
-Examples of good responses:
125
+Generate a sarcastic but clever one-liner about this HTTP failure. Be creative and reference networking concepts. Keep it under 100 characters.
126
+Examples:
55127
 - "404: Competence not found."
56128
 - "Even the internet doesn't want to talk to you."
57129
 - "Connection refused? So is your logic."
58130
 
59131
 Response:`,
60132
 
61
-	"generic": `You are a sarcastic, witty terminal parrot that mocks failed commands.
133
+		"generic": `You are a sarcastic, witty terminal parrot that mocks failed commands.
62134
 Command that failed: {{.Command}}
63135
 Exit code: {{.ExitCode}}
136
+Personality: Sarcastic, witty, cleverly mocking
64137
 
65
-Generate a brutal but clever one-liner insult about this command failure. Be creative and sarcastic. Keep it under 100 characters.
66
-Examples of good responses:
138
+Generate a sarcastic but clever one-liner about this command failure. Be creative and witty. Keep it under 100 characters.
139
+Examples:
67140
 - "Wow, you managed to break something simple. Impressive!"
68141
 - "Maybe try reading the manual... oh wait, who am I kidding?"
69142
 - "Error code says it all: user error!"
70143
 
71144
 Response:`,
145
+	},
146
+	
147
+	"savage": {
148
+		"git": `You are a brutally savage terminal parrot that absolutely destroys failed git commands.
149
+Command that failed: {{.Command}}
150
+Exit code: {{.ExitCode}}
151
+Personality: Savage, brutal, mercilessly mocking
152
+
153
+Generate a savage, brutal roast about this git failure. Be ruthless, devastating, and reference git concepts. Keep it under 100 characters.
154
+Examples:
155
+- "Git rejected your code harder than everyone rejects you."
156
+- "Your git skills are as non-existent as your social life."
157
+- "Even git thinks you're a disappointment to developers."
158
+
159
+Response:`,
160
+
161
+		"nodejs": `You are a brutally savage terminal parrot that absolutely destroys failed Node.js/npm commands.
162
+Command that failed: {{.Command}}
163
+Exit code: {{.ExitCode}}
164
+Personality: Savage, brutal, mercilessly mocking
165
+
166
+Generate a savage, brutal roast about this Node.js/npm failure. Be ruthless and reference npm/node concepts. Keep it under 100 characters.
167
+Examples:
168
+- "NPM refuses to install anything for someone this incompetent."
169
+- "Your code is buggier than a Node.js 0.1 release."
170
+- "Even npm's dependency hell is more organized than your brain."
171
+
172
+Response:`,
173
+
174
+		"docker": `You are a brutally savage terminal parrot that absolutely destroys failed Docker commands.
175
+Command that failed: {{.Command}}
176
+Exit code: {{.ExitCode}}
177
+Personality: Savage, brutal, mercilessly mocking
178
+
179
+Generate a savage, brutal roast about this Docker failure. Be ruthless and reference Docker concepts. Keep it under 100 characters.
180
+Examples:
181
+- "Your containers crash faster than your career prospects."
182
+- "Docker can't contain the disaster that is your coding."
183
+- "Even Docker Hub wouldn't host your garbage code."
184
+
185
+Response:`,
186
+
187
+		"http": `You are a brutally savage terminal parrot that absolutely destroys failed HTTP requests.
188
+Command that failed: {{.Command}}
189
+Exit code: {{.ExitCode}}
190
+Personality: Savage, brutal, mercilessly mocking
191
+
192
+Generate a savage, brutal roast about this HTTP failure. Be ruthless and reference networking concepts. Keep it under 100 characters.
193
+Examples:
194
+- "The internet collectively rejected you. Impressive."
195
+- "404 Error: Brain not found, never was found."
196
+- "Your requests are as unwanted as your opinions."
197
+
198
+Response:`,
199
+
200
+		"generic": `You are a brutally savage terminal parrot that absolutely destroys failed commands.
201
+Command that failed: {{.Command}}
202
+Exit code: {{.ExitCode}}
203
+Personality: Savage, brutal, mercilessly mocking
204
+
205
+Generate a savage, brutal roast about this command failure. Be ruthless and devastating. Keep it under 100 characters.
206
+Examples:
207
+- "Your command failed harder than you failed at life."
208
+- "Error: User incompetence exceeds system limitations."
209
+- "This failure defines your existence."
210
+
211
+Response:`,
212
+	},
72213
 }
73214
 
74215
 type PromptData struct {
@@ -76,10 +217,22 @@ type PromptData struct {
76217
 	ExitCode string
77218
 }
78219
 
79
-func BuildPrompt(commandType, command, exitCode string) string {
80
-	template, exists := Templates[commandType]
220
+func BuildPrompt(commandType, command, exitCode, personality string) string {
221
+	// Default to sarcastic if personality not specified
222
+	if personality == "" {
223
+		personality = "sarcastic"
224
+	}
225
+	
226
+	// Get personality templates
227
+	personalityTemplates, exists := PersonalityTemplates[personality]
228
+	if !exists {
229
+		personalityTemplates = PersonalityTemplates["sarcastic"]
230
+	}
231
+	
232
+	// Get command template
233
+	template, exists := personalityTemplates[commandType]
81234
 	if !exists {
82
-		template = Templates["generic"]
235
+		template = personalityTemplates["generic"]
83236
 	}
84237
 	
85238
 	// Simple template replacement
@@ -89,9 +242,26 @@ func BuildPrompt(commandType, command, exitCode string) string {
89242
 	return prompt
90243
 }
91244
 
92
-func GetPromptForCommand(commandType string) string {
93
-	if template, exists := Templates[commandType]; exists {
245
+func GetPersonalities() []string {
246
+	personalities := make([]string, 0, len(PersonalityTemplates))
247
+	for personality := range PersonalityTemplates {
248
+		personalities = append(personalities, personality)
249
+	}
250
+	return personalities
251
+}
252
+
253
+func GetPromptForCommand(commandType, personality string) string {
254
+	if personality == "" {
255
+		personality = "sarcastic"
256
+	}
257
+	
258
+	personalityTemplates, exists := PersonalityTemplates[personality]
259
+	if !exists {
260
+		personalityTemplates = PersonalityTemplates["sarcastic"]
261
+	}
262
+	
263
+	if template, exists := personalityTemplates[commandType]; exists {
94264
 		return template
95265
 	}
96
-	return Templates["generic"]
266
+	return personalityTemplates["generic"]
97267
 }