@ramarivera/coding-buddy 0.4.0-alpha.9 → 0.7.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/.claude-plugin/marketplace.json +45 -0
- package/.claude-plugin/plugin.json +30 -0
- package/LICENSE +1 -1
- package/README.md +96 -45
- 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 +130 -33
- package/cli/buddy-shell.ts +1 -1
- package/{adapters/claude/install → cli}/disable.ts +19 -32
- package/cli/doctor.ts +231 -0
- package/{adapters/claude/install → cli}/hunt.ts +7 -9
- package/cli/index.ts +30 -26
- package/{adapters/claude/install → cli}/install.ts +65 -83
- package/{adapters/claude/install → cli}/pick.ts +136 -57
- 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 +21 -11
- package/cli/tui.tsx +106 -17
- package/{adapters/claude/install → cli}/uninstall.ts +40 -47
- package/cli/upgrade.ts +46 -13
- 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 +22 -9
- package/hooks/file-type-react.sh +403 -0
- package/hooks/hooks.json +52 -0
- package/hooks/mood-react.sh +210 -0
- package/{adapters/claude/hooks → hooks}/name-react.sh +3 -4
- package/hooks/react.sh +1364 -0
- package/hooks/suggest.sh +55 -0
- package/package.json +70 -31
- package/scripts/gen-emoji-widths.ts +34 -0
- package/scripts/paths.sh +33 -2
- package/scripts/postinstall.cjs +42 -0
- package/scripts/snapshot-statusline.sh +54 -0
- package/server/achievements.ts +1419 -15
- package/server/art.ts +450 -1
- package/server/award-xp.ts +48 -0
- package/server/check-suggestions.ts +102 -0
- package/server/consolidate.ts +33 -0
- package/{adapters/claude/server → server}/index.ts +556 -95
- package/server/mcp-launcher.sh +16 -6
- package/server/memory.ts +404 -0
- package/server/mood.ts +254 -0
- package/server/path.ts +70 -18
- package/server/shift-mood.ts +43 -0
- package/server/state.ts +552 -3
- 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 +36 -7
- package/statusline/buddy-status.sh +402 -0
- package/statusline/combined-status.sh +135 -0
- package/statusline/emoji-widths.data +4 -0
- package/adapters/claude/hooks/hooks.json +0 -35
- package/adapters/claude/hooks/react.sh +0 -207
- package/adapters/claude/install/doctor.ts +0 -172
- package/adapters/claude/plugin/marketplace.json +0 -40
- package/adapters/claude/plugin/plugin.json +0 -26
- package/adapters/claude/rendering/art.ts +0 -264
- package/adapters/claude/server/instructions.ts +0 -24
- package/adapters/claude/server/path.ts +0 -12
- package/adapters/claude/server/resources.ts +0 -38
- package/adapters/claude/statusline/buddy-status.sh +0 -408
- package/adapters/claude/storage/achievements.ts +0 -253
- package/adapters/claude/storage/identity.ts +0 -13
- package/adapters/claude/storage/paths.ts +0 -33
- package/adapters/claude/storage/settings.ts +0 -91
- package/adapters/claude/storage/state.ts +0 -365
- package/adapters/pi/README.md +0 -83
- package/adapters/pi/tools.ts +0 -6
- package/server/engine.ts +0 -1
- package/server/reactions.ts +0 -1
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "coding-buddy",
|
|
3
|
+
"owner": {
|
|
4
|
+
"name": "ramarivera"
|
|
5
|
+
},
|
|
6
|
+
"metadata": {
|
|
7
|
+
"description": "Persistent coding companion for Claude Code, Pi, and Oh My Pi (requires bun: https://bun.sh/install)",
|
|
8
|
+
"version": "0.7.0"
|
|
9
|
+
},
|
|
10
|
+
"plugins": [
|
|
11
|
+
{
|
|
12
|
+
"name": "coding-buddy",
|
|
13
|
+
"source": "./",
|
|
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.7.0",
|
|
16
|
+
"author": {
|
|
17
|
+
"name": "ramarivera"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://github.com/ramarivera/coding-buddy#readme",
|
|
20
|
+
"repository": "https://github.com/ramarivera/coding-buddy",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"keywords": [
|
|
23
|
+
"coding-buddy",
|
|
24
|
+
"claude-code",
|
|
25
|
+
"pi",
|
|
26
|
+
"oh-my-pi",
|
|
27
|
+
"omp",
|
|
28
|
+
"buddy",
|
|
29
|
+
"companion",
|
|
30
|
+
"mcp",
|
|
31
|
+
"terminal-pet",
|
|
32
|
+
"tamagotchi"
|
|
33
|
+
],
|
|
34
|
+
"category": "companion",
|
|
35
|
+
"tags": [
|
|
36
|
+
"coding-buddy",
|
|
37
|
+
"buddy",
|
|
38
|
+
"companion",
|
|
39
|
+
"mcp",
|
|
40
|
+
"terminal-pet",
|
|
41
|
+
"tamagotchi"
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "coding-buddy",
|
|
3
|
+
"version": "0.7.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
|
+
"author": {
|
|
6
|
+
"name": "ramarivera"
|
|
7
|
+
},
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"repository": "https://github.com/ramarivera/coding-buddy",
|
|
10
|
+
"homepage": "https://github.com/ramarivera/coding-buddy#readme",
|
|
11
|
+
"keywords": [
|
|
12
|
+
"coding-buddy",
|
|
13
|
+
"claude-code",
|
|
14
|
+
"pi",
|
|
15
|
+
"oh-my-pi",
|
|
16
|
+
"omp",
|
|
17
|
+
"buddy",
|
|
18
|
+
"companion",
|
|
19
|
+
"mcp",
|
|
20
|
+
"terminal-pet",
|
|
21
|
+
"tamagotchi"
|
|
22
|
+
],
|
|
23
|
+
"mcpServers": {
|
|
24
|
+
"claude-buddy": {
|
|
25
|
+
"type": "stdio",
|
|
26
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/server/mcp-launcher.sh",
|
|
27
|
+
"args": []
|
|
28
|
+
}
|
|
29
|
+
}
|
|
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,19 +4,18 @@
|
|
|
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 -->
|
|
15
14
|
<!-- ============================================================ -->
|
|
16
15
|
|
|
17
|
-
[](https://github.com/ramarivera/coding-buddy/releases)
|
|
17
|
+
[](LICENSE)
|
|
18
|
+
[](https://github.com/ramarivera/coding-buddy/stargazers)
|
|
20
19
|
[](https://claude.ai/code)
|
|
21
20
|
[](#requirements)
|
|
22
21
|
[](https://modelcontextprotocol.io)
|
|
@@ -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,34 +71,77 @@
|
|
|
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
|
+
<!-- ============================================================ -->
|
|
75
87
|
## 📋 Requirements
|
|
76
88
|
|
|
77
|
-
- **[bun](https://bun.sh/install)** on `PATH` —
|
|
78
|
-
- **Claude Code v2.1.80+**
|
|
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`
|
|
79
90
|
- **Linux or macOS** (Windows is experimental)
|
|
80
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
|
+
|
|
81
98
|
## 🚀 Quick Start
|
|
82
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
|
+
|
|
118
|
+
```bash
|
|
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
|
+
|
|
83
126
|
```bash
|
|
84
|
-
git clone https://github.com/
|
|
85
|
-
cd
|
|
127
|
+
git clone https://github.com/ramarivera/coding-buddy coding-buddy
|
|
128
|
+
cd coding-buddy
|
|
86
129
|
bun install
|
|
87
130
|
bun run install-buddy
|
|
88
131
|
```
|
|
89
132
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
<sub>💡 Want a global `claude-buddy` command? → `bun link`</sub>
|
|
133
|
+
<sub>💡 Want a global `coding-buddy` command? → `bun link`</sub>
|
|
93
134
|
<br>
|
|
94
|
-
<sub>💡 Need help? → `bun run help` or `
|
|
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
|
+
|
|
95
137
|
|
|
96
138
|
### Multiple Claude profiles?
|
|
97
139
|
|
|
98
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:
|
|
99
141
|
|
|
100
142
|
```bash
|
|
101
|
-
CLAUDE_CONFIG_DIR=~/.claude-personal
|
|
102
|
-
CLAUDE_CONFIG_DIR=~/.claude-personal
|
|
143
|
+
CLAUDE_CONFIG_DIR=~/.claude-personal npx -y @ramarivera/coding-buddy install
|
|
144
|
+
CLAUDE_CONFIG_DIR=~/.claude-personal npx -y @ramarivera/coding-buddy uninstall
|
|
103
145
|
```
|
|
104
146
|
|
|
105
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/`).
|
|
@@ -113,11 +155,11 @@ The installer prints `Target profile: <path>` at the top so you can see at a gla
|
|
|
113
155
|
<!-- ============================================================ -->
|
|
114
156
|
|
|
115
157
|
<details>
|
|
116
|
-
<summary><b>🐙 Meet the
|
|
158
|
+
<summary><b>🐙 Meet the 19 Species</b></summary>
|
|
117
159
|
|
|
118
160
|
<br>
|
|
119
161
|
|
|
120
|
-
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.
|
|
121
163
|
|
|
122
164
|
<!-- Later replace with: docs/species-grid.png -->
|
|
123
165
|
<p align="center">
|
|
@@ -171,7 +213,7 @@ High SNARK buddies are sarcastic. High WISDOM ones are insightful. High CHAOS on
|
|
|
171
213
|
|
|
172
214
|
<br>
|
|
173
215
|
|
|
174
|
-
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.
|
|
175
217
|
|
|
176
218
|
```
|
|
177
219
|
┌────────────── Claude Code (any version) ──────────────┐
|
|
@@ -181,7 +223,7 @@ Five integration points, zero binary dependencies. When Claude Code updates, you
|
|
|
181
223
|
└───────────────────────┬────────────────────────────────┘
|
|
182
224
|
│ stable extension points
|
|
183
225
|
┌──────────┴──────────┐
|
|
184
|
-
│
|
|
226
|
+
│ coding-buddy │
|
|
185
227
|
│ │
|
|
186
228
|
│ wyhash + mulberry32│
|
|
187
229
|
│ 18 species, 3 anim │
|
|
@@ -203,18 +245,19 @@ Five integration points, zero binary dependencies. When Claude Code updates, you
|
|
|
203
245
|
|---|:---:|:---:|:---:|---|
|
|
204
246
|
| Binary patching | ❌ | ❌ | ❌ | Breaks on update |
|
|
205
247
|
| Pin old version | ❌ | ✅ | ✅ | No security fixes |
|
|
206
|
-
| **
|
|
248
|
+
| **coding-buddy** | **✅** | **✅** | **✅** | **None** |
|
|
207
249
|
|
|
208
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.
|
|
209
251
|
|
|
210
252
|
### Repository Layout
|
|
211
253
|
|
|
212
254
|
```
|
|
213
|
-
|
|
214
|
-
├──
|
|
215
|
-
├──
|
|
216
|
-
├──
|
|
217
|
-
|
|
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
|
|
218
261
|
```
|
|
219
262
|
|
|
220
263
|
</details>
|
|
@@ -240,18 +283,26 @@ claude-buddy/
|
|
|
240
283
|
| `/buddy save [slot]` | Save current buddy to a named slot |
|
|
241
284
|
| `/buddy list` | List all saved buddies |
|
|
242
285
|
| `/buddy dismiss <slot>` | Remove a saved buddy slot |
|
|
243
|
-
| `/buddy pick` | Launch interactive TUI picker (`!
|
|
286
|
+
| `/buddy pick` | Launch interactive TUI picker (`! npx -y @ramarivera/coding-buddy pick`) |
|
|
244
287
|
| `/buddy frequency [seconds]` | Show or set comment cooldown |
|
|
245
288
|
| `/buddy style [classic\|round]` | Bubble border style (tmux only) |
|
|
246
289
|
| `/buddy position [top\|left]` | Bubble position (tmux only) |
|
|
247
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 |
|
|
248
296
|
| `/buddy help` | Show all buddy commands |
|
|
249
297
|
|
|
250
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
|
+
|
|
251
301
|
|
|
252
302
|
| Command | Description |
|
|
253
303
|
|---|---|
|
|
254
304
|
| `bun run install-buddy` | Automated setup |
|
|
305
|
+
| `bun run upgrade` | Pull latest version and reinstall integrations |
|
|
255
306
|
| `bun run show` | Show buddy in terminal |
|
|
256
307
|
| `bun run pick` | Interactive TUI to find and save your dream buddy |
|
|
257
308
|
| `bun run hunt` | Legacy search (use `pick` instead) |
|
|
@@ -262,16 +313,16 @@ claude-buddy/
|
|
|
262
313
|
| `bun run disable` | Temporarily deactivate buddy |
|
|
263
314
|
| `bun run enable` | Re-enable buddy |
|
|
264
315
|
| `bun run help` | Full CLI reference |
|
|
265
|
-
| `bun run
|
|
316
|
+
| `bun run uninstall` | Clean removal |
|
|
266
317
|
|
|
267
|
-
<sub>💡 Want a global `
|
|
318
|
+
<sub>💡 Want a global `coding-buddy` command? → `cd coding-buddy && bun link`</sub>
|
|
268
319
|
|
|
269
320
|
### 🎯 Buddy Pick
|
|
270
321
|
|
|
271
322
|
Want a specific species, rarity, or stat distribution?
|
|
272
323
|
|
|
273
324
|
```bash
|
|
274
|
-
|
|
325
|
+
npx -y @ramarivera/coding-buddy pick
|
|
275
326
|
```
|
|
276
327
|
|
|
277
328
|
Interactive TUI with saved buddies, criteria search, vim keys, and two-pane preview. Uses the exact same `wyhash + mulberry32` algorithm as Claude Code.
|
|
@@ -285,7 +336,7 @@ Interactive TUI with saved buddies, criteria search, vim keys, and two-pane prev
|
|
|
285
336
|
|
|
286
337
|
<br>
|
|
287
338
|
|
|
288
|
-
|
|
339
|
+
coding-buddy ships with built-in diagnostics for debugging across terminals and platforms.
|
|
289
340
|
|
|
290
341
|
### `bun run doctor`
|
|
291
342
|
Complete diagnostic — environment, terminal info, state, settings, padding test, live status line output. **Always run this first when filing a bug report.**
|
|
@@ -300,7 +351,7 @@ bun run test-statusline restore # restore buddy
|
|
|
300
351
|
```
|
|
301
352
|
|
|
302
353
|
### `bun run backup`
|
|
303
|
-
Snapshot all
|
|
354
|
+
Snapshot all coding-buddy state to a timestamped backup. Use before experiments.
|
|
304
355
|
|
|
305
356
|
```bash
|
|
306
357
|
bun run backup # create snapshot
|
|
@@ -327,7 +378,7 @@ bun run backup restore <ts> # restore specific
|
|
|
327
378
|
|
|
328
379
|
### Buddy comments not showing
|
|
329
380
|
|
|
330
|
-
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.
|
|
331
382
|
|
|
332
383
|
```bash
|
|
333
384
|
jq '.mcpServers["claude-buddy"]' ~/.claude.json
|
|
@@ -338,7 +389,7 @@ jq '.mcpServers["claude-buddy"]' ~/.claude.json
|
|
|
338
389
|
Known MVP issue on some terminal/platform combos — different terminals render Braille Pattern Blank (U+2800) at different widths.
|
|
339
390
|
|
|
340
391
|
To help us fix it:
|
|
341
|
-
1. Run `bun run doctor` and paste output in a [new issue](https://github.com/
|
|
392
|
+
1. Run `bun run doctor` and paste output in a [new issue](https://github.com/ramarivera/coding-buddy/issues/new)
|
|
342
393
|
2. Run `bun run test-statusline` and screenshot the result
|
|
343
394
|
3. Then `bun run test-statusline restore`
|
|
344
395
|
|
|
@@ -346,7 +397,7 @@ To help us fix it:
|
|
|
346
397
|
|
|
347
398
|
```bash
|
|
348
399
|
bun run backup restore # restore latest backup
|
|
349
|
-
bun run
|
|
400
|
+
bun run uninstall # full clean removal
|
|
350
401
|
```
|
|
351
402
|
|
|
352
403
|
</details>
|
|
@@ -364,7 +415,7 @@ bun run cli/uninstall.ts # full clean removal
|
|
|
364
415
|
| **Claude Code** v2.1.80+ | Any version with MCP support |
|
|
365
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)|
|
|
366
417
|
|
|
367
|
-
> **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.
|
|
368
419
|
|
|
369
420
|
</details>
|
|
370
421
|
|
|
@@ -381,8 +432,8 @@ bun run cli/uninstall.ts # full clean removal
|
|
|
381
432
|
- [ ] **Mood system** — shifts based on code quality, tests, time of day
|
|
382
433
|
- [x] **Achievement badges** — "1000 lines reviewed", "week streak", etc. 💜[ndcorder](https://github.com/ndcorder)💜
|
|
383
434
|
- [ ] **Light theme colors** — auto-detect and match light theme RGB
|
|
384
|
-
- [
|
|
385
|
-
- [ ] **`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
|
|
386
437
|
|
|
387
438
|
<br>
|
|
388
439
|
|
|
@@ -392,15 +443,15 @@ bun run cli/uninstall.ts # full clean removal
|
|
|
392
443
|
|
|
393
444
|
Thank you to everyone who helped bring buddies back to life.
|
|
394
445
|
|
|
395
|
-
<a href="https://github.com/
|
|
396
|
-
<img src="https://contrib.rocks/image?repo=
|
|
446
|
+
<a href="https://github.com/ramarivera/coding-buddy/graphs/contributors">
|
|
447
|
+
<img src="https://contrib.rocks/image?repo=ramarivera/coding-buddy" alt="Contributors" />
|
|
397
448
|
</a>
|
|
398
449
|
|
|
399
|
-
<sub>Automatically generated from the [contributors graph](https://github.com/
|
|
450
|
+
<sub>Automatically generated from the [contributors graph](https://github.com/ramarivera/coding-buddy/graphs/contributors) via [contrib.rocks](https://contrib.rocks).</sub>
|
|
400
451
|
|
|
401
452
|
<br>
|
|
402
453
|
|
|
403
|
-
Want to help? New species, better reactions, bugfixes, wild new features — [PRs welcome](https://github.com/
|
|
454
|
+
Want to help? New species, better reactions, bugfixes, wild new features — [PRs welcome](https://github.com/ramarivera/coding-buddy/pulls).
|
|
404
455
|
|
|
405
456
|
<br>
|
|
406
457
|
|
|
@@ -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
|
+
}
|