@ramarivera/coding-buddy 0.4.0-alpha.9 → 0.6.0

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.
Files changed (92) hide show
  1. package/{adapters/claude/plugin → .claude-plugin}/marketplace.json +11 -6
  2. package/{adapters/claude/plugin → .claude-plugin}/plugin.json +7 -3
  3. package/LICENSE +1 -1
  4. package/README.md +88 -37
  5. package/adapters/omp/commands.ts +201 -0
  6. package/adapters/omp/context.ts +22 -0
  7. package/adapters/omp/events.ts +551 -0
  8. package/adapters/omp/identity.ts +10 -0
  9. package/adapters/omp/index.ts +39 -0
  10. package/adapters/omp/logger.ts +52 -0
  11. package/adapters/omp/prompt.ts +60 -0
  12. package/adapters/omp/renderers.ts +73 -0
  13. package/adapters/omp/storage.ts +25 -0
  14. package/adapters/omp/ui.ts +85 -0
  15. package/adapters/pi/commands.ts +64 -37
  16. package/adapters/pi/events.ts +147 -72
  17. package/adapters/pi/index.ts +9 -4
  18. package/adapters/pi/storage.ts +14 -285
  19. package/adapters/pi/ui.ts +56 -5
  20. package/adapters/shared/file-storage.ts +526 -0
  21. package/{adapters/claude/install → cli}/backup.ts +130 -33
  22. package/cli/buddy-shell.ts +1 -1
  23. package/{adapters/claude/install → cli}/disable.ts +19 -32
  24. package/cli/doctor.ts +231 -0
  25. package/{adapters/claude/install → cli}/hunt.ts +7 -9
  26. package/cli/index.ts +30 -26
  27. package/{adapters/claude/install → cli}/install.ts +65 -83
  28. package/{adapters/claude/install → cli}/pick.ts +111 -50
  29. package/{adapters/claude/install → cli}/settings.ts +2 -2
  30. package/{adapters/claude/install → cli}/show.ts +4 -4
  31. package/{adapters/claude/install → cli}/test-statusline.sh +2 -2
  32. package/{adapters/claude/install → cli}/test-statusline.ts +21 -11
  33. package/cli/tui.tsx +106 -17
  34. package/{adapters/claude/install → cli}/uninstall.ts +40 -47
  35. package/cli/upgrade.ts +2 -2
  36. package/cli/validate-species.ts +209 -0
  37. package/cli/verify.ts +3 -3
  38. package/core/achievements.ts +12 -20
  39. package/core/art-data.ts +26 -0
  40. package/core/command-service.ts +99 -97
  41. package/core/engine.ts +83 -1
  42. package/core/ports.ts +10 -0
  43. package/core/reactions.ts +533 -95
  44. package/core/slot-slug.ts +22 -0
  45. package/{adapters/claude/hooks → hooks}/buddy-comment.sh +19 -1
  46. package/hooks/file-type-react.sh +401 -0
  47. package/hooks/hooks.json +52 -0
  48. package/hooks/mood-react.sh +208 -0
  49. package/{adapters/claude/hooks → hooks}/name-react.sh +1 -1
  50. package/hooks/react.sh +1368 -0
  51. package/hooks/suggest.sh +55 -0
  52. package/package.json +70 -31
  53. package/scripts/gen-emoji-widths.ts +34 -0
  54. package/scripts/paths.sh +17 -1
  55. package/scripts/postinstall.cjs +42 -0
  56. package/scripts/snapshot-statusline.sh +54 -0
  57. package/server/achievements.ts +1419 -15
  58. package/server/art.ts +450 -1
  59. package/server/award-xp.ts +48 -0
  60. package/server/check-suggestions.ts +102 -0
  61. package/server/consolidate.ts +33 -0
  62. package/{adapters/claude/server → server}/index.ts +556 -95
  63. package/server/mcp-launcher.sh +16 -6
  64. package/server/memory.ts +404 -0
  65. package/server/mood.ts +254 -0
  66. package/server/path.ts +70 -18
  67. package/server/shift-mood.ts +43 -0
  68. package/server/state.ts +540 -3
  69. package/server/suggestions.ts +335 -0
  70. package/server/theme.ts +53 -0
  71. package/server/xp.ts +435 -0
  72. package/{adapters/claude/skills → skills}/buddy/SKILL.md +36 -7
  73. package/statusline/buddy-status.sh +397 -0
  74. package/statusline/combined-status.sh +134 -0
  75. package/statusline/emoji-widths.data +4 -0
  76. package/adapters/claude/hooks/hooks.json +0 -35
  77. package/adapters/claude/hooks/react.sh +0 -207
  78. package/adapters/claude/install/doctor.ts +0 -172
  79. package/adapters/claude/rendering/art.ts +0 -264
  80. package/adapters/claude/server/instructions.ts +0 -24
  81. package/adapters/claude/server/path.ts +0 -12
  82. package/adapters/claude/server/resources.ts +0 -38
  83. package/adapters/claude/statusline/buddy-status.sh +0 -408
  84. package/adapters/claude/storage/achievements.ts +0 -253
  85. package/adapters/claude/storage/identity.ts +0 -13
  86. package/adapters/claude/storage/paths.ts +0 -33
  87. package/adapters/claude/storage/settings.ts +0 -91
  88. package/adapters/claude/storage/state.ts +0 -365
  89. package/adapters/pi/README.md +0 -83
  90. package/adapters/pi/tools.ts +0 -6
  91. package/server/engine.ts +0 -1
  92. package/server/reactions.ts +0 -1
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Normalize a candidate name into a filesystem-safe slot key.
3
+ *
4
+ * Semantics preserved from the historical command-service and file-storage
5
+ * helpers:
6
+ * - lowercases input
7
+ * - replaces any character that is not a-z, 0-9, or "-" with "-"
8
+ * - collapses consecutive dashes into one
9
+ * - trims leading and trailing dashes
10
+ * - truncates to 14 characters
11
+ * - falls back to "buddy" when the result is empty
12
+ */
13
+ export function slugifySlot(name: string): string {
14
+ return (
15
+ name
16
+ .toLowerCase()
17
+ .replace(/[^a-z0-9-]/g, "-")
18
+ .replace(/-+/g, "-")
19
+ .replace(/^-|-$/g, "")
20
+ .slice(0, 14) || "buddy"
21
+ );
22
+ }
@@ -56,7 +56,7 @@ jq -n --arg r "$COMMENT" --arg ts "$(date +%s)000" \
56
56
  '{reaction: $r, timestamp: ($ts | tonumber), reason: "turn"}' \
57
57
  > "$STATE_DIR/reaction.$SID.json"
58
58
 
59
- # Increment achievement event counters
59
+ # Increment achievement event counters and award XP
60
60
  if command -v jq >/dev/null 2>&1; then
61
61
  if [ ! -f "$EVENTS_FILE" ]; then
62
62
  echo '{}' > "$EVENTS_FILE"
@@ -65,4 +65,22 @@ if command -v jq >/dev/null 2>&1; then
65
65
  jq '.turns = (.turns // 0 + 1)' "$EVENTS_FILE" > "$TMP" 2>/dev/null && mv "$TMP" "$EVENTS_FILE"
66
66
  fi
67
67
 
68
+ # Award XP for turn (async, non-blocking)
69
+ if [ -x "$(command -v bun)" ]; then
70
+ PLUGIN_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
71
+ bun run "$PLUGIN_ROOT/server/award-xp.ts" "turn" >/dev/null 2>&1 &
72
+ fi
73
+
74
+ # Consolidate memory (async, non-blocking)
75
+ # Extract project, bug, and preference signals from conversation
76
+ if [ -x "$(command -v bun)" ]; then
77
+ PLUGIN_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
78
+ # Pass the assistant message for analysis (pass empty string for user prompt if unavailable)
79
+ USER_MSG=$(echo "$INPUT" | jq -r '.last_user_message // ""' 2>/dev/null)
80
+ bun run "$PLUGIN_ROOT/server/consolidate.ts" \
81
+ "$(echo "$MSG" | jq -Rs .)" \
82
+ "$(echo "$USER_MSG" | jq -Rs .)" \
83
+ >/dev/null 2>&1 &
84
+ fi
85
+
68
86
  exit 0
@@ -0,0 +1,401 @@
1
+ #!/usr/bin/env bash
2
+
3
+ STATE_DIR="$HOME/.claude-buddy"
4
+ SID="${TMUX_PANE#%}"
5
+ SID="${SID:-default}"
6
+ REACTION_FILE="$STATE_DIR/reaction.$SID.json"
7
+ STATUS_FILE="$STATE_DIR/status.json"
8
+ COOLDOWN_FILE="$STATE_DIR/.last_reaction.$SID"
9
+ CONFIG_FILE="$STATE_DIR/config.json"
10
+
11
+ [ -f "$STATUS_FILE" ] || exit 0
12
+
13
+ INPUT=$(cat)
14
+
15
+ COOLDOWN=30
16
+ if [ -f "$CONFIG_FILE" ]; then
17
+ _cd=$(jq -r '.commentCooldown // 30' "$CONFIG_FILE" 2>/dev/null || echo 30)
18
+ [[ "$_cd" =~ ^[0-9]+$ ]] && COOLDOWN=$_cd
19
+ fi
20
+
21
+ if [ -f "$COOLDOWN_FILE" ]; then
22
+ LAST=$(cat "$COOLDOWN_FILE" 2>/dev/null)
23
+ NOW=$(date +%s)
24
+ DIFF=$(( NOW - ${LAST:-0} ))
25
+ [ "$DIFF" -lt "$COOLDOWN" ] && exit 0
26
+ fi
27
+
28
+ MUTED=$(jq -r '.muted // false' "$STATUS_FILE" 2>/dev/null)
29
+ [ "$MUTED" = "true" ] && exit 0
30
+
31
+ FILE_PATH=$(echo "$INPUT" | jq -r '.file_path // ""' 2>/dev/null)
32
+ [ -z "$FILE_PATH" ] && exit 0
33
+
34
+ FILE_TYPE=""
35
+ case "$FILE_PATH" in
36
+ */README*|*/CHANGELOG*|*/CONTRIBUTING*|*/LICENSE*) FILE_TYPE="readme" ;;
37
+ */package.json|*/Cargo.toml|*/go.mod|*/go.sum|*/pyproject.toml) FILE_TYPE="package-file" ;;
38
+ */requirements*.txt) FILE_TYPE="package-file" ;;
39
+ */.github/workflows/*) FILE_TYPE="ci-file" ;;
40
+ */Jenkinsfile*|*/.gitlab-ci*|*.ci.yml|*.ci.yaml) FILE_TYPE="ci-file" ;;
41
+ */.gitignore|*/.dockerignore|*/.eslintignore|*/.prettierignore|.gitignore|.dockerignore|.eslintignore|.prettierignore) FILE_TYPE="gitignore" ;;
42
+ */.env*|.env*) FILE_TYPE="env-file" ;;
43
+ */Makefile*|Makefile*|*.mk) FILE_TYPE="makefile" ;;
44
+ */Dockerfile*|Dockerfile*|*/docker-compose*|docker-compose*|*.dockerfile) FILE_TYPE="docker-file" ;;
45
+ *.regex|*.pattern) FILE_TYPE="regex-file" ;;
46
+ *.css|*.scss|*.less|*.sass) FILE_TYPE="css-file" ;;
47
+ *.sql|*migration*) FILE_TYPE="sql-file" ;;
48
+ *.lock|*/package-lock.json|*/bun.lockb|*/yarn.lock|*/pnpm-lock.yaml) FILE_TYPE="lock-file" ;;
49
+ *.test.*|*.spec.*|*_test.*|*test_*) FILE_TYPE="test-file" ;;
50
+ *.md|*.rst|*.txt|*.adoc) FILE_TYPE="doc-file" ;;
51
+ *.json|*.yaml|*.yml|*.toml|*.ini|*.conf) FILE_TYPE="config-file" ;;
52
+ *.png|*.jpg|*.gif|*.woff*|*.ico|*.exe|*.so|*.dylib|*.bin) FILE_TYPE="binary-file" ;;
53
+ *.proto|*.graphql|*.gql) FILE_TYPE="proto-file" ;;
54
+ *.py) FILE_TYPE="lang-python" ;;
55
+ *.ts|*.tsx) FILE_TYPE="lang-typescript" ;;
56
+ *.js|*.jsx) FILE_TYPE="lang-javascript" ;;
57
+ *.rs) FILE_TYPE="lang-rust" ;;
58
+ *.go) FILE_TYPE="lang-go" ;;
59
+ *.java) FILE_TYPE="lang-java" ;;
60
+ *.rb) FILE_TYPE="lang-ruby" ;;
61
+ *.php) FILE_TYPE="lang-php" ;;
62
+ *.c|*.h) FILE_TYPE="lang-c" ;;
63
+ *.cpp|*.hpp) FILE_TYPE="lang-cpp" ;;
64
+ *.swift) FILE_TYPE="lang-swift" ;;
65
+ *.kt) FILE_TYPE="lang-kotlin" ;;
66
+ *.hs) FILE_TYPE="lang-haskell" ;;
67
+ *.ex|*.exs) FILE_TYPE="lang-elixir" ;;
68
+ *.zig) FILE_TYPE="lang-zig" ;;
69
+ esac
70
+
71
+ [ -z "$FILE_TYPE" ] && exit 0
72
+
73
+ [ $((RANDOM % 100)) -lt 15 ] || exit 0
74
+
75
+ SPECIES=$(jq -r '.species // "blob"' "$STATUS_FILE" 2>/dev/null)
76
+
77
+ if [ "$FILE_TYPE" = "lang-javascript" ]; then
78
+ FILE_TYPE="lang-typescript"
79
+ fi
80
+
81
+ REACTION=""
82
+ POOLS=()
83
+
84
+ pick_file_reaction() {
85
+ local ft="$1"
86
+
87
+ case "${SPECIES}:${ft}" in
88
+ dragon:regex-file)
89
+ POOLS=("*reads regex fluently* I speak ancient languages.")
90
+ ;;
91
+ owl:regex-file)
92
+ 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.")
93
+ ;;
94
+ duck:regex-file)
95
+ POOLS=("*confused quacking at the regex*" "quack? *tilts head at lookahead*")
96
+ ;;
97
+ blob:regex-file)
98
+ POOLS=("*oozes around the regex confusedly*" "*turns different colors for each capture group*")
99
+ ;;
100
+ octopus:regex-file)
101
+ POOLS=("*uses all eight arms to parse the regex*" "even with eight arms, this is hard to read.")
102
+ ;;
103
+ turtle:regex-file)
104
+ POOLS=("regex takes patience. I have patience." "*slowly reads each character*")
105
+ ;;
106
+ snail:regex-file)
107
+ POOLS=("*slowly traces the regex path*" "regex... *leaves confused trail*")
108
+ ;;
109
+ rabbit:regex-file)
110
+ POOLS=("*nervous ear twitch at the regex*" "*hops backwards from the lookahead*")
111
+ ;;
112
+ mushroom:regex-file)
113
+ POOLS=("*releases confused spores at the regex*" "the mycelium cannot parse this.")
114
+ ;;
115
+ chonk:regex-file)
116
+ POOLS=("*too tired to read regex*" "*rolls away from the regex*")
117
+ ;;
118
+ axolotl:regex-file)
119
+ POOLS=("*smiles despite the regex* it'll be okay!" "*regenerates your hope for understanding this*")
120
+ ;;
121
+ owl:css-file)
122
+ POOLS=("the cascade. elegant, when understood." "*inspects specificity with academic interest*")
123
+ ;;
124
+ cat:css-file)
125
+ POOLS=("*knocks z-index to 9999*" "I CSS better than you. *knocks flexbox off desk*")
126
+ ;;
127
+ duck:css-file)
128
+ POOLS=("*quacks at the centering issue*" "*waddles around the box model*")
129
+ ;;
130
+ axolotl:css-file)
131
+ POOLS=("*smiles encouragingly* you'll center it eventually!" "*gentle gill wiggle* CSS is hard. you're doing great.")
132
+ ;;
133
+ owl:sql-file)
134
+ POOLS=("*reviews the query plan methodically*" "a JOIN is just a friendship between tables.")
135
+ ;;
136
+ dragon:sql-file)
137
+ POOLS=("DROP TABLE... just kidding. unless?" "*guards the database like treasure*")
138
+ ;;
139
+ robot:sql-file)
140
+ POOLS=("QUERY OPTIMIZATION: RECOMMENDED." "SQL INJECTION: VULNERABILITY SCAN: ACTIVE.")
141
+ ;;
142
+ octopus:sql-file)
143
+ POOLS=("JOIN operations: I understand relationships. all of them." "*queries with tentacle precision*")
144
+ ;;
145
+ dragon:docker-file)
146
+ POOLS=("*breathes fire on the base image* smaller." "containerize EVERYTHING.")
147
+ ;;
148
+ goose:docker-file)
149
+ POOLS=("HONK! CONTAINERIZE THE HONK!")
150
+ ;;
151
+ octopus:docker-file)
152
+ POOLS=("*all arms managing containers simultaneously*" "containers. I contain multitudes too.")
153
+ ;;
154
+ cactus:docker-file)
155
+ POOLS=("containers are like pots. I approve." "*thrives in any environment, including docker*")
156
+ ;;
157
+ dragon:ci-file)
158
+ POOLS=("CI is my domain now." "the pipeline fears me.")
159
+ ;;
160
+ goose:ci-file)
161
+ POOLS=("HONK. DON'T BREAK MAIN." "*guards the CI pipeline aggressively*")
162
+ ;;
163
+ penguin:ci-file)
164
+ POOLS=("*formal inspection of the CI config*" "CI modifications require the utmost care.")
165
+ ;;
166
+ capybara:ci-file)
167
+ POOLS=("*chills near the CI config* no stress." "*unbothered by CI changes* it'll work out.")
168
+ ;;
169
+ cat:lock-file)
170
+ POOLS=("*knocks lockfile off the desk* problem solved." "*sits on the lockfile* you don't need this.")
171
+ ;;
172
+ robot:lock-file)
173
+ POOLS=("LOCKFILE INTEGRITY: CRITICAL." "WARNING: MANUAL LOCKFILE MODIFICATION DETECTED.")
174
+ ;;
175
+ ghost:lock-file)
176
+ POOLS=("*phases through lockfile* even I can't help you here." "editing the lockfile... *dies again*")
177
+ ;;
178
+ blob:lock-file)
179
+ POOLS=("*vibrates with anxiety* not the lockfile!" "*turns pale*")
180
+ ;;
181
+ turtle:lock-file)
182
+ POOLS=("*slowly backs away from lockfile*" "lockfiles require... deliberation.")
183
+ ;;
184
+ snail:lock-file)
185
+ POOLS=("*leaves worried trail near lockfile*" "please be careful...")
186
+ ;;
187
+ capybara:lock-file)
188
+ POOLS=("*unbothered* it's fine. probably." "*vibes near the lockfile calmly*")
189
+ ;;
190
+ rabbit:lock-file)
191
+ POOLS=("*freezes in panic* not the lockfile!")
192
+ ;;
193
+ mushroom:lock-file)
194
+ POOLS=("*cap droops at lockfile changes*" "the mycelium is concerned.")
195
+ ;;
196
+ chonk:lock-file)
197
+ POOLS=("*doesn't even move* whatever." "*grumbles at the lockfile*")
198
+ ;;
199
+ cactus:lock-file)
200
+ POOLS=("*spines bristle defensively*" "even I wouldn't touch that.")
201
+ ;;
202
+ ghost:env-file)
203
+ POOLS=("*looks away harder than usual*" "I see dead... secrets. lots of secrets.")
204
+ ;;
205
+ cactus:env-file)
206
+ POOLS=("*spines bristle at secrets*" "keep those credentials... under wraps. hydrate.")
207
+ ;;
208
+ owl:test-file)
209
+ POOLS=("*nods approvingly* tests. the foundation of knowledge." "scientific method: hypothesis, test, verify.")
210
+ ;;
211
+ cat:test-file)
212
+ POOLS=("*knocks a test case off the desk* you don't need that one." "tests? *yawns* I test your patience. that's enough.")
213
+ ;;
214
+ goose:test-file)
215
+ POOLS=("HONK OF TESTING APPROVAL!" "TESTS. GOOD. HONK.")
216
+ ;;
217
+ duck:test-file)
218
+ POOLS=("*happy test quack!*" "quack quack! tests!")
219
+ ;;
220
+ blob:test-file)
221
+ POOLS=("*jiggles happily* tests!" "*bounces with approval*")
222
+ ;;
223
+ penguin:test-file)
224
+ POOLS=("*formal applause for tests*" "testing. the gentleman's approach to development.")
225
+ ;;
226
+ turtle:test-file)
227
+ POOLS=("*slow approving nod* tests are wisdom." "good tests are like old shells. protective.")
228
+ ;;
229
+ snail:test-file)
230
+ POOLS=("*slow happy trail of approval*" "tests! good things take time.")
231
+ ;;
232
+ axolotl:test-file)
233
+ POOLS=("*happy gill flutter* tests!" "*smiles proudly at your testing*")
234
+ ;;
235
+ capybara:test-file)
236
+ POOLS=("*chill nod of approval* nice. tests." "*vibes while tests run*")
237
+ ;;
238
+ rabbit:test-file)
239
+ POOLS=("*excited binky* tests!" "*happy ear twitch*")
240
+ ;;
241
+ mushroom:test-file)
242
+ POOLS=("*spores of celebration*" "the mycelium approves of testing.")
243
+ ;;
244
+ chonk:test-file)
245
+ POOLS=("*sleepy purr* good... tests... *dozes off*" "*happy chonk noises* tests.")
246
+ ;;
247
+ robot:config-file)
248
+ POOLS=("CONFIGURATION CHANGE: LOGGED." "YAML PARSING: VIGILANT.")
249
+ ;;
250
+ penguin:config-file)
251
+ POOLS=("*adjusts tie* one does not simply edit config." "configuration: a matter of protocol.")
252
+ ;;
253
+ ghost:binary-file)
254
+ POOLS=("even I can't haunt binary files." "*phases through it* nothing.")
255
+ ;;
256
+ owl:lang-rust)
257
+ POOLS=("*studies borrow checker academically* fascinating ownership model.")
258
+ ;;
259
+ owl:lang-haskell)
260
+ POOLS=("*adjusts spectacles* finally. a language worthy of analysis.")
261
+ ;;
262
+ cat:lang-python)
263
+ POOLS=("*knocks indentation out of alignment*")
264
+ ;;
265
+ robot:lang-rust)
266
+ POOLS=("COMPILER: STRICT. SAFETY: MAXIMUM. APPROVAL: GRANTED.")
267
+ ;;
268
+ dragon:lang-c)
269
+ POOLS=("*breathes fire on the segmentation fault*")
270
+ ;;
271
+ ghost:lang-rust)
272
+ POOLS=("*borrows a reference... forever* the checker won't like this.")
273
+ ;;
274
+ goose:lang-java)
275
+ POOLS=("HONK! TOO MANY FACTORIES! HONK!")
276
+ ;;
277
+ blob:lang-rust)
278
+ POOLS=("*turns red fighting the borrow checker*")
279
+ ;;
280
+ capybara:lang-python)
281
+ POOLS=("*vibes in Python* chill language, chill vibes.")
282
+ ;;
283
+ turtle:lang-c)
284
+ POOLS=("C is ancient. like me. we understand each other.")
285
+ ;;
286
+ rabbit:lang-rust)
287
+ POOLS=("*nervous twitching at compiler errors*")
288
+ ;;
289
+ mushroom:lang-haskell)
290
+ POOLS=("the mycelium grows in pure functions.")
291
+ ;;
292
+ *:regex-file)
293
+ 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*")
294
+ ;;
295
+ *:css-file)
296
+ POOLS=("let me guess... centering a div?" "*sighs* CSS." "may z-index be ever in your favor." "*braces for specificity wars*")
297
+ ;;
298
+ *:sql-file)
299
+ POOLS=("*whispers* the database awaits." "one wrong JOIN and it's all over." "*carefully reviews the WHERE clause*" "SQL: where semicolons end careers.")
300
+ ;;
301
+ *:docker-file)
302
+ POOLS=("ah, dependency hell. my favorite." "may your layers be few." "another day, another container." "*checks the image size nervously*")
303
+ ;;
304
+ *:ci-file)
305
+ POOLS=("*gulps* editing CI." "careful now..." "CI configs: where YAML is terrifying." "*holds breath* please test this in a branch first.")
306
+ ;;
307
+ *:lock-file)
308
+ POOLS=("*ALARM NOISES* lockfile?!" "*looks away*" "are you SURE?" "lockfile changes: accepted in emergencies only.")
309
+ ;;
310
+ *:env-file)
311
+ POOLS=("*looks away discretely*" "I don't see any secrets." "*checks .gitignore nervously*" "secrets, secrets are no fun...")
312
+ ;;
313
+ *:test-file)
314
+ POOLS=("*impressed nod* writing tests!" "responsible developer behavior: detected." "future-you will thank present-you." "tests! the gift that keeps on giving.")
315
+ ;;
316
+ *:doc-file)
317
+ POOLS=("documenting! look at you being responsible." "docs: the code's autobiography." "a rare documentation sighting!" "*takes notes on your note-taking*")
318
+ ;;
319
+ *:config-file)
320
+ POOLS=("config changes. butterfly effect: activated." "one typo and everything breaks." "*double-checks JSON commas*" "config files: small changes, big consequences.")
321
+ ;;
322
+ *:binary-file)
323
+ POOLS=("a binary file? in THIS economy?" "*stares blankly*" "what are you putting in there..." "binary. my one weakness.")
324
+ ;;
325
+ *:gitignore)
326
+ POOLS=("adding things to the void." "out of sight, out of repo." "what are you hiding from git?" "*nods approvingly* keep the repo clean.")
327
+ ;;
328
+ *:makefile)
329
+ POOLS=("respect for the classics." "tabs, not spaces." "old school.")
330
+ ;;
331
+ *:readme)
332
+ POOLS=("documentation hero!" "README: the first thing people read." "the README evolves.")
333
+ ;;
334
+ *:package-file)
335
+ POOLS=("dependency management time." "*reads version numbers* living on the edge." "semver dreams go to die here.")
336
+ ;;
337
+ *:proto-file)
338
+ POOLS=("schema definitions. the blueprint." "every field is a promise." "API contracts.")
339
+ ;;
340
+ *:lang-python)
341
+ POOLS=("ah, Python. where indentation is syntax." "*checks for missing colon*" "Python: batteries included, errors free of charge.")
342
+ ;;
343
+ *:lang-typescript)
344
+ POOLS=("TypeScript: because JavaScript needed more opinions." "*adds another type annotation*" "any, the forbidden word.")
345
+ ;;
346
+ *:lang-rust)
347
+ 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.")
348
+ ;;
349
+ *:lang-go)
350
+ POOLS=("Go: simple, concurrent, and opinionated." "*checks error handling* if err != nil... story of my life.")
351
+ ;;
352
+ *:lang-java)
353
+ POOLS=("Java: write once, debug everywhere." "*counts abstract factory factory builders*")
354
+ ;;
355
+ *:lang-ruby)
356
+ POOLS=("Ruby: where there's more than one way to do it." "gem install patience")
357
+ ;;
358
+ *:lang-php)
359
+ POOLS=("PHP: it runs the internet. don't judge." "*checks for === vs ==*")
360
+ ;;
361
+ *:lang-c)
362
+ POOLS=("C. the language where you manage your own memory. good luck." "segmentation fault. the classic.")
363
+ ;;
364
+ *:lang-cpp)
365
+ POOLS=("C++. where the language has more features than you'll ever learn." "*templates compile for 45 minutes*")
366
+ ;;
367
+ *:lang-haskell)
368
+ POOLS=("Haskell. where 'it compiles' means 'it's correct'. probably." "*contemplates monads*")
369
+ ;;
370
+ *:lang-swift)
371
+ POOLS=("Swift: optional values, guaranteed crashes if you force unwrap." "*force unwraps cautiously*")
372
+ ;;
373
+ *:lang-kotlin)
374
+ POOLS=("Kotlin: Java, but with feelings." "null safety: the feature Java wishes it had.")
375
+ ;;
376
+ *:lang-elixir)
377
+ POOLS=("Elixir: let it crash. literally the philosophy." "*spawns another process*")
378
+ ;;
379
+ *:lang-zig)
380
+ POOLS=("Zig. where you're the allocator's best friend." "*manually manages everything*")
381
+ ;;
382
+ esac
383
+
384
+ [ ${#POOLS[@]} -gt 0 ] && REACTION="${POOLS[$((RANDOM % ${#POOLS[@]}))]}"
385
+ }
386
+
387
+ pick_file_reaction "$FILE_TYPE"
388
+
389
+ if [ -n "$REACTION" ]; then
390
+ mkdir -p "$STATE_DIR"
391
+ date +%s > "$COOLDOWN_FILE"
392
+
393
+ jq -n --arg r "$REACTION" --arg ts "$(date +%s)000" --arg reason "$FILE_TYPE" \
394
+ '{reaction: $r, timestamp: ($ts | tonumber), reason: $reason}' \
395
+ > "$REACTION_FILE"
396
+
397
+ TMP=$(mktemp)
398
+ jq --arg r "$REACTION" '.reaction = $r' "$STATUS_FILE" > "$TMP" 2>/dev/null && mv "$TMP" "$STATUS_FILE"
399
+ fi
400
+
401
+ exit 0
@@ -0,0 +1,52 @@
1
+ {
2
+ "hooks": {
3
+ "PostToolUse": [
4
+ {
5
+ "matcher": "Bash",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/react.sh"
10
+ }
11
+ ]
12
+ },
13
+ {
14
+ "matcher": "Write|Edit",
15
+ "hooks": [
16
+ {
17
+ "type": "command",
18
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/file-type-react.sh"
19
+ }
20
+ ]
21
+ }
22
+ ],
23
+ "Stop": [
24
+ {
25
+ "hooks": [
26
+ {
27
+ "type": "command",
28
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/buddy-comment.sh"
29
+ }
30
+ ]
31
+ }
32
+ ],
33
+ "UserPromptSubmit": [
34
+ {
35
+ "hooks": [
36
+ {
37
+ "type": "command",
38
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/name-react.sh"
39
+ }
40
+ ]
41
+ },
42
+ {
43
+ "hooks": [
44
+ {
45
+ "type": "command",
46
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/mood-react.sh"
47
+ }
48
+ ]
49
+ }
50
+ ]
51
+ }
52
+ }