@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.
- package/{adapters/claude/plugin → .claude-plugin}/marketplace.json +11 -6
- package/{adapters/claude/plugin → .claude-plugin}/plugin.json +9 -7
- package/LICENSE +1 -1
- package/README.md +102 -72
- package/adapters/omp/commands.ts +201 -0
- package/adapters/omp/context.ts +22 -0
- package/adapters/omp/events.ts +551 -0
- package/adapters/omp/identity.ts +10 -0
- package/adapters/omp/index.ts +39 -0
- package/adapters/omp/logger.ts +52 -0
- package/adapters/omp/prompt.ts +60 -0
- package/adapters/omp/renderers.ts +73 -0
- package/adapters/omp/storage.ts +25 -0
- package/adapters/omp/ui.ts +85 -0
- package/adapters/pi/commands.ts +64 -37
- package/adapters/pi/events.ts +147 -72
- package/adapters/pi/index.ts +9 -4
- package/adapters/pi/storage.ts +14 -285
- package/adapters/pi/ui.ts +56 -5
- package/adapters/shared/file-storage.ts +526 -0
- package/{adapters/claude/install → cli}/backup.ts +59 -44
- package/cli/biomes.ts +309 -0
- package/cli/buddy-shell.ts +818 -0
- package/{adapters/claude/install → cli}/disable.ts +14 -32
- package/{adapters/claude/install → cli}/doctor.ts +55 -57
- package/{adapters/claude/install → cli}/hunt.ts +7 -9
- package/cli/index.ts +36 -25
- package/{adapters/claude/install → cli}/install.ts +100 -145
- package/{adapters/claude/install → cli}/pick.ts +111 -50
- package/{adapters/claude/install → cli}/settings.ts +2 -2
- package/{adapters/claude/install → cli}/show.ts +4 -4
- package/{adapters/claude/install → cli}/test-statusline.sh +2 -2
- package/{adapters/claude/install → cli}/test-statusline.ts +11 -11
- package/cli/tui.tsx +2333 -0
- package/{adapters/claude/install → cli}/uninstall.ts +23 -38
- package/cli/upgrade.ts +213 -0
- package/cli/validate-species.ts +209 -0
- package/cli/verify.ts +3 -3
- package/core/achievements.ts +12 -20
- package/core/art-data.ts +26 -0
- package/core/command-service.ts +99 -97
- package/core/engine.ts +83 -1
- package/core/ports.ts +10 -0
- package/core/reactions.ts +533 -95
- package/core/slot-slug.ts +22 -0
- package/{adapters/claude/hooks → hooks}/buddy-comment.sh +23 -2
- package/hooks/file-type-react.sh +401 -0
- package/hooks/hooks.json +52 -0
- package/hooks/mood-react.sh +208 -0
- package/{adapters/claude/hooks → hooks}/name-react.sh +5 -2
- package/hooks/react.sh +1368 -0
- package/hooks/suggest.sh +55 -0
- package/package.json +113 -59
- package/scripts/gen-emoji-widths.ts +34 -0
- package/scripts/paths.sh +56 -0
- package/scripts/postinstall.cjs +42 -0
- package/scripts/snapshot-statusline.sh +54 -0
- package/server/achievements.ts +1419 -0
- package/server/art.ts +450 -0
- package/server/award-xp.ts +48 -0
- package/server/check-suggestions.ts +102 -0
- package/server/consolidate.ts +33 -0
- package/server/index.ts +1340 -0
- package/server/mcp-launcher.sh +26 -0
- package/server/memory.ts +404 -0
- package/server/mood.ts +254 -0
- package/server/path.ts +82 -0
- package/server/shift-mood.ts +43 -0
- package/{adapters/claude/storage → server}/state.ts +200 -7
- package/server/suggestions.ts +335 -0
- package/server/theme.ts +53 -0
- package/server/xp.ts +435 -0
- package/{adapters/claude/skills → skills}/buddy/SKILL.md +48 -4
- package/statusline/buddy-status.sh +397 -0
- package/statusline/combined-status.sh +134 -0
- package/statusline/emoji-widths.data +4 -0
- package/adapters/claude/hooks/hooks.json +0 -35
- package/adapters/claude/hooks/react.sh +0 -204
- package/adapters/claude/popup/buddy-popup.sh +0 -92
- package/adapters/claude/popup/buddy-render.sh +0 -540
- package/adapters/claude/popup/popup-manager.sh +0 -355
- package/adapters/claude/rendering/art.ts +0 -264
- package/adapters/claude/server/index.ts +0 -752
- package/adapters/claude/server/instructions.ts +0 -24
- package/adapters/claude/server/resources.ts +0 -38
- package/adapters/claude/statusline/buddy-status.sh +0 -389
- package/adapters/claude/storage/achievements.ts +0 -253
- package/adapters/claude/storage/identity.ts +0 -13
- package/adapters/claude/storage/paths.ts +0 -24
- package/adapters/claude/storage/settings.ts +0 -41
- package/adapters/pi/README.md +0 -83
- package/adapters/pi/tools.ts +0 -6
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "
|
|
2
|
+
"name": "coding-buddy",
|
|
3
3
|
"owner": {
|
|
4
4
|
"name": "1270011"
|
|
5
5
|
},
|
|
6
6
|
"metadata": {
|
|
7
|
-
"description": "
|
|
8
|
-
"version": "0.
|
|
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": "
|
|
12
|
+
"name": "coding-buddy",
|
|
13
13
|
"source": "./",
|
|
14
|
-
"description": "
|
|
15
|
-
"version": "0.
|
|
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": "
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
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": "
|
|
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
|
|
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="
|
|
7
|
+
<img src="https://placehold.co/120x120/6366f1/ffffff?text=%F0%9F%A6%89" alt="coding-buddy logo" width="120" />
|
|
8
8
|
|
|
9
|
-
#
|
|
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="
|
|
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>
|
|
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
|
-
|
|
79
|
-
|
|
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
|
-
|
|
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>💡
|
|
89
|
-
|
|
90
|
-
|
|
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>🐙 Meet the
|
|
158
|
+
<summary><b>🐙 Meet the 19 Species</b></summary>
|
|
102
159
|
|
|
103
160
|
<br>
|
|
104
161
|
|
|
105
|
-
Every buddy is uniquely generated
|
|
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
|
-
│
|
|
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
|
-
| **
|
|
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
|
-
|
|
199
|
-
├──
|
|
200
|
-
├──
|
|
201
|
-
├──
|
|
202
|
-
|
|
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 (`!
|
|
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
|
|
316
|
+
| `bun run uninstall` | Clean removal |
|
|
251
317
|
|
|
252
|
-
<sub>💡 Want a global `
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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>🖥️ 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>📋 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.
|
|
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
|
-
- [
|
|
406
|
-
- [ ] **`npx
|
|
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
|
+
}
|