roguelike-cli 1.3.2 → 1.3.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/README.md +115 -85
- package/dist/commands/init.js +4 -4
- package/dist/config/config.js +12 -2
- package/dist/data/dictionaries.js +450 -0
- package/dist/data/loot.js +141 -0
- package/dist/interactive/commands.js +79 -75
- package/dist/interactive/startup.js +6 -5
- package/dist/storage/profile.js +222 -97
- package/package.json +1 -1
- package/src/commands/init.ts +4 -4
- package/src/config/config.ts +12 -1
- package/src/data/dictionaries.ts +521 -0
- package/src/data/loot.ts +177 -0
- package/src/interactive/commands.ts +92 -85
- package/src/interactive/startup.ts +6 -5
- package/src/storage/profile.ts +253 -94
package/dist/storage/profile.js
CHANGED
|
@@ -33,42 +33,39 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.
|
|
36
|
+
exports.ACHIEVEMENT_THRESHOLDS = void 0;
|
|
37
37
|
exports.xpForLevel = xpForLevel;
|
|
38
38
|
exports.levelFromXP = levelFromXP;
|
|
39
39
|
exports.xpToNextLevel = xpToNextLevel;
|
|
40
40
|
exports.readProfile = readProfile;
|
|
41
41
|
exports.saveProfile = saveProfile;
|
|
42
|
-
exports.addXP = addXP;
|
|
43
42
|
exports.completeTask = completeTask;
|
|
43
|
+
exports.getAchievementInfo = getAchievementInfo;
|
|
44
44
|
exports.formatStats = formatStats;
|
|
45
45
|
exports.formatAchievements = formatAchievements;
|
|
46
|
+
exports.formatInventoryDisplay = formatInventoryDisplay;
|
|
46
47
|
exports.addToUndoHistory = addToUndoHistory;
|
|
47
48
|
exports.getLastUndo = getLastUndo;
|
|
48
49
|
exports.performUndo = performUndo;
|
|
49
50
|
const fs = __importStar(require("fs"));
|
|
50
51
|
const path = __importStar(require("path"));
|
|
51
52
|
const os = __importStar(require("os"));
|
|
53
|
+
const dictionaries_1 = require("../data/dictionaries");
|
|
54
|
+
const loot_1 = require("../data/loot");
|
|
52
55
|
const PROFILE_FILE = path.join(os.homedir(), '.rlc', 'profile.json');
|
|
53
|
-
// Achievement
|
|
54
|
-
exports.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
{
|
|
65
|
-
|
|
66
|
-
{ id: 'level_5', name: 'Adventurer', description: 'Reach level 5' },
|
|
67
|
-
{ id: 'level_10', name: 'Veteran', description: 'Reach level 10' },
|
|
68
|
-
{ id: 'level_25', name: 'Legend', description: 'Reach level 25' },
|
|
69
|
-
{ id: 'xp_1000', name: 'XP Collector', description: 'Earn 1000 XP' },
|
|
70
|
-
{ id: 'xp_10000', name: 'XP Hoarder', description: 'Earn 10000 XP' },
|
|
71
|
-
];
|
|
56
|
+
// Achievement thresholds (infinite, level-based)
|
|
57
|
+
exports.ACHIEVEMENT_THRESHOLDS = {
|
|
58
|
+
tasks: [1, 10, 50, 100, 250, 500, 1000, 2500, 5000, 10000],
|
|
59
|
+
bosses: [1, 5, 10, 25, 50, 100, 250, 500],
|
|
60
|
+
streaks: [3, 7, 14, 30, 60, 90, 180, 365],
|
|
61
|
+
depths: [3, 5, 7, 10, 15, 20],
|
|
62
|
+
levels: [5, 10, 25, 50, 100, 150, 200],
|
|
63
|
+
xp: [1000, 5000, 10000, 50000, 100000, 500000, 1000000],
|
|
64
|
+
};
|
|
65
|
+
// Map threshold to achievement ID
|
|
66
|
+
function getAchievementId(type, threshold) {
|
|
67
|
+
return `${type}_${threshold}`;
|
|
68
|
+
}
|
|
72
69
|
// XP required for each level (cumulative)
|
|
73
70
|
function xpForLevel(level) {
|
|
74
71
|
return Math.floor(100 * Math.pow(1.5, level - 1));
|
|
@@ -106,6 +103,10 @@ function readProfile() {
|
|
|
106
103
|
return {
|
|
107
104
|
...createDefaultProfile(),
|
|
108
105
|
...profile,
|
|
106
|
+
stats: {
|
|
107
|
+
...createDefaultProfile().stats,
|
|
108
|
+
...profile.stats,
|
|
109
|
+
},
|
|
109
110
|
};
|
|
110
111
|
}
|
|
111
112
|
catch {
|
|
@@ -130,36 +131,41 @@ function createDefaultProfile() {
|
|
|
130
131
|
currentStreak: 0,
|
|
131
132
|
longestStreak: 0,
|
|
132
133
|
achievements: [],
|
|
134
|
+
inventory: [],
|
|
133
135
|
undoHistory: [],
|
|
134
136
|
stats: {
|
|
135
137
|
completedByDay: {},
|
|
136
138
|
createdAt: new Date().toISOString(),
|
|
139
|
+
deepestDepth: 0,
|
|
140
|
+
speedruns: 0,
|
|
141
|
+
nightOwlTasks: 0,
|
|
142
|
+
earlyBirdTasks: 0,
|
|
137
143
|
},
|
|
138
144
|
};
|
|
139
145
|
}
|
|
140
|
-
function
|
|
141
|
-
const profile = readProfile();
|
|
142
|
-
const oldLevel = profile.level;
|
|
143
|
-
profile.totalXP += amount;
|
|
144
|
-
profile.level = levelFromXP(profile.totalXP);
|
|
145
|
-
saveProfile(profile);
|
|
146
|
-
return {
|
|
147
|
-
newXP: profile.totalXP,
|
|
148
|
-
levelUp: profile.level > oldLevel,
|
|
149
|
-
newLevel: profile.level,
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
function completeTask(xp, isBoss, depth, createdAt) {
|
|
146
|
+
function completeTask(xp, isBoss, depth, createdAt, rulesPreset) {
|
|
153
147
|
const profile = readProfile();
|
|
154
148
|
const oldLevel = profile.level;
|
|
155
149
|
const today = new Date().toISOString().split('T')[0];
|
|
156
150
|
const createdDate = createdAt.split('T')[0];
|
|
151
|
+
const hour = new Date().getHours();
|
|
157
152
|
// Add XP
|
|
158
153
|
profile.totalXP += xp;
|
|
159
154
|
profile.tasksCompleted += 1;
|
|
160
155
|
if (isBoss) {
|
|
161
156
|
profile.bossesDefeated += 1;
|
|
162
157
|
}
|
|
158
|
+
// Track special completions
|
|
159
|
+
if (createdDate === today) {
|
|
160
|
+
profile.stats.speedruns = (profile.stats.speedruns || 0) + 1;
|
|
161
|
+
}
|
|
162
|
+
if (hour >= 0 && hour < 6) {
|
|
163
|
+
profile.stats.earlyBirdTasks = (profile.stats.earlyBirdTasks || 0) + 1;
|
|
164
|
+
}
|
|
165
|
+
if (hour >= 0 && hour < 5) {
|
|
166
|
+
profile.stats.nightOwlTasks = (profile.stats.nightOwlTasks || 0) + 1;
|
|
167
|
+
}
|
|
168
|
+
profile.stats.deepestDepth = Math.max(profile.stats.deepestDepth || 0, depth);
|
|
163
169
|
// Update streak
|
|
164
170
|
if (profile.lastCompletionDate) {
|
|
165
171
|
const lastDate = new Date(profile.lastCompletionDate);
|
|
@@ -171,7 +177,6 @@ function completeTask(xp, isBoss, depth, createdAt) {
|
|
|
171
177
|
else if (diffDays > 1) {
|
|
172
178
|
profile.currentStreak = 1;
|
|
173
179
|
}
|
|
174
|
-
// Same day - streak continues but doesn't increment
|
|
175
180
|
}
|
|
176
181
|
else {
|
|
177
182
|
profile.currentStreak = 1;
|
|
@@ -182,105 +187,228 @@ function completeTask(xp, isBoss, depth, createdAt) {
|
|
|
182
187
|
profile.stats.completedByDay[today] = (profile.stats.completedByDay[today] || 0) + 1;
|
|
183
188
|
// Update level
|
|
184
189
|
profile.level = levelFromXP(profile.totalXP);
|
|
190
|
+
const levelUp = profile.level > oldLevel;
|
|
185
191
|
// Check for new achievements
|
|
186
192
|
const newAchievements = [];
|
|
187
193
|
const checkAchievement = (id) => {
|
|
188
194
|
if (!profile.achievements.includes(id)) {
|
|
189
195
|
profile.achievements.push(id);
|
|
190
|
-
|
|
191
|
-
if (achievement)
|
|
192
|
-
newAchievements.push(achievement);
|
|
196
|
+
newAchievements.push(id);
|
|
193
197
|
}
|
|
194
198
|
};
|
|
195
|
-
// Task count achievements
|
|
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
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
if (
|
|
233
|
-
checkAchievement('
|
|
199
|
+
// Task count achievements (infinite)
|
|
200
|
+
for (const threshold of exports.ACHIEVEMENT_THRESHOLDS.tasks) {
|
|
201
|
+
if (profile.tasksCompleted >= threshold) {
|
|
202
|
+
checkAchievement(getAchievementId('tasks', threshold));
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// Boss achievements (infinite)
|
|
206
|
+
for (const threshold of exports.ACHIEVEMENT_THRESHOLDS.bosses) {
|
|
207
|
+
if (profile.bossesDefeated >= threshold) {
|
|
208
|
+
checkAchievement(getAchievementId('bosses', threshold));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// Depth achievements (infinite)
|
|
212
|
+
for (const threshold of exports.ACHIEVEMENT_THRESHOLDS.depths) {
|
|
213
|
+
if (profile.stats.deepestDepth >= threshold) {
|
|
214
|
+
checkAchievement(getAchievementId('depth', threshold));
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
// Streak achievements (infinite)
|
|
218
|
+
for (const threshold of exports.ACHIEVEMENT_THRESHOLDS.streaks) {
|
|
219
|
+
if (profile.currentStreak >= threshold) {
|
|
220
|
+
checkAchievement(getAchievementId('streak', threshold));
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// Level achievements (infinite)
|
|
224
|
+
for (const threshold of exports.ACHIEVEMENT_THRESHOLDS.levels) {
|
|
225
|
+
if (profile.level >= threshold) {
|
|
226
|
+
checkAchievement(getAchievementId('level', threshold));
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
// XP achievements (infinite)
|
|
230
|
+
for (const threshold of exports.ACHIEVEMENT_THRESHOLDS.xp) {
|
|
231
|
+
if (profile.totalXP >= threshold) {
|
|
232
|
+
checkAchievement(getAchievementId('xp', threshold));
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
// Special achievements
|
|
236
|
+
if (createdDate === today) {
|
|
237
|
+
checkAchievement('speedrun');
|
|
238
|
+
}
|
|
239
|
+
if (hour >= 0 && hour < 5) {
|
|
240
|
+
checkAchievement('nightowl');
|
|
241
|
+
}
|
|
242
|
+
if (hour >= 5 && hour < 7) {
|
|
243
|
+
checkAchievement('earlybird');
|
|
244
|
+
}
|
|
245
|
+
// Roll for loot
|
|
246
|
+
let lootDropped;
|
|
247
|
+
// Roll from task completion
|
|
248
|
+
const taskLoot = (0, loot_1.rollForLoot)(profile.level, isBoss ? 'boss' : 'task', rulesPreset);
|
|
249
|
+
if (taskLoot.dropped && taskLoot.item) {
|
|
250
|
+
lootDropped = taskLoot.item;
|
|
251
|
+
profile.inventory = profile.inventory || [];
|
|
252
|
+
profile.inventory.push(taskLoot.item);
|
|
253
|
+
}
|
|
254
|
+
// Additional roll on level up
|
|
255
|
+
if (levelUp) {
|
|
256
|
+
const levelLoot = (0, loot_1.rollForLoot)(profile.level, 'levelup', rulesPreset);
|
|
257
|
+
if (levelLoot.dropped && levelLoot.item) {
|
|
258
|
+
if (!lootDropped) {
|
|
259
|
+
lootDropped = levelLoot.item;
|
|
260
|
+
}
|
|
261
|
+
profile.inventory.push(levelLoot.item);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
// Additional roll on new achievements
|
|
265
|
+
if (newAchievements.length > 0) {
|
|
266
|
+
const achievementLoot = (0, loot_1.rollForLoot)(profile.level, 'achievement', rulesPreset);
|
|
267
|
+
if (achievementLoot.dropped && achievementLoot.item) {
|
|
268
|
+
if (!lootDropped) {
|
|
269
|
+
lootDropped = achievementLoot.item;
|
|
270
|
+
}
|
|
271
|
+
profile.inventory.push(achievementLoot.item);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
234
274
|
saveProfile(profile);
|
|
235
275
|
return {
|
|
236
276
|
xpGained: xp,
|
|
237
|
-
levelUp
|
|
277
|
+
levelUp,
|
|
278
|
+
oldLevel,
|
|
238
279
|
newLevel: profile.level,
|
|
239
280
|
newAchievements,
|
|
281
|
+
lootDropped,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
// Get achievement display info from dictionary
|
|
285
|
+
function getAchievementInfo(achievementId, dict) {
|
|
286
|
+
// Parse achievement ID
|
|
287
|
+
const parts = achievementId.split('_');
|
|
288
|
+
const type = parts[0];
|
|
289
|
+
const threshold = parseInt(parts[1]);
|
|
290
|
+
// Map to dictionary keys
|
|
291
|
+
const keyMap = {
|
|
292
|
+
tasks: { 1: 'firstTask', 10: 'tasks10', 50: 'tasks50', 100: 'tasks100', 500: 'tasks500', 1000: 'tasks1000' },
|
|
293
|
+
bosses: { 1: 'boss1', 5: 'boss5', 10: 'boss10', 25: 'boss25' },
|
|
294
|
+
streak: { 3: 'streak3', 7: 'streak7', 14: 'streak14', 30: 'streak30' },
|
|
295
|
+
depth: { 3: 'depth3', 5: 'depth5', 10: 'depth10' },
|
|
296
|
+
};
|
|
297
|
+
// Special achievements
|
|
298
|
+
if (achievementId === 'speedrun') {
|
|
299
|
+
return dict.achievements.speedrun;
|
|
300
|
+
}
|
|
301
|
+
if (achievementId === 'nightowl') {
|
|
302
|
+
return dict.achievements.nightOwl;
|
|
303
|
+
}
|
|
304
|
+
if (achievementId === 'earlybird') {
|
|
305
|
+
return dict.achievements.earlyBird;
|
|
306
|
+
}
|
|
307
|
+
// Get from map
|
|
308
|
+
if (keyMap[type] && keyMap[type][threshold]) {
|
|
309
|
+
const key = keyMap[type][threshold];
|
|
310
|
+
return dict.achievements[key];
|
|
311
|
+
}
|
|
312
|
+
// Generate for achievements beyond dictionary
|
|
313
|
+
return {
|
|
314
|
+
name: `${type.charAt(0).toUpperCase() + type.slice(1)} ${threshold}`,
|
|
315
|
+
desc: `Reach ${threshold} ${type}`,
|
|
240
316
|
};
|
|
241
317
|
}
|
|
242
|
-
function formatStats() {
|
|
318
|
+
function formatStats(rulesPreset) {
|
|
243
319
|
const profile = readProfile();
|
|
320
|
+
const dict = (0, dictionaries_1.getDictionary)(rulesPreset);
|
|
244
321
|
const nextLevel = xpToNextLevel(profile.totalXP);
|
|
245
322
|
const lines = [
|
|
246
323
|
'',
|
|
247
324
|
'=== PLAYER STATS ===',
|
|
248
325
|
'',
|
|
249
|
-
|
|
250
|
-
|
|
326
|
+
`${dict.stats.level}: ${profile.level}`,
|
|
327
|
+
`${dict.stats.xp}: ${profile.totalXP} (${nextLevel.current}/${nextLevel.required} to next)`,
|
|
251
328
|
`Progress: [${'#'.repeat(Math.floor(nextLevel.progress / 5))}${'.'.repeat(20 - Math.floor(nextLevel.progress / 5))}] ${nextLevel.progress}%`,
|
|
252
329
|
'',
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
330
|
+
`${dict.stats.tasksCompleted}: ${profile.tasksCompleted}`,
|
|
331
|
+
`${dict.stats.bossesDefeated}: ${profile.bossesDefeated}`,
|
|
332
|
+
`${dict.stats.currentStreak}: ${profile.currentStreak} days`,
|
|
333
|
+
`${dict.stats.longestStreak}: ${profile.longestStreak} days`,
|
|
257
334
|
'',
|
|
258
|
-
`Achievements: ${profile.achievements.length}
|
|
335
|
+
`Achievements: ${profile.achievements.length}`,
|
|
336
|
+
`${dict.stats.inventory}: ${(profile.inventory || []).length} items`,
|
|
337
|
+
`Inventory Value: ${(0, loot_1.calculateInventoryValue)(profile.inventory || [])}`,
|
|
259
338
|
'',
|
|
260
339
|
];
|
|
261
340
|
return lines.join('\n');
|
|
262
341
|
}
|
|
263
|
-
function formatAchievements() {
|
|
342
|
+
function formatAchievements(rulesPreset) {
|
|
264
343
|
const profile = readProfile();
|
|
344
|
+
const dict = (0, dictionaries_1.getDictionary)(rulesPreset);
|
|
265
345
|
const lines = [
|
|
266
346
|
'',
|
|
267
347
|
'=== ACHIEVEMENTS ===',
|
|
268
348
|
'',
|
|
269
349
|
];
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
350
|
+
// Show unlocked achievements
|
|
351
|
+
if (profile.achievements.length === 0) {
|
|
352
|
+
lines.push('No achievements yet. Complete tasks to unlock!');
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
lines.push('Unlocked:');
|
|
356
|
+
for (const id of profile.achievements) {
|
|
357
|
+
const info = getAchievementInfo(id, dict);
|
|
358
|
+
if (info) {
|
|
359
|
+
lines.push(`[x] ${info.name}`);
|
|
360
|
+
lines.push(` ${info.desc}`);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
275
363
|
}
|
|
364
|
+
// Show next achievements to unlock
|
|
276
365
|
lines.push('');
|
|
277
|
-
lines.push(
|
|
366
|
+
lines.push('Next to unlock:');
|
|
367
|
+
// Find next task achievement
|
|
368
|
+
for (const threshold of exports.ACHIEVEMENT_THRESHOLDS.tasks) {
|
|
369
|
+
const id = getAchievementId('tasks', threshold);
|
|
370
|
+
if (!profile.achievements.includes(id)) {
|
|
371
|
+
const info = getAchievementInfo(id, dict);
|
|
372
|
+
if (info) {
|
|
373
|
+
lines.push(`[ ] ${info.name} (${profile.tasksCompleted}/${threshold})`);
|
|
374
|
+
}
|
|
375
|
+
break;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
// Find next boss achievement
|
|
379
|
+
for (const threshold of exports.ACHIEVEMENT_THRESHOLDS.bosses) {
|
|
380
|
+
const id = getAchievementId('bosses', threshold);
|
|
381
|
+
if (!profile.achievements.includes(id)) {
|
|
382
|
+
const info = getAchievementInfo(id, dict);
|
|
383
|
+
if (info) {
|
|
384
|
+
lines.push(`[ ] ${info.name} (${profile.bossesDefeated}/${threshold})`);
|
|
385
|
+
}
|
|
386
|
+
break;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
// Find next streak achievement
|
|
390
|
+
for (const threshold of exports.ACHIEVEMENT_THRESHOLDS.streaks) {
|
|
391
|
+
const id = getAchievementId('streak', threshold);
|
|
392
|
+
if (!profile.achievements.includes(id)) {
|
|
393
|
+
const info = getAchievementInfo(id, dict);
|
|
394
|
+
if (info) {
|
|
395
|
+
lines.push(`[ ] ${info.name} (${profile.currentStreak}/${threshold})`);
|
|
396
|
+
}
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
lines.push('');
|
|
401
|
+
lines.push(`Total unlocked: ${profile.achievements.length}`);
|
|
278
402
|
lines.push('');
|
|
279
403
|
return lines.join('\n');
|
|
280
404
|
}
|
|
405
|
+
function formatInventoryDisplay(rulesPreset) {
|
|
406
|
+
const profile = readProfile();
|
|
407
|
+
const dict = (0, dictionaries_1.getDictionary)(rulesPreset);
|
|
408
|
+
return (0, loot_1.formatInventory)(profile.inventory || [], dict);
|
|
409
|
+
}
|
|
281
410
|
function addToUndoHistory(entry) {
|
|
282
411
|
const profile = readProfile();
|
|
283
|
-
// Keep only last 10 undo entries
|
|
284
412
|
profile.undoHistory = profile.undoHistory || [];
|
|
285
413
|
profile.undoHistory.unshift(entry);
|
|
286
414
|
if (profile.undoHistory.length > 10) {
|
|
@@ -301,15 +429,12 @@ function performUndo() {
|
|
|
301
429
|
return { success: false, entry: null, message: 'Nothing to undo.' };
|
|
302
430
|
}
|
|
303
431
|
const entry = profile.undoHistory.shift();
|
|
304
|
-
// Subtract XP
|
|
305
432
|
profile.totalXP = Math.max(0, profile.totalXP - entry.xpLost);
|
|
306
433
|
profile.tasksCompleted = Math.max(0, profile.tasksCompleted - 1);
|
|
307
434
|
if (entry.wasBoss) {
|
|
308
435
|
profile.bossesDefeated = Math.max(0, profile.bossesDefeated - 1);
|
|
309
436
|
}
|
|
310
|
-
// Update level
|
|
311
437
|
profile.level = levelFromXP(profile.totalXP);
|
|
312
|
-
// Update daily stats
|
|
313
438
|
const today = new Date().toISOString().split('T')[0];
|
|
314
439
|
if (profile.stats.completedByDay[today]) {
|
|
315
440
|
profile.stats.completedByDay[today] = Math.max(0, profile.stats.completedByDay[today] - 1);
|
package/package.json
CHANGED
package/src/commands/init.ts
CHANGED
|
@@ -110,15 +110,15 @@ export async function initCommand(existingRl?: readline.Interface): Promise<void
|
|
|
110
110
|
const apiKey = apiKeyInput.trim() || existingApiKey;
|
|
111
111
|
|
|
112
112
|
if (!apiKey) {
|
|
113
|
-
console.log('Warning: API key not set. You can set it later with: config -
|
|
113
|
+
console.log('Warning: API key not set. You can set it later with: config -K=<key>');
|
|
114
114
|
} else if (apiKeyInput.trim()) {
|
|
115
115
|
console.log('API key saved');
|
|
116
116
|
} else {
|
|
117
117
|
console.log('Using existing API key');
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
// 4.
|
|
121
|
-
console.log('\nSelect AI
|
|
120
|
+
// 4. Rules preset selection
|
|
121
|
+
console.log('\nSelect AI Rules (affects language style):');
|
|
122
122
|
const presetKeys = Object.keys(RULES_PRESETS);
|
|
123
123
|
presetKeys.forEach((key, index) => {
|
|
124
124
|
console.log(` ${index + 1}. ${RULES_PRESETS[key].name}`);
|
|
@@ -177,7 +177,7 @@ export async function initCommand(existingRl?: readline.Interface): Promise<void
|
|
|
177
177
|
console.log(`Root directory: ${rootDir}`);
|
|
178
178
|
console.log(`AI Provider: ${selectedProvider.name}`);
|
|
179
179
|
console.log(`Model: ${selectedProvider.model}`);
|
|
180
|
-
console.log(`
|
|
180
|
+
console.log(`Rules: ${RULES_PRESETS[selectedPreset]?.name || 'Custom'}\n`);
|
|
181
181
|
} finally {
|
|
182
182
|
if (shouldCloseRl) {
|
|
183
183
|
rl.close();
|
package/src/config/config.ts
CHANGED
|
@@ -13,7 +13,18 @@ export interface Config {
|
|
|
13
13
|
rulesPreset?: string;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
//
|
|
16
|
+
// Supported models for validation
|
|
17
|
+
export const SUPPORTED_MODELS = [
|
|
18
|
+
'claude-sonnet-4-20250514',
|
|
19
|
+
'claude-opus-4-20250514',
|
|
20
|
+
'gpt-4o',
|
|
21
|
+
'gpt-4-turbo',
|
|
22
|
+
'gemini-3-pro',
|
|
23
|
+
'gemini-2.0-flash',
|
|
24
|
+
'grok-beta',
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
// Preset rules
|
|
17
28
|
export const RULES_PRESETS: Record<string, { name: string; rules: string }> = {
|
|
18
29
|
default: {
|
|
19
30
|
name: 'Default (No theme)',
|