@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.
- package/dist/src/engine/constants.js +9 -3
- package/dist/src/engine/encounters.js +50 -10
- package/dist/src/engine/mood.js +187 -0
- package/dist/src/engine/types.js +2 -0
- package/dist/src/gamification/achievements.js +3 -3
- package/dist/src/gamification/legendary-quests.js +4 -4
- package/dist/src/hooks/award-xp.js +60 -5
- package/dist/src/server/index.js +8 -0
- package/dist/src/server/instructions.js +23 -0
- package/dist/src/server/tools/catch.js +3 -0
- package/dist/src/server/tools/evolve.js +3 -0
- package/dist/src/server/tools/feed.js +120 -0
- package/dist/src/server/tools/play.js +310 -0
- package/dist/src/server/tools/settings.js +80 -0
- package/dist/src/server/tools/show.js +5 -0
- package/dist/src/server/tools/train.js +144 -0
- package/dist/src/state/schemas.js +17 -1
- package/dist/src/state/state-manager.js +22 -6
- package/package.json +1 -1
- package/skills/buddy/SKILL.md +15 -0
- package/src/engine/constants.ts +12 -3
- package/src/engine/encounters.ts +65 -9
- package/src/engine/mood.ts +220 -0
- package/src/engine/types.ts +23 -0
- package/src/gamification/achievements.ts +3 -3
- package/src/gamification/legendary-quests.ts +4 -4
- package/src/hooks/award-xp.ts +82 -5
- package/src/server/index.ts +8 -0
- package/src/server/instructions.ts +25 -0
- package/src/server/tools/catch.ts +4 -0
- package/src/server/tools/evolve.ts +4 -0
- package/src/server/tools/feed.ts +145 -0
- package/src/server/tools/play.ts +378 -0
- package/src/server/tools/settings.ts +101 -0
- package/src/server/tools/show.ts +7 -0
- package/src/server/tools/train.ts +180 -0
- package/src/state/schemas.ts +19 -0
- package/src/state/state-manager.ts +25 -6
- package/statusline/buddy-status.sh +77 -62
package/src/state/schemas.ts
CHANGED
|
@@ -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
|
-
/**
|
|
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
|
-
|
|
235
|
-
|
|
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
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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
|