@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.
- package/README.md +164 -0
- package/bin/claudemon.js +52 -0
- package/bunfig.toml +2 -0
- package/cli/doctor.ts +334 -0
- package/cli/index.ts +42 -0
- package/cli/install.ts +248 -0
- package/cli/shared.ts +102 -0
- package/cli/uninstall.ts +155 -0
- package/cli/update.ts +318 -0
- package/hooks/post-tool-use.sh +127 -0
- package/hooks/stop.sh +49 -0
- package/hooks/user-prompt-submit.sh +73 -0
- package/package.json +68 -0
- package/scripts/download-colorscripts.ts +311 -0
- package/skills/buddy/SKILL.md +47 -0
- package/sprites/colorscripts/small/1-bulbasaur.txt +11 -0
- package/sprites/colorscripts/small/10-caterpie.txt +9 -0
- package/sprites/colorscripts/small/100-voltorb.txt +8 -0
- package/sprites/colorscripts/small/101-electrode.txt +9 -0
- package/sprites/colorscripts/small/102-exeggcute.txt +10 -0
- package/sprites/colorscripts/small/103-exeggutor.txt +23 -0
- package/sprites/colorscripts/small/104-cubone.txt +11 -0
- package/sprites/colorscripts/small/105-marowak.txt +16 -0
- package/sprites/colorscripts/small/106-hitmonlee.txt +16 -0
- package/sprites/colorscripts/small/107-hitmonchan.txt +19 -0
- package/sprites/colorscripts/small/108-lickitung.txt +10 -0
- package/sprites/colorscripts/small/109-koffing.txt +14 -0
- package/sprites/colorscripts/small/11-metapod.txt +10 -0
- package/sprites/colorscripts/small/110-weezing.txt +23 -0
- package/sprites/colorscripts/small/111-rhyhorn.txt +11 -0
- package/sprites/colorscripts/small/112-rhydon.txt +20 -0
- package/sprites/colorscripts/small/113-chansey.txt +11 -0
- package/sprites/colorscripts/small/114-tangela.txt +10 -0
- package/sprites/colorscripts/small/115-kangaskhan.txt +18 -0
- package/sprites/colorscripts/small/116-horsea.txt +10 -0
- package/sprites/colorscripts/small/117-seadra.txt +11 -0
- package/sprites/colorscripts/small/118-goldeen.txt +11 -0
- package/sprites/colorscripts/small/119-seaking.txt +16 -0
- package/sprites/colorscripts/small/12-butterfree.txt +20 -0
- package/sprites/colorscripts/small/120-staryu.txt +10 -0
- package/sprites/colorscripts/small/121-starmie.txt +17 -0
- package/sprites/colorscripts/small/122-mr-mime.txt +18 -0
- package/sprites/colorscripts/small/123-scyther.txt +21 -0
- package/sprites/colorscripts/small/124-jynx.txt +18 -0
- package/sprites/colorscripts/small/125-electabuzz.txt +19 -0
- package/sprites/colorscripts/small/126-magmar.txt +19 -0
- package/sprites/colorscripts/small/127-pinsir.txt +19 -0
- package/sprites/colorscripts/small/128-tauros.txt +20 -0
- package/sprites/colorscripts/small/129-magikarp.txt +13 -0
- package/sprites/colorscripts/small/13-weedle.txt +10 -0
- package/sprites/colorscripts/small/130-gyarados.txt +21 -0
- package/sprites/colorscripts/small/131-lapras.txt +19 -0
- package/sprites/colorscripts/small/132-ditto.txt +8 -0
- package/sprites/colorscripts/small/133-eevee.txt +10 -0
- package/sprites/colorscripts/small/134-vaporeon.txt +16 -0
- package/sprites/colorscripts/small/135-jolteon.txt +17 -0
- package/sprites/colorscripts/small/136-flareon.txt +18 -0
- package/sprites/colorscripts/small/137-porygon.txt +10 -0
- package/sprites/colorscripts/small/138-omanyte.txt +10 -0
- package/sprites/colorscripts/small/139-omastar.txt +18 -0
- package/sprites/colorscripts/small/14-kakuna.txt +10 -0
- package/sprites/colorscripts/small/140-kabuto.txt +8 -0
- package/sprites/colorscripts/small/141-kabutops.txt +17 -0
- package/sprites/colorscripts/small/142-aerodactyl.txt +17 -0
- package/sprites/colorscripts/small/143-snorlax.txt +21 -0
- package/sprites/colorscripts/small/144-articuno.txt +24 -0
- package/sprites/colorscripts/small/145-zapdos.txt +20 -0
- package/sprites/colorscripts/small/146-moltres.txt +23 -0
- package/sprites/colorscripts/small/147-dratini.txt +10 -0
- package/sprites/colorscripts/small/148-dragonair.txt +12 -0
- package/sprites/colorscripts/small/149-dragonite.txt +21 -0
- package/sprites/colorscripts/small/15-beedrill.txt +13 -0
- package/sprites/colorscripts/small/150-mewtwo.txt +22 -0
- package/sprites/colorscripts/small/151-mew.txt +14 -0
- package/sprites/colorscripts/small/16-pidgey.txt +10 -0
- package/sprites/colorscripts/small/17-pidgeotto.txt +11 -0
- package/sprites/colorscripts/small/18-pidgeot.txt +18 -0
- package/sprites/colorscripts/small/19-rattata.txt +12 -0
- package/sprites/colorscripts/small/2-ivysaur.txt +11 -0
- package/sprites/colorscripts/small/20-raticate.txt +12 -0
- package/sprites/colorscripts/small/21-spearow.txt +9 -0
- package/sprites/colorscripts/small/22-fearow.txt +12 -0
- package/sprites/colorscripts/small/23-ekans.txt +12 -0
- package/sprites/colorscripts/small/24-arbok.txt +16 -0
- package/sprites/colorscripts/small/25-pikachu.txt +11 -0
- package/sprites/colorscripts/small/26-raichu.txt +19 -0
- package/sprites/colorscripts/small/27-sandshrew.txt +10 -0
- package/sprites/colorscripts/small/28-sandslash.txt +16 -0
- package/sprites/colorscripts/small/29-nidoran-f.txt +11 -0
- package/sprites/colorscripts/small/3-venusaur.txt +21 -0
- package/sprites/colorscripts/small/30-nidorina.txt +12 -0
- package/sprites/colorscripts/small/31-nidoqueen.txt +19 -0
- package/sprites/colorscripts/small/32-nidoran-m.txt +11 -0
- package/sprites/colorscripts/small/33-nidorino.txt +12 -0
- package/sprites/colorscripts/small/34-nidoking.txt +18 -0
- package/sprites/colorscripts/small/35-clefairy.txt +11 -0
- package/sprites/colorscripts/small/36-clefable.txt +17 -0
- package/sprites/colorscripts/small/37-vulpix.txt +11 -0
- package/sprites/colorscripts/small/38-ninetales.txt +18 -0
- package/sprites/colorscripts/small/39-jigglypuff.txt +11 -0
- package/sprites/colorscripts/small/4-charmander.txt +11 -0
- package/sprites/colorscripts/small/40-wigglytuff.txt +20 -0
- package/sprites/colorscripts/small/41-zubat.txt +11 -0
- package/sprites/colorscripts/small/42-golbat.txt +18 -0
- package/sprites/colorscripts/small/43-oddish.txt +11 -0
- package/sprites/colorscripts/small/44-gloom.txt +12 -0
- package/sprites/colorscripts/small/45-vileplume.txt +17 -0
- package/sprites/colorscripts/small/46-paras.txt +11 -0
- package/sprites/colorscripts/small/47-parasect.txt +12 -0
- package/sprites/colorscripts/small/48-venonat.txt +14 -0
- package/sprites/colorscripts/small/49-venomoth.txt +19 -0
- package/sprites/colorscripts/small/5-charmeleon.txt +13 -0
- package/sprites/colorscripts/small/50-diglett.txt +8 -0
- package/sprites/colorscripts/small/51-dugtrio.txt +18 -0
- package/sprites/colorscripts/small/52-meowth.txt +12 -0
- package/sprites/colorscripts/small/53-persian.txt +20 -0
- package/sprites/colorscripts/small/54-psyduck.txt +12 -0
- package/sprites/colorscripts/small/55-golduck.txt +17 -0
- package/sprites/colorscripts/small/56-mankey.txt +11 -0
- package/sprites/colorscripts/small/57-primeape.txt +13 -0
- package/sprites/colorscripts/small/58-growlithe.txt +12 -0
- package/sprites/colorscripts/small/59-arcanine.txt +20 -0
- package/sprites/colorscripts/small/6-charizard.txt +21 -0
- package/sprites/colorscripts/small/60-poliwag.txt +9 -0
- package/sprites/colorscripts/small/61-poliwhirl.txt +11 -0
- package/sprites/colorscripts/small/62-poliwrath.txt +17 -0
- package/sprites/colorscripts/small/63-abra.txt +12 -0
- package/sprites/colorscripts/small/64-kadabra.txt +14 -0
- package/sprites/colorscripts/small/65-alakazam.txt +19 -0
- package/sprites/colorscripts/small/66-machop.txt +11 -0
- package/sprites/colorscripts/small/67-machoke.txt +12 -0
- package/sprites/colorscripts/small/68-machamp.txt +19 -0
- package/sprites/colorscripts/small/69-bellsprout.txt +9 -0
- package/sprites/colorscripts/small/7-squirtle.txt +10 -0
- package/sprites/colorscripts/small/70-weepinbell.txt +11 -0
- package/sprites/colorscripts/small/71-victreebel.txt +17 -0
- package/sprites/colorscripts/small/72-tentacool.txt +12 -0
- package/sprites/colorscripts/small/73-tentacruel.txt +20 -0
- package/sprites/colorscripts/small/74-geodude.txt +9 -0
- package/sprites/colorscripts/small/75-graveler.txt +12 -0
- package/sprites/colorscripts/small/76-golem.txt +18 -0
- package/sprites/colorscripts/small/77-ponyta.txt +13 -0
- package/sprites/colorscripts/small/78-rapidash.txt +18 -0
- package/sprites/colorscripts/small/79-slowpoke.txt +12 -0
- package/sprites/colorscripts/small/8-wartortle.txt +12 -0
- package/sprites/colorscripts/small/80-slowbro.txt +18 -0
- package/sprites/colorscripts/small/81-magnemite.txt +9 -0
- package/sprites/colorscripts/small/82-magneton.txt +18 -0
- package/sprites/colorscripts/small/83-farfetchd.txt +12 -0
- package/sprites/colorscripts/small/84-doduo.txt +10 -0
- package/sprites/colorscripts/small/85-dodrio.txt +17 -0
- package/sprites/colorscripts/small/86-seel.txt +13 -0
- package/sprites/colorscripts/small/87-dewgong.txt +20 -0
- package/sprites/colorscripts/small/88-grimer.txt +10 -0
- package/sprites/colorscripts/small/89-muk.txt +14 -0
- package/sprites/colorscripts/small/9-blastoise.txt +20 -0
- package/sprites/colorscripts/small/90-shellder.txt +10 -0
- package/sprites/colorscripts/small/91-cloyster.txt +18 -0
- package/sprites/colorscripts/small/92-gastly.txt +12 -0
- package/sprites/colorscripts/small/93-haunter.txt +14 -0
- package/sprites/colorscripts/small/94-gengar.txt +19 -0
- package/sprites/colorscripts/small/95-onix.txt +22 -0
- package/sprites/colorscripts/small/96-drowzee.txt +12 -0
- package/sprites/colorscripts/small/97-hypno.txt +19 -0
- package/sprites/colorscripts/small/98-krabby.txt +12 -0
- package/sprites/colorscripts/small/99-kingler.txt +20 -0
- package/src/engine/constants.ts +121 -0
- package/src/engine/encounter-pool.ts +71 -0
- package/src/engine/encounters.ts +308 -0
- package/src/engine/evolution-data.ts +535 -0
- package/src/engine/evolution.ts +310 -0
- package/src/engine/pokemon-data.ts +1838 -0
- package/src/engine/reactions.ts +877 -0
- package/src/engine/starter-pool.ts +47 -0
- package/src/engine/stats.ts +97 -0
- package/src/engine/types.ts +312 -0
- package/src/engine/xp.ts +135 -0
- package/src/gamification/achievements.ts +204 -0
- package/src/gamification/legendary-quests.ts +265 -0
- package/src/gamification/milestones.ts +86 -0
- package/src/hooks/award-xp.ts +131 -0
- package/src/hooks/increment-counter.ts +27 -0
- package/src/server/index.ts +78 -0
- package/src/server/instructions.ts +194 -0
- package/src/server/tools/achievements.ts +118 -0
- package/src/server/tools/catch.ts +295 -0
- package/src/server/tools/display-helpers.ts +35 -0
- package/src/server/tools/evolve.ts +236 -0
- package/src/server/tools/legendary.ts +78 -0
- package/src/server/tools/party.ts +251 -0
- package/src/server/tools/pet.ts +124 -0
- package/src/server/tools/pokedex.ts +286 -0
- package/src/server/tools/rename.ts +63 -0
- package/src/server/tools/show.ts +136 -0
- package/src/server/tools/starter.ts +175 -0
- package/src/server/tools/stats.ts +123 -0
- package/src/server/tools/visibility.ts +65 -0
- package/src/sprites/index.ts +45 -0
- package/src/state/io.ts +91 -0
- package/src/state/schemas.ts +131 -0
- package/src/state/state-manager.ts +321 -0
- package/statusline/buddy-status.sh +233 -0
- package/tsconfig.json +37 -0
package/cli/install.ts
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claudemon CLI Installer.
|
|
3
|
+
* Registers MCP server, hooks, and skill into Claude Code.
|
|
4
|
+
*
|
|
5
|
+
* Usage: bun run cli/install.ts
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { mkdir, copyFile, chmod, access } from "node:fs/promises";
|
|
9
|
+
import { constants as fsConstants } from "node:fs";
|
|
10
|
+
|
|
11
|
+
import type { ClaudeConfig, ClaudeSettings, HookCommand, HookMatcher } from "./shared.js";
|
|
12
|
+
import {
|
|
13
|
+
ok,
|
|
14
|
+
fail,
|
|
15
|
+
readJson,
|
|
16
|
+
writeJson,
|
|
17
|
+
CLAUDE_DIR,
|
|
18
|
+
CLAUDE_CONFIG,
|
|
19
|
+
CLAUDE_SETTINGS,
|
|
20
|
+
STATE_DIR,
|
|
21
|
+
SKILL_SRC,
|
|
22
|
+
SKILL_DEST_DIR,
|
|
23
|
+
SKILL_DEST,
|
|
24
|
+
HOOK_SCRIPT,
|
|
25
|
+
STOP_HOOK_SCRIPT,
|
|
26
|
+
USER_PROMPT_HOOK_SCRIPT,
|
|
27
|
+
STATUSLINE_SCRIPT,
|
|
28
|
+
SERVER_ENTRY,
|
|
29
|
+
getBunPath,
|
|
30
|
+
} from "./shared.js";
|
|
31
|
+
|
|
32
|
+
// ── Step 1: Check Prerequisites ──────────────────────────────
|
|
33
|
+
|
|
34
|
+
async function checkPrerequisites(): Promise<boolean> {
|
|
35
|
+
let allGood = true;
|
|
36
|
+
|
|
37
|
+
// Check bun (sanity)
|
|
38
|
+
try {
|
|
39
|
+
const proc = Bun.spawn(["bun", "--version"], { stdout: "pipe", stderr: "pipe" });
|
|
40
|
+
const output = await new Response(proc.stdout).text();
|
|
41
|
+
await proc.exited;
|
|
42
|
+
ok(`Bun runtime: v${output.trim()}`);
|
|
43
|
+
} catch {
|
|
44
|
+
fail("Bun runtime not found. Install from https://bun.sh");
|
|
45
|
+
allGood = false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Check Claude Code directory
|
|
49
|
+
try {
|
|
50
|
+
await access(CLAUDE_DIR, fsConstants.F_OK);
|
|
51
|
+
ok("Claude Code directory: ~/.claude/");
|
|
52
|
+
} catch {
|
|
53
|
+
fail("Claude Code not found. Install Claude Code first: https://claude.ai/download");
|
|
54
|
+
console.error(" Expected directory: ~/.claude/");
|
|
55
|
+
allGood = false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return allGood;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ── Step 2: Create State Directory ───────────────────────────
|
|
62
|
+
|
|
63
|
+
async function createStateDirectory(): Promise<void> {
|
|
64
|
+
await mkdir(STATE_DIR, { recursive: true });
|
|
65
|
+
ok(`State directory: ${STATE_DIR}/`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ── Step 3: Register MCP Server ──────────────────────────────
|
|
69
|
+
|
|
70
|
+
async function registerMcpServer(): Promise<void> {
|
|
71
|
+
const existing = await readJson<ClaudeConfig>(CLAUDE_CONFIG);
|
|
72
|
+
const config: ClaudeConfig = existing ?? {};
|
|
73
|
+
|
|
74
|
+
if (!config.mcpServers) {
|
|
75
|
+
config.mcpServers = {};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
config.mcpServers["claudemon"] = {
|
|
79
|
+
command: getBunPath(),
|
|
80
|
+
args: ["run", SERVER_ENTRY],
|
|
81
|
+
env: {},
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
await writeJson(CLAUDE_CONFIG, config);
|
|
85
|
+
ok("MCP server: registered in ~/.claude.json");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ── Step 4: Install Hooks ────────────────────────────────────
|
|
89
|
+
|
|
90
|
+
async function installHooks(): Promise<void> {
|
|
91
|
+
const existing = await readJson<ClaudeSettings>(CLAUDE_SETTINGS);
|
|
92
|
+
const settings: ClaudeSettings = existing ?? {};
|
|
93
|
+
|
|
94
|
+
if (!settings.hooks) {
|
|
95
|
+
settings.hooks = {};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const ourHook: HookCommand = {
|
|
99
|
+
type: "command",
|
|
100
|
+
command: HOOK_SCRIPT,
|
|
101
|
+
timeout: 5000,
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const ourMatcher: HookMatcher = {
|
|
105
|
+
matcher: "Bash|Write|Edit|Read|Grep|Glob",
|
|
106
|
+
hooks: [ourHook],
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// Check if PostToolUse already has our matcher
|
|
110
|
+
const postToolUse = settings.hooks["PostToolUse"];
|
|
111
|
+
if (postToolUse) {
|
|
112
|
+
// Remove any existing claudemon matcher (idempotent reinstall)
|
|
113
|
+
const filtered = postToolUse.filter(
|
|
114
|
+
(m) => !m.hooks.some((h) => h.command.includes("post-tool-use.sh")),
|
|
115
|
+
);
|
|
116
|
+
filtered.push(ourMatcher);
|
|
117
|
+
settings.hooks["PostToolUse"] = filtered;
|
|
118
|
+
} else {
|
|
119
|
+
settings.hooks["PostToolUse"] = [ourMatcher];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ── Stop hook (extracts buddy comments from responses) ────
|
|
123
|
+
const stopHook: HookCommand = {
|
|
124
|
+
type: "command",
|
|
125
|
+
command: STOP_HOOK_SCRIPT,
|
|
126
|
+
timeout: 3000,
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const stopEntry: HookMatcher = {
|
|
130
|
+
hooks: [stopHook],
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const stopHooks = settings.hooks["Stop"];
|
|
134
|
+
if (stopHooks) {
|
|
135
|
+
const filtered = stopHooks.filter((m) => !m.hooks.some((h) => h.command.includes("stop.sh")));
|
|
136
|
+
filtered.push(stopEntry);
|
|
137
|
+
settings.hooks["Stop"] = filtered;
|
|
138
|
+
} else {
|
|
139
|
+
settings.hooks["Stop"] = [stopEntry];
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ── UserPromptSubmit hook (reacts to Pokemon name mentions) ─
|
|
143
|
+
const userPromptHook: HookCommand = {
|
|
144
|
+
type: "command",
|
|
145
|
+
command: USER_PROMPT_HOOK_SCRIPT,
|
|
146
|
+
timeout: 3000,
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const userPromptEntry: HookMatcher = {
|
|
150
|
+
hooks: [userPromptHook],
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const userPromptHooks = settings.hooks["UserPromptSubmit"];
|
|
154
|
+
if (userPromptHooks) {
|
|
155
|
+
const filtered = userPromptHooks.filter(
|
|
156
|
+
(m) => !m.hooks.some((h) => h.command.includes("user-prompt-submit.sh")),
|
|
157
|
+
);
|
|
158
|
+
filtered.push(userPromptEntry);
|
|
159
|
+
settings.hooks["UserPromptSubmit"] = filtered;
|
|
160
|
+
} else {
|
|
161
|
+
settings.hooks["UserPromptSubmit"] = [userPromptEntry];
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
await writeJson(CLAUDE_SETTINGS, settings);
|
|
165
|
+
ok("Hooks: PostToolUse, Stop, UserPromptSubmit configured in ~/.claude/settings.json");
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// ── Step 5: Register Status Line ─────────────────────────────
|
|
169
|
+
|
|
170
|
+
async function registerStatusLine(): Promise<void> {
|
|
171
|
+
const existing = await readJson<ClaudeSettings>(CLAUDE_SETTINGS);
|
|
172
|
+
const settings: ClaudeSettings = existing ?? {};
|
|
173
|
+
|
|
174
|
+
// Merge status line config without overwriting other settings
|
|
175
|
+
settings.statusLine = {
|
|
176
|
+
type: "command",
|
|
177
|
+
command: STATUSLINE_SCRIPT,
|
|
178
|
+
refreshInterval: 1,
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
await writeJson(CLAUDE_SETTINGS, settings);
|
|
182
|
+
ok("Status line: registered in ~/.claude/settings.json");
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ── Step 6: Install Skill ─────────────────────────────────────
|
|
186
|
+
|
|
187
|
+
async function installSkill(): Promise<void> {
|
|
188
|
+
await mkdir(SKILL_DEST_DIR, { recursive: true });
|
|
189
|
+
await copyFile(SKILL_SRC, SKILL_DEST);
|
|
190
|
+
ok("Skill: /buddy command installed to ~/.claude/skills/buddy/");
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// ── Step 7: Set Script Permissions ───────────────────────────
|
|
194
|
+
|
|
195
|
+
async function setScriptPermissions(): Promise<void> {
|
|
196
|
+
await chmod(HOOK_SCRIPT, 0o755);
|
|
197
|
+
ok("Hook script: post-tool-use.sh set executable");
|
|
198
|
+
await chmod(STOP_HOOK_SCRIPT, 0o755);
|
|
199
|
+
ok("Hook script: stop.sh set executable");
|
|
200
|
+
await chmod(USER_PROMPT_HOOK_SCRIPT, 0o755);
|
|
201
|
+
ok("Hook script: user-prompt-submit.sh set executable");
|
|
202
|
+
await chmod(STATUSLINE_SCRIPT, 0o755);
|
|
203
|
+
ok("Status line script: set executable");
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// ── Main ─────────────────────────────────────────────────────
|
|
207
|
+
|
|
208
|
+
export async function install(): Promise<void> {
|
|
209
|
+
console.log("");
|
|
210
|
+
console.log("Claudemon Installer");
|
|
211
|
+
console.log("===================");
|
|
212
|
+
console.log("");
|
|
213
|
+
console.log("Checking prerequisites...");
|
|
214
|
+
|
|
215
|
+
const prereqOk = await checkPrerequisites();
|
|
216
|
+
if (!prereqOk) {
|
|
217
|
+
console.log("");
|
|
218
|
+
fail("Prerequisites not met. Fix the issues above and try again.");
|
|
219
|
+
process.exit(1);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
console.log("");
|
|
223
|
+
console.log("Installing...");
|
|
224
|
+
|
|
225
|
+
await createStateDirectory();
|
|
226
|
+
await registerMcpServer();
|
|
227
|
+
await installHooks();
|
|
228
|
+
await registerStatusLine();
|
|
229
|
+
await installSkill();
|
|
230
|
+
await setScriptPermissions();
|
|
231
|
+
|
|
232
|
+
console.log("");
|
|
233
|
+
console.log("\u2713 Claudemon installed successfully!");
|
|
234
|
+
console.log("");
|
|
235
|
+
console.log("Next steps:");
|
|
236
|
+
console.log(" 1. Start a new Claude Code session");
|
|
237
|
+
console.log(" 2. Type /buddy starter to pick your first Pokemon!");
|
|
238
|
+
console.log("");
|
|
239
|
+
console.log("Run 'bun run cli/doctor.ts' to verify installation.");
|
|
240
|
+
console.log("");
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Run if executed directly
|
|
244
|
+
install().catch((err: unknown) => {
|
|
245
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
246
|
+
console.error(`\nInstallation failed: ${message}`);
|
|
247
|
+
process.exit(1);
|
|
248
|
+
});
|
package/cli/shared.ts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types and utilities for Claudemon CLI commands.
|
|
3
|
+
* Centralizes Claude Code config interfaces and common helpers.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { resolve, dirname } from "node:path";
|
|
7
|
+
|
|
8
|
+
// ── Config shape interfaces ──────────────────────────────────
|
|
9
|
+
|
|
10
|
+
export interface McpServerEntry {
|
|
11
|
+
command: string;
|
|
12
|
+
args: string[];
|
|
13
|
+
env: Record<string, string>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ClaudeConfig {
|
|
17
|
+
mcpServers?: Record<string, McpServerEntry>;
|
|
18
|
+
[key: string]: unknown;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface HookCommand {
|
|
22
|
+
type: "command";
|
|
23
|
+
command: string;
|
|
24
|
+
timeout: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface HookMatcher {
|
|
28
|
+
matcher?: string;
|
|
29
|
+
hooks: HookCommand[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface StatusLineConfig {
|
|
33
|
+
type: "command";
|
|
34
|
+
command: string;
|
|
35
|
+
refreshInterval: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface ClaudeSettings {
|
|
39
|
+
hooks?: Record<string, HookMatcher[]>;
|
|
40
|
+
statusLine?: StatusLineConfig;
|
|
41
|
+
[key: string]: unknown;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ── Constants ────────────────────────────────────────────────
|
|
45
|
+
|
|
46
|
+
const HOME = process.env["HOME"];
|
|
47
|
+
if (!HOME) {
|
|
48
|
+
console.error("Error: HOME environment variable is not set.");
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const CLI_HOME = HOME;
|
|
53
|
+
export const PROJECT_DIR = resolve(dirname(import.meta.dir));
|
|
54
|
+
export const CLAUDE_DIR = `${HOME}/.claude`;
|
|
55
|
+
export const CLAUDE_CONFIG = `${HOME}/.claude.json`;
|
|
56
|
+
export const CLAUDE_SETTINGS = `${CLAUDE_DIR}/settings.json`;
|
|
57
|
+
export const STATE_DIR = `${HOME}/.claudemon`;
|
|
58
|
+
export const SKILL_SRC = `${PROJECT_DIR}/skills/buddy/SKILL.md`;
|
|
59
|
+
export const SKILL_DEST_DIR = `${CLAUDE_DIR}/skills/buddy`;
|
|
60
|
+
export const SKILL_DEST = `${SKILL_DEST_DIR}/SKILL.md`;
|
|
61
|
+
export const HOOK_SCRIPT = `${PROJECT_DIR}/hooks/post-tool-use.sh`;
|
|
62
|
+
export const STOP_HOOK_SCRIPT = `${PROJECT_DIR}/hooks/stop.sh`;
|
|
63
|
+
export const USER_PROMPT_HOOK_SCRIPT = `${PROJECT_DIR}/hooks/user-prompt-submit.sh`;
|
|
64
|
+
export const STATUSLINE_SCRIPT = `${PROJECT_DIR}/statusline/buddy-status.sh`;
|
|
65
|
+
export const SERVER_ENTRY = `${PROJECT_DIR}/src/server/index.ts`;
|
|
66
|
+
|
|
67
|
+
/** Resolve full path to bun binary (Claude Code may not have bun in PATH) */
|
|
68
|
+
export function getBunPath(): string {
|
|
69
|
+
const { existsSync } = require("node:fs") as { existsSync: (p: string) => boolean };
|
|
70
|
+
const candidates = [`${HOME}/.bun/bin/bun`, "/usr/local/bin/bun", "/usr/bin/bun"];
|
|
71
|
+
for (const p of candidates) {
|
|
72
|
+
if (existsSync(p)) return p;
|
|
73
|
+
}
|
|
74
|
+
return "bun";
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ── Helpers ──────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
export function ok(msg: string): void {
|
|
80
|
+
console.log(` \u2713 ${msg}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function fail(msg: string): void {
|
|
84
|
+
console.error(` \u2717 ${msg}`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function info(msg: string): void {
|
|
88
|
+
console.log(` - ${msg}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export async function readJson<T>(path: string): Promise<T | null> {
|
|
92
|
+
const file = Bun.file(path);
|
|
93
|
+
if (!(await file.exists())) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
const text = await file.text();
|
|
97
|
+
return JSON.parse(text) as T;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export async function writeJson(path: string, data: unknown): Promise<void> {
|
|
101
|
+
await Bun.write(path, JSON.stringify(data, null, 2) + "\n");
|
|
102
|
+
}
|
package/cli/uninstall.ts
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claudemon CLI Uninstaller.
|
|
3
|
+
* Cleanly removes MCP server, hooks, and skill from Claude Code.
|
|
4
|
+
*
|
|
5
|
+
* Usage: bun run cli/uninstall.ts
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { rm } from "node:fs/promises";
|
|
9
|
+
|
|
10
|
+
import type { ClaudeConfig, ClaudeSettings } from "./shared.js";
|
|
11
|
+
import {
|
|
12
|
+
ok,
|
|
13
|
+
info,
|
|
14
|
+
readJson,
|
|
15
|
+
writeJson,
|
|
16
|
+
CLAUDE_CONFIG,
|
|
17
|
+
CLAUDE_SETTINGS,
|
|
18
|
+
STATE_DIR,
|
|
19
|
+
SKILL_DEST_DIR,
|
|
20
|
+
} from "./shared.js";
|
|
21
|
+
|
|
22
|
+
// ── Step 1: Remove MCP Server Registration ───────────────────
|
|
23
|
+
|
|
24
|
+
async function removeMcpServer(): Promise<void> {
|
|
25
|
+
const config = await readJson<ClaudeConfig>(CLAUDE_CONFIG);
|
|
26
|
+
if (!config) {
|
|
27
|
+
info("MCP server: ~/.claude.json not found (nothing to remove)");
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!config.mcpServers || !config.mcpServers["claudemon"]) {
|
|
32
|
+
info("MCP server: not registered (nothing to remove)");
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
delete config.mcpServers["claudemon"];
|
|
37
|
+
|
|
38
|
+
// Clean up empty mcpServers object
|
|
39
|
+
if (Object.keys(config.mcpServers).length === 0) {
|
|
40
|
+
delete config.mcpServers;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
await writeJson(CLAUDE_CONFIG, config);
|
|
44
|
+
ok("MCP server: removed from ~/.claude.json");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ── Step 2: Remove Hooks ─────────────────────────────────────
|
|
48
|
+
|
|
49
|
+
async function removeHooks(): Promise<void> {
|
|
50
|
+
const settings = await readJson<ClaudeSettings>(CLAUDE_SETTINGS);
|
|
51
|
+
if (!settings) {
|
|
52
|
+
info("Hooks: ~/.claude/settings.json not found (nothing to remove)");
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!settings.hooks) {
|
|
57
|
+
info("Hooks: no hooks found (nothing to remove)");
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Remove PostToolUse hook
|
|
62
|
+
if (settings.hooks["PostToolUse"]) {
|
|
63
|
+
const filtered = settings.hooks["PostToolUse"].filter(
|
|
64
|
+
(m) => !m.hooks.some((h) => h.command.includes("post-tool-use.sh")),
|
|
65
|
+
);
|
|
66
|
+
if (filtered.length === 0) {
|
|
67
|
+
delete settings.hooks["PostToolUse"];
|
|
68
|
+
} else {
|
|
69
|
+
settings.hooks["PostToolUse"] = filtered;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Remove Stop hook
|
|
74
|
+
if (settings.hooks["Stop"]) {
|
|
75
|
+
const filtered = settings.hooks["Stop"].filter(
|
|
76
|
+
(m) => !m.hooks.some((h) => h.command.includes("stop.sh")),
|
|
77
|
+
);
|
|
78
|
+
if (filtered.length === 0) {
|
|
79
|
+
delete settings.hooks["Stop"];
|
|
80
|
+
} else {
|
|
81
|
+
settings.hooks["Stop"] = filtered;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Remove UserPromptSubmit hook
|
|
86
|
+
if (settings.hooks["UserPromptSubmit"]) {
|
|
87
|
+
const filtered = settings.hooks["UserPromptSubmit"].filter(
|
|
88
|
+
(m) => !m.hooks.some((h) => h.command.includes("user-prompt-submit.sh")),
|
|
89
|
+
);
|
|
90
|
+
if (filtered.length === 0) {
|
|
91
|
+
delete settings.hooks["UserPromptSubmit"];
|
|
92
|
+
} else {
|
|
93
|
+
settings.hooks["UserPromptSubmit"] = filtered;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Clean up empty hooks object
|
|
98
|
+
if (Object.keys(settings.hooks).length === 0) {
|
|
99
|
+
delete settings.hooks;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
await writeJson(CLAUDE_SETTINGS, settings);
|
|
103
|
+
ok("Hooks: removed PostToolUse, Stop, UserPromptSubmit entries from ~/.claude/settings.json");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ── Step 3: Remove Skill ─────────────────────────────────────
|
|
107
|
+
|
|
108
|
+
async function removeSkill(): Promise<void> {
|
|
109
|
+
const file = Bun.file(`${SKILL_DEST_DIR}/SKILL.md`);
|
|
110
|
+
if (!(await file.exists())) {
|
|
111
|
+
info("Skill: ~/.claude/skills/buddy/ not found (nothing to remove)");
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
await rm(SKILL_DEST_DIR, { recursive: true, force: true });
|
|
116
|
+
ok("Skill: removed ~/.claude/skills/buddy/");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ── Step 4: Preserve State Data ──────────────────────────────
|
|
120
|
+
|
|
121
|
+
async function preserveStateData(): Promise<void> {
|
|
122
|
+
const stateFile = Bun.file(`${STATE_DIR}/state.json`);
|
|
123
|
+
if (await stateFile.exists()) {
|
|
124
|
+
info(`Your Pokemon data is preserved at ${STATE_DIR}/. Delete manually to remove.`);
|
|
125
|
+
} else {
|
|
126
|
+
info(`State directory: ${STATE_DIR}/ (kept, no state file found)`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ── Main ─────────────────────────────────────────────────────
|
|
131
|
+
|
|
132
|
+
export async function uninstall(): Promise<void> {
|
|
133
|
+
console.log("");
|
|
134
|
+
console.log("Claudemon Uninstaller");
|
|
135
|
+
console.log("=====================");
|
|
136
|
+
console.log("");
|
|
137
|
+
console.log("Removing Claudemon from Claude Code...");
|
|
138
|
+
console.log("");
|
|
139
|
+
|
|
140
|
+
await removeMcpServer();
|
|
141
|
+
await removeHooks();
|
|
142
|
+
await removeSkill();
|
|
143
|
+
await preserveStateData();
|
|
144
|
+
|
|
145
|
+
console.log("");
|
|
146
|
+
console.log("\u2713 Claudemon uninstalled successfully.");
|
|
147
|
+
console.log("");
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Run if executed directly
|
|
151
|
+
uninstall().catch((err: unknown) => {
|
|
152
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
153
|
+
console.error(`\nUninstall failed: ${message}`);
|
|
154
|
+
process.exit(1);
|
|
155
|
+
});
|