@umang-boss/claudemon 1.1.2 → 1.1.4

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/cli/shared.ts CHANGED
@@ -5,6 +5,7 @@
5
5
 
6
6
  import { resolve, dirname } from "node:path";
7
7
  import { fileURLToPath } from "node:url";
8
+ import { existsSync } from "node:fs";
8
9
 
9
10
  // ── Config shape interfaces ──────────────────────────────────
10
11
 
@@ -53,7 +54,13 @@ if (!HOME) {
53
54
  export const CLI_HOME = HOME;
54
55
  const __filename = fileURLToPath(import.meta.url);
55
56
  const __dirname = dirname(__filename);
56
- export const PROJECT_DIR = resolve(dirname(__dirname));
57
+
58
+ // Resolve project root — works from both src (cli/) and compiled (dist/cli/)
59
+ let projectDir = resolve(dirname(__dirname));
60
+ if (projectDir.endsWith("/dist") || projectDir.endsWith("\\dist")) {
61
+ projectDir = resolve(projectDir, "..");
62
+ }
63
+ export const PROJECT_DIR = projectDir;
57
64
  export const CLAUDE_DIR = `${HOME}/.claude`;
58
65
  export const CLAUDE_CONFIG = `${HOME}/.claude.json`;
59
66
  export const CLAUDE_SETTINGS = `${CLAUDE_DIR}/settings.json`;
@@ -70,8 +77,6 @@ export const SERVER_ENTRY_JS = `${PROJECT_DIR}/dist/src/server/index.js`;
70
77
 
71
78
  /** Find the best runtime and server entry — prefers bun (fast), falls back to node (compiled JS) */
72
79
  export function getRuntime(): { command: string; serverEntry: string } {
73
- const { existsSync } = require("node:fs") as { existsSync: (p: string) => boolean };
74
-
75
80
  // Check for bun (can run .ts directly)
76
81
  const bunCandidates = [`${HOME}/.bun/bin/bun`, "/usr/local/bin/bun", "/usr/bin/bun"];
77
82
  for (const p of bunCandidates) {
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { resolve, dirname } from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
+ import { existsSync } from "node:fs";
7
8
  // ── Constants ────────────────────────────────────────────────
8
9
  const HOME = process.env["HOME"];
9
10
  if (!HOME) {
@@ -13,7 +14,12 @@ if (!HOME) {
13
14
  export const CLI_HOME = HOME;
14
15
  const __filename = fileURLToPath(import.meta.url);
15
16
  const __dirname = dirname(__filename);
16
- export const PROJECT_DIR = resolve(dirname(__dirname));
17
+ // Resolve project root — works from both src (cli/) and compiled (dist/cli/)
18
+ let projectDir = resolve(dirname(__dirname));
19
+ if (projectDir.endsWith("/dist") || projectDir.endsWith("\\dist")) {
20
+ projectDir = resolve(projectDir, "..");
21
+ }
22
+ export const PROJECT_DIR = projectDir;
17
23
  export const CLAUDE_DIR = `${HOME}/.claude`;
18
24
  export const CLAUDE_CONFIG = `${HOME}/.claude.json`;
19
25
  export const CLAUDE_SETTINGS = `${CLAUDE_DIR}/settings.json`;
@@ -29,7 +35,6 @@ export const SERVER_ENTRY_TS = `${PROJECT_DIR}/src/server/index.ts`;
29
35
  export const SERVER_ENTRY_JS = `${PROJECT_DIR}/dist/src/server/index.js`;
30
36
  /** Find the best runtime and server entry — prefers bun (fast), falls back to node (compiled JS) */
31
37
  export function getRuntime() {
32
- const { existsSync } = require("node:fs");
33
38
  // Check for bun (can run .ts directly)
34
39
  const bunCandidates = [`${HOME}/.bun/bin/bun`, "/usr/local/bin/bun", "/usr/bin/bun"];
35
40
  for (const p of bunCandidates) {
@@ -30,19 +30,56 @@ function seededShuffle(array, seed) {
30
30
  }
31
31
  return result;
32
32
  }
33
- /** Get today's date string in YYYY-MM-DD for daily seed. */
34
- function todayString() {
35
- const now = new Date();
36
- const year = now.getFullYear();
37
- const month = String(now.getMonth() + 1).padStart(2, "0");
38
- const day = String(now.getDate()).padStart(2, "0");
39
- return `${year}-${month}-${day}`;
33
+ /** Path to stored starter options */
34
+ function getStarterOptionsPath() {
35
+ return `${process.env["HOME"] ?? "~"}/.claudemon/starter-options.json`;
40
36
  }
41
- /** Generate today's 3 starter options (deterministic per day). */
42
- function getDailyStarters() {
43
- const seed = hashString(`claudemon-starter-${todayString()}`);
37
+ /** Get or generate 3 random starters. Stored for 24 hours so user sees same 3. */
38
+ async function getStarters() {
39
+ const optionsPath = getStarterOptionsPath();
40
+ // Try to load previously generated options (valid for 24 hours)
41
+ try {
42
+ const { readFile, stat, access: fsAccess } = await import("node:fs/promises");
43
+ const { constants } = await import("node:fs");
44
+ await fsAccess(optionsPath, constants.F_OK);
45
+ const fileStat = await stat(optionsPath);
46
+ const ageMs = Date.now() - fileStat.mtimeMs;
47
+ const ONE_DAY = 24 * 60 * 60 * 1000;
48
+ if (ageMs < ONE_DAY) {
49
+ const text = await readFile(optionsPath, "utf-8");
50
+ const saved = JSON.parse(text);
51
+ if (Array.isArray(saved) && saved.length === 3) {
52
+ return saved;
53
+ }
54
+ }
55
+ }
56
+ catch {
57
+ // No saved options or expired — generate new ones
58
+ }
59
+ // Generate fresh random 3
60
+ const seed = hashString(`claudemon-${Date.now()}-${Math.random()}`);
44
61
  const shuffled = seededShuffle(STARTER_POOL, seed);
45
- return [shuffled[0], shuffled[1], shuffled[2]];
62
+ const starters = [shuffled[0], shuffled[1], shuffled[2]];
63
+ // Save for consistency until they pick
64
+ try {
65
+ const { writeFile, mkdir } = await import("node:fs/promises");
66
+ await mkdir(`${process.env["HOME"] ?? "~"}/.claudemon`, { recursive: true });
67
+ await writeFile(optionsPath, JSON.stringify(starters), "utf-8");
68
+ }
69
+ catch {
70
+ // Non-critical — they'll just get new options next time
71
+ }
72
+ return starters;
73
+ }
74
+ /** Clear saved starter options (called after picking) */
75
+ async function clearStarterOptions() {
76
+ try {
77
+ const { unlink } = await import("node:fs/promises");
78
+ await unlink(getStarterOptionsPath());
79
+ }
80
+ catch {
81
+ // Ignore
82
+ }
46
83
  }
47
84
  /** Generate a UUID v4. */
48
85
  function generateUUID() {
@@ -65,7 +102,7 @@ export function registerStarterTool(server) {
65
102
  isError: true,
66
103
  };
67
104
  }
68
- const starterIds = getDailyStarters();
105
+ const starterIds = await getStarters();
69
106
  // No choice provided — show the 3 options
70
107
  if (params.choice === undefined) {
71
108
  const lines = ["Welcome to Claudemon! Choose your coding companion:", ""];
@@ -118,6 +155,7 @@ export function registerStarterTool(server) {
118
155
  const trainerName = "Trainer";
119
156
  await stateManager.initializePlayer(trainerId, trainerName, starter);
120
157
  await stateManager.writeStatus();
158
+ await clearStarterOptions();
121
159
  const lines = [];
122
160
  lines.push(`Congratulations! You chose **${species.name}**!`, "", ` Species: ${species.name} (#${String(species.id).padStart(3, "0")})`, ` Type: ${formatTypes(species.types)}`, ` Level: ${STARTER_LEVEL}`, ` Exp Group: ${species.expGroup}`, "", ` "${species.description}"`, "", `Your coding journey begins now. Write code, fix bugs, and watch ${species.name} grow!`, "", "Use buddy_show to see your Pokemon, or buddy_pet to bond with them.");
123
161
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umang-boss/claudemon",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "Pokemon Gen 1 coding companion for Claude Code — Gotta code 'em all!",
5
5
  "type": "module",
6
6
  "main": "dist/src/server/index.js",
@@ -36,20 +36,60 @@ function seededShuffle<T>(array: readonly T[], seed: number): T[] {
36
36
  return result;
37
37
  }
38
38
 
39
- /** Get today's date string in YYYY-MM-DD for daily seed. */
40
- function todayString(): string {
41
- const now = new Date();
42
- const year = now.getFullYear();
43
- const month = String(now.getMonth() + 1).padStart(2, "0");
44
- const day = String(now.getDate()).padStart(2, "0");
45
- return `${year}-${month}-${day}`;
39
+ /** Path to stored starter options */
40
+ function getStarterOptionsPath(): string {
41
+ return `${process.env["HOME"] ?? "~"}/.claudemon/starter-options.json`;
46
42
  }
47
43
 
48
- /** Generate today's 3 starter options (deterministic per day). */
49
- function getDailyStarters(): number[] {
50
- const seed = hashString(`claudemon-starter-${todayString()}`);
44
+ /** Get or generate 3 random starters. Stored for 24 hours so user sees same 3. */
45
+ async function getStarters(): Promise<number[]> {
46
+ const optionsPath = getStarterOptionsPath();
47
+
48
+ // Try to load previously generated options (valid for 24 hours)
49
+ try {
50
+ const { readFile, stat, access: fsAccess } = await import("node:fs/promises");
51
+ const { constants } = await import("node:fs");
52
+ await fsAccess(optionsPath, constants.F_OK);
53
+ const fileStat = await stat(optionsPath);
54
+ const ageMs = Date.now() - fileStat.mtimeMs;
55
+ const ONE_DAY = 24 * 60 * 60 * 1000;
56
+
57
+ if (ageMs < ONE_DAY) {
58
+ const text = await readFile(optionsPath, "utf-8");
59
+ const saved = JSON.parse(text) as number[];
60
+ if (Array.isArray(saved) && saved.length === 3) {
61
+ return saved;
62
+ }
63
+ }
64
+ } catch {
65
+ // No saved options or expired — generate new ones
66
+ }
67
+
68
+ // Generate fresh random 3
69
+ const seed = hashString(`claudemon-${Date.now()}-${Math.random()}`);
51
70
  const shuffled = seededShuffle(STARTER_POOL, seed);
52
- return [shuffled[0]!, shuffled[1]!, shuffled[2]!];
71
+ const starters = [shuffled[0]!, shuffled[1]!, shuffled[2]!];
72
+
73
+ // Save for consistency until they pick
74
+ try {
75
+ const { writeFile, mkdir } = await import("node:fs/promises");
76
+ await mkdir(`${process.env["HOME"] ?? "~"}/.claudemon`, { recursive: true });
77
+ await writeFile(optionsPath, JSON.stringify(starters), "utf-8");
78
+ } catch {
79
+ // Non-critical — they'll just get new options next time
80
+ }
81
+
82
+ return starters;
83
+ }
84
+
85
+ /** Clear saved starter options (called after picking) */
86
+ async function clearStarterOptions(): Promise<void> {
87
+ try {
88
+ const { unlink } = await import("node:fs/promises");
89
+ await unlink(getStarterOptionsPath());
90
+ } catch {
91
+ // Ignore
92
+ }
53
93
  }
54
94
 
55
95
  /** Generate a UUID v4. */
@@ -80,7 +120,7 @@ export function registerStarterTool(server: McpServer): void {
80
120
  };
81
121
  }
82
122
 
83
- const starterIds = getDailyStarters();
123
+ const starterIds = await getStarters();
84
124
 
85
125
  // No choice provided — show the 3 options
86
126
  if (params.choice === undefined) {
@@ -149,6 +189,7 @@ export function registerStarterTool(server: McpServer): void {
149
189
  const trainerName = "Trainer";
150
190
  await stateManager.initializePlayer(trainerId, trainerName, starter);
151
191
  await stateManager.writeStatus();
192
+ await clearStarterOptions();
152
193
 
153
194
  const lines: string[] = [];
154
195