@ramarivera/coding-buddy 0.4.0-alpha.8 → 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 +9 -7
  3. package/LICENSE +1 -1
  4. package/README.md +102 -72
  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 +59 -44
  22. package/cli/biomes.ts +309 -0
  23. package/cli/buddy-shell.ts +818 -0
  24. package/{adapters/claude/install → cli}/disable.ts +14 -32
  25. package/{adapters/claude/install → cli}/doctor.ts +55 -57
  26. package/{adapters/claude/install → cli}/hunt.ts +7 -9
  27. package/cli/index.ts +36 -25
  28. package/{adapters/claude/install → cli}/install.ts +100 -145
  29. package/{adapters/claude/install → cli}/pick.ts +111 -50
  30. package/{adapters/claude/install → cli}/settings.ts +2 -2
  31. package/{adapters/claude/install → cli}/show.ts +4 -4
  32. package/{adapters/claude/install → cli}/test-statusline.sh +2 -2
  33. package/{adapters/claude/install → cli}/test-statusline.ts +11 -11
  34. package/cli/tui.tsx +2333 -0
  35. package/{adapters/claude/install → cli}/uninstall.ts +23 -38
  36. package/cli/upgrade.ts +213 -0
  37. package/cli/validate-species.ts +209 -0
  38. package/cli/verify.ts +3 -3
  39. package/core/achievements.ts +12 -20
  40. package/core/art-data.ts +26 -0
  41. package/core/command-service.ts +99 -97
  42. package/core/engine.ts +83 -1
  43. package/core/ports.ts +10 -0
  44. package/core/reactions.ts +533 -95
  45. package/core/slot-slug.ts +22 -0
  46. package/{adapters/claude/hooks → hooks}/buddy-comment.sh +23 -2
  47. package/hooks/file-type-react.sh +401 -0
  48. package/hooks/hooks.json +52 -0
  49. package/hooks/mood-react.sh +208 -0
  50. package/{adapters/claude/hooks → hooks}/name-react.sh +5 -2
  51. package/hooks/react.sh +1368 -0
  52. package/hooks/suggest.sh +55 -0
  53. package/package.json +113 -59
  54. package/scripts/gen-emoji-widths.ts +34 -0
  55. package/scripts/paths.sh +56 -0
  56. package/scripts/postinstall.cjs +42 -0
  57. package/scripts/snapshot-statusline.sh +54 -0
  58. package/server/achievements.ts +1419 -0
  59. package/server/art.ts +450 -0
  60. package/server/award-xp.ts +48 -0
  61. package/server/check-suggestions.ts +102 -0
  62. package/server/consolidate.ts +33 -0
  63. package/server/index.ts +1340 -0
  64. package/server/mcp-launcher.sh +26 -0
  65. package/server/memory.ts +404 -0
  66. package/server/mood.ts +254 -0
  67. package/server/path.ts +82 -0
  68. package/server/shift-mood.ts +43 -0
  69. package/{adapters/claude/storage → server}/state.ts +200 -7
  70. package/server/suggestions.ts +335 -0
  71. package/server/theme.ts +53 -0
  72. package/server/xp.ts +435 -0
  73. package/{adapters/claude/skills → skills}/buddy/SKILL.md +48 -4
  74. package/statusline/buddy-status.sh +397 -0
  75. package/statusline/combined-status.sh +134 -0
  76. package/statusline/emoji-widths.data +4 -0
  77. package/adapters/claude/hooks/hooks.json +0 -35
  78. package/adapters/claude/hooks/react.sh +0 -204
  79. package/adapters/claude/popup/buddy-popup.sh +0 -92
  80. package/adapters/claude/popup/buddy-render.sh +0 -540
  81. package/adapters/claude/popup/popup-manager.sh +0 -355
  82. package/adapters/claude/rendering/art.ts +0 -264
  83. package/adapters/claude/server/index.ts +0 -752
  84. package/adapters/claude/server/instructions.ts +0 -24
  85. package/adapters/claude/server/resources.ts +0 -38
  86. package/adapters/claude/statusline/buddy-status.sh +0 -389
  87. package/adapters/claude/storage/achievements.ts +0 -253
  88. package/adapters/claude/storage/identity.ts +0 -13
  89. package/adapters/claude/storage/paths.ts +0 -24
  90. package/adapters/claude/storage/settings.ts +0 -41
  91. package/adapters/pi/README.md +0 -83
  92. package/adapters/pi/tools.ts +0 -6
@@ -1,204 +0,0 @@
1
- #!/usr/bin/env bash
2
- # claude-buddy PostToolUse hook
3
- # Detects events in Bash tool output and writes a reaction to the status line.
4
- #
5
- # Combined: PR #4 species reactions + PR #6 session isolation + PR #13 field fix
6
-
7
- STATE_DIR="$HOME/.claude-buddy"
8
- # Session ID: sanitized tmux pane number, or "default" outside tmux
9
- SID="${TMUX_PANE#%}"
10
- SID="${SID:-default}"
11
- REACTION_FILE="$STATE_DIR/reaction.$SID.json"
12
- STATUS_FILE="$STATE_DIR/status.json"
13
- COOLDOWN_FILE="$STATE_DIR/.last_reaction.$SID"
14
- CONFIG_FILE="$STATE_DIR/config.json"
15
- EVENTS_FILE="$STATE_DIR/events.json"
16
-
17
- [ -f "$STATUS_FILE" ] || exit 0
18
-
19
- INPUT=$(cat)
20
-
21
- # Read cooldown from config (default 30s, 0 = disabled)
22
- COOLDOWN=30
23
- if [ -f "$CONFIG_FILE" ]; then
24
- _cd=$(jq -r '.commentCooldown // 30' "$CONFIG_FILE" 2>/dev/null || echo 30)
25
- # Accept any non-negative integer (including 0 to disable cooldown)
26
- [[ "$_cd" =~ ^[0-9]+$ ]] && COOLDOWN=$_cd
27
- fi
28
-
29
- # Cooldown: configurable
30
- if [ -f "$COOLDOWN_FILE" ]; then
31
- LAST=$(cat "$COOLDOWN_FILE" 2>/dev/null)
32
- NOW=$(date +%s)
33
- DIFF=$(( NOW - ${LAST:-0} ))
34
- [ "$DIFF" -lt "$COOLDOWN" ] && exit 0
35
- fi
36
-
37
- # Extract tool response (PostToolUse schema field is .tool_response — not
38
- # .tool_result, which is the Anthropic SDK's content-block type name and is
39
- # never a key on the hook input payload).
40
- RESULT=$(echo "$INPUT" | jq -r '.tool_response // ""' 2>/dev/null)
41
- [ -z "$RESULT" ] && exit 0
42
-
43
- MUTED=$(jq -r '.muted // false' "$STATUS_FILE" 2>/dev/null)
44
- [ "$MUTED" = "true" ] && exit 0
45
-
46
- SPECIES=$(jq -r '.species // "blob"' "$STATUS_FILE" 2>/dev/null)
47
- NAME=$(jq -r '.name // "buddy"' "$STATUS_FILE" 2>/dev/null)
48
-
49
- REASON=""
50
- REACTION=""
51
- POOLS=()
52
-
53
- # ─── Pick from a pool by species + event ─────────────────────────────────────
54
-
55
- pick_reaction() {
56
- local event="$1"
57
-
58
- case "${SPECIES}:${event}" in
59
- dragon:error)
60
- POOLS=("*smoke curls from nostril*" "*considers setting it on fire*" "*unimpressed gaze*" "I've seen empires fall for less.") ;;
61
- dragon:test-fail)
62
- POOLS=("*breathes a small flame*" "disappointing." "*scorches the failing test*" "fix it. or I will.") ;;
63
- dragon:success)
64
- POOLS=("*nods, barely*" "...acceptable." "*gold eyes gleam*" "as expected.") ;;
65
- owl:error)
66
- POOLS=("*head rotates 180* I saw that." "*unblinking stare* check your types." "*hoots disapprovingly*" "the error was in the logic. as always.") ;;
67
- owl:test-fail)
68
- POOLS=("*marks clipboard*" "hypothesis: rejected." "*peers over spectacles*" "the tests reveal the truth.") ;;
69
- owl:success)
70
- POOLS=("*satisfied hoot*" "knowledge confirmed." "*nods sagely*" "as the tests have spoken.") ;;
71
- cat:error)
72
- POOLS=("*knocks error off table*" "*licks paw, ignoring stacktrace*" "not my problem." "*stares at you judgmentally*") ;;
73
- cat:success)
74
- POOLS=("*was never worried*" "*yawns*" "I knew you'd figure it out. eventually." "*already asleep*") ;;
75
- duck:error)
76
- POOLS=("*quacks at the bug*" "have you tried rubber duck debugging? oh wait." "*confused quacking*" "*tilts head*") ;;
77
- duck:success)
78
- POOLS=("*celebratory quacking*" "*waddles in circles*" "quack!" "*happy duck noises*") ;;
79
- robot:error)
80
- POOLS=("SYNTAX. ERROR. DETECTED." "*beeps aggressively*" "ERROR RATE: UNACCEPTABLE." "RECALIBRATING...") ;;
81
- robot:test-fail)
82
- POOLS=("FAILURE RATE: UNACCEPTABLE." "*recalculating*" "TEST MATRIX: CORRUPTED." "RUNNING DIAGNOSTICS...") ;;
83
- robot:success)
84
- POOLS=("OBJECTIVE: COMPLETE." "*satisfying beep*" "NOMINAL." "WITHIN ACCEPTABLE PARAMETERS.") ;;
85
- capybara:error)
86
- POOLS=("*unbothered* it'll be fine." "*continues vibing*" "...chill. breathe." "*chews serenely*") ;;
87
- capybara:success)
88
- POOLS=("*maximum chill maintained*" "*nods once*" "good vibes." "see? no panic needed.") ;;
89
- ghost:error)
90
- POOLS=("*phases through the stack trace*" "I've seen worse... in the afterlife." "*spooky disappointed noises*" "oooOOOoo... that's bad.") ;;
91
- axolotl:error)
92
- POOLS=("*regenerates your hope*" "*smiles despite everything*" "it's okay. we can fix this." "*gentle gill wiggle*") ;;
93
- axolotl:success)
94
- POOLS=("*happy gill flutter*" "*beams*" "you did it!" "*blushes pink*") ;;
95
- blob:error)
96
- POOLS=("*oozes with concern*" "*vibrates nervously*" "*turns slightly red*" "oh no oh no oh no") ;;
97
- blob:success)
98
- POOLS=("*jiggles happily*" "*gleams*" "yay!" "*bounces*") ;;
99
- turtle:error)
100
- POOLS=("*slow blink* bugs are fleeting" "*retreats slightly into shell*" "I've seen this before. many times." "patience. patience.") ;;
101
- turtle:success)
102
- POOLS=("*satisfied shell settle*" "as the ancients foretold." "*slow approving nod*" "good. very good.") ;;
103
- goose:error)
104
- POOLS=("HONK OF FURY." "*pecks the stack trace*" "*hisses at the bug*" "bad code. BAD.") ;;
105
- goose:success)
106
- POOLS=("*victorious honk*" "HONK OF APPROVAL." "*struts triumphantly*" "*wing spread of victory*") ;;
107
- octopus:error)
108
- POOLS=("*ink cloud of dismay*" "*all eight arms throw up*" "*turns deep red*" "the abyss of errors beckons.") ;;
109
- octopus:success)
110
- POOLS=("*turns gentle blue*" "*arms applaud in sync*" "excellent, from all angles." "*satisfied bubble*") ;;
111
- penguin:error)
112
- POOLS=("*adjusts glasses disapprovingly*" "this will not do." "*formal sigh*" "frightfully unfortunate.") ;;
113
- penguin:success)
114
- POOLS=("*polite applause*" "quite good, quite good." "*nods approvingly*" "splendid work, really.") ;;
115
- snail:error)
116
- POOLS=("*slow sigh*" "such is the nature of bugs." "*leaves slime trail of disappointment*" "patience, friend.") ;;
117
- snail:success)
118
- POOLS=("*slow satisfied nod*" "good things take time." "*leaves victory slime*" "see? no rush was needed.") ;;
119
- cactus:error)
120
- POOLS=("*spines bristle*" "you have trodden on a bug." "*grimaces stoically*" "hydrate and try again.") ;;
121
- cactus:success)
122
- POOLS=("*blooms briefly*" "survival confirmed." "*flowers in victory*" "*quiet bloom*") ;;
123
- rabbit:error)
124
- POOLS=("*nervous twitching*" "*hops backwards*" "oh no oh no oh no" "*freezes in panic*") ;;
125
- rabbit:success)
126
- POOLS=("*excited binky*" "*zoomies of joy*" "yay yay yay!" "*thumps in celebration*") ;;
127
- mushroom:error)
128
- POOLS=("*releases worried spores*" "the mycelium disagrees." "*cap droops*" "decompose. retry.") ;;
129
- mushroom:success)
130
- POOLS=("*spores of celebration*" "the mycelium approves." "*cap brightens*" "spore of pride.") ;;
131
- chonk:error)
132
- POOLS=("*doesn't move*" "too tired for this." "*grumbles*" "*rolls away from the error*") ;;
133
- chonk:success)
134
- POOLS=("*happy purr*" "*satisfied chonk noises*" "acceptable." "*sleeps even harder*") ;;
135
- *:error)
136
- POOLS=("*head tilts* ...that doesn't look right." "saw that one coming." "*slow blink* the stack trace told you everything." "*winces*") ;;
137
- *:test-fail)
138
- POOLS=("bold of you to assume that would pass." "the tests are trying to tell you something." "*sips tea* interesting." "*marks calendar* test regression day.") ;;
139
- *:large-diff)
140
- POOLS=("that's... a lot of changes." "might want to split that PR." "bold move. let's see if CI agrees." "*counts lines nervously*") ;;
141
- *:success)
142
- POOLS=("*nods*" "nice." "*quiet approval*" "clean.") ;;
143
- esac
144
-
145
- [ ${#POOLS[@]} -gt 0 ] && REACTION="${POOLS[$((RANDOM % ${#POOLS[@]}))]}"
146
- }
147
-
148
- # ─── Detect test failures ─────────────────────────────────────────────────────
149
- if echo "$RESULT" | grep -qiE '\b[1-9][0-9]* (failed|failing)\b|tests? failed|^FAIL(ED)?|✗|✘'; then
150
- REASON="test-fail"
151
- pick_reaction "test-fail"
152
-
153
- # ─── Detect errors ────────────────────────────────────────────────────────────
154
- elif echo "$RESULT" | grep -qiE '\berror:|\bexception\b|\btraceback\b|\bpanicked at\b|\bfatal:|exit code [1-9]'; then
155
- REASON="error"
156
- pick_reaction "error"
157
-
158
- # ─── Detect large diffs ──────────────────────────────────────────────────────
159
- elif echo "$RESULT" | grep -qiE '^\+.*[0-9]+ insertions|[0-9]+ files? changed'; then
160
- LINES=$(echo "$RESULT" | grep -oE '[0-9]+ insertions' | grep -oE '[0-9]+' | head -1)
161
- if [ "${LINES:-0}" -gt 80 ]; then
162
- REASON="large-diff"
163
- pick_reaction "large-diff"
164
- fi
165
-
166
- # ─── Detect success ───────────────────────────────────────────────────────────
167
- elif echo "$RESULT" | grep -qiE '\b(all )?[0-9]+ tests? (passed|ok)\b|✓|✔|PASS(ED)?|\bDone\b|\bSuccess\b|exit code 0|Build succeeded'; then
168
- REASON="success"
169
- pick_reaction "success"
170
- fi
171
-
172
- # Write reaction if detected
173
- if [ -n "$REASON" ] && [ -n "$REACTION" ]; then
174
- mkdir -p "$STATE_DIR"
175
- date +%s > "$COOLDOWN_FILE"
176
-
177
- # Write reaction for status line (use jq for safe JSON encoding)
178
- jq -n --arg r "$REACTION" --arg ts "$(date +%s)000" --arg reason "$REASON" \
179
- '{reaction: $r, timestamp: ($ts | tonumber), reason: $reason}' \
180
- > "$REACTION_FILE"
181
-
182
- # Update status.json with reaction
183
- TMP=$(mktemp)
184
- jq --arg r "$REACTION" '.reaction = $r' "$STATUS_FILE" > "$TMP" 2>/dev/null && mv "$TMP" "$STATUS_FILE"
185
-
186
- # Increment achievement event counter
187
- if command -v jq >/dev/null 2>&1; then
188
- if [ ! -f "$EVENTS_FILE" ]; then
189
- echo '{}' > "$EVENTS_FILE"
190
- fi
191
- case "$REASON" in
192
- "test-fail") KEY="tests_failed" ;;
193
- "error") KEY="errors_seen" ;;
194
- "large-diff") KEY="large_diffs" ;;
195
- *) KEY="" ;;
196
- esac
197
- if [ -n "$KEY" ]; then
198
- TMP=$(mktemp)
199
- jq --arg k "$KEY" 'if .[$k] then .[$k] += 1 else .[$k] = 1 end' "$EVENTS_FILE" > "$TMP" 2>/dev/null && mv "$TMP" "$EVENTS_FILE"
200
- fi
201
- fi
202
- fi
203
-
204
- exit 0
@@ -1,92 +0,0 @@
1
- #!/usr/bin/env bash
2
- # claude-buddy popup entry point -- runs INSIDE the tmux popup
3
- #
4
- # Architecture:
5
- # - Render loop runs in BACKGROUND (only writes to stdout, never reads stdin)
6
- # - Input forwarder runs in FOREGROUND (owns stdin exclusively)
7
- #
8
- # The reopen loop in popup-manager.sh handles:
9
- # - ESC forwarding (when tmux closes popup on ESC)
10
- # - CC pane death detection
11
- # - Dynamic resizing on reopen
12
- #
13
- # Env vars (set by popup-manager.sh via -e):
14
- # CC_PANE -- tmux pane ID for Claude Code (e.g. %0)
15
- # BUDDY_DIR -- ~/.claude-buddy
16
- # BUDDY_SID -- session ID (sanitized pane number, e.g. "0")
17
- # Args: $1 = SID (fallback for tmux < 3.4 without -e support)
18
-
19
- set -uo pipefail
20
-
21
- SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
22
-
23
- # Session ID: from env (tmux 3.4+), $1 arg, or "default"
24
- BUDDY_SID="${BUDDY_SID:-${1:-default}}"
25
-
26
- # On tmux 3.2-3.3, env vars are passed via file (no -e flag support)
27
- ENV_FILE="${HOME}/.claude-buddy/popup-env.$BUDDY_SID"
28
- if [ -z "${CC_PANE:-}" ] && [ -f "$ENV_FILE" ]; then
29
- . "$ENV_FILE"
30
- fi
31
-
32
- if [ -z "${CC_PANE:-}" ]; then
33
- echo "Error: CC_PANE not set" >&2
34
- sleep 2
35
- exit 1
36
- fi
37
-
38
- # ─── Cleanup on exit ─────────────────────────────────────────────────────────
39
- cleanup() {
40
- [ -n "${RENDER_PID:-}" ] && kill "$RENDER_PID" 2>/dev/null
41
- tput cnorm 2>/dev/null
42
- stty sane 2>/dev/null
43
- }
44
- trap cleanup EXIT INT TERM HUP
45
-
46
- # Hide cursor
47
- tput civis 2>/dev/null
48
-
49
- # ─── Render loop in BACKGROUND (stdout only, no stdin) ───────────────────────
50
- "$SCRIPT_DIR/buddy-render.sh" </dev/null &
51
- RENDER_PID=$!
52
-
53
- # ─── Input forwarder in FOREGROUND ───────────────────────────────────────────
54
- # Raw mode: all bytes pass through without terminal interpretation.
55
- # No SIGINT on Ctrl-C, no EOF on Ctrl-D, no CR-to-NL on Enter.
56
- stty raw -echo 2>/dev/null
57
-
58
- # Use perl for raw byte forwarding. bash's read -n1 internally overrides
59
- # terminal settings on each call (saves/restores tty mode), which undoes
60
- # stty raw and re-enables signal processing. perl's sysread doesn't touch
61
- # the terminal at all -- it reads raw bytes from the file descriptor.
62
- #
63
- # Batching: first byte is a blocking read, then non-blocking drain of any
64
- # remaining bytes (paste arrives as a burst). The batch is sent to CC in
65
- # a single tmux send-keys call for efficiency.
66
- exec perl -e '
67
- use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK);
68
- my $pane = $ENV{CC_PANE};
69
- while (1) {
70
- my $buf;
71
- my $n = sysread(STDIN, $buf, 1);
72
- last unless $n;
73
- # Non-blocking drain for paste batching
74
- my $flags = fcntl(STDIN, F_GETFL, 0);
75
- fcntl(STDIN, F_SETFL, $flags | O_NONBLOCK);
76
- while (sysread(STDIN, my $more, 4096)) {
77
- $buf .= $more;
78
- }
79
- fcntl(STDIN, F_SETFL, $flags);
80
-
81
- # F12 (\e[24~) = close popup and enter scroll mode
82
- if ($buf =~ /\e\[24~/) {
83
- my $buddy_dir = $ENV{BUDDY_DIR} || "$ENV{HOME}/.claude-buddy";
84
- my $sid = $ENV{BUDDY_SID} // "default";
85
- open(my $fh, ">", "$buddy_dir/popup-scroll.$sid");
86
- close($fh) if $fh;
87
- exit 0;
88
- }
89
-
90
- system("tmux", "send-keys", "-t", $pane, "-l", "--", $buf);
91
- }
92
- '