@umang-boss/claudemon 1.0.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 (203) hide show
  1. package/README.md +164 -0
  2. package/bin/claudemon.js +52 -0
  3. package/bunfig.toml +2 -0
  4. package/cli/doctor.ts +334 -0
  5. package/cli/index.ts +42 -0
  6. package/cli/install.ts +248 -0
  7. package/cli/shared.ts +102 -0
  8. package/cli/uninstall.ts +155 -0
  9. package/cli/update.ts +318 -0
  10. package/hooks/post-tool-use.sh +127 -0
  11. package/hooks/stop.sh +49 -0
  12. package/hooks/user-prompt-submit.sh +73 -0
  13. package/package.json +68 -0
  14. package/scripts/download-colorscripts.ts +311 -0
  15. package/skills/buddy/SKILL.md +47 -0
  16. package/sprites/colorscripts/small/1-bulbasaur.txt +11 -0
  17. package/sprites/colorscripts/small/10-caterpie.txt +9 -0
  18. package/sprites/colorscripts/small/100-voltorb.txt +8 -0
  19. package/sprites/colorscripts/small/101-electrode.txt +9 -0
  20. package/sprites/colorscripts/small/102-exeggcute.txt +10 -0
  21. package/sprites/colorscripts/small/103-exeggutor.txt +23 -0
  22. package/sprites/colorscripts/small/104-cubone.txt +11 -0
  23. package/sprites/colorscripts/small/105-marowak.txt +16 -0
  24. package/sprites/colorscripts/small/106-hitmonlee.txt +16 -0
  25. package/sprites/colorscripts/small/107-hitmonchan.txt +19 -0
  26. package/sprites/colorscripts/small/108-lickitung.txt +10 -0
  27. package/sprites/colorscripts/small/109-koffing.txt +14 -0
  28. package/sprites/colorscripts/small/11-metapod.txt +10 -0
  29. package/sprites/colorscripts/small/110-weezing.txt +23 -0
  30. package/sprites/colorscripts/small/111-rhyhorn.txt +11 -0
  31. package/sprites/colorscripts/small/112-rhydon.txt +20 -0
  32. package/sprites/colorscripts/small/113-chansey.txt +11 -0
  33. package/sprites/colorscripts/small/114-tangela.txt +10 -0
  34. package/sprites/colorscripts/small/115-kangaskhan.txt +18 -0
  35. package/sprites/colorscripts/small/116-horsea.txt +10 -0
  36. package/sprites/colorscripts/small/117-seadra.txt +11 -0
  37. package/sprites/colorscripts/small/118-goldeen.txt +11 -0
  38. package/sprites/colorscripts/small/119-seaking.txt +16 -0
  39. package/sprites/colorscripts/small/12-butterfree.txt +20 -0
  40. package/sprites/colorscripts/small/120-staryu.txt +10 -0
  41. package/sprites/colorscripts/small/121-starmie.txt +17 -0
  42. package/sprites/colorscripts/small/122-mr-mime.txt +18 -0
  43. package/sprites/colorscripts/small/123-scyther.txt +21 -0
  44. package/sprites/colorscripts/small/124-jynx.txt +18 -0
  45. package/sprites/colorscripts/small/125-electabuzz.txt +19 -0
  46. package/sprites/colorscripts/small/126-magmar.txt +19 -0
  47. package/sprites/colorscripts/small/127-pinsir.txt +19 -0
  48. package/sprites/colorscripts/small/128-tauros.txt +20 -0
  49. package/sprites/colorscripts/small/129-magikarp.txt +13 -0
  50. package/sprites/colorscripts/small/13-weedle.txt +10 -0
  51. package/sprites/colorscripts/small/130-gyarados.txt +21 -0
  52. package/sprites/colorscripts/small/131-lapras.txt +19 -0
  53. package/sprites/colorscripts/small/132-ditto.txt +8 -0
  54. package/sprites/colorscripts/small/133-eevee.txt +10 -0
  55. package/sprites/colorscripts/small/134-vaporeon.txt +16 -0
  56. package/sprites/colorscripts/small/135-jolteon.txt +17 -0
  57. package/sprites/colorscripts/small/136-flareon.txt +18 -0
  58. package/sprites/colorscripts/small/137-porygon.txt +10 -0
  59. package/sprites/colorscripts/small/138-omanyte.txt +10 -0
  60. package/sprites/colorscripts/small/139-omastar.txt +18 -0
  61. package/sprites/colorscripts/small/14-kakuna.txt +10 -0
  62. package/sprites/colorscripts/small/140-kabuto.txt +8 -0
  63. package/sprites/colorscripts/small/141-kabutops.txt +17 -0
  64. package/sprites/colorscripts/small/142-aerodactyl.txt +17 -0
  65. package/sprites/colorscripts/small/143-snorlax.txt +21 -0
  66. package/sprites/colorscripts/small/144-articuno.txt +24 -0
  67. package/sprites/colorscripts/small/145-zapdos.txt +20 -0
  68. package/sprites/colorscripts/small/146-moltres.txt +23 -0
  69. package/sprites/colorscripts/small/147-dratini.txt +10 -0
  70. package/sprites/colorscripts/small/148-dragonair.txt +12 -0
  71. package/sprites/colorscripts/small/149-dragonite.txt +21 -0
  72. package/sprites/colorscripts/small/15-beedrill.txt +13 -0
  73. package/sprites/colorscripts/small/150-mewtwo.txt +22 -0
  74. package/sprites/colorscripts/small/151-mew.txt +14 -0
  75. package/sprites/colorscripts/small/16-pidgey.txt +10 -0
  76. package/sprites/colorscripts/small/17-pidgeotto.txt +11 -0
  77. package/sprites/colorscripts/small/18-pidgeot.txt +18 -0
  78. package/sprites/colorscripts/small/19-rattata.txt +12 -0
  79. package/sprites/colorscripts/small/2-ivysaur.txt +11 -0
  80. package/sprites/colorscripts/small/20-raticate.txt +12 -0
  81. package/sprites/colorscripts/small/21-spearow.txt +9 -0
  82. package/sprites/colorscripts/small/22-fearow.txt +12 -0
  83. package/sprites/colorscripts/small/23-ekans.txt +12 -0
  84. package/sprites/colorscripts/small/24-arbok.txt +16 -0
  85. package/sprites/colorscripts/small/25-pikachu.txt +11 -0
  86. package/sprites/colorscripts/small/26-raichu.txt +19 -0
  87. package/sprites/colorscripts/small/27-sandshrew.txt +10 -0
  88. package/sprites/colorscripts/small/28-sandslash.txt +16 -0
  89. package/sprites/colorscripts/small/29-nidoran-f.txt +11 -0
  90. package/sprites/colorscripts/small/3-venusaur.txt +21 -0
  91. package/sprites/colorscripts/small/30-nidorina.txt +12 -0
  92. package/sprites/colorscripts/small/31-nidoqueen.txt +19 -0
  93. package/sprites/colorscripts/small/32-nidoran-m.txt +11 -0
  94. package/sprites/colorscripts/small/33-nidorino.txt +12 -0
  95. package/sprites/colorscripts/small/34-nidoking.txt +18 -0
  96. package/sprites/colorscripts/small/35-clefairy.txt +11 -0
  97. package/sprites/colorscripts/small/36-clefable.txt +17 -0
  98. package/sprites/colorscripts/small/37-vulpix.txt +11 -0
  99. package/sprites/colorscripts/small/38-ninetales.txt +18 -0
  100. package/sprites/colorscripts/small/39-jigglypuff.txt +11 -0
  101. package/sprites/colorscripts/small/4-charmander.txt +11 -0
  102. package/sprites/colorscripts/small/40-wigglytuff.txt +20 -0
  103. package/sprites/colorscripts/small/41-zubat.txt +11 -0
  104. package/sprites/colorscripts/small/42-golbat.txt +18 -0
  105. package/sprites/colorscripts/small/43-oddish.txt +11 -0
  106. package/sprites/colorscripts/small/44-gloom.txt +12 -0
  107. package/sprites/colorscripts/small/45-vileplume.txt +17 -0
  108. package/sprites/colorscripts/small/46-paras.txt +11 -0
  109. package/sprites/colorscripts/small/47-parasect.txt +12 -0
  110. package/sprites/colorscripts/small/48-venonat.txt +14 -0
  111. package/sprites/colorscripts/small/49-venomoth.txt +19 -0
  112. package/sprites/colorscripts/small/5-charmeleon.txt +13 -0
  113. package/sprites/colorscripts/small/50-diglett.txt +8 -0
  114. package/sprites/colorscripts/small/51-dugtrio.txt +18 -0
  115. package/sprites/colorscripts/small/52-meowth.txt +12 -0
  116. package/sprites/colorscripts/small/53-persian.txt +20 -0
  117. package/sprites/colorscripts/small/54-psyduck.txt +12 -0
  118. package/sprites/colorscripts/small/55-golduck.txt +17 -0
  119. package/sprites/colorscripts/small/56-mankey.txt +11 -0
  120. package/sprites/colorscripts/small/57-primeape.txt +13 -0
  121. package/sprites/colorscripts/small/58-growlithe.txt +12 -0
  122. package/sprites/colorscripts/small/59-arcanine.txt +20 -0
  123. package/sprites/colorscripts/small/6-charizard.txt +21 -0
  124. package/sprites/colorscripts/small/60-poliwag.txt +9 -0
  125. package/sprites/colorscripts/small/61-poliwhirl.txt +11 -0
  126. package/sprites/colorscripts/small/62-poliwrath.txt +17 -0
  127. package/sprites/colorscripts/small/63-abra.txt +12 -0
  128. package/sprites/colorscripts/small/64-kadabra.txt +14 -0
  129. package/sprites/colorscripts/small/65-alakazam.txt +19 -0
  130. package/sprites/colorscripts/small/66-machop.txt +11 -0
  131. package/sprites/colorscripts/small/67-machoke.txt +12 -0
  132. package/sprites/colorscripts/small/68-machamp.txt +19 -0
  133. package/sprites/colorscripts/small/69-bellsprout.txt +9 -0
  134. package/sprites/colorscripts/small/7-squirtle.txt +10 -0
  135. package/sprites/colorscripts/small/70-weepinbell.txt +11 -0
  136. package/sprites/colorscripts/small/71-victreebel.txt +17 -0
  137. package/sprites/colorscripts/small/72-tentacool.txt +12 -0
  138. package/sprites/colorscripts/small/73-tentacruel.txt +20 -0
  139. package/sprites/colorscripts/small/74-geodude.txt +9 -0
  140. package/sprites/colorscripts/small/75-graveler.txt +12 -0
  141. package/sprites/colorscripts/small/76-golem.txt +18 -0
  142. package/sprites/colorscripts/small/77-ponyta.txt +13 -0
  143. package/sprites/colorscripts/small/78-rapidash.txt +18 -0
  144. package/sprites/colorscripts/small/79-slowpoke.txt +12 -0
  145. package/sprites/colorscripts/small/8-wartortle.txt +12 -0
  146. package/sprites/colorscripts/small/80-slowbro.txt +18 -0
  147. package/sprites/colorscripts/small/81-magnemite.txt +9 -0
  148. package/sprites/colorscripts/small/82-magneton.txt +18 -0
  149. package/sprites/colorscripts/small/83-farfetchd.txt +12 -0
  150. package/sprites/colorscripts/small/84-doduo.txt +10 -0
  151. package/sprites/colorscripts/small/85-dodrio.txt +17 -0
  152. package/sprites/colorscripts/small/86-seel.txt +13 -0
  153. package/sprites/colorscripts/small/87-dewgong.txt +20 -0
  154. package/sprites/colorscripts/small/88-grimer.txt +10 -0
  155. package/sprites/colorscripts/small/89-muk.txt +14 -0
  156. package/sprites/colorscripts/small/9-blastoise.txt +20 -0
  157. package/sprites/colorscripts/small/90-shellder.txt +10 -0
  158. package/sprites/colorscripts/small/91-cloyster.txt +18 -0
  159. package/sprites/colorscripts/small/92-gastly.txt +12 -0
  160. package/sprites/colorscripts/small/93-haunter.txt +14 -0
  161. package/sprites/colorscripts/small/94-gengar.txt +19 -0
  162. package/sprites/colorscripts/small/95-onix.txt +22 -0
  163. package/sprites/colorscripts/small/96-drowzee.txt +12 -0
  164. package/sprites/colorscripts/small/97-hypno.txt +19 -0
  165. package/sprites/colorscripts/small/98-krabby.txt +12 -0
  166. package/sprites/colorscripts/small/99-kingler.txt +20 -0
  167. package/src/engine/constants.ts +121 -0
  168. package/src/engine/encounter-pool.ts +71 -0
  169. package/src/engine/encounters.ts +308 -0
  170. package/src/engine/evolution-data.ts +535 -0
  171. package/src/engine/evolution.ts +310 -0
  172. package/src/engine/pokemon-data.ts +1838 -0
  173. package/src/engine/reactions.ts +877 -0
  174. package/src/engine/starter-pool.ts +47 -0
  175. package/src/engine/stats.ts +97 -0
  176. package/src/engine/types.ts +312 -0
  177. package/src/engine/xp.ts +135 -0
  178. package/src/gamification/achievements.ts +204 -0
  179. package/src/gamification/legendary-quests.ts +265 -0
  180. package/src/gamification/milestones.ts +86 -0
  181. package/src/hooks/award-xp.ts +131 -0
  182. package/src/hooks/increment-counter.ts +27 -0
  183. package/src/server/index.ts +78 -0
  184. package/src/server/instructions.ts +194 -0
  185. package/src/server/tools/achievements.ts +118 -0
  186. package/src/server/tools/catch.ts +295 -0
  187. package/src/server/tools/display-helpers.ts +35 -0
  188. package/src/server/tools/evolve.ts +236 -0
  189. package/src/server/tools/legendary.ts +78 -0
  190. package/src/server/tools/party.ts +251 -0
  191. package/src/server/tools/pet.ts +124 -0
  192. package/src/server/tools/pokedex.ts +286 -0
  193. package/src/server/tools/rename.ts +63 -0
  194. package/src/server/tools/show.ts +136 -0
  195. package/src/server/tools/starter.ts +175 -0
  196. package/src/server/tools/stats.ts +123 -0
  197. package/src/server/tools/visibility.ts +65 -0
  198. package/src/sprites/index.ts +45 -0
  199. package/src/state/io.ts +91 -0
  200. package/src/state/schemas.ts +131 -0
  201. package/src/state/state-manager.ts +321 -0
  202. package/statusline/buddy-status.sh +233 -0
  203. package/tsconfig.json +37 -0
package/cli/update.ts ADDED
@@ -0,0 +1,318 @@
1
+ /**
2
+ * Claudemon CLI Updater.
3
+ * Re-registers MCP server, hooks, skill, and status line without
4
+ * touching the user's saved state/Pokemon data.
5
+ *
6
+ * Effectively: uninstall registrations + reinstall registrations.
7
+ * State directory (~/.claudemon/) is preserved completely.
8
+ *
9
+ * Usage: bun run cli/update.ts
10
+ */
11
+
12
+ import { mkdir, copyFile, chmod, access } from "node:fs/promises";
13
+ import { constants as fsConstants } from "node:fs";
14
+ import { join } from "node:path";
15
+ import { existsSync } from "node:fs";
16
+
17
+ import type { ClaudeConfig, ClaudeSettings, HookCommand, HookMatcher } from "./shared.js";
18
+ import {
19
+ ok,
20
+ fail,
21
+ info,
22
+ readJson,
23
+ writeJson,
24
+ CLAUDE_DIR,
25
+ CLAUDE_CONFIG,
26
+ CLAUDE_SETTINGS,
27
+ STATE_DIR,
28
+ SKILL_SRC,
29
+ SKILL_DEST_DIR,
30
+ SKILL_DEST,
31
+ HOOK_SCRIPT,
32
+ STOP_HOOK_SCRIPT,
33
+ USER_PROMPT_HOOK_SCRIPT,
34
+ STATUSLINE_SCRIPT,
35
+ SERVER_ENTRY,
36
+ PROJECT_DIR,
37
+ getBunPath,
38
+ } from "./shared.js";
39
+
40
+ // ── Step 1: Check Prerequisites ──────────────────────────────
41
+
42
+ async function checkPrerequisites(): Promise<boolean> {
43
+ let allGood = true;
44
+
45
+ try {
46
+ const proc = Bun.spawn(["bun", "--version"], { stdout: "pipe", stderr: "pipe" });
47
+ const output = await new Response(proc.stdout).text();
48
+ await proc.exited;
49
+ ok(`Bun runtime: v${output.trim()}`);
50
+ } catch {
51
+ fail("Bun runtime not found.");
52
+ allGood = false;
53
+ }
54
+
55
+ try {
56
+ await access(CLAUDE_DIR, fsConstants.F_OK);
57
+ ok("Claude Code directory: ~/.claude/");
58
+ } catch {
59
+ fail("Claude Code not found at ~/.claude/");
60
+ allGood = false;
61
+ }
62
+
63
+ return allGood;
64
+ }
65
+
66
+ // ── Step 2: Remove Old Registrations ─────────────────────────
67
+
68
+ async function removeOldMcpServer(): Promise<void> {
69
+ const config = await readJson<ClaudeConfig>(CLAUDE_CONFIG);
70
+ if (!config?.mcpServers?.["claudemon"]) {
71
+ info("MCP server: not previously registered");
72
+ return;
73
+ }
74
+ delete config.mcpServers["claudemon"];
75
+ if (Object.keys(config.mcpServers).length === 0) {
76
+ delete config.mcpServers;
77
+ }
78
+ await writeJson(CLAUDE_CONFIG, config);
79
+ ok("MCP server: removed old registration");
80
+ }
81
+
82
+ async function removeOldHooks(): Promise<void> {
83
+ const settings = await readJson<ClaudeSettings>(CLAUDE_SETTINGS);
84
+ if (!settings?.hooks) {
85
+ info("Hooks: none found");
86
+ return;
87
+ }
88
+
89
+ const hookTypes = ["PostToolUse", "Stop", "UserPromptSubmit"] as const;
90
+ const patterns = ["post-tool-use.sh", "stop.sh", "user-prompt-submit.sh"] as const;
91
+
92
+ for (let i = 0; i < hookTypes.length; i++) {
93
+ const hookType = hookTypes[i]!;
94
+ const pattern = patterns[i]!;
95
+ const entries = settings.hooks[hookType];
96
+ if (entries) {
97
+ const filtered = entries.filter((m) => !m.hooks.some((h) => h.command.includes(pattern)));
98
+ if (filtered.length === 0) {
99
+ delete settings.hooks[hookType];
100
+ } else {
101
+ settings.hooks[hookType] = filtered;
102
+ }
103
+ }
104
+ }
105
+
106
+ if (Object.keys(settings.hooks).length === 0) {
107
+ delete settings.hooks;
108
+ }
109
+
110
+ await writeJson(CLAUDE_SETTINGS, settings);
111
+ ok("Hooks: removed old registrations");
112
+ }
113
+
114
+ // ── Step 3: Re-register Everything ───────────────────────────
115
+
116
+ async function registerMcpServer(): Promise<void> {
117
+ const existing = await readJson<ClaudeConfig>(CLAUDE_CONFIG);
118
+ const config: ClaudeConfig = existing ?? {};
119
+
120
+ if (!config.mcpServers) {
121
+ config.mcpServers = {};
122
+ }
123
+
124
+ config.mcpServers["claudemon"] = {
125
+ command: getBunPath(),
126
+ args: ["run", SERVER_ENTRY],
127
+ env: {},
128
+ };
129
+
130
+ await writeJson(CLAUDE_CONFIG, config);
131
+ ok("MCP server: registered in ~/.claude.json");
132
+ }
133
+
134
+ async function installHooks(): Promise<void> {
135
+ const existing = await readJson<ClaudeSettings>(CLAUDE_SETTINGS);
136
+ const settings: ClaudeSettings = existing ?? {};
137
+
138
+ if (!settings.hooks) {
139
+ settings.hooks = {};
140
+ }
141
+
142
+ // PostToolUse hook
143
+ const ourHook: HookCommand = {
144
+ type: "command",
145
+ command: HOOK_SCRIPT,
146
+ timeout: 5000,
147
+ };
148
+ const ourMatcher: HookMatcher = {
149
+ matcher: "Bash|Write|Edit|Read|Grep|Glob",
150
+ hooks: [ourHook],
151
+ };
152
+ const postToolUse = settings.hooks["PostToolUse"];
153
+ if (postToolUse) {
154
+ const filtered = postToolUse.filter(
155
+ (m) => !m.hooks.some((h) => h.command.includes("post-tool-use.sh")),
156
+ );
157
+ filtered.push(ourMatcher);
158
+ settings.hooks["PostToolUse"] = filtered;
159
+ } else {
160
+ settings.hooks["PostToolUse"] = [ourMatcher];
161
+ }
162
+
163
+ // Stop hook
164
+ const stopHook: HookCommand = { type: "command", command: STOP_HOOK_SCRIPT, timeout: 3000 };
165
+ const stopEntry: HookMatcher = { hooks: [stopHook] };
166
+ const stopHooks = settings.hooks["Stop"];
167
+ if (stopHooks) {
168
+ const filtered = stopHooks.filter((m) => !m.hooks.some((h) => h.command.includes("stop.sh")));
169
+ filtered.push(stopEntry);
170
+ settings.hooks["Stop"] = filtered;
171
+ } else {
172
+ settings.hooks["Stop"] = [stopEntry];
173
+ }
174
+
175
+ // UserPromptSubmit hook
176
+ const userPromptHook: HookCommand = {
177
+ type: "command",
178
+ command: USER_PROMPT_HOOK_SCRIPT,
179
+ timeout: 3000,
180
+ };
181
+ const userPromptEntry: HookMatcher = { hooks: [userPromptHook] };
182
+ const userPromptHooks = settings.hooks["UserPromptSubmit"];
183
+ if (userPromptHooks) {
184
+ const filtered = userPromptHooks.filter(
185
+ (m) => !m.hooks.some((h) => h.command.includes("user-prompt-submit.sh")),
186
+ );
187
+ filtered.push(userPromptEntry);
188
+ settings.hooks["UserPromptSubmit"] = filtered;
189
+ } else {
190
+ settings.hooks["UserPromptSubmit"] = [userPromptEntry];
191
+ }
192
+
193
+ await writeJson(CLAUDE_SETTINGS, settings);
194
+ ok("Hooks: PostToolUse, Stop, UserPromptSubmit configured");
195
+ }
196
+
197
+ async function registerStatusLine(): Promise<void> {
198
+ const existing = await readJson<ClaudeSettings>(CLAUDE_SETTINGS);
199
+ const settings: ClaudeSettings = existing ?? {};
200
+
201
+ settings.statusLine = {
202
+ type: "command",
203
+ command: STATUSLINE_SCRIPT,
204
+ refreshInterval: 1,
205
+ };
206
+
207
+ await writeJson(CLAUDE_SETTINGS, settings);
208
+ ok("Status line: registered");
209
+ }
210
+
211
+ async function installSkill(): Promise<void> {
212
+ await mkdir(SKILL_DEST_DIR, { recursive: true });
213
+ await copyFile(SKILL_SRC, SKILL_DEST);
214
+ ok("Skill: /buddy command updated");
215
+ }
216
+
217
+ async function setScriptPermissions(): Promise<void> {
218
+ await chmod(HOOK_SCRIPT, 0o755);
219
+ await chmod(STOP_HOOK_SCRIPT, 0o755);
220
+ await chmod(USER_PROMPT_HOOK_SCRIPT, 0o755);
221
+ await chmod(STATUSLINE_SCRIPT, 0o755);
222
+ ok("Scripts: set executable");
223
+ }
224
+
225
+ // ── Step 4: Check for Missing Colorscripts ───────────────────
226
+
227
+ async function checkColorscripts(): Promise<{ missing: number; total: number }> {
228
+ const smallDir = join(PROJECT_DIR, "sprites", "colorscripts", "small");
229
+ let missing = 0;
230
+ const total = 151;
231
+
232
+ for (let id = 1; id <= total; id++) {
233
+ // We don't know the name mapping without importing the data module,
234
+ // so check if any file starting with the ID exists
235
+ const prefix = `${id}-`;
236
+ const dirExists = existsSync(smallDir);
237
+ if (!dirExists) {
238
+ missing = total;
239
+ break;
240
+ }
241
+
242
+ // Check for files matching the pattern
243
+ const files = await Array.fromAsync(new Bun.Glob(`${prefix}*.txt`).scan(smallDir));
244
+ if (files.length === 0) {
245
+ missing++;
246
+ }
247
+ }
248
+
249
+ return { missing, total };
250
+ }
251
+
252
+ // ── Step 5: State Preservation Check ─────────────────────────
253
+
254
+ async function checkStatePreserved(): Promise<void> {
255
+ const stateFile = Bun.file(`${STATE_DIR}/state.json`);
256
+ if (await stateFile.exists()) {
257
+ ok("State data: preserved (not modified)");
258
+ } else {
259
+ info("State data: no save file found (run /buddy starter to begin)");
260
+ }
261
+ }
262
+
263
+ // ── Main ─────────────────────────────────────────────────────
264
+
265
+ export async function update(): Promise<void> {
266
+ console.log("");
267
+ console.log("Claudemon Updater");
268
+ console.log("=================");
269
+ console.log("");
270
+ console.log("Checking prerequisites...");
271
+
272
+ const prereqOk = await checkPrerequisites();
273
+ if (!prereqOk) {
274
+ console.log("");
275
+ fail("Prerequisites not met. Fix the issues above and try again.");
276
+ process.exit(1);
277
+ }
278
+
279
+ console.log("");
280
+ console.log("Removing old registrations...");
281
+ await removeOldMcpServer();
282
+ await removeOldHooks();
283
+
284
+ console.log("");
285
+ console.log("Re-registering...");
286
+ await registerMcpServer();
287
+ await installHooks();
288
+ await registerStatusLine();
289
+ await installSkill();
290
+ await setScriptPermissions();
291
+
292
+ console.log("");
293
+ console.log("Checking sprites...");
294
+ const { missing, total } = await checkColorscripts();
295
+ if (missing === 0) {
296
+ ok(`Colorscripts: all ${total} sprites present`);
297
+ } else if (missing === total) {
298
+ fail(`Colorscripts: sprite directory not found. Run: bun run download-sprites`);
299
+ } else {
300
+ info(`Colorscripts: ${missing}/${total} missing. Run: bun run download-sprites`);
301
+ }
302
+
303
+ console.log("");
304
+ await checkStatePreserved();
305
+
306
+ console.log("");
307
+ console.log("\u2713 Claudemon updated successfully!");
308
+ console.log("");
309
+ console.log("Start a new Claude Code session to use the updated version.");
310
+ console.log("");
311
+ }
312
+
313
+ // Run if executed directly
314
+ update().catch((err: unknown) => {
315
+ const message = err instanceof Error ? err.message : String(err);
316
+ console.error(`\nUpdate failed: ${message}`);
317
+ process.exit(1);
318
+ });
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env bash
2
+ # chmod +x hooks/post-tool-use.sh
3
+ #
4
+ # PostToolUse hook for Claudemon — the XP pipeline.
5
+ # Reads JSON from stdin, pattern-matches coding events, delegates XP math to bun.
6
+ # MUST exit 0 always — hooks must never block Claude Code.
7
+
8
+ # Defensive: do NOT use set -e. We never want to crash.
9
+ # Wrap everything so any unexpected error is swallowed.
10
+ _claudemon_post_tool_use() {
11
+ # ── Locate dependencies ────────────────────────────────────
12
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
13
+ BUN_PATH="${HOME}/.bun/bin/bun"
14
+
15
+ # Bail silently if bun is not installed
16
+ if [ ! -x "$BUN_PATH" ]; then
17
+ return 0
18
+ fi
19
+
20
+ # Bail silently if jq is not available
21
+ if ! command -v jq >/dev/null 2>&1; then
22
+ return 0
23
+ fi
24
+
25
+ # ── Read stdin ─────────────────────────────────────────────
26
+ INPUT=$(cat)
27
+ if [ -z "$INPUT" ]; then
28
+ return 0
29
+ fi
30
+
31
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty')
32
+ if [ -z "$TOOL_NAME" ]; then
33
+ return 0
34
+ fi
35
+
36
+ TOOL_INPUT=$(echo "$INPUT" | jq -r '.tool_input // empty')
37
+ TOOL_RESPONSE=$(echo "$INPUT" | jq -r '.tool_response // empty')
38
+
39
+ # ── Pattern match coding events ────────────────────────────
40
+ EVENT=""
41
+ COUNTER=""
42
+
43
+ if [ "$TOOL_NAME" = "Bash" ]; then
44
+ STDOUT=$(echo "$TOOL_RESPONSE" | jq -r '.stdout // empty')
45
+ EXIT_CODE=$(echo "$TOOL_RESPONSE" | jq -r '.exit_code // 0')
46
+ COMMAND=$(echo "$TOOL_INPUT" | jq -r '.command // empty')
47
+
48
+ # Git commit
49
+ if echo "$COMMAND" | grep -qi "git commit"; then
50
+ if [ "$EXIT_CODE" = "0" ]; then
51
+ EVENT="commit"
52
+ COUNTER="commits"
53
+ fi
54
+
55
+ # Test pass
56
+ elif echo "$STDOUT" | grep -qiE "(passed|✓|PASS|tests? (passed|succeeded)|0 failed)"; then
57
+ if [ "$EXIT_CODE" = "0" ]; then
58
+ EVENT="test_pass"
59
+ COUNTER="tests_passed"
60
+ fi
61
+
62
+ # Test fail
63
+ elif echo "$STDOUT" | grep -qiE "(failed|✗|FAIL|error)"; then
64
+ COUNTER="tests_failed"
65
+ # No XP for failures, but track the counter
66
+
67
+ # Build success / failure
68
+ elif echo "$COMMAND" | grep -qiE "(build|compile|make|tsc)"; then
69
+ if [ "$EXIT_CODE" = "0" ]; then
70
+ EVENT="build_success"
71
+ COUNTER="builds_succeeded"
72
+ else
73
+ COUNTER="builds_failed"
74
+ fi
75
+
76
+ # Lint fix
77
+ elif echo "$COMMAND" | grep -qiE "(lint|eslint|prettier)"; then
78
+ if [ "$EXIT_CODE" = "0" ]; then
79
+ EVENT="lint_fix"
80
+ COUNTER="lint_fixes"
81
+ fi
82
+
83
+ # Generic error encountered
84
+ elif [ "$EXIT_CODE" != "0" ]; then
85
+ COUNTER="errors_encountered"
86
+ fi
87
+
88
+ elif [ "$TOOL_NAME" = "Write" ]; then
89
+ FILE_PATH=$(echo "$TOOL_INPUT" | jq -r '.file_path // empty')
90
+ if echo "$FILE_PATH" | grep -qiE '\.(test|spec)\.(ts|tsx|js|jsx|py)$'; then
91
+ EVENT="test_written"
92
+ COUNTER="tests_written"
93
+ else
94
+ EVENT="file_create"
95
+ COUNTER="files_created"
96
+ fi
97
+
98
+ elif [ "$TOOL_NAME" = "Edit" ]; then
99
+ FILE_PATH=$(echo "$TOOL_INPUT" | jq -r '.file_path // empty')
100
+ if echo "$FILE_PATH" | grep -qiE '\.(test|spec)\.(ts|tsx|js|jsx|py)$'; then
101
+ EVENT="test_written"
102
+ COUNTER="tests_written"
103
+ else
104
+ EVENT="file_edit"
105
+ COUNTER="files_edited"
106
+ fi
107
+
108
+ elif [ "$TOOL_NAME" = "Grep" ] || [ "$TOOL_NAME" = "Read" ] || [ "$TOOL_NAME" = "Glob" ]; then
109
+ EVENT="search"
110
+ COUNTER="searches"
111
+ fi
112
+
113
+ # ── Delegate to bun scripts ────────────────────────────────
114
+ # Use timeout to ensure bun never blocks beyond 3s (leaves 2s buffer for Claude Code's 5s limit)
115
+
116
+ if [ -n "$EVENT" ]; then
117
+ # XP award + counter increment
118
+ timeout 3 "$BUN_PATH" run "$SCRIPT_DIR/../src/hooks/award-xp.ts" "$EVENT" "$COUNTER" 2>/dev/null &
119
+ elif [ -n "$COUNTER" ]; then
120
+ # Counter-only increment (no XP)
121
+ timeout 3 "$BUN_PATH" run "$SCRIPT_DIR/../src/hooks/increment-counter.ts" "$COUNTER" 2>/dev/null &
122
+ fi
123
+ }
124
+
125
+ # Run the function, swallow all errors, always exit 0
126
+ _claudemon_post_tool_use 2>/dev/null || true
127
+ exit 0
package/hooks/stop.sh ADDED
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env bash
2
+ # chmod +x hooks/stop.sh
3
+ #
4
+ # Stop hook for Claudemon — extracts buddy comments from Claude's response.
5
+ # Looks for <!-- buddy: ... --> patterns and writes them as reactions to status.json.
6
+ # MUST exit 0 always — hooks must never block Claude Code.
7
+
8
+ # Defensive: do NOT use set -e. We never want to crash.
9
+ # Wrap everything so any unexpected error is swallowed.
10
+ _claudemon_stop() {
11
+ # ── Locate dependencies ────────────────────────────────────
12
+ command -v jq >/dev/null 2>&1 || return 0
13
+
14
+ # ── Read stdin ─────────────────────────────────────────────
15
+ local INPUT
16
+ INPUT=$(cat)
17
+ [ -z "$INPUT" ] && return 0
18
+
19
+ local STATE_DIR="$HOME/.claudemon"
20
+ local STATUS_FILE="$STATE_DIR/status.json"
21
+
22
+ [ -f "$STATUS_FILE" ] || return 0
23
+
24
+ # ── Extract the response text ──────────────────────────────
25
+ local RESPONSE_TEXT
26
+ RESPONSE_TEXT=$(echo "$INPUT" | timeout 2 jq -r '.tool_response.content // empty' 2>/dev/null)
27
+ [ -z "$RESPONSE_TEXT" ] && RESPONSE_TEXT=$(echo "$INPUT" | timeout 2 jq -r '.tool_response // empty' 2>/dev/null)
28
+ [ -z "$RESPONSE_TEXT" ] && return 0
29
+
30
+ # ── Look for <!-- buddy: ... --> pattern ───────────────────
31
+ local BUDDY_COMMENT
32
+ BUDDY_COMMENT=$(echo "$RESPONSE_TEXT" | grep -oP '<!-- buddy: \K[^-]+(?= -->)' 2>/dev/null | head -1)
33
+ [ -z "$BUDDY_COMMENT" ] && return 0
34
+
35
+ # Trim trailing whitespace
36
+ BUDDY_COMMENT=$(echo "$BUDDY_COMMENT" | sed 's/[[:space:]]*$//')
37
+
38
+ # ── Write reaction to status.json ──────────────────────────
39
+ local CURRENT
40
+ CURRENT=$(cat "$STATUS_FILE" 2>/dev/null)
41
+ [ -z "$CURRENT" ] && return 0
42
+
43
+ echo "$CURRENT" | timeout 2 jq --arg reaction "$BUDDY_COMMENT" '. + {reaction: $reaction}' > "${STATUS_FILE}.tmp" 2>/dev/null
44
+ mv "${STATUS_FILE}.tmp" "$STATUS_FILE" 2>/dev/null
45
+ }
46
+
47
+ # Run the function, swallow all errors, always exit 0
48
+ _claudemon_stop 2>/dev/null || true
49
+ exit 0
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env bash
2
+ # chmod +x hooks/user-prompt-submit.sh
3
+ #
4
+ # UserPromptSubmit hook for Claudemon — reacts when Pokemon name is mentioned.
5
+ # Reads JSON from stdin, checks if the user mentioned their active Pokemon's name,
6
+ # and writes a fun reaction to status.json.
7
+ # MUST exit 0 always — hooks must never block Claude Code.
8
+
9
+ # Defensive: do NOT use set -e. We never want to crash.
10
+ # Wrap everything so any unexpected error is swallowed.
11
+ _claudemon_name_react() {
12
+ # ── Locate dependencies ────────────────────────────────────
13
+ command -v jq >/dev/null 2>&1 || return 0
14
+
15
+ # ── Read stdin ─────────────────────────────────────────────
16
+ local INPUT
17
+ INPUT=$(cat)
18
+ [ -z "$INPUT" ] && return 0
19
+
20
+ local STATE_DIR="$HOME/.claudemon"
21
+ local STATE_FILE="$STATE_DIR/state.json"
22
+ local STATUS_FILE="$STATE_DIR/status.json"
23
+
24
+ [ -f "$STATE_FILE" ] || return 0
25
+ [ -f "$STATUS_FILE" ] || return 0
26
+
27
+ # ── Get user's prompt text ─────────────────────────────────
28
+ local USER_TEXT
29
+ USER_TEXT=$(echo "$INPUT" | timeout 2 jq -r '.user_prompt // empty' 2>/dev/null)
30
+ [ -z "$USER_TEXT" ] && return 0
31
+
32
+ # ── Get active Pokemon's nickname or species name ──────────
33
+ local POKEMON_NAME
34
+ POKEMON_NAME=$(timeout 2 jq -r '.party[] | select(.isActive == true) | .nickname // empty' "$STATE_FILE" 2>/dev/null)
35
+
36
+ if [ -z "$POKEMON_NAME" ]; then
37
+ # No nickname — fall back to the name from status.json
38
+ POKEMON_NAME=$(timeout 2 jq -r '.name // empty' "$STATUS_FILE" 2>/dev/null)
39
+ fi
40
+
41
+ [ -z "$POKEMON_NAME" ] && return 0
42
+
43
+ # ── Case-insensitive check if name appears in user text ────
44
+ local NAME_LOWER
45
+ NAME_LOWER=$(echo "$POKEMON_NAME" | tr '[:upper:]' '[:lower:]')
46
+ local TEXT_LOWER
47
+ TEXT_LOWER=$(echo "$USER_TEXT" | tr '[:upper:]' '[:lower:]')
48
+
49
+ echo "$TEXT_LOWER" | grep -qi "$NAME_LOWER" || return 0
50
+
51
+ # ── Name mentioned! Pick a reaction ────────────────────────
52
+ local REACTIONS=(
53
+ "perks up!"
54
+ "looks at you!"
55
+ "wiggles happily!"
56
+ "tilts head curiously!"
57
+ "chirps with excitement!"
58
+ )
59
+ local IDX=$(( $(date +%s) % ${#REACTIONS[@]} ))
60
+ local REACTION="*${POKEMON_NAME} ${REACTIONS[$IDX]}*"
61
+
62
+ # ── Update status.json with reaction ───────────────────────
63
+ local CURRENT
64
+ CURRENT=$(cat "$STATUS_FILE" 2>/dev/null)
65
+ [ -z "$CURRENT" ] && return 0
66
+
67
+ echo "$CURRENT" | timeout 2 jq --arg reaction "$REACTION" '. + {reaction: $reaction}' > "${STATUS_FILE}.tmp" 2>/dev/null
68
+ mv "${STATUS_FILE}.tmp" "$STATUS_FILE" 2>/dev/null
69
+ }
70
+
71
+ # Run the function, swallow all errors, always exit 0
72
+ _claudemon_name_react 2>/dev/null || true
73
+ exit 0
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "@umang-boss/claudemon",
3
+ "version": "1.0.0",
4
+ "description": "Pokemon Gen 1 coding companion for Claude Code — Gotta code 'em all!",
5
+ "type": "module",
6
+ "main": "src/server/index.ts",
7
+ "bin": {
8
+ "claudemon": "bin/claudemon.js"
9
+ },
10
+ "files": [
11
+ "src/",
12
+ "cli/",
13
+ "bin/",
14
+ "hooks/",
15
+ "skills/",
16
+ "sprites/",
17
+ "statusline/",
18
+ "scripts/",
19
+ "package.json",
20
+ "tsconfig.json",
21
+ "bunfig.toml"
22
+ ],
23
+ "scripts": {
24
+ "server": "bun run src/server/index.ts",
25
+ "install-buddy": "bun run cli/install.ts",
26
+ "uninstall-buddy": "bun run cli/uninstall.ts",
27
+ "doctor": "bun run cli/doctor.ts",
28
+ "update-buddy": "bun run cli/update.ts",
29
+ "download-sprites": "bun run scripts/download-colorscripts.ts",
30
+ "test": "bun test",
31
+ "typecheck": "tsc --noEmit",
32
+ "format": "prettier --write \"src/**/*.ts\" \"cli/**/*.ts\" \"scripts/**/*.ts\" \"tests/**/*.ts\" --ignore-unknown",
33
+ "format:check": "prettier --check \"src/**/*.ts\" \"cli/**/*.ts\" \"scripts/**/*.ts\" \"tests/**/*.ts\" --ignore-unknown"
34
+ },
35
+ "dependencies": {
36
+ "@modelcontextprotocol/sdk": "^1.12.1",
37
+ "zod": "^3.25.23"
38
+ },
39
+ "devDependencies": {
40
+ "@types/bun": "latest",
41
+ "prettier": "^3.8.2",
42
+ "typescript": "^5.8.3"
43
+ },
44
+ "keywords": [
45
+ "claude-code",
46
+ "mcp",
47
+ "pokemon",
48
+ "coding-companion",
49
+ "gamification",
50
+ "claudemon",
51
+ "terminal-art",
52
+ "status-line"
53
+ ],
54
+ "author": "Umang Dabhi <umang@dabhi.dev>",
55
+ "license": "MIT",
56
+ "repository": {
57
+ "type": "git",
58
+ "url": "https://github.com/umang-dabhi/claudemon.git"
59
+ },
60
+ "homepage": "https://github.com/umang-dabhi/claudemon",
61
+ "bugs": {
62
+ "url": "https://github.com/umang-dabhi/claudemon/issues"
63
+ },
64
+ "engines": {
65
+ "node": ">=18",
66
+ "bun": ">=1.0"
67
+ }
68
+ }