@umang-boss/claudemon 1.3.0 → 1.4.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 (39) hide show
  1. package/dist/src/engine/constants.js +9 -3
  2. package/dist/src/engine/encounters.js +50 -10
  3. package/dist/src/engine/mood.js +187 -0
  4. package/dist/src/engine/types.js +2 -0
  5. package/dist/src/gamification/achievements.js +3 -3
  6. package/dist/src/gamification/legendary-quests.js +4 -4
  7. package/dist/src/hooks/award-xp.js +60 -5
  8. package/dist/src/server/index.js +8 -0
  9. package/dist/src/server/instructions.js +23 -0
  10. package/dist/src/server/tools/catch.js +3 -0
  11. package/dist/src/server/tools/evolve.js +3 -0
  12. package/dist/src/server/tools/feed.js +120 -0
  13. package/dist/src/server/tools/play.js +310 -0
  14. package/dist/src/server/tools/settings.js +80 -0
  15. package/dist/src/server/tools/show.js +5 -0
  16. package/dist/src/server/tools/train.js +144 -0
  17. package/dist/src/state/schemas.js +17 -1
  18. package/dist/src/state/state-manager.js +22 -6
  19. package/package.json +1 -1
  20. package/skills/buddy/SKILL.md +15 -0
  21. package/src/engine/constants.ts +12 -3
  22. package/src/engine/encounters.ts +65 -9
  23. package/src/engine/mood.ts +220 -0
  24. package/src/engine/types.ts +23 -0
  25. package/src/gamification/achievements.ts +3 -3
  26. package/src/gamification/legendary-quests.ts +4 -4
  27. package/src/hooks/award-xp.ts +82 -5
  28. package/src/server/index.ts +8 -0
  29. package/src/server/instructions.ts +25 -0
  30. package/src/server/tools/catch.ts +4 -0
  31. package/src/server/tools/evolve.ts +4 -0
  32. package/src/server/tools/feed.ts +145 -0
  33. package/src/server/tools/play.ts +378 -0
  34. package/src/server/tools/settings.ts +101 -0
  35. package/src/server/tools/show.ts +7 -0
  36. package/src/server/tools/train.ts +180 -0
  37. package/src/state/schemas.ts +19 -0
  38. package/src/state/state-manager.ts +25 -6
  39. package/statusline/buddy-status.sh +77 -62
@@ -9,6 +9,7 @@ import {
9
9
  BADGE_TYPES,
10
10
  CODING_STATS,
11
11
  EVENT_COUNTER_KEYS,
12
+ MOOD_TYPES,
12
13
  } from "../engine/types.js";
13
14
 
14
15
  // ---- Shared Primitives ----
@@ -69,6 +70,7 @@ export const BuddyConfigSchema = z.object({
69
70
  reactionCooldownMs: z.number().int().min(0).default(30_000),
70
71
  statusLineEnabled: z.boolean().default(true),
71
72
  bellEnabled: z.boolean().default(true),
73
+ encounterSpeed: z.enum(["fast", "normal", "slow"]).default("normal"),
72
74
  });
73
75
 
74
76
  // ---- Pokedex ----
@@ -93,6 +95,15 @@ export const UnlockedAchievementSchema = z.object({
93
95
  unlockedAt: z.string(),
94
96
  });
95
97
 
98
+ // ---- Pending Quiz ----
99
+
100
+ export const PendingQuizSchema = z.object({
101
+ type: z.enum(["type_matchup", "stat_compare", "evolution", "pokedex_trivia"]),
102
+ question: z.string(),
103
+ options: z.array(z.string()),
104
+ correctAnswer: z.number().int().min(1).max(4),
105
+ });
106
+
96
107
  // ---- Catch Condition ----
97
108
 
98
109
  export const CatchConditionSchema = z.object({
@@ -127,5 +138,13 @@ export const PlayerStateSchema = z.object({
127
138
  totalSessions: z.number().int().min(0).default(0),
128
139
  pendingEncounter: WildEncounterSchema.nullable().default(null),
129
140
  xpSinceLastEncounter: z.number().int().min(0).default(0),
141
+ recentToolTypes: z.array(z.string()).default([]),
142
+ lastEncounterTime: z.number().int().min(0).default(0),
143
+ mood: z.enum(MOOD_TYPES).default("neutral"),
144
+ moodSetAt: z.number().default(0),
145
+ lastFedAt: z.number().default(0),
146
+ lastTrainedAt: z.number().default(0),
147
+ lastPlayedAt: z.number().default(0),
148
+ pendingQuiz: PendingQuizSchema.nullable().default(null),
130
149
  });
131
150
 
@@ -13,6 +13,7 @@ import type {
13
13
  BuddyConfig,
14
14
  PokedexState,
15
15
  WildEncounter,
16
+ MoodType,
16
17
  } from "../engine/types.js";
17
18
  import { EVENT_COUNTER_KEYS } from "../engine/types.js";
18
19
  import { atomicWrite, safeRead, ensureDir, backupCorrupted, withLock } from "./io.js";
@@ -25,6 +26,7 @@ interface StatusPayload {
25
26
  xpPercent: number;
26
27
  speciesId: number;
27
28
  evolutionReady: boolean;
29
+ mood: MoodType;
28
30
  }
29
31
 
30
32
  /** Build a zeroed-out EventCounters record */
@@ -53,6 +55,7 @@ function defaultConfig(): BuddyConfig {
53
55
  reactionCooldownMs: 30_000,
54
56
  statusLineEnabled: true,
55
57
  bellEnabled: true,
58
+ encounterSpeed: "normal",
56
59
  };
57
60
  }
58
61
 
@@ -188,6 +191,14 @@ export class StateManager {
188
191
  totalSessions: 0,
189
192
  pendingEncounter: null,
190
193
  xpSinceLastEncounter: 0,
194
+ recentToolTypes: [],
195
+ lastEncounterTime: 0,
196
+ mood: "neutral",
197
+ moodSetAt: 0,
198
+ lastFedAt: 0,
199
+ lastTrainedAt: 0,
200
+ lastPlayedAt: 0,
201
+ pendingQuiz: null,
191
202
  };
192
203
 
193
204
  this.state = state;
@@ -210,19 +221,21 @@ export class StateManager {
210
221
  await this.save();
211
222
  }
212
223
 
213
- /** Update daily streak based on today vs lastActiveDate, then save */
224
+ /**
225
+ * Update daily streak with weekend grace period.
226
+ * Allows up to 2 days off without breaking the streak (covers weekends).
227
+ * Streak counts "coding days" not "consecutive calendar days".
228
+ */
214
229
  async updateStreak(): Promise<void> {
215
230
  const state = this.getState();
216
231
  const today = todayDateString();
217
232
  const { streak } = state;
218
233
 
219
234
  if (streak.lastActiveDate === today) {
220
- // Already recorded today, nothing to do
221
235
  return;
222
236
  }
223
237
 
224
238
  if (streak.lastActiveDate === null) {
225
- // First ever activity
226
239
  streak.currentStreak = 1;
227
240
  streak.totalDaysActive = 1;
228
241
  } else {
@@ -231,11 +244,14 @@ export class StateManager {
231
244
  const diffMs = now.getTime() - last.getTime();
232
245
  const diffDays = Math.round(diffMs / (1000 * 60 * 60 * 24));
233
246
 
234
- if (diffDays === 1) {
235
- // Consecutive day
247
+ // Grace period: up to 2 days off (covers weekends)
248
+ // 1 day gap = next day (consecutive) ✓
249
+ // 2 day gap = skipped 1 day (e.g., Friday → Sunday) ✓
250
+ // 3 day gap = skipped 2 days (e.g., Friday → Monday) ✓
251
+ // 4+ day gap = streak broken
252
+ if (diffDays <= 3) {
236
253
  streak.currentStreak += 1;
237
254
  } else {
238
- // Streak broken
239
255
  streak.currentStreak = 1;
240
256
  }
241
257
  streak.totalDaysActive += 1;
@@ -256,6 +272,8 @@ export class StateManager {
256
272
  return;
257
273
  }
258
274
 
275
+ const state = this.getState();
276
+
259
277
  // XP percent is currentXp as a rough percentage toward next level
260
278
  // Exact formula depends on exp group; use a simple ratio for the status line
261
279
  const xpPercent = active.level >= 100 ? 100 : Math.min(100, Math.floor((active.currentXp / Math.max(1, active.currentXp + 50)) * 100));
@@ -274,6 +292,7 @@ export class StateManager {
274
292
  xpPercent,
275
293
  speciesId: active.pokemonId,
276
294
  evolutionReady,
295
+ mood: state.mood ?? "neutral",
277
296
  };
278
297
 
279
298
  const stateDir = getStateDir();
@@ -56,6 +56,7 @@ SPECIES_ID=$(echo "$STATUS" | jq -r '.speciesId // 0')
56
56
  EVOLVING=$(echo "$STATUS" | jq -r '.evolutionReady // false')
57
57
  REACTION=$(echo "$STATUS" | jq -r '.reaction // empty')
58
58
  ENCOUNTER=$(echo "$STATUS" | jq -r '.encounter // empty')
59
+ MOOD=$(echo "$STATUS" | jq -r '.mood // "neutral"')
59
60
 
60
61
  # ── Colors ──────────────────────────────────────────────────
61
62
  NC=$'\033[0m'
@@ -158,69 +159,83 @@ if [ -n "$ENCOUNTER" ]; then
158
159
  elif [ -n "$REACTION" ]; then
159
160
  SPEECH="$REACTION"
160
161
  else
162
+ # Mood-based speeches — pick from mood-specific arrays
161
163
  NOW=$(date +%s)
162
- IDX=$(( (NOW / 30) % 60 ))
163
- SPEECHES=(
164
- "*${NAME} looks at your code curiously*"
165
- ""
166
- "*${NAME} nods along as you type*"
167
- ""
168
- "*${NAME} is watching closely*"
169
- ""
170
- "*${NAME} hums softly*"
171
- ""
172
- "*${NAME} stretches and yawns*"
173
- ""
174
- "*${NAME} bounces excitedly*"
175
- ""
176
- "*${NAME} waits patiently*"
177
- ""
178
- "*${NAME} tilts head at the screen*"
179
- ""
180
- "*${NAME} chirps encouragingly*"
181
- ""
182
- "*${NAME} peers at a variable name*"
183
- ""
184
- "*${NAME} sniffs at a function*"
185
- ""
186
- "*${NAME} sits on the keyboard*"
187
- ""
188
- "*${NAME} chases the cursor*"
189
- ""
190
- "*${NAME} judges your indentation*"
191
- ""
192
- "*${NAME} found a semicolon!*"
193
- ""
194
- "*${NAME} debugs alongside you*"
195
- ""
196
- "*${NAME} spots a typo... maybe*"
197
- ""
198
- "*${NAME} celebrates a clean build*"
199
- ""
200
- "*${NAME} is impressed by that refactor*"
201
- ""
202
- "*${NAME} blinks at the test results*"
203
- ""
204
- "*${NAME} dreams of evolution*"
205
- ""
206
- "*${NAME} wants to learn new moves*"
207
- ""
208
- "*${NAME} is proud of your progress*"
209
- ""
210
- "*${NAME} snoozes between commits*"
211
- ""
212
- "*${NAME} practices its type moves*"
213
- ""
214
- "*${NAME} stares at the linter output*"
215
- ""
216
- "*${NAME} wonders about that TODO*"
217
- ""
218
- "*${NAME} approves of that commit msg*"
219
- ""
220
- "*${NAME} is ready for action!*"
221
- ""
222
- )
223
- SPEECH="${SPEECHES[$IDX]}"
164
+
165
+ case "$MOOD" in
166
+ happy)
167
+ MOOD_SPEECHES=(
168
+ "*${NAME} is beaming with pride!*"
169
+ "*${NAME} does a little victory dance*"
170
+ "*${NAME} radiates positive energy*"
171
+ "*${NAME} bounces happily*"
172
+ "*${NAME} gives you a thumbs up*"
173
+ )
174
+ ;;
175
+ worried)
176
+ MOOD_SPEECHES=(
177
+ "*${NAME} looks concerned...*"
178
+ "*${NAME} nervously watches the errors*"
179
+ "*${NAME} hides behind the terminal*"
180
+ "*${NAME} paces back and forth*"
181
+ "*${NAME} offers you a virtual hug*"
182
+ )
183
+ ;;
184
+ sleepy)
185
+ MOOD_SPEECHES=(
186
+ "*${NAME} yawns widely*"
187
+ "*${NAME} dozes off... zzz*"
188
+ "*${NAME} rubs its eyes*"
189
+ "*${NAME} curls up near the keyboard*"
190
+ "*${NAME} mumbles in its sleep*"
191
+ )
192
+ ;;
193
+ energetic)
194
+ MOOD_SPEECHES=(
195
+ "*${NAME} is fired up! Let's go!*"
196
+ "*${NAME} bounces off the walls*"
197
+ "*${NAME} can't sit still!*"
198
+ "*${NAME} is ready to code all day!*"
199
+ "*${NAME} stretches and flexes*"
200
+ )
201
+ ;;
202
+ proud)
203
+ MOOD_SPEECHES=(
204
+ "*${NAME} puffs up with pride*"
205
+ "*${NAME} strikes a victory pose*"
206
+ "*${NAME} shows off to everyone*"
207
+ "*${NAME} earned bragging rights!*"
208
+ "*${NAME} stands tall and proud*"
209
+ )
210
+ ;;
211
+ *)
212
+ # neutral / default — use the original idle speeches
213
+ MOOD_SPEECHES=(
214
+ "*${NAME} looks at your code curiously*"
215
+ "*${NAME} nods along as you type*"
216
+ "*${NAME} is watching closely*"
217
+ "*${NAME} hums softly*"
218
+ "*${NAME} waits patiently*"
219
+ "*${NAME} tilts head at the screen*"
220
+ "*${NAME} chirps encouragingly*"
221
+ "*${NAME} peers at a variable name*"
222
+ "*${NAME} sniffs at a function*"
223
+ "*${NAME} sits on the keyboard*"
224
+ "*${NAME} chases the cursor*"
225
+ "*${NAME} judges your indentation*"
226
+ "*${NAME} found a semicolon!*"
227
+ "*${NAME} debugs alongside you*"
228
+ "*${NAME} spots a typo... maybe*"
229
+ "*${NAME} celebrates a clean build*"
230
+ "*${NAME} dreams of evolution*"
231
+ "*${NAME} is ready for action!*"
232
+ )
233
+ ;;
234
+ esac
235
+
236
+ MOOD_COUNT=${#MOOD_SPEECHES[@]}
237
+ IDX=$(( (NOW / 30) % MOOD_COUNT ))
238
+ SPEECH="${MOOD_SPEECHES[$IDX]}"
224
239
  fi
225
240
  if [ -n "$ENCOUNTER" ]; then
226
241
  # Bright yellow for encounter alerts