@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,18 +1,18 @@
1
1
  {
2
- "name": "claude-buddy",
2
+ "name": "coding-buddy",
3
3
  "owner": {
4
4
  "name": "1270011"
5
5
  },
6
6
  "metadata": {
7
- "description": "Permanent coding companion for Claude Code",
8
- "version": "0.4.0-alpha.8"
7
+ "description": "Persistent coding companion for Claude Code, Pi, and Oh My Pi (requires bun: https://bun.sh/install)",
8
+ "version": "0.6.0"
9
9
  },
10
10
  "plugins": [
11
11
  {
12
- "name": "claude-buddy",
12
+ "name": "coding-buddy",
13
13
  "source": "./",
14
- "description": "Permanent coding companion for Claude Code \u2014 survives any update. MCP-based terminal pet with ASCII art, stats, reactions, and personality.",
15
- "version": "0.4.0-alpha.8",
14
+ "description": "Persistent coding companion for Claude Code, Pi, and Oh My Pi. MCP-based terminal pet with ASCII art, stats, reactions, and personality. Requires bun runtime (https://bun.sh/install).",
15
+ "version": "0.6.0",
16
16
  "author": {
17
17
  "name": "1270011"
18
18
  },
@@ -20,7 +20,11 @@
20
20
  "repository": "https://github.com/1270011/claude-buddy",
21
21
  "license": "MIT",
22
22
  "keywords": [
23
+ "coding-buddy",
23
24
  "claude-code",
25
+ "pi",
26
+ "oh-my-pi",
27
+ "omp",
24
28
  "buddy",
25
29
  "companion",
26
30
  "mcp",
@@ -29,6 +33,7 @@
29
33
  ],
30
34
  "category": "companion",
31
35
  "tags": [
36
+ "coding-buddy",
32
37
  "buddy",
33
38
  "companion",
34
39
  "mcp",
@@ -1,7 +1,7 @@
1
1
  {
2
- "name": "claude-buddy",
3
- "version": "0.4.0-alpha.8"
4
- "description": "Permanent coding companion for Claude Code \u2014 survives any update. MCP-based terminal pet with ASCII art, stats, reactions, and personality.",
2
+ "name": "coding-buddy",
3
+ "version": "0.6.0",
4
+ "description": "Persistent coding companion for Claude Code, Pi, and Oh My Pi. MCP-based terminal pet with ASCII art, stats, reactions, and personality.",
5
5
  "author": {
6
6
  "name": "1270011"
7
7
  },
@@ -9,7 +9,11 @@
9
9
  "repository": "https://github.com/1270011/claude-buddy",
10
10
  "homepage": "https://github.com/1270011/claude-buddy#readme",
11
11
  "keywords": [
12
+ "coding-buddy",
12
13
  "claude-code",
14
+ "pi",
15
+ "oh-my-pi",
16
+ "omp",
13
17
  "buddy",
14
18
  "companion",
15
19
  "mcp",
@@ -19,10 +23,8 @@
19
23
  "mcpServers": {
20
24
  "claude-buddy": {
21
25
  "type": "stdio",
22
- "command": "bun",
23
- "args": [
24
- "adapters/claude/server/index.ts"
25
- ]
26
+ "command": "${CLAUDE_PLUGIN_ROOT}/server/mcp-launcher.sh",
27
+ "args": []
26
28
  }
27
29
  }
28
30
  }
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 claude-buddy contributors
3
+ Copyright (c) 2026 coding-buddy contributors
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -4,11 +4,10 @@
4
4
  <!-- LOGO / HERO -->
5
5
  <!-- Later replace with: docs/logo.png -->
6
6
  <!-- ============================================================ -->
7
- <img src="https://placehold.co/120x120/6366f1/ffffff?text=%F0%9F%A6%89" alt="claude-buddy logo" width="120" />
7
+ <img src="https://placehold.co/120x120/6366f1/ffffff?text=%F0%9F%A6%89" alt="coding-buddy logo" width="120" />
8
8
 
9
- # Claude Code Buddy
10
-
11
- ### Your Claude Code buddy — permanently. Survives every update.
9
+ # Coding Buddy
10
+ ### Your persistent coding companion — for Claude Code, Pi, and Oh My Pi. Survives every update.
12
11
 
13
12
  <!-- ============================================================ -->
14
13
  <!-- BADGES -->
@@ -27,7 +26,7 @@
27
26
 
28
27
  <br>
29
28
 
30
- <img src="docs/hero.gif" alt="claude-buddy in action" width="800" />
29
+ <img src="docs/hero.gif" alt="coding-buddy in action" width="800" />
31
30
 
32
31
  <br><br>
33
32
 
@@ -48,7 +47,7 @@
48
47
  </td>
49
48
  <td align="center" width="25%">
50
49
  <h3>🎨</h3>
51
- <b>18 Species</b><br>
50
+ <b>19 Species</b><br>
52
51
  <sub>From ducks to dragons — each with animated ASCII art and rarity colors.</sub>
53
52
  </td>
54
53
  <td align="center" width="25%">
@@ -72,22 +71,80 @@
72
71
  <!-- QUICK START -->
73
72
  <!-- ============================================================ -->
74
73
 
74
+ <!-- ============================================================ -->
75
+ <!-- CONTINUITY / MIGRATION -->
76
+ <!-- ============================================================ -->
77
+
78
+ ## 📦 Continuity / Migration
79
+
80
+ The `claude-buddy` package ended at **v0.5.2**.
81
+ **`@ramarivera/coding-buddy` begins at v0.6.0** and is its continuation.
82
+
83
+ - Your existing buddy identity, menagerie, stats, and unlocks remain fully compatible.
84
+ - Legacy state and config identifiers (for example `~/.claude-buddy/`, `mcpServers["claude-buddy"]`) are intentionally preserved so current installs keep working without reconfiguration.
85
+
86
+ <!-- ============================================================ -->
87
+ ## 📋 Requirements
88
+
89
+ - **[bun](https://bun.sh/install)** on `PATH` — the MCP server and extension runtime require Bun. Install once: `curl -fsSL https://bun.sh/install | bash`
90
+ - **Linux or macOS** (Windows is experimental)
91
+
92
+ | Host | Minimum version | Identity source |
93
+ |---|---|---|
94
+ | **Claude Code** | v2.1.80+ | `accountUuid` from `~/.claude.json` |
95
+ | **Pi** | `mariozechner/pi-coding-agent` workspace | Stable random UUID persisted in `~/.pi/agent/buddy/identity.json` |
96
+ | **Oh My Pi (OMP)** | OMP harness with `omp.extensions` support | Stable random UUID persisted in `~/.omp/agent/buddy/identity.json` |
97
+
75
98
  ## 🚀 Quick Start
76
99
 
100
+ ### Claude Code
101
+
102
+ ```bash
103
+ npx -y @ramarivera/coding-buddy
104
+ ```
105
+
106
+ This installs the MCP server, skill, hooks, and status line. Restart Claude Code and type `/buddy`.
107
+
108
+ ### Pi
109
+
110
+ ```bash
111
+ pi install npm:@ramarivera/coding-buddy
112
+ ```
113
+
114
+ Pi registers the package's native extension and stores companion state under `~/.pi/agent/buddy`.
115
+
116
+ ### Oh My Pi
117
+
77
118
  ```bash
78
- git clone https://github.com/1270011/claude-buddy
79
- cd claude-buddy
119
+ omp plugin install @ramarivera/coding-buddy
120
+ ```
121
+
122
+ OMP registers the package's native extension and stores companion state under `~/.omp/agent/buddy`.
123
+
124
+ ### From source
125
+
126
+ ```bash
127
+ git clone https://github.com/1270011/claude-buddy coding-buddy
128
+ cd coding-buddy
80
129
  bun install
81
130
  bun run install-buddy
82
131
  ```
83
132
 
84
- Then restart Claude Code and type `/buddy`. That's it.
85
-
86
- <sub>💡 Need Bun? → `curl -fsSL https://bun.sh/install | bash`</sub>
133
+ <sub>💡 Want a global `coding-buddy` command? → `bun link`</sub>
87
134
  <br>
88
- <sub>💡 Want a global `claude-buddy` command? → `bun link`</sub>
89
- <br>
90
- <sub>💡 Need help? → `bun run help` or `claude-buddy help` (if linked) in terminal · `/buddy help` in Claude Code</sub>
135
+ <sub>💡 Need help? → `bun run help`, `npx -y @ramarivera/coding-buddy help`, or `coding-buddy help` (if linked) · `/buddy help` in Claude Code</sub>
136
+
137
+
138
+ ### Multiple Claude profiles?
139
+
140
+ If you run Claude Code with `CLAUDE_CONFIG_DIR` set (e.g. separate work and personal accounts), pass the same env var to install so buddy lands in the active profile and gets its own menagerie:
141
+
142
+ ```bash
143
+ CLAUDE_CONFIG_DIR=~/.claude-personal npx -y @ramarivera/coding-buddy install
144
+ CLAUDE_CONFIG_DIR=~/.claude-personal npx -y @ramarivera/coding-buddy uninstall
145
+ ```
146
+
147
+ The installer prints `Target profile: <path>` at the top so you can see at a glance which profile you're targeting. Each profile gets its own MCP entry, skill, hooks, status line, and `$CLAUDE_CONFIG_DIR/buddy-state/` menagerie — installs in one profile don't touch another. With `CLAUDE_CONFIG_DIR` unset, behaviour is identical to single-profile (`~/.claude/`, `~/.claude-buddy/`).
91
148
 
92
149
  <br>
93
150
 
@@ -98,11 +155,11 @@ Then restart Claude Code and type `/buddy`. That's it.
98
155
  <!-- ============================================================ -->
99
156
 
100
157
  <details>
101
- <summary><b>🐙 &nbsp; Meet the 18 Species</b></summary>
158
+ <summary><b>🐙 &nbsp; Meet the 19 Species</b></summary>
102
159
 
103
160
  <br>
104
161
 
105
- Every buddy is uniquely generated from your Claude Code account — same species, same stats, same personality every time. 18 species, each with 3 idle animation frames + a blink.
162
+ Every buddy is uniquely generated — same species, same stats, same personality every time. On **Claude Code** the seed is derived from your `accountUuid` in `~/.claude.json`; on **Pi** and **Oh My Pi** a stable random UUID is generated once and persisted in `~/.pi/agent/buddy/identity.json` or `~/.omp/agent/buddy/identity.json` (deleting the file and letting it regenerate will change your buddy). 19 species, each with 3 idle animation frames + a blink.
106
163
 
107
164
  <!-- Later replace with: docs/species-grid.png -->
108
165
  <p align="center">
@@ -156,7 +213,7 @@ High SNARK buddies are sarcastic. High WISDOM ones are insightful. High CHAOS on
156
213
 
157
214
  <br>
158
215
 
159
- Five integration points, zero binary dependencies. When Claude Code updates, your buddy stays.
216
+ Five integration points, zero binary dependencies. When Claude Code, Pi, or Oh My Pi updates, your buddy stays.
160
217
 
161
218
  ```
162
219
  ┌────────────── Claude Code (any version) ──────────────┐
@@ -166,7 +223,7 @@ Five integration points, zero binary dependencies. When Claude Code updates, you
166
223
  └───────────────────────┬────────────────────────────────┘
167
224
  │ stable extension points
168
225
  ┌──────────┴──────────┐
169
- │ claude-buddy │
226
+ │ coding-buddy │
170
227
  │ │
171
228
  │ wyhash + mulberry32│
172
229
  │ 18 species, 3 anim │
@@ -188,18 +245,19 @@ Five integration points, zero binary dependencies. When Claude Code updates, you
188
245
  |---|:---:|:---:|:---:|---|
189
246
  | Binary patching | ❌ | ❌ | ❌ | Breaks on update |
190
247
  | Pin old version | ❌ | ✅ | ✅ | No security fixes |
191
- | **claude-buddy** | **✅** | **✅** | **✅** | **None** |
248
+ | **coding-buddy** | **✅** | **✅** | **✅** | **None** |
192
249
 
193
250
  MCP is an industry-standard protocol. Skills are Markdown files. Hooks and status line are shell scripts. Nothing depends on Claude Code's binary internals.
194
251
 
195
252
  ### Repository Layout
196
253
 
197
254
  ```
198
- claude-buddy/
199
- ├── core/ # shared buddy domain logic — engine, reactions, achievements, models
200
- ├── adapters/claude/ # Claude adapter — MCP server, storage, skills, hooks, UI, installer
201
- ├── adapters/pi/ # pi adapter — native pi extension (planned)
202
- └── cli/ # host-agnostic CLI entrypoints/utilities
255
+ coding-buddy/
256
+ ├── server/ # MCP server — tools, engine, art, reactions, state
257
+ ├── skills/buddy/ # /buddy slash command
258
+ ├── hooks/ # PostToolUse + Stop hooks (error & comment detection)
259
+ ├── statusline/ # Animated right-aligned buddy display
260
+ └── cli/ # install, show, hunt, verify, doctor, backup, uninstall
203
261
  ```
204
262
 
205
263
  </details>
@@ -225,18 +283,26 @@ claude-buddy/
225
283
  | `/buddy save [slot]` | Save current buddy to a named slot |
226
284
  | `/buddy list` | List all saved buddies |
227
285
  | `/buddy dismiss <slot>` | Remove a saved buddy slot |
228
- | `/buddy pick` | Launch interactive TUI picker (`! bun run pick`) |
286
+ | `/buddy pick` | Launch interactive TUI picker (`! npx -y @ramarivera/coding-buddy pick`) |
229
287
  | `/buddy frequency [seconds]` | Show or set comment cooldown |
230
288
  | `/buddy style [classic\|round]` | Bubble border style (tmux only) |
231
289
  | `/buddy position [top\|left]` | Bubble position (tmux only) |
232
290
  | `/buddy rarity [on\|off]` | Show or hide stars + rarity line (tmux only) |
291
+ | `/buddy width [10-60]` | Set bubble text width in chars (tmux only) |
292
+ | `/buddy margin [0-20]` | Set right-side margin (tmux only) |
293
+ | `/buddy statusline [on\|off]` | Enable or disable buddy in the status line |
294
+ | `/buddy statusline combined` | Show rate-limit usage bars alongside buddy (needs python3) |
295
+ | `/buddy statusline basic` | Switch back to buddy-only status line |
233
296
  | `/buddy help` | Show all buddy commands |
234
297
 
235
298
  ### CLI
299
+ You can run any command with `npx -y @ramarivera/coding-buddy <command>` from anywhere, or `bun run <script>` from a local clone.
300
+
236
301
 
237
302
  | Command | Description |
238
303
  |---|---|
239
304
  | `bun run install-buddy` | Automated setup |
305
+ | `bun run upgrade` | Pull latest version and reinstall integrations |
240
306
  | `bun run show` | Show buddy in terminal |
241
307
  | `bun run pick` | Interactive TUI to find and save your dream buddy |
242
308
  | `bun run hunt` | Legacy search (use `pick` instead) |
@@ -247,16 +313,16 @@ claude-buddy/
247
313
  | `bun run disable` | Temporarily deactivate buddy |
248
314
  | `bun run enable` | Re-enable buddy |
249
315
  | `bun run help` | Full CLI reference |
250
- | `bun run cli/uninstall.ts` | Clean removal |
316
+ | `bun run uninstall` | Clean removal |
251
317
 
252
- <sub>💡 Want a global `claude-buddy` command? → `cd claude-buddy && bun link`</sub>
318
+ <sub>💡 Want a global `coding-buddy` command? → `cd coding-buddy && bun link`</sub>
253
319
 
254
320
  ### 🎯 Buddy Pick
255
321
 
256
322
  Want a specific species, rarity, or stat distribution?
257
323
 
258
324
  ```bash
259
- bun run pick
325
+ npx -y @ramarivera/coding-buddy pick
260
326
  ```
261
327
 
262
328
  Interactive TUI with saved buddies, criteria search, vim keys, and two-pane preview. Uses the exact same `wyhash + mulberry32` algorithm as Claude Code.
@@ -270,7 +336,7 @@ Interactive TUI with saved buddies, criteria search, vim keys, and two-pane prev
270
336
 
271
337
  <br>
272
338
 
273
- claude-buddy ships with built-in diagnostics for debugging across terminals and platforms.
339
+ coding-buddy ships with built-in diagnostics for debugging across terminals and platforms.
274
340
 
275
341
  ### `bun run doctor`
276
342
  Complete diagnostic — environment, terminal info, state, settings, padding test, live status line output. **Always run this first when filing a bug report.**
@@ -285,7 +351,7 @@ bun run test-statusline restore # restore buddy
285
351
  ```
286
352
 
287
353
  ### `bun run backup`
288
- Snapshot all claude-buddy state to a timestamped backup. Use before experiments.
354
+ Snapshot all coding-buddy state to a timestamped backup. Use before experiments.
289
355
 
290
356
  ```bash
291
357
  bun run backup # create snapshot
@@ -312,7 +378,7 @@ bun run backup restore <ts> # restore specific
312
378
 
313
379
  ### Buddy comments not showing
314
380
 
315
- The buddy comment mechanism uses an MCP server `instructions` field that Claude only reads at **session start**. If you installed claude-buddy in an existing session, restart Claude Code.
381
+ The buddy comment mechanism uses an MCP server `instructions` field that Claude only reads at **session start**. If you installed coding-buddy in an existing session, restart Claude Code.
316
382
 
317
383
  ```bash
318
384
  jq '.mcpServers["claude-buddy"]' ~/.claude.json
@@ -331,48 +397,13 @@ To help us fix it:
331
397
 
332
398
  ```bash
333
399
  bun run backup restore # restore latest backup
334
- bun run cli/uninstall.ts # full clean removal
400
+ bun run uninstall # full clean removal
335
401
  ```
336
402
 
337
403
  </details>
338
404
 
339
405
  ---
340
406
 
341
- <details>
342
- <summary><b>🖥️ &nbsp; tmux Popup Mode</b></summary>
343
-
344
- <br>
345
-
346
- Inside tmux, buddy appears as a floating popup overlay in the bottom-right corner instead of the status line.
347
-
348
- **Features:** animated ASCII art with speech bubbles · ESC passthrough · dynamic resizing · full keyboard forwarding
349
-
350
- | tmux version | Support |
351
- |---|---|
352
- | **3.4+** | Full support (borderless) |
353
- | **3.2 – 3.3** | Supported with border |
354
- | **< 3.2** | Falls back to status line |
355
-
356
- ### Recommended `~/.tmux.conf`
357
-
358
- ```
359
- set -g set-titles on
360
- set -g set-titles-string "#{pane_title}"
361
- set -g mouse on
362
- set -g history-limit 10000
363
- ```
364
-
365
- ### Scrolling
366
-
367
- The popup is modal. To scroll:
368
- 1. Press **F12** → scroll mode (popup hides, copy-mode activates)
369
- 2. Scroll with **mouse wheel** or **arrows**
370
- 3. Press **q** → exit scroll mode
371
-
372
- </details>
373
-
374
- ---
375
-
376
407
  <details>
377
408
  <summary><b>📋 &nbsp; Requirements</b></summary>
378
409
 
@@ -382,9 +413,9 @@ The popup is modal. To scroll:
382
413
  |---|---|
383
414
  | **[Bun](https://bun.sh)** | `curl -fsSL https://bun.sh/install \| bash` |
384
415
  | **Claude Code** v2.1.80+ | Any version with MCP support |
385
- | **jq** | `apt install jq` / `brew install jq` |
416
+ | **jq** | `apt install jq` / `brew install jq` / [`windows: download and add 'jq.exe' from jqlang/jq to path`](https://github.com/jqlang/jq/releases/latest)|
386
417
 
387
- > **Will I get the same buddy I had?** Yes. claude-buddy uses the exact same algorithm as the original (`wyhash + mulberry32`, same salt, same identity resolution). If your `~/.claude.json` still has your `accountUuid`, you'll get the identical species, rarity, stats, and cosmetics.
418
+ > **Will I get the same buddy I had?** Yes. coding-buddy uses the exact same algorithm as the original (`wyhash + mulberry32`, same salt, same identity resolution). For Claude Code, if your `~/.claude.json` still has your `accountUuid`, you'll get the identical species, rarity, stats, and cosmetics. For Pi/OMP, keep `~/.pi/agent/buddy/identity.json` or `~/.omp/agent/buddy/identity.json`; the same stable UUID produces the same buddy. Resetting (deleting) that identity file generates a new random UUID and changes your buddy.
388
419
 
389
420
  </details>
390
421
 
@@ -395,15 +426,14 @@ The popup is modal. To scroll:
395
426
  ## 🗺️ Roadmap
396
427
 
397
428
  - [x] **Multi-buddy support** — menagerie system with named slots, interactive TUI picker 💜[@doctor-ew](https://github.com/doctor-ew)💜
398
- - [x] **tmux popup mode** — floating overlay via `tmux display-popup` 💜[@gzenz](https://github.com/gzenz)💜
399
429
  - [ ] **Leveling system** — XP from coding sessions, unlockable reactions and upgrades
400
430
  - [ ] **Buddy pair-programming** — active help during sessions, pattern detection
401
431
  - [ ] **Cross-session memory** — remembers past projects and earlier conversations
402
432
  - [ ] **Mood system** — shifts based on code quality, tests, time of day
403
433
  - [x] **Achievement badges** — "1000 lines reviewed", "week streak", etc. 💜[ndcorder](https://github.com/ndcorder)💜
404
434
  - [ ] **Light theme colors** — auto-detect and match light theme RGB
405
- - [ ] **New species + community art** — submit your own designs
406
- - [ ] **`npx claude-buddy`** — one-command install without cloning
435
+ - [x] **New species + community art** — wyvern added 💜[@jpmalone0](https://github.com/jpmalone0)💜 (community contributions welcome)
436
+ - [ ] **`npx -y @ramarivera/coding-buddy <command>`** — one-command usage without cloning
407
437
 
408
438
  <br>
409
439
 
@@ -0,0 +1,201 @@
1
+ import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";
2
+ import { BuddyCommandService, mergeAchievements } from "../../core/command-service.ts";
3
+ import { renderBuddyStats } from "./renderers.ts";
4
+ import { OmpBuddyStorage, slugifySlot } from "./storage.ts";
5
+ import { OmpBuddyUI } from "./ui.ts";
6
+ import type { OmpBuddyUiContext } from "./context.ts";
7
+
8
+ interface RegisterBuddyCommandsDeps {
9
+ service: BuddyCommandService;
10
+ storage: OmpBuddyStorage;
11
+ ui: OmpBuddyUI;
12
+ }
13
+
14
+ export function registerBuddyCommands(pi: ExtensionAPI, deps: RegisterBuddyCommandsDeps): void {
15
+ pi.registerCommand("buddy", {
16
+ description: "Manage your coding companion",
17
+ handler: async (args: string, ctx: OmpBuddyUiContext) => {
18
+ const input = args.trim();
19
+ const [command, ...rest] = input ? input.split(/\s+/) : [];
20
+ const remainder = rest.join(" ").trim();
21
+
22
+ try {
23
+ switch (command) {
24
+ case undefined:
25
+ case "show": {
26
+ const ensured = deps.service.ensureCompanion();
27
+ const commandAchievements = deps.service.incrementCommandsRun();
28
+ const achievements = mergeAchievements(ensured.achievements, commandAchievements);
29
+ const reaction = deps.storage.loadLatest();
30
+ deps.ui.refresh(ctx, ensured.companion, reaction, achievements);
31
+ if (ensured.created) {
32
+ ctx.ui.notify(`Meet ${ensured.companion.name}!`, "info");
33
+ } else {
34
+ ctx.ui.notify(deps.service.formatCompanionSummary(ensured.companion), "info");
35
+ }
36
+ deps.ui.notifyAchievements(ctx, achievements);
37
+ return;
38
+ }
39
+
40
+ case "pet": {
41
+ const commandAchievements = deps.service.incrementCommandsRun();
42
+ const result = deps.service.petBuddy();
43
+ const achievements = mergeAchievements(commandAchievements, result.achievements);
44
+ deps.ui.refresh(ctx, result.companion, result.state, achievements);
45
+ ctx.ui.notify(`${result.companion.name} seems pleased.`, "info");
46
+ deps.ui.notifyAchievements(ctx, achievements);
47
+ return;
48
+ }
49
+
50
+ case "stats": {
51
+ const ensured = deps.service.ensureCompanion();
52
+ const commandAchievements = deps.service.incrementCommandsRun();
53
+ const achievements = mergeAchievements(ensured.achievements, commandAchievements);
54
+ ctx.ui.setWidget("buddy", renderBuddyStats(ensured.companion));
55
+ ctx.ui.notify(`${ensured.companion.name}'s stats`, "info");
56
+ deps.ui.notifyAchievements(ctx, achievements);
57
+ return;
58
+ }
59
+
60
+ case "rename": {
61
+ if (!remainder) throw new Error("Usage: /buddy rename <name>");
62
+ const commandAchievements = deps.service.incrementCommandsRun();
63
+ const companion = deps.service.renameBuddy(remainder);
64
+ const ensured = deps.service.ensureCompanion();
65
+ const achievements = mergeAchievements(commandAchievements, ensured.achievements);
66
+ deps.ui.refresh(ctx, companion, deps.storage.loadLatest(), achievements);
67
+ ctx.ui.notify(`Buddy renamed to ${companion.name}.`, "info");
68
+ deps.ui.notifyAchievements(ctx, achievements);
69
+ return;
70
+ }
71
+
72
+ case "personality": {
73
+ if (!remainder) throw new Error("Usage: /buddy personality <text>");
74
+ const commandAchievements = deps.service.incrementCommandsRun();
75
+ const companion = deps.service.setPersonality(remainder);
76
+ const ensured = deps.service.ensureCompanion();
77
+ const achievements = mergeAchievements(commandAchievements, ensured.achievements);
78
+ deps.ui.refresh(ctx, companion, deps.storage.loadLatest(), achievements);
79
+ ctx.ui.notify(`${companion.name}'s personality was updated.`, "info");
80
+ deps.ui.notifyAchievements(ctx, achievements);
81
+ return;
82
+ }
83
+
84
+ case "off": {
85
+ const commandAchievements = deps.service.incrementCommandsRun();
86
+ deps.storage.setMuted(true);
87
+ const ensured = deps.service.ensureCompanion();
88
+ const achievements = mergeAchievements(commandAchievements, ensured.achievements);
89
+ deps.ui.refresh(ctx, ensured.companion, null, achievements);
90
+ ctx.ui.notify(`${ensured.companion.name} goes quiet.`, "info");
91
+ deps.ui.notifyAchievements(ctx, achievements);
92
+ return;
93
+ }
94
+
95
+ case "on": {
96
+ const commandAchievements = deps.service.incrementCommandsRun();
97
+ deps.storage.setMuted(false);
98
+ const result = deps.service.recordComment("*stretches* I'm back!");
99
+ const achievements = mergeAchievements(commandAchievements, result.achievements);
100
+ deps.ui.refresh(ctx, result.companion, result.state, achievements);
101
+ ctx.ui.notify(`${result.companion.name} is back.`, "info");
102
+ deps.ui.notifyAchievements(ctx, achievements);
103
+ return;
104
+ }
105
+
106
+ case "save": {
107
+ const commandAchievements = deps.service.incrementCommandsRun();
108
+ const result = deps.service.saveBuddy(remainder || undefined);
109
+ const ensured = deps.service.ensureCompanion();
110
+ const achievements = mergeAchievements(commandAchievements, ensured.achievements);
111
+ deps.ui.refresh(ctx, result.companion, deps.storage.loadLatest(), achievements);
112
+ ctx.ui.notify(`Saved ${result.companion.name} to [${result.slot}].`, "info");
113
+ deps.ui.notifyAchievements(ctx, achievements);
114
+ return;
115
+ }
116
+
117
+ case "summon": {
118
+ const commandAchievements = deps.service.incrementCommandsRun();
119
+ const result = deps.service.summonBuddy(remainder || undefined);
120
+ if (!result) throw new Error("No saved buddy found for that slot.");
121
+ const ensured = deps.service.ensureCompanion();
122
+ const achievements = mergeAchievements(commandAchievements, ensured.achievements);
123
+ deps.ui.refresh(ctx, result.companion, deps.storage.loadLatest(), achievements);
124
+ ctx.ui.notify(`${result.companion.name} arrives from [${result.slot}].`, "info");
125
+ deps.ui.notifyAchievements(ctx, achievements);
126
+ return;
127
+ }
128
+
129
+ case "list": {
130
+ const commandAchievements = deps.service.incrementCommandsRun();
131
+ const active = deps.storage.loadActiveSlot();
132
+ const companions = deps.service.listCompanions();
133
+ if (companions.length === 0) {
134
+ ctx.ui.notify("No saved buddies yet.", "info");
135
+ deps.ui.notifyAchievements(ctx, commandAchievements);
136
+ return;
137
+ }
138
+ ctx.ui.setWidget(
139
+ "buddy",
140
+ companions.map(({ slot, companion }) => {
141
+ const marker = slot === active ? " <- active" : "";
142
+ return `${companion.name} [${slot}] - ${companion.bones.rarity} ${companion.bones.species}${marker}`;
143
+ }),
144
+ );
145
+ ctx.ui.notify(`Saved buddies: ${companions.length}`, "info");
146
+ deps.ui.notifyAchievements(ctx, commandAchievements);
147
+ return;
148
+ }
149
+
150
+ case "dismiss": {
151
+ if (!remainder) throw new Error("Usage: /buddy dismiss <slot>");
152
+ const commandAchievements = deps.service.incrementCommandsRun();
153
+ const result = deps.service.dismissBuddy(remainder);
154
+ const ensured = deps.service.ensureCompanion();
155
+ const achievements = mergeAchievements(commandAchievements, ensured.achievements);
156
+ deps.ui.refresh(ctx, ensured.companion, deps.storage.loadLatest(), achievements);
157
+ ctx.ui.notify(`Dismissed ${result.companion.name} [${result.slot}].`, "info");
158
+ deps.ui.notifyAchievements(ctx, achievements);
159
+ return;
160
+ }
161
+
162
+ case "achievements": {
163
+ const commandAchievements = deps.service.incrementCommandsRun();
164
+ const activeSlot = deps.storage.loadActiveSlot() ?? undefined;
165
+ const progress = deps.service.getAchievementProgress(activeSlot);
166
+ deps.ui.showAchievements(ctx, progress.unlocked, progress.remaining);
167
+ deps.ui.notifyAchievements(ctx, commandAchievements);
168
+ return;
169
+ }
170
+
171
+ case "help":
172
+ default: {
173
+ const commandAchievements = deps.service.incrementCommandsRun();
174
+ ctx.ui.setWidget("buddy", [
175
+ "/buddy",
176
+ "/buddy pet",
177
+ "/buddy stats",
178
+ "/buddy rename <name>",
179
+ "/buddy personality <text>",
180
+ "/buddy off | on",
181
+ "/buddy save [slot]",
182
+ "/buddy summon [slot]",
183
+ "/buddy list",
184
+ "/buddy dismiss <slot>",
185
+ "/buddy achievements",
186
+ ]);
187
+ ctx.ui.notify("Buddy commands ready.", "info");
188
+ deps.ui.notifyAchievements(ctx, commandAchievements);
189
+ return;
190
+ }
191
+ }
192
+ } catch (error) {
193
+ const message = error instanceof Error ? error.message : String(error);
194
+ ctx.ui.notify(message, "error");
195
+ if (command === "save" && remainder) {
196
+ ctx.ui.notify(`Try a different slot, for example: ${slugifySlot(remainder)}-2`, "info");
197
+ }
198
+ }
199
+ },
200
+ });
201
+ }
@@ -0,0 +1,22 @@
1
+ import type { ExtensionContext } from "@oh-my-pi/pi-coding-agent";
2
+
3
+ export interface OmpBuddyUi {
4
+ notify(message: string, type?: "info" | "warning" | "error"): void;
5
+ setStatus(key: string, text: string | undefined): void;
6
+ setWidget(key: string, content: string[] | undefined): void;
7
+ }
8
+
9
+ export interface OmpBuddyUiContext {
10
+ ui: OmpBuddyUi;
11
+ setTimeout?: (callback: () => void, ms?: number) => Timer;
12
+ clearTimer?: (timer: Timer) => void;
13
+ }
14
+
15
+ export interface OmpBuddyContext extends OmpBuddyUiContext {
16
+ model: ExtensionContext["model"];
17
+ modelRegistry: Pick<
18
+ ExtensionContext["modelRegistry"],
19
+ "find" | "getApiKey" | "getProviderHeaders"
20
+ >;
21
+ sessionManager: Pick<ExtensionContext["sessionManager"], "getBranch">;
22
+ }