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

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 (43) hide show
  1. package/README.md +18 -39
  2. package/adapters/claude/hooks/buddy-comment.sh +4 -1
  3. package/adapters/claude/hooks/name-react.sh +4 -1
  4. package/adapters/claude/hooks/react.sh +4 -1
  5. package/adapters/claude/install/backup.ts +36 -118
  6. package/adapters/claude/install/disable.ts +9 -14
  7. package/adapters/claude/install/doctor.ts +26 -87
  8. package/adapters/claude/install/install.ts +39 -66
  9. package/adapters/claude/install/test-statusline.ts +8 -18
  10. package/adapters/claude/install/uninstall.ts +18 -26
  11. package/adapters/claude/plugin/marketplace.json +4 -4
  12. package/adapters/claude/plugin/plugin.json +3 -5
  13. package/adapters/claude/server/index.ts +132 -5
  14. package/adapters/claude/server/path.ts +12 -0
  15. package/adapters/claude/skills/buddy/SKILL.md +16 -1
  16. package/adapters/claude/statusline/buddy-status.sh +22 -3
  17. package/adapters/claude/storage/paths.ts +9 -0
  18. package/adapters/claude/storage/settings.ts +53 -3
  19. package/adapters/claude/storage/state.ts +22 -4
  20. package/adapters/pi/README.md +19 -0
  21. package/adapters/pi/events.ts +176 -19
  22. package/adapters/pi/index.ts +3 -1
  23. package/adapters/pi/logger.ts +52 -0
  24. package/adapters/pi/prompt.ts +18 -0
  25. package/adapters/pi/storage.ts +1 -0
  26. package/cli/biomes.ts +309 -0
  27. package/cli/buddy-shell.ts +818 -0
  28. package/cli/index.ts +7 -0
  29. package/cli/tui.tsx +2244 -0
  30. package/cli/upgrade.ts +213 -0
  31. package/core/model.ts +6 -0
  32. package/package.json +78 -62
  33. package/scripts/paths.sh +40 -0
  34. package/server/achievements.ts +15 -0
  35. package/server/art.ts +1 -0
  36. package/server/engine.ts +1 -0
  37. package/server/mcp-launcher.sh +16 -0
  38. package/server/path.ts +30 -0
  39. package/server/reactions.ts +1 -0
  40. package/server/state.ts +3 -0
  41. package/adapters/claude/popup/buddy-popup.sh +0 -92
  42. package/adapters/claude/popup/buddy-render.sh +0 -540
  43. package/adapters/claude/popup/popup-manager.sh +0 -355
package/cli/biomes.ts ADDED
@@ -0,0 +1,309 @@
1
+ /**
2
+ * biomes.ts — landscape themes for the buddy panel
3
+ *
4
+ * 5 default biomes (mapped to rarity) + 10 extra biomes selectable via --biome flag.
5
+ *
6
+ * Usage:
7
+ * npx tsx cli/buddy-shell.ts claude # auto-detect by rarity
8
+ * npx tsx cli/buddy-shell.ts claude --biome volcano # force a specific biome
9
+ * npx tsx cli/buddy-shell.ts --biomes # list all available biomes
10
+ */
11
+
12
+ const ESC = "\x1b";
13
+ const CSI = `${ESC}[`;
14
+ const NC = `${CSI}0m`;
15
+
16
+ function c(r: number, g: number, b: number): string {
17
+ return `${CSI}38;2;${r};${g};${b}m`;
18
+ }
19
+
20
+ // ─── Biome definition ───────────────────────────────────────────────────────
21
+
22
+ export interface Biome {
23
+ name: string;
24
+ sky: string;
25
+ ground: string;
26
+ particle: { color: string; chars: string[] };
27
+ groundChars: string[];
28
+ structure: string[];
29
+ }
30
+
31
+ // ═══════════════════════════════════════════════════════════════════════════
32
+ // DEFAULT BIOMES (by rarity)
33
+ // ═══════════════════════════════════════════════════════════════════════════
34
+
35
+ const meadow: Biome = {
36
+ name: "meadow",
37
+ sky: c(40, 40, 60),
38
+ ground: c(60, 90, 40),
39
+ particle: { color: c(150, 150, 130), chars: ["✦", "·", "·"] },
40
+ groundChars: ["▄", "▄", "▄", "▓", "▄", "▒"],
41
+ structure: [
42
+ `${c(120, 80, 60)} ░░${NC}`,
43
+ `${c(160, 60, 50)} ▄████████▄${NC}`,
44
+ `${c(160, 60, 50)} ▟${c(140, 90, 50)}██${c(200, 200, 100)}▪▪${c(140, 90, 50)}██${c(200, 200, 100)}▪▪${c(140, 90, 50)}██${c(160, 60, 50)}▙${NC}`,
45
+ `${c(140, 90, 50)} ██${c(200, 200, 100)}▪▪${c(140, 90, 50)}██${c(200, 200, 100)}▪▪${c(140, 90, 50)}████${NC}`,
46
+ `${c(140, 90, 50)} ██${c(200, 200, 100)}▪▪${c(140, 90, 50)}█${c(100, 60, 30)}▐██▌${c(140, 90, 50)}█${c(200, 200, 100)}▪▪${c(140, 90, 50)}██${NC}`,
47
+ `${c(140, 90, 50)} ██${c(200, 200, 100)}▪▪${c(140, 90, 50)}█${c(100, 60, 30)}▐██▌${c(140, 90, 50)}█${c(200, 200, 100)}▪▪${c(140, 90, 50)}██${NC}`,
48
+ ],
49
+ };
50
+
51
+ const forest: Biome = {
52
+ name: "forest",
53
+ sky: c(15, 35, 25),
54
+ ground: c(30, 80, 35),
55
+ particle: { color: c(80, 160, 80), chars: ["✦", "·", "·"] },
56
+ groundChars: ["▄", "▄", "▓", "▄", "▒", "▄"],
57
+ structure: [
58
+ `${c(40, 110, 40)} ▄██████▄${NC}`,
59
+ `${c(50, 130, 50)}▄██${c(200, 50, 30)}●${c(50, 130, 50)}███${c(200, 50, 30)}●${c(50, 130, 50)}██▄${NC}`,
60
+ `${c(60, 150, 60)}████${c(200, 50, 30)}●${c(60, 150, 60)}███████${NC}`,
61
+ `${c(60, 150, 60)}██████${c(200, 50, 30)}●${c(60, 150, 60)}████${NC}`,
62
+ `${c(50, 130, 50)} ▀████████▀${NC}`,
63
+ `${c(80, 60, 40)} ▟${c(60, 100, 50)}██${c(200, 200, 100)}▪▪${c(60, 100, 50)}██${c(80, 60, 40)}▙${NC}`,
64
+ `${c(80, 60, 40)} ║${c(60, 100, 50)}██${c(200, 200, 100)}▪▪${c(60, 100, 50)}██${c(80, 60, 40)}║${NC}`,
65
+ `${c(80, 60, 40)} ║██${c(100, 60, 30)}▐██▌${c(80, 60, 40)}██║${NC}`,
66
+ ],
67
+ };
68
+
69
+ const ocean: Biome = {
70
+ name: "ocean",
71
+ sky: c(15, 20, 55),
72
+ ground: c(40, 90, 140),
73
+ particle: { color: c(100, 140, 220), chars: ["✦", "·", "~"] },
74
+ groundChars: ["▄", "~", "≈", "▄", "~", "▄"],
75
+ structure: [
76
+ `${c(200, 200, 100)} ◉${NC}`,
77
+ `${c(200, 50, 50)} ▄███▄${NC}`,
78
+ `${c(220, 220, 220)} ▐${c(200, 50, 50)}█${c(220, 220, 220)}███${c(200, 50, 50)}█${c(220, 220, 220)}▌${NC}`,
79
+ `${c(220, 220, 220)} ▐█████▌${NC}`,
80
+ `${c(220, 220, 220)} ▐█████▌${NC}`,
81
+ `${c(180, 180, 180)} ▟███████▙${NC}`,
82
+ ],
83
+ };
84
+
85
+ const cyberpunk: Biome = {
86
+ name: "cyberpunk",
87
+ sky: c(20, 10, 35),
88
+ ground: c(40, 30, 60),
89
+ particle: { color: c(180, 80, 220), chars: ["✦", "·", "░"] },
90
+ groundChars: ["▀", "▄", "█", "▀", "▄", "░"],
91
+ structure: [
92
+ `${c(255, 0, 100)} ▄${c(0, 255, 200)}█${c(255, 0, 100)}█${c(0, 255, 200)}█${c(255, 0, 100)}▄${NC}`,
93
+ `${c(80, 60, 100)} █${c(0, 255, 200)}▪${c(80, 60, 100)}█${c(255, 0, 100)}▪${c(80, 60, 100)}█${NC}`,
94
+ `${c(80, 60, 100)} █${c(255, 0, 100)}▪${c(80, 60, 100)}█${c(0, 255, 200)}▪${c(80, 60, 100)}█${NC}`,
95
+ `${c(80, 60, 100)} █${c(0, 255, 200)}▪${c(80, 60, 100)}█${c(255, 0, 100)}▪${c(80, 60, 100)}█${NC}`,
96
+ `${c(80, 60, 100)} ██${c(0, 255, 200)}▪${c(80, 60, 100)}█${c(255, 0, 100)}▪${c(80, 60, 100)}██${NC}`,
97
+ `${c(80, 60, 100)} ██${c(255, 200, 0)}▐▌${c(80, 60, 100)}█${c(255, 200, 0)}▐▌${c(80, 60, 100)}██${NC}`,
98
+ ],
99
+ };
100
+
101
+ const space: Biome = {
102
+ name: "space",
103
+ sky: c(8, 5, 20),
104
+ ground: c(50, 40, 70),
105
+ particle: { color: c(255, 200, 50), chars: ["✦", "·", "·", "✧"] },
106
+ groundChars: ["▄", "░", "▒", "▄", "▓", "░"],
107
+ structure: [
108
+ `${c(255, 200, 50)} ✨${NC}`,
109
+ `${c(100, 80, 150)} ▄████▄${NC}`,
110
+ `${c(120, 100, 170)} ▟${c(150, 130, 200)}██${c(255, 220, 100)}◆◆${c(150, 130, 200)}██${c(120, 100, 170)}▙${NC}`,
111
+ `${c(150, 130, 200)} ██${c(255, 220, 100)}◆◆${c(150, 130, 200)}████${NC}`,
112
+ `${c(150, 130, 200)} ██${c(255, 220, 100)}◆◆${c(150, 130, 200)}█${c(200, 180, 255)}▐██▌${c(150, 130, 200)}██${NC}`,
113
+ `${c(150, 130, 200)} ████████████${NC}`,
114
+ ],
115
+ };
116
+
117
+ // ═══════════════════════════════════════════════════════════════════════════
118
+ // EXTRA BIOMES (selectable via --biome flag)
119
+ // ═══════════════════════════════════════════════════════════════════════════
120
+
121
+ const volcano: Biome = {
122
+ name: "volcano",
123
+ sky: c(50, 15, 5),
124
+ ground: c(80, 30, 10),
125
+ particle: { color: c(255, 120, 20), chars: ["✦", "·", "▪"] },
126
+ groundChars: ["▄", "▓", "▄", "█", "▒", "▄"],
127
+ structure: [
128
+ `${c(255, 80, 0)} ▓${NC}`,
129
+ `${c(255, 120, 20)} ▄${c(200, 50, 10)}█${c(255, 120, 20)}▄${NC}`,
130
+ `${c(200, 50, 10)} ▟███▙${NC}`,
131
+ `${c(150, 40, 10)} ▟█████▙${NC}`,
132
+ `${c(120, 30, 10)} ▟███████▙${NC}`,
133
+ `${c(100, 25, 8)} ▟█████████▙${NC}`,
134
+ ],
135
+ };
136
+
137
+ const arctic: Biome = {
138
+ name: "arctic",
139
+ sky: c(20, 30, 50),
140
+ ground: c(180, 200, 220),
141
+ particle: { color: c(200, 220, 255), chars: ["❄", "·", "✧"] },
142
+ groundChars: ["▄", "▄", "░", "▄", "▄", "▒"],
143
+ structure: [
144
+ `${c(200, 220, 240)} ▄▄▄${NC}`,
145
+ `${c(200, 220, 240)} ▄█████▄${NC}`,
146
+ `${c(220, 230, 245)} █████████${NC}`,
147
+ `${c(220, 230, 245)} ███${c(60, 80, 120)}▐██▌${c(220, 230, 245)}███${NC}`,
148
+ `${c(220, 230, 245)} █████████${NC}`,
149
+ `${c(200, 220, 240)} █████████${NC}`,
150
+ ],
151
+ };
152
+
153
+ const desert: Biome = {
154
+ name: "desert",
155
+ sky: c(40, 25, 15),
156
+ ground: c(180, 150, 80),
157
+ particle: { color: c(200, 180, 100), chars: ["✦", "·", "·"] },
158
+ groundChars: ["▄", "░", "▄", "▄", "░", "▄"],
159
+ structure: [
160
+ `${c(200, 180, 100)} ▲${NC}`,
161
+ `${c(200, 180, 100)} ▟█▙${NC}`,
162
+ `${c(190, 170, 90)} ▟███▙${NC}`,
163
+ `${c(180, 160, 80)} ▟█████▙${NC}`,
164
+ `${c(170, 150, 70)} ▟███████▙${NC}`,
165
+ `${c(160, 140, 60)} ▟█████████▙${NC}`,
166
+ ],
167
+ };
168
+
169
+ const haunted: Biome = {
170
+ name: "haunted",
171
+ sky: c(15, 10, 25),
172
+ ground: c(40, 35, 50),
173
+ particle: { color: c(100, 80, 140), chars: ["✦", "·", "👻"] },
174
+ groundChars: ["▄", "▒", "▄", "▓", "▄", "▒"],
175
+ structure: [
176
+ `${c(60, 50, 80)} ▄█▄${NC}`,
177
+ `${c(60, 50, 80)} ▄█████▄${NC}`,
178
+ `${c(50, 40, 70)} ██${c(200, 50, 50)}▪▪▪${c(50, 40, 70)}██${NC}`,
179
+ `${c(50, 40, 70)} ██${c(200, 50, 50)}▪▪▪${c(50, 40, 70)}██${NC}`,
180
+ `${c(50, 40, 70)} ████${c(30, 20, 40)}▐▌${c(50, 40, 70)}████${NC}`,
181
+ `${c(40, 30, 60)} ████████████${NC}`,
182
+ ],
183
+ };
184
+
185
+ const sakura: Biome = {
186
+ name: "sakura",
187
+ sky: c(40, 20, 35),
188
+ ground: c(80, 60, 50),
189
+ particle: { color: c(255, 150, 180), chars: ["✿", "·", "✦"] },
190
+ groundChars: ["▄", "▄", "▓", "▄", "▄", "▒"],
191
+ structure: [
192
+ `${c(200, 50, 80)} ▄▄▄▄▄${NC}`,
193
+ `${c(200, 50, 80)} ▄███████▄${NC}`,
194
+ `${c(160, 40, 60)} ▀███████▀${NC}`,
195
+ `${c(120, 70, 50)} ║║${NC}`,
196
+ `${c(120, 70, 50)} ║║${NC}`,
197
+ `${c(120, 70, 50)} ▄▄║║▄▄${NC}`,
198
+ ],
199
+ };
200
+
201
+ const underwater: Biome = {
202
+ name: "underwater",
203
+ sky: c(10, 30, 60),
204
+ ground: c(40, 80, 60),
205
+ particle: { color: c(80, 180, 220), chars: ["°", "·", "○"] },
206
+ groundChars: ["▄", "▒", "▓", "▄", "▒", "▄"],
207
+ structure: [
208
+ `${c(80, 200, 100)} \\│//${NC}`,
209
+ `${c(80, 200, 100)} \\│/${NC}`,
210
+ `${c(80, 200, 100)} │${NC}`,
211
+ `${c(255, 100, 80)} ▄███▄${NC}`,
212
+ `${c(255, 80, 60)} ▟█████▙${NC}`,
213
+ `${c(255, 60, 40)} ▟███████▙${NC}`,
214
+ ],
215
+ };
216
+
217
+ const candyland: Biome = {
218
+ name: "candyland",
219
+ sky: c(50, 20, 50),
220
+ ground: c(200, 100, 150),
221
+ particle: { color: c(255, 150, 200), chars: ["✦", "♥", "·"] },
222
+ groundChars: ["▄", "▄", "░", "▄", "▄", "▒"],
223
+ structure: [
224
+ `${c(255, 200, 100)} ★${NC}`,
225
+ `${c(200, 100, 50)} ▄█████▄${NC}`,
226
+ `${c(220, 150, 100)} █${c(255, 100, 150)}▪${c(100, 200, 255)}▪${c(255, 255, 100)}▪${c(100, 255, 100)}▪${c(255, 100, 150)}▪${c(220, 150, 100)}█${NC}`,
227
+ `${c(220, 150, 100)} █${c(100, 255, 100)}▪${c(255, 100, 150)}▪${c(100, 200, 255)}▪${c(255, 255, 100)}▪${c(100, 255, 100)}▪${c(220, 150, 100)}█${NC}`,
228
+ `${c(220, 150, 100)} █████${c(180, 80, 50)}▐▌${c(220, 150, 100)}████${NC}`,
229
+ `${c(220, 150, 100)} █████████████${NC}`,
230
+ ],
231
+ };
232
+
233
+ const dungeon: Biome = {
234
+ name: "dungeon",
235
+ sky: c(20, 18, 15),
236
+ ground: c(60, 55, 45),
237
+ particle: { color: c(255, 150, 30), chars: ["✦", "·", "·"] },
238
+ groundChars: ["▄", "█", "▄", "▓", "▄", "█"],
239
+ structure: [
240
+ `${c(80, 75, 65)} ▄█▄█▄█▄█▄${NC}`,
241
+ `${c(80, 75, 65)} ██████████${NC}`,
242
+ `${c(70, 65, 55)} ██${c(255, 150, 30)}▪▪${c(70, 65, 55)}██${c(255, 150, 30)}▪▪${c(70, 65, 55)}██${NC}`,
243
+ `${c(70, 65, 55)} ██████████${NC}`,
244
+ `${c(70, 65, 55)} ██${c(40, 35, 25)}▐██▌${c(70, 65, 55)}████${NC}`,
245
+ `${c(60, 55, 45)} ██████████${NC}`,
246
+ ],
247
+ };
248
+
249
+ const cloudkingdom: Biome = {
250
+ name: "cloudkingdom",
251
+ sky: c(30, 40, 70),
252
+ ground: c(180, 190, 220),
253
+ particle: { color: c(200, 210, 240), chars: ["✦", "☁", "·"] },
254
+ groundChars: ["▄", "░", "▄", "▄", "░", "░"],
255
+ structure: [
256
+ `${c(200, 180, 255)} ▲${NC}`,
257
+ `${c(200, 180, 255)} ▟█▙${NC}`,
258
+ `${c(180, 160, 240)} ▟███▙${NC}`,
259
+ `${c(180, 160, 240)} █${c(255, 220, 100)}◆◆${c(180, 160, 240)}██${NC}`,
260
+ `${c(180, 160, 240)} █████${NC}`,
261
+ `${c(200, 200, 230)} ▟█████████▙${NC}`,
262
+ ],
263
+ };
264
+
265
+ const matrix: Biome = {
266
+ name: "matrix",
267
+ sky: c(0, 10, 0),
268
+ ground: c(0, 40, 0),
269
+ particle: { color: c(0, 200, 0), chars: ["0", "1", "·"] },
270
+ groundChars: ["▄", "█", "▄", "▀", "▄", "█"],
271
+ structure: [
272
+ `${c(0, 150, 0)} ▄██████▄${NC}`,
273
+ `${c(0, 120, 0)} █${c(0, 255, 0)}01${c(0, 120, 0)}██${c(0, 255, 0)}10${c(0, 120, 0)}█${NC}`,
274
+ `${c(0, 120, 0)} █${c(0, 255, 0)}10${c(0, 120, 0)}██${c(0, 255, 0)}01${c(0, 120, 0)}█${NC}`,
275
+ `${c(0, 120, 0)} █${c(0, 255, 0)}01${c(0, 120, 0)}██${c(0, 255, 0)}10${c(0, 120, 0)}█${NC}`,
276
+ `${c(0, 100, 0)} █${c(0, 200, 0)}▐▌${c(0, 100, 0)}██${c(0, 200, 0)}▐▌${c(0, 100, 0)}█${NC}`,
277
+ `${c(0, 80, 0)} ▀██████▀${NC}`,
278
+ ],
279
+ };
280
+
281
+ // ─── Registry ───────────────────────────────────────────────────────────────
282
+
283
+ const RARITY_BIOMES: Record<string, Biome> = {
284
+ common: meadow,
285
+ uncommon: forest,
286
+ rare: ocean,
287
+ epic: cyberpunk,
288
+ legendary: space,
289
+ };
290
+
291
+ const ALL_BIOMES: Record<string, Biome> = {
292
+ // Defaults (by rarity name)
293
+ meadow, forest, ocean, cyberpunk, space,
294
+ // Extras
295
+ volcano, arctic, desert, haunted, sakura,
296
+ underwater, candyland, dungeon, cloudkingdom, matrix,
297
+ };
298
+
299
+ export function getBiome(rarity: string, override?: string): Biome {
300
+ if (override && ALL_BIOMES[override]) return ALL_BIOMES[override];
301
+ return RARITY_BIOMES[rarity] ?? meadow;
302
+ }
303
+
304
+ export function listBiomes(): { name: string; isDefault: boolean }[] {
305
+ return Object.keys(ALL_BIOMES).map(name => ({
306
+ name,
307
+ isDefault: Object.values(RARITY_BIOMES).some(b => b.name === name),
308
+ }));
309
+ }