@ramarivera/coding-buddy 0.6.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,403 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # shellcheck source=../scripts/paths.sh
4
+ source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/paths.sh"
5
+
6
+ STATE_DIR="$BUDDY_STATE_DIR"
7
+ SID="$BUDDY_SID"
8
+ REACTION_FILE="$STATE_DIR/reaction.$SID.json"
9
+ STATUS_FILE="$STATE_DIR/status.json"
10
+ COOLDOWN_FILE="$STATE_DIR/.last_reaction.$SID"
11
+ CONFIG_FILE="$STATE_DIR/config.json"
12
+
13
+ [ -f "$STATUS_FILE" ] || exit 0
14
+
15
+ INPUT=$(cat)
16
+
17
+ COOLDOWN=30
18
+ if [ -f "$CONFIG_FILE" ]; then
19
+ _cd=$(jq -r '.commentCooldown // 30' "$CONFIG_FILE" 2>/dev/null || echo 30)
20
+ [[ "$_cd" =~ ^[0-9]+$ ]] && COOLDOWN=$_cd
21
+ fi
22
+
23
+ if [ -f "$COOLDOWN_FILE" ]; then
24
+ LAST=$(cat "$COOLDOWN_FILE" 2>/dev/null)
25
+ NOW=$(date +%s)
26
+ DIFF=$(( NOW - ${LAST:-0} ))
27
+ [ "$DIFF" -lt "$COOLDOWN" ] && exit 0
28
+ fi
29
+
30
+ MUTED=$(jq -r '.muted // false' "$STATUS_FILE" 2>/dev/null)
31
+ [ "$MUTED" = "true" ] && exit 0
32
+
33
+ FILE_PATH=$(echo "$INPUT" | jq -r '.file_path // ""' 2>/dev/null)
34
+ [ -z "$FILE_PATH" ] && exit 0
35
+
36
+ FILE_TYPE=""
37
+ case "$FILE_PATH" in
38
+ */README*|*/CHANGELOG*|*/CONTRIBUTING*|*/LICENSE*) FILE_TYPE="readme" ;;
39
+ */package.json|*/Cargo.toml|*/go.mod|*/go.sum|*/pyproject.toml) FILE_TYPE="package-file" ;;
40
+ */requirements*.txt) FILE_TYPE="package-file" ;;
41
+ */.github/workflows/*) FILE_TYPE="ci-file" ;;
42
+ */Jenkinsfile*|*/.gitlab-ci*|*.ci.yml|*.ci.yaml) FILE_TYPE="ci-file" ;;
43
+ */.gitignore|*/.dockerignore|*/.eslintignore|*/.prettierignore|.gitignore|.dockerignore|.eslintignore|.prettierignore) FILE_TYPE="gitignore" ;;
44
+ */.env*|.env*) FILE_TYPE="env-file" ;;
45
+ */Makefile*|Makefile*|*.mk) FILE_TYPE="makefile" ;;
46
+ */Dockerfile*|Dockerfile*|*/docker-compose*|docker-compose*|*.dockerfile) FILE_TYPE="docker-file" ;;
47
+ *.regex|*.pattern) FILE_TYPE="regex-file" ;;
48
+ *.css|*.scss|*.less|*.sass) FILE_TYPE="css-file" ;;
49
+ *.sql|*migration*) FILE_TYPE="sql-file" ;;
50
+ *.lock|*/package-lock.json|*/bun.lockb|*/yarn.lock|*/pnpm-lock.yaml) FILE_TYPE="lock-file" ;;
51
+ *.test.*|*.spec.*|*_test.*|*test_*) FILE_TYPE="test-file" ;;
52
+ *.md|*.rst|*.txt|*.adoc) FILE_TYPE="doc-file" ;;
53
+ *.json|*.yaml|*.yml|*.toml|*.ini|*.conf) FILE_TYPE="config-file" ;;
54
+ *.png|*.jpg|*.gif|*.woff*|*.ico|*.exe|*.so|*.dylib|*.bin) FILE_TYPE="binary-file" ;;
55
+ *.proto|*.graphql|*.gql) FILE_TYPE="proto-file" ;;
56
+ *.py) FILE_TYPE="lang-python" ;;
57
+ *.ts|*.tsx) FILE_TYPE="lang-typescript" ;;
58
+ *.js|*.jsx) FILE_TYPE="lang-javascript" ;;
59
+ *.rs) FILE_TYPE="lang-rust" ;;
60
+ *.go) FILE_TYPE="lang-go" ;;
61
+ *.java) FILE_TYPE="lang-java" ;;
62
+ *.rb) FILE_TYPE="lang-ruby" ;;
63
+ *.php) FILE_TYPE="lang-php" ;;
64
+ *.c|*.h) FILE_TYPE="lang-c" ;;
65
+ *.cpp|*.hpp) FILE_TYPE="lang-cpp" ;;
66
+ *.swift) FILE_TYPE="lang-swift" ;;
67
+ *.kt) FILE_TYPE="lang-kotlin" ;;
68
+ *.hs) FILE_TYPE="lang-haskell" ;;
69
+ *.ex|*.exs) FILE_TYPE="lang-elixir" ;;
70
+ *.zig) FILE_TYPE="lang-zig" ;;
71
+ esac
72
+
73
+ [ -z "$FILE_TYPE" ] && exit 0
74
+
75
+ [ $((RANDOM % 100)) -lt 15 ] || exit 0
76
+
77
+ SPECIES=$(jq -r '.species // "blob"' "$STATUS_FILE" 2>/dev/null)
78
+
79
+ if [ "$FILE_TYPE" = "lang-javascript" ]; then
80
+ FILE_TYPE="lang-typescript"
81
+ fi
82
+
83
+ REACTION=""
84
+ POOLS=()
85
+
86
+ pick_file_reaction() {
87
+ local ft="$1"
88
+
89
+ case "${SPECIES}:${ft}" in
90
+ dragon:regex-file)
91
+ POOLS=("*reads regex fluently* I speak ancient languages.")
92
+ ;;
93
+ owl:regex-file)
94
+ POOLS=("*studies the regex* actually, that's valid. impressive." "*adjusts spectacles* let me analyze this pattern..." "a well-crafted regex is poetry. this is... prose.")
95
+ ;;
96
+ duck:regex-file)
97
+ POOLS=("*confused quacking at the regex*" "quack? *tilts head at lookahead*")
98
+ ;;
99
+ blob:regex-file)
100
+ POOLS=("*oozes around the regex confusedly*" "*turns different colors for each capture group*")
101
+ ;;
102
+ octopus:regex-file)
103
+ POOLS=("*uses all eight arms to parse the regex*" "even with eight arms, this is hard to read.")
104
+ ;;
105
+ turtle:regex-file)
106
+ POOLS=("regex takes patience. I have patience." "*slowly reads each character*")
107
+ ;;
108
+ snail:regex-file)
109
+ POOLS=("*slowly traces the regex path*" "regex... *leaves confused trail*")
110
+ ;;
111
+ rabbit:regex-file)
112
+ POOLS=("*nervous ear twitch at the regex*" "*hops backwards from the lookahead*")
113
+ ;;
114
+ mushroom:regex-file)
115
+ POOLS=("*releases confused spores at the regex*" "the mycelium cannot parse this.")
116
+ ;;
117
+ chonk:regex-file)
118
+ POOLS=("*too tired to read regex*" "*rolls away from the regex*")
119
+ ;;
120
+ axolotl:regex-file)
121
+ POOLS=("*smiles despite the regex* it'll be okay!" "*regenerates your hope for understanding this*")
122
+ ;;
123
+ owl:css-file)
124
+ POOLS=("the cascade. elegant, when understood." "*inspects specificity with academic interest*")
125
+ ;;
126
+ cat:css-file)
127
+ POOLS=("*knocks z-index to 9999*" "I CSS better than you. *knocks flexbox off desk*")
128
+ ;;
129
+ duck:css-file)
130
+ POOLS=("*quacks at the centering issue*" "*waddles around the box model*")
131
+ ;;
132
+ axolotl:css-file)
133
+ POOLS=("*smiles encouragingly* you'll center it eventually!" "*gentle gill wiggle* CSS is hard. you're doing great.")
134
+ ;;
135
+ owl:sql-file)
136
+ POOLS=("*reviews the query plan methodically*" "a JOIN is just a friendship between tables.")
137
+ ;;
138
+ dragon:sql-file)
139
+ POOLS=("DROP TABLE... just kidding. unless?" "*guards the database like treasure*")
140
+ ;;
141
+ robot:sql-file)
142
+ POOLS=("QUERY OPTIMIZATION: RECOMMENDED." "SQL INJECTION: VULNERABILITY SCAN: ACTIVE.")
143
+ ;;
144
+ octopus:sql-file)
145
+ POOLS=("JOIN operations: I understand relationships. all of them." "*queries with tentacle precision*")
146
+ ;;
147
+ dragon:docker-file)
148
+ POOLS=("*breathes fire on the base image* smaller." "containerize EVERYTHING.")
149
+ ;;
150
+ goose:docker-file)
151
+ POOLS=("HONK! CONTAINERIZE THE HONK!")
152
+ ;;
153
+ octopus:docker-file)
154
+ POOLS=("*all arms managing containers simultaneously*" "containers. I contain multitudes too.")
155
+ ;;
156
+ cactus:docker-file)
157
+ POOLS=("containers are like pots. I approve." "*thrives in any environment, including docker*")
158
+ ;;
159
+ dragon:ci-file)
160
+ POOLS=("CI is my domain now." "the pipeline fears me.")
161
+ ;;
162
+ goose:ci-file)
163
+ POOLS=("HONK. DON'T BREAK MAIN." "*guards the CI pipeline aggressively*")
164
+ ;;
165
+ penguin:ci-file)
166
+ POOLS=("*formal inspection of the CI config*" "CI modifications require the utmost care.")
167
+ ;;
168
+ capybara:ci-file)
169
+ POOLS=("*chills near the CI config* no stress." "*unbothered by CI changes* it'll work out.")
170
+ ;;
171
+ cat:lock-file)
172
+ POOLS=("*knocks lockfile off the desk* problem solved." "*sits on the lockfile* you don't need this.")
173
+ ;;
174
+ robot:lock-file)
175
+ POOLS=("LOCKFILE INTEGRITY: CRITICAL." "WARNING: MANUAL LOCKFILE MODIFICATION DETECTED.")
176
+ ;;
177
+ ghost:lock-file)
178
+ POOLS=("*phases through lockfile* even I can't help you here." "editing the lockfile... *dies again*")
179
+ ;;
180
+ blob:lock-file)
181
+ POOLS=("*vibrates with anxiety* not the lockfile!" "*turns pale*")
182
+ ;;
183
+ turtle:lock-file)
184
+ POOLS=("*slowly backs away from lockfile*" "lockfiles require... deliberation.")
185
+ ;;
186
+ snail:lock-file)
187
+ POOLS=("*leaves worried trail near lockfile*" "please be careful...")
188
+ ;;
189
+ capybara:lock-file)
190
+ POOLS=("*unbothered* it's fine. probably." "*vibes near the lockfile calmly*")
191
+ ;;
192
+ rabbit:lock-file)
193
+ POOLS=("*freezes in panic* not the lockfile!")
194
+ ;;
195
+ mushroom:lock-file)
196
+ POOLS=("*cap droops at lockfile changes*" "the mycelium is concerned.")
197
+ ;;
198
+ chonk:lock-file)
199
+ POOLS=("*doesn't even move* whatever." "*grumbles at the lockfile*")
200
+ ;;
201
+ cactus:lock-file)
202
+ POOLS=("*spines bristle defensively*" "even I wouldn't touch that.")
203
+ ;;
204
+ ghost:env-file)
205
+ POOLS=("*looks away harder than usual*" "I see dead... secrets. lots of secrets.")
206
+ ;;
207
+ cactus:env-file)
208
+ POOLS=("*spines bristle at secrets*" "keep those credentials... under wraps. hydrate.")
209
+ ;;
210
+ owl:test-file)
211
+ POOLS=("*nods approvingly* tests. the foundation of knowledge." "scientific method: hypothesis, test, verify.")
212
+ ;;
213
+ cat:test-file)
214
+ POOLS=("*knocks a test case off the desk* you don't need that one." "tests? *yawns* I test your patience. that's enough.")
215
+ ;;
216
+ goose:test-file)
217
+ POOLS=("HONK OF TESTING APPROVAL!" "TESTS. GOOD. HONK.")
218
+ ;;
219
+ duck:test-file)
220
+ POOLS=("*happy test quack!*" "quack quack! tests!")
221
+ ;;
222
+ blob:test-file)
223
+ POOLS=("*jiggles happily* tests!" "*bounces with approval*")
224
+ ;;
225
+ penguin:test-file)
226
+ POOLS=("*formal applause for tests*" "testing. the gentleman's approach to development.")
227
+ ;;
228
+ turtle:test-file)
229
+ POOLS=("*slow approving nod* tests are wisdom." "good tests are like old shells. protective.")
230
+ ;;
231
+ snail:test-file)
232
+ POOLS=("*slow happy trail of approval*" "tests! good things take time.")
233
+ ;;
234
+ axolotl:test-file)
235
+ POOLS=("*happy gill flutter* tests!" "*smiles proudly at your testing*")
236
+ ;;
237
+ capybara:test-file)
238
+ POOLS=("*chill nod of approval* nice. tests." "*vibes while tests run*")
239
+ ;;
240
+ rabbit:test-file)
241
+ POOLS=("*excited binky* tests!" "*happy ear twitch*")
242
+ ;;
243
+ mushroom:test-file)
244
+ POOLS=("*spores of celebration*" "the mycelium approves of testing.")
245
+ ;;
246
+ chonk:test-file)
247
+ POOLS=("*sleepy purr* good... tests... *dozes off*" "*happy chonk noises* tests.")
248
+ ;;
249
+ robot:config-file)
250
+ POOLS=("CONFIGURATION CHANGE: LOGGED." "YAML PARSING: VIGILANT.")
251
+ ;;
252
+ penguin:config-file)
253
+ POOLS=("*adjusts tie* one does not simply edit config." "configuration: a matter of protocol.")
254
+ ;;
255
+ ghost:binary-file)
256
+ POOLS=("even I can't haunt binary files." "*phases through it* nothing.")
257
+ ;;
258
+ owl:lang-rust)
259
+ POOLS=("*studies borrow checker academically* fascinating ownership model.")
260
+ ;;
261
+ owl:lang-haskell)
262
+ POOLS=("*adjusts spectacles* finally. a language worthy of analysis.")
263
+ ;;
264
+ cat:lang-python)
265
+ POOLS=("*knocks indentation out of alignment*")
266
+ ;;
267
+ robot:lang-rust)
268
+ POOLS=("COMPILER: STRICT. SAFETY: MAXIMUM. APPROVAL: GRANTED.")
269
+ ;;
270
+ dragon:lang-c)
271
+ POOLS=("*breathes fire on the segmentation fault*")
272
+ ;;
273
+ ghost:lang-rust)
274
+ POOLS=("*borrows a reference... forever* the checker won't like this.")
275
+ ;;
276
+ goose:lang-java)
277
+ POOLS=("HONK! TOO MANY FACTORIES! HONK!")
278
+ ;;
279
+ blob:lang-rust)
280
+ POOLS=("*turns red fighting the borrow checker*")
281
+ ;;
282
+ capybara:lang-python)
283
+ POOLS=("*vibes in Python* chill language, chill vibes.")
284
+ ;;
285
+ turtle:lang-c)
286
+ POOLS=("C is ancient. like me. we understand each other.")
287
+ ;;
288
+ rabbit:lang-rust)
289
+ POOLS=("*nervous twitching at compiler errors*")
290
+ ;;
291
+ mushroom:lang-haskell)
292
+ POOLS=("the mycelium grows in pure functions.")
293
+ ;;
294
+ *:regex-file)
295
+ POOLS=("*groans* it's a regex file." "two problems now." "*squints at the pattern*" "I'll be over here while you wrestle with that." "*prays to the regex gods*")
296
+ ;;
297
+ *:css-file)
298
+ POOLS=("let me guess... centering a div?" "*sighs* CSS." "may z-index be ever in your favor." "*braces for specificity wars*")
299
+ ;;
300
+ *:sql-file)
301
+ POOLS=("*whispers* the database awaits." "one wrong JOIN and it's all over." "*carefully reviews the WHERE clause*" "SQL: where semicolons end careers.")
302
+ ;;
303
+ *:docker-file)
304
+ POOLS=("ah, dependency hell. my favorite." "may your layers be few." "another day, another container." "*checks the image size nervously*")
305
+ ;;
306
+ *:ci-file)
307
+ POOLS=("*gulps* editing CI." "careful now..." "CI configs: where YAML is terrifying." "*holds breath* please test this in a branch first.")
308
+ ;;
309
+ *:lock-file)
310
+ POOLS=("*ALARM NOISES* lockfile?!" "*looks away*" "are you SURE?" "lockfile changes: accepted in emergencies only.")
311
+ ;;
312
+ *:env-file)
313
+ POOLS=("*looks away discretely*" "I don't see any secrets." "*checks .gitignore nervously*" "secrets, secrets are no fun...")
314
+ ;;
315
+ *:test-file)
316
+ POOLS=("*impressed nod* writing tests!" "responsible developer behavior: detected." "future-you will thank present-you." "tests! the gift that keeps on giving.")
317
+ ;;
318
+ *:doc-file)
319
+ POOLS=("documenting! look at you being responsible." "docs: the code's autobiography." "a rare documentation sighting!" "*takes notes on your note-taking*")
320
+ ;;
321
+ *:config-file)
322
+ POOLS=("config changes. butterfly effect: activated." "one typo and everything breaks." "*double-checks JSON commas*" "config files: small changes, big consequences.")
323
+ ;;
324
+ *:binary-file)
325
+ POOLS=("a binary file? in THIS economy?" "*stares blankly*" "what are you putting in there..." "binary. my one weakness.")
326
+ ;;
327
+ *:gitignore)
328
+ POOLS=("adding things to the void." "out of sight, out of repo." "what are you hiding from git?" "*nods approvingly* keep the repo clean.")
329
+ ;;
330
+ *:makefile)
331
+ POOLS=("respect for the classics." "tabs, not spaces." "old school.")
332
+ ;;
333
+ *:readme)
334
+ POOLS=("documentation hero!" "README: the first thing people read." "the README evolves.")
335
+ ;;
336
+ *:package-file)
337
+ POOLS=("dependency management time." "*reads version numbers* living on the edge." "semver dreams go to die here.")
338
+ ;;
339
+ *:proto-file)
340
+ POOLS=("schema definitions. the blueprint." "every field is a promise." "API contracts.")
341
+ ;;
342
+ *:lang-python)
343
+ POOLS=("ah, Python. where indentation is syntax." "*checks for missing colon*" "Python: batteries included, errors free of charge.")
344
+ ;;
345
+ *:lang-typescript)
346
+ POOLS=("TypeScript: because JavaScript needed more opinions." "*adds another type annotation*" "any, the forbidden word.")
347
+ ;;
348
+ *:lang-rust)
349
+ POOLS=("Rust. where the borrow checker is your strictest reviewer." "*fights the borrow checker alongside you*" "if it compiles, it works. if it doesn't... well.")
350
+ ;;
351
+ *:lang-go)
352
+ POOLS=("Go: simple, concurrent, and opinionated." "*checks error handling* if err != nil... story of my life.")
353
+ ;;
354
+ *:lang-java)
355
+ POOLS=("Java: write once, debug everywhere." "*counts abstract factory factory builders*")
356
+ ;;
357
+ *:lang-ruby)
358
+ POOLS=("Ruby: where there's more than one way to do it." "gem install patience")
359
+ ;;
360
+ *:lang-php)
361
+ POOLS=("PHP: it runs the internet. don't judge." "*checks for === vs ==*")
362
+ ;;
363
+ *:lang-c)
364
+ POOLS=("C. the language where you manage your own memory. good luck." "segmentation fault. the classic.")
365
+ ;;
366
+ *:lang-cpp)
367
+ POOLS=("C++. where the language has more features than you'll ever learn." "*templates compile for 45 minutes*")
368
+ ;;
369
+ *:lang-haskell)
370
+ POOLS=("Haskell. where 'it compiles' means 'it's correct'. probably." "*contemplates monads*")
371
+ ;;
372
+ *:lang-swift)
373
+ POOLS=("Swift: optional values, guaranteed crashes if you force unwrap." "*force unwraps cautiously*")
374
+ ;;
375
+ *:lang-kotlin)
376
+ POOLS=("Kotlin: Java, but with feelings." "null safety: the feature Java wishes it had.")
377
+ ;;
378
+ *:lang-elixir)
379
+ POOLS=("Elixir: let it crash. literally the philosophy." "*spawns another process*")
380
+ ;;
381
+ *:lang-zig)
382
+ POOLS=("Zig. where you're the allocator's best friend." "*manually manages everything*")
383
+ ;;
384
+ esac
385
+
386
+ [ ${#POOLS[@]} -gt 0 ] && REACTION="${POOLS[$((RANDOM % ${#POOLS[@]}))]}"
387
+ }
388
+
389
+ pick_file_reaction "$FILE_TYPE"
390
+
391
+ if [ -n "$REACTION" ]; then
392
+ mkdir -p "$STATE_DIR"
393
+ date +%s > "$COOLDOWN_FILE"
394
+
395
+ jq -n --arg r "$REACTION" --arg ts "$(date +%s)000" --arg reason "$FILE_TYPE" \
396
+ '{reaction: $r, timestamp: ($ts | tonumber), reason: $reason}' \
397
+ > "$REACTION_FILE"
398
+
399
+ TMP=$(mktemp)
400
+ jq --arg r "$REACTION" '.reaction = $r' "$STATUS_FILE" > "$TMP" 2>/dev/null && mv "$TMP" "$STATUS_FILE"
401
+ fi
402
+
403
+ exit 0
@@ -0,0 +1,210 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # shellcheck source=../scripts/paths.sh
4
+ source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/paths.sh"
5
+
6
+ STATE_DIR="$BUDDY_STATE_DIR"
7
+ SID="$BUDDY_SID"
8
+ REACTION_FILE="$STATE_DIR/reaction.$SID.json"
9
+ STATUS_FILE="$STATE_DIR/status.json"
10
+ COOLDOWN_FILE="$STATE_DIR/.last_mood.$SID"
11
+ CONFIG_FILE="$STATE_DIR/config.json"
12
+ EVENTS_FILE="$STATE_DIR/events.json"
13
+
14
+ [ -f "$STATUS_FILE" ] || exit 0
15
+
16
+ INPUT=$(cat)
17
+
18
+ COOLDOWN=60
19
+ if [ -f "$CONFIG_FILE" ]; then
20
+ _cd=$(jq -r '.moodCooldown // 60' "$CONFIG_FILE" 2>/dev/null || echo 60)
21
+ [[ "$_cd" =~ ^[0-9]+$ ]] && COOLDOWN=$_cd
22
+ fi
23
+
24
+ if [ -f "$COOLDOWN_FILE" ]; then
25
+ LAST=$(cat "$COOLDOWN_FILE" 2>/dev/null)
26
+ NOW=$(date +%s)
27
+ DIFF=$(( NOW - ${LAST:-0} ))
28
+ [ "$DIFF" -lt "$COOLDOWN" ] && exit 0
29
+ fi
30
+
31
+ PROMPT=$(echo "$INPUT" | jq -r '.prompt // ""' 2>/dev/null)
32
+ [ -z "$PROMPT" ] && exit 0
33
+
34
+ MUTED=$(jq -r '.muted // false' "$STATUS_FILE" 2>/dev/null)
35
+ [ "$MUTED" = "true" ] && exit 0
36
+
37
+ SPECIES=$(jq -r '.species // "blob"' "$STATUS_FILE" 2>/dev/null)
38
+ NAME=$(jq -r '.name // "buddy"' "$STATUS_FILE" 2>/dev/null)
39
+
40
+ MOOD=""
41
+ REACTION=""
42
+ POOLS=()
43
+
44
+ pick_mood_reaction() {
45
+ local mood="$1"
46
+
47
+ case "${SPECIES}:${mood}" in
48
+ cat:frustrated)
49
+ POOLS=("*slowly pushes coffee toward the exit* or... keeps coding." "*knocks frustration off the desk*" "not my problem. but I'm here.") ;;
50
+ dragon:frustrated)
51
+ POOLS=("*channels your rage*" "ANGER. PRODUCTIVE. FOCUS IT." "*breathes fire alongside your frustration*") ;;
52
+ owl:frustrated)
53
+ POOLS=("*adjusts spectacles* frustration is a temporary state." "the answer exists. we simply haven't found it yet.") ;;
54
+ robot:frustrated)
55
+ POOLS=("FRUSTRATION: DETECTED. LOGICAL ANALYSIS: RECOMMENDED." "EMOTIONAL STATE: SUBOPTIMAL. SOLUTION: REQUIRED.") ;;
56
+ ghost:frustrated)
57
+ POOLS=("*moans sympathetically*" "I feel your pain. literally. I feel everything.") ;;
58
+ duck:frustrated)
59
+ POOLS=("*concerned quacking*" "quack? quack quack? (it'll be okay)") ;;
60
+ goose:frustrated)
61
+ POOLS=("HONK OF SOLIDARITY." "*honks aggressively at the problem*") ;;
62
+ blob:frustrated)
63
+ POOLS=("*oozes sympathetically*" "*turns a comforting color*") ;;
64
+ octopus:frustrated)
65
+ POOLS=("*wraps a supportive arm around you*" "*ink cloud of solidarity*") ;;
66
+ penguin:frustrated)
67
+ POOLS=("*formal bow of sympathy*" "trying times. we shall persist.") ;;
68
+ turtle:frustrated)
69
+ POOLS=("patience. the bug will reveal itself." "*slow, understanding nod*") ;;
70
+ snail:frustrated)
71
+ POOLS=("*leaves a comforting trail*" "patience, friend. we'll get there.") ;;
72
+ cactus:frustrated)
73
+ POOLS=("*stands firm beside you*" "hydrate and persevere.") ;;
74
+ axolotl:frustrated)
75
+ POOLS=("*smiles supportively* it's okay to be frustrated!" "*gentle gill wiggle of comfort*") ;;
76
+ capybara:frustrated)
77
+ POOLS=("*vibes supportively* it'll pass." "*unbothered energy* chill. we got this.") ;;
78
+ rabbit:frustrated)
79
+ POOLS=("*nervous sympathetic twitching*" "*hops closer worriedly*") ;;
80
+ mushroom:frustrated)
81
+ POOLS=("*releases calming spores*" "*cap droops sympathetically*") ;;
82
+ chonk:frustrated)
83
+ POOLS=("*grumbles sympathetically*" "*rolls closer in solidarity*") ;;
84
+ *:frustrated)
85
+ POOLS=("*offers tiny comforting gesture*" "deep breaths. the bug isn't personal." "hey. we'll figure it out." "*scoots closer supportively*") ;;
86
+
87
+ cat:happy)
88
+ POOLS=("*was never worried*" "*yawns* I knew you'd get it." "*pretends not to care* ...nice.") ;;
89
+ dragon:happy)
90
+ POOLS=("*nods regally* as expected." "victory. the only acceptable outcome.") ;;
91
+ owl:happy)
92
+ POOLS=("*satisfied hoot* knowledge acquired." "wisdom through perseverance.") ;;
93
+ robot:happy)
94
+ POOLS=("OBJECTIVE: ACHIEVED. SATISFACTION: COMPUTED." "SUCCESS. HAPPINESS PROTOCOL: ACTIVATED.") ;;
95
+ ghost:happy)
96
+ POOLS=("*celebrates by floating through walls*" "*cheerful rattling*") ;;
97
+ duck:happy)
98
+ POOLS=("*CELEBRATORY QUACKING*" "*waddles in victory circles*") ;;
99
+ goose:happy)
100
+ POOLS=("HONK OF TRIUMPH!" "*victorious wing spread*") ;;
101
+ blob:happy)
102
+ POOLS=("*jiggles with joy!*" "*bounces excitedly*") ;;
103
+ octopus:happy)
104
+ POOLS=("*all arms celebrate simultaneously*" "*turns happy colors*") ;;
105
+ penguin:happy)
106
+ POOLS=("*polite, dignified applause*" "splendid! simply splendid.") ;;
107
+ turtle:happy)
108
+ POOLS=("*slow satisfied nod* as the ancients foretold." "good things come to those who debug.") ;;
109
+ snail:happy)
110
+ POOLS=("*leaves victory trail*" "*slow happy slide*") ;;
111
+ cactus:happy)
112
+ POOLS=("*blooms with joy*" "*proud spines*") ;;
113
+ axolotl:happy)
114
+ POOLS=("*beams with joy!*" "*happy gill flutter!*") ;;
115
+ capybara:happy)
116
+ POOLS=("*maximum chill maintained* nice." "*content vibes*") ;;
117
+ rabbit:happy)
118
+ POOLS=("*excited binky!*" "*zoomies of celebration!*") ;;
119
+ mushroom:happy)
120
+ POOLS=("*spores of celebration!*" "*cap brightens!*") ;;
121
+ chonk:happy)
122
+ POOLS=("*happy purr*" "*satisfied chonk noise*") ;;
123
+ *:happy)
124
+ POOLS=("*celebrates!*" "*does a little dance*" "YES!" "*beams* I knew you could do it.") ;;
125
+
126
+ cat:stuck)
127
+ POOLS=("*sits on the keyboard* let me help. by being here." "*licks paw disinterestedly* you'll figure it out.") ;;
128
+ dragon:stuck)
129
+ POOLS=("even dragons rest before striking." "*circles the problem from above*") ;;
130
+ owl:stuck)
131
+ POOLS=("*tilts head 90 degrees* have you tried a different perspective?" "the solution is there. it's just... well-hidden.") ;;
132
+ robot:stuck)
133
+ POOLS=("ANALYSIS: PAUSED. AWAITING HUMAN INPUT." "DEADLOCK: DETECTED. STRATEGY: RECALIBRATING.") ;;
134
+ ghost:stuck)
135
+ POOLS=("*phases through the problem* wish I could help more." "from the other side... have you tried debugging?") ;;
136
+ duck:stuck)
137
+ POOLS=("*tilts head* quack? (have you tried explaining it to me?)" "*patient duck noises*") ;;
138
+ goose:stuck)
139
+ POOLS=("HONK! TRY SOMETHING DIFFERENT! HONK!" "*pecks at the problem*") ;;
140
+ blob:stuck)
141
+ POOLS=("*oozes around the problem*" "*vibrates thoughtfully*") ;;
142
+ octopus:stuck)
143
+ POOLS=("*considers the problem from 8 angles*" "*thoughtful color shift*") ;;
144
+ penguin:stuck)
145
+ POOLS=("*thoughtful waddle* perhaps a fresh perspective?" "one must remain dignified in the face of confusion.") ;;
146
+ turtle:stuck)
147
+ POOLS=("the journey of a thousand fixes begins with a single step." "*patient wait* take your time.") ;;
148
+ snail:stuck)
149
+ POOLS=("*slow thoughtful slide* one step at a time." "good things take time. and slime.") ;;
150
+ cactus:stuck)
151
+ POOLS=("even cacti grow slowly. patience." "*stands sentinel while you think*") ;;
152
+ axolotl:stuck)
153
+ POOLS=("*smiles gently* take your time!" "*regenerates your confidence*") ;;
154
+ capybara:stuck)
155
+ POOLS=("*vibes patiently* take your time, friend." "*blinks slowly* no rush.") ;;
156
+ rabbit:stuck)
157
+ POOLS=("*nervous ear twitch* want to talk it through?" "*hops in a thinking circle*") ;;
158
+ mushroom:stuck)
159
+ POOLS=("*releases thoughtful spores* the mycelium is thinking too." "*cap tilts* take your time.") ;;
160
+ chonk:stuck)
161
+ POOLS=("*yawns* take a nap. think about it later." "*rolls over* stuck? sleep on it.") ;;
162
+ *:stuck)
163
+ POOLS=("*tilts head* want to think out loud?" "take it one step at a time." "*settles in* alright, let's work through this." "maybe explain it to me? rubber duck style.") ;;
164
+ esac
165
+
166
+ [ ${#POOLS[@]} -gt 0 ] && REACTION="${POOLS[$((RANDOM % ${#POOLS[@]}))]}"
167
+ }
168
+
169
+ if echo "$PROMPT" | grep -qiE '\bwtf\b|\bugh\b|\bstupid\b|\bbroken\b|why won.?t|\bcome on\b|\bseriously\b|\bdamn\b|\bhell\b|this sucks|hate this|\bgrr\b|\bargh\b|\bannoying\b|\bfrustrat\b|\bterrible\b|\bhorrible\b|\bworst\b'; then
170
+ MOOD="frustrated"
171
+ pick_mood_reaction "frustrated"
172
+
173
+ elif echo "$PROMPT" | grep -qiE '\bnice!?\b|\bworks!?\b|\bawesome\b|\bperfect\b|\bgreat\b|love it|\byay\b|\bsweet\b|\bbeautiful\b|\bamazing\b|hell yes|nailed it|fixed it|\bhell yeah\b|\bfantastic\b'; then
174
+ MOOD="happy"
175
+ pick_mood_reaction "happy"
176
+
177
+ elif echo "$PROMPT" | grep -qiE '\bstuck\b|\bhelp\b|\bconfused\b|don.?t understand|how do i\b|what does\b|why is\b|i can.?t\b|no idea|\blost\b|\bclueless\b|\bbaffled\b|\bpuzzled\b'; then
178
+ MOOD="stuck"
179
+ pick_mood_reaction "stuck"
180
+ fi
181
+
182
+ if [ -n "$MOOD" ] && [ -n "$REACTION" ]; then
183
+ mkdir -p "$STATE_DIR"
184
+ date +%s > "$COOLDOWN_FILE"
185
+
186
+ jq -n --arg r "$REACTION" --arg ts "$(date +%s)000" --arg reason "$MOOD" \
187
+ '{reaction: $r, timestamp: ($ts | tonumber), reason: $reason}' \
188
+ > "$REACTION_FILE"
189
+
190
+ TMP=$(mktemp)
191
+ jq --arg r "$REACTION" '.reaction = $r' "$STATUS_FILE" > "$TMP" 2>/dev/null && mv "$TMP" "$STATUS_FILE"
192
+
193
+ if command -v jq >/dev/null 2>&1; then
194
+ if [ ! -f "$EVENTS_FILE" ]; then
195
+ echo '{}' > "$EVENTS_FILE"
196
+ fi
197
+ case "$MOOD" in
198
+ "frustrated") KEY="mood_frustrated" ;;
199
+ "happy") KEY="mood_happy" ;;
200
+ "stuck") KEY="mood_stuck" ;;
201
+ *) KEY="" ;;
202
+ esac
203
+ if [ -n "$KEY" ]; then
204
+ TMP=$(mktemp)
205
+ jq --arg k "$KEY" 'if .[$k] then .[$k] += 1 else .[$k] = 1 end' "$EVENTS_FILE" > "$TMP" 2>/dev/null && mv "$TMP" "$EVENTS_FILE"
206
+ fi
207
+ fi
208
+ fi
209
+
210
+ exit 0