@night-slayer18/leetcode-cli 1.4.0 → 1.5.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 (3) hide show
  1. package/README.md +17 -1
  2. package/dist/index.js +402 -112
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -71,7 +71,7 @@ leetcode submit 1
71
71
  | `submit <id\|file>` | Submit solution to LeetCode |
72
72
  | `submissions <id>` | View past submissions |
73
73
  | `stat [username]` | Show user statistics |
74
- | `stat [username]` | Show user statistics |
74
+ | `timer <id>` | Interview mode with timer |
75
75
  | `config` | View or set configuration |
76
76
  | `sync` | Sync solutions to Git repository |
77
77
 
@@ -207,6 +207,22 @@ leetcode stat -t
207
207
  # Sync all solutions to your configured git repo
208
208
  leetcode sync
209
209
  ```
210
+
211
+ ### Interview Timer
212
+
213
+ ```bash
214
+ # Start timer for a problem (default: Easy=20m, Medium=40m, Hard=60m)
215
+ leetcode timer 1
216
+
217
+ # Custom time limit
218
+ leetcode timer 1 -m 30
219
+
220
+ # View your solve time stats
221
+ leetcode timer --stats
222
+
223
+ # Stop active timer
224
+ leetcode timer --stop
225
+ ```
210
226
 
211
227
  ### Configuration
212
228
 
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { Command } from "commander";
5
- import chalk20 from "chalk";
5
+ import chalk21 from "chalk";
6
6
 
7
7
  // src/commands/login.ts
8
8
  import inquirer from "inquirer";
@@ -1278,12 +1278,16 @@ async function findSolutionFile(dir, problemId, currentDepth = 0) {
1278
1278
  if (currentDepth >= MAX_SEARCH_DEPTH) return null;
1279
1279
  const entries = await readdir(dir, { withFileTypes: true });
1280
1280
  for (const entry of entries) {
1281
+ if (entry.name.startsWith(".")) continue;
1281
1282
  const fullPath = join3(dir, entry.name);
1282
1283
  if (entry.isDirectory()) {
1283
1284
  const found = await findSolutionFile(fullPath, problemId, currentDepth + 1);
1284
1285
  if (found) return found;
1285
1286
  } else if (entry.name.startsWith(`${problemId}.`)) {
1286
- return fullPath;
1287
+ const ext = entry.name.split(".").pop()?.toLowerCase();
1288
+ if (ext && ext in EXT_TO_LANG_MAP) {
1289
+ return fullPath;
1290
+ }
1287
1291
  }
1288
1292
  }
1289
1293
  return null;
@@ -1303,6 +1307,19 @@ async function findFileByName(dir, fileName, currentDepth = 0) {
1303
1307
  }
1304
1308
  return null;
1305
1309
  }
1310
+ var EXT_TO_LANG_MAP = {
1311
+ ts: "typescript",
1312
+ js: "javascript",
1313
+ py: "python3",
1314
+ java: "java",
1315
+ cpp: "cpp",
1316
+ c: "c",
1317
+ cs: "csharp",
1318
+ go: "go",
1319
+ rs: "rust",
1320
+ kt: "kotlin",
1321
+ swift: "swift"
1322
+ };
1306
1323
  function getLangSlugFromExtension(ext) {
1307
1324
  const langMap = {
1308
1325
  ts: "typescript",
@@ -1398,6 +1415,82 @@ import { existsSync as existsSync4 } from "fs";
1398
1415
  import { basename as basename2 } from "path";
1399
1416
  import ora6 from "ora";
1400
1417
  import chalk9 from "chalk";
1418
+
1419
+ // src/storage/timer.ts
1420
+ import Conf2 from "conf";
1421
+ import { homedir as homedir2 } from "os";
1422
+ import { join as join4 } from "path";
1423
+ var timerStore = new Conf2({
1424
+ projectName: "leetcode-cli-timer",
1425
+ cwd: join4(homedir2(), ".leetcode"),
1426
+ defaults: {
1427
+ solveTimes: {},
1428
+ activeTimer: null
1429
+ }
1430
+ });
1431
+ var timerStorage = {
1432
+ startTimer(problemId, title, difficulty, durationMinutes) {
1433
+ timerStore.set("activeTimer", {
1434
+ problemId,
1435
+ title,
1436
+ difficulty,
1437
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
1438
+ durationMinutes
1439
+ });
1440
+ },
1441
+ getActiveTimer() {
1442
+ return timerStore.get("activeTimer");
1443
+ },
1444
+ stopTimer() {
1445
+ const active = timerStore.get("activeTimer");
1446
+ if (!active) return null;
1447
+ const startedAt = new Date(active.startedAt);
1448
+ const now = /* @__PURE__ */ new Date();
1449
+ const durationSeconds = Math.floor((now.getTime() - startedAt.getTime()) / 1e3);
1450
+ timerStore.set("activeTimer", null);
1451
+ return { durationSeconds };
1452
+ },
1453
+ recordSolveTime(problemId, title, difficulty, durationSeconds, timerMinutes) {
1454
+ const solveTimes = timerStore.get("solveTimes") ?? {};
1455
+ if (!solveTimes[problemId]) {
1456
+ solveTimes[problemId] = [];
1457
+ }
1458
+ solveTimes[problemId].push({
1459
+ problemId,
1460
+ title,
1461
+ difficulty,
1462
+ solvedAt: (/* @__PURE__ */ new Date()).toISOString(),
1463
+ durationSeconds,
1464
+ timerMinutes
1465
+ });
1466
+ timerStore.set("solveTimes", solveTimes);
1467
+ },
1468
+ getSolveTimes(problemId) {
1469
+ const solveTimes = timerStore.get("solveTimes") ?? {};
1470
+ return solveTimes[problemId] ?? [];
1471
+ },
1472
+ getAllSolveTimes() {
1473
+ return timerStore.get("solveTimes") ?? {};
1474
+ },
1475
+ getStats() {
1476
+ const solveTimes = timerStore.get("solveTimes") ?? {};
1477
+ let totalProblems = 0;
1478
+ let totalTime = 0;
1479
+ for (const times of Object.values(solveTimes)) {
1480
+ totalProblems += times.length;
1481
+ for (const t of times) {
1482
+ totalTime += t.durationSeconds;
1483
+ }
1484
+ }
1485
+ return {
1486
+ totalProblems,
1487
+ totalTime,
1488
+ avgTime: totalProblems > 0 ? Math.floor(totalTime / totalProblems) : 0
1489
+ };
1490
+ }
1491
+ };
1492
+
1493
+ // src/commands/submit.ts
1401
1494
  async function submitCommand(fileOrId) {
1402
1495
  const { authorized } = await requireAuth();
1403
1496
  if (!authorized) return;
@@ -1457,6 +1550,35 @@ async function submitCommand(fileOrId) {
1457
1550
  );
1458
1551
  spinner.stop();
1459
1552
  displaySubmissionResult(result);
1553
+ if (result.status_msg === "Accepted") {
1554
+ const activeTimer = timerStorage.getActiveTimer();
1555
+ if (activeTimer && activeTimer.problemId === problemId) {
1556
+ const timerResult = timerStorage.stopTimer();
1557
+ if (timerResult) {
1558
+ timerStorage.recordSolveTime(
1559
+ problemId,
1560
+ problem.title,
1561
+ problem.difficulty,
1562
+ timerResult.durationSeconds,
1563
+ activeTimer.durationMinutes
1564
+ );
1565
+ const mins = Math.floor(timerResult.durationSeconds / 60);
1566
+ const secs = timerResult.durationSeconds % 60;
1567
+ const timeStr = `${mins}m ${secs}s`;
1568
+ const withinLimit = timerResult.durationSeconds <= activeTimer.durationMinutes * 60;
1569
+ console.log();
1570
+ console.log(chalk9.bold("\u23F1\uFE0F Timer Result:"));
1571
+ console.log(
1572
+ ` Solved in ${withinLimit ? chalk9.green(timeStr) : chalk9.yellow(timeStr)} (limit: ${activeTimer.durationMinutes}m)`
1573
+ );
1574
+ if (withinLimit) {
1575
+ console.log(chalk9.green(" \u2713 Within time limit!"));
1576
+ } else {
1577
+ console.log(chalk9.yellow(" \u26A0 Exceeded time limit"));
1578
+ }
1579
+ }
1580
+ }
1581
+ }
1460
1582
  } catch (error) {
1461
1583
  spinner.fail("Submission failed");
1462
1584
  if (error instanceof Error) {
@@ -1704,7 +1826,7 @@ async function randomCommand(options) {
1704
1826
  // src/commands/submissions.ts
1705
1827
  import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
1706
1828
  import { existsSync as existsSync5 } from "fs";
1707
- import { join as join4 } from "path";
1829
+ import { join as join5 } from "path";
1708
1830
  import ora10 from "ora";
1709
1831
  import chalk14 from "chalk";
1710
1832
  async function submissionsCommand(idOrSlug, options) {
@@ -1758,7 +1880,7 @@ async function submissionsCommand(idOrSlug, options) {
1758
1880
  const workDir = config.getWorkDir();
1759
1881
  const difficulty = problem.difficulty;
1760
1882
  const category = problem.topicTags.length > 0 ? problem.topicTags[0].name.replace(/[^\w\s-]/g, "").trim() : "Uncategorized";
1761
- const targetDir = join4(workDir, difficulty, category);
1883
+ const targetDir = join5(workDir, difficulty, category);
1762
1884
  if (!existsSync5(targetDir)) {
1763
1885
  await mkdir2(targetDir, { recursive: true });
1764
1886
  }
@@ -1766,7 +1888,7 @@ async function submissionsCommand(idOrSlug, options) {
1766
1888
  const supportedLang = LANG_SLUG_MAP[langSlug] ?? "txt";
1767
1889
  const ext = LANGUAGE_EXTENSIONS[supportedLang] ?? langSlug;
1768
1890
  const fileName = `${problem.questionFrontendId}.${problem.titleSlug}.submission-${lastAC.id}.${ext}`;
1769
- const filePath = join4(targetDir, fileName);
1891
+ const filePath = join5(targetDir, fileName);
1770
1892
  await writeFile2(filePath, details.code, "utf-8");
1771
1893
  downloadSpinner.succeed(`Downloaded to ${chalk14.green(fileName)}`);
1772
1894
  console.log(chalk14.gray(`Path: ${filePath}`));
@@ -1892,8 +2014,8 @@ import Table2 from "cli-table3";
1892
2014
  import ora11 from "ora";
1893
2015
 
1894
2016
  // src/storage/bookmarks.ts
1895
- import Conf2 from "conf";
1896
- var bookmarksStore = new Conf2({
2017
+ import Conf3 from "conf";
2018
+ var bookmarksStore = new Conf3({
1897
2019
  projectName: "leetcode-cli-bookmarks",
1898
2020
  defaults: { bookmarks: [] }
1899
2021
  });
@@ -2046,7 +2168,7 @@ function colorDifficulty2(difficulty) {
2046
2168
 
2047
2169
  // src/commands/notes.ts
2048
2170
  import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
2049
- import { join as join5 } from "path";
2171
+ import { join as join6 } from "path";
2050
2172
  import { existsSync as existsSync6 } from "fs";
2051
2173
  import chalk17 from "chalk";
2052
2174
  async function notesCommand(problemId, action) {
@@ -2056,8 +2178,8 @@ async function notesCommand(problemId, action) {
2056
2178
  return;
2057
2179
  }
2058
2180
  const noteAction = action === "view" ? "view" : "edit";
2059
- const notesDir = join5(config.getWorkDir(), ".notes");
2060
- const notePath = join5(notesDir, `${problemId}.md`);
2181
+ const notesDir = join6(config.getWorkDir(), ".notes");
2182
+ const notePath = join6(notesDir, `${problemId}.md`);
2061
2183
  if (!existsSync6(notesDir)) {
2062
2184
  await mkdir3(notesDir, { recursive: true });
2063
2185
  }
@@ -2376,162 +2498,318 @@ async function syncCommand() {
2376
2498
  }
2377
2499
  }
2378
2500
 
2501
+ // src/commands/timer.ts
2502
+ import ora14 from "ora";
2503
+ import chalk20 from "chalk";
2504
+ var DEFAULT_TIMES = {
2505
+ Easy: 20,
2506
+ Medium: 40,
2507
+ Hard: 60
2508
+ };
2509
+ function formatDuration(seconds) {
2510
+ if (seconds < 60) {
2511
+ return `${seconds}s`;
2512
+ } else if (seconds < 3600) {
2513
+ const mins = Math.floor(seconds / 60);
2514
+ const secs = seconds % 60;
2515
+ return secs > 0 ? `${mins}m ${secs}s` : `${mins}m`;
2516
+ } else {
2517
+ const hours = Math.floor(seconds / 3600);
2518
+ const mins = Math.floor(seconds % 3600 / 60);
2519
+ return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`;
2520
+ }
2521
+ }
2522
+ async function timerCommand(idOrSlug, options) {
2523
+ if (options.stats) {
2524
+ await showTimerStats(idOrSlug);
2525
+ return;
2526
+ }
2527
+ if (options.stop) {
2528
+ await stopActiveTimer();
2529
+ return;
2530
+ }
2531
+ if (!idOrSlug) {
2532
+ console.log(chalk20.yellow("Please provide a problem ID to start the timer."));
2533
+ console.log(chalk20.gray("Usage: leetcode timer <id>"));
2534
+ console.log(chalk20.gray(" leetcode timer --stats"));
2535
+ console.log(chalk20.gray(" leetcode timer --stop"));
2536
+ return;
2537
+ }
2538
+ const { authorized } = await requireAuth();
2539
+ if (!authorized) return;
2540
+ const activeTimer = timerStorage.getActiveTimer();
2541
+ if (activeTimer) {
2542
+ const startedAt = new Date(activeTimer.startedAt);
2543
+ const elapsed = Math.floor((Date.now() - startedAt.getTime()) / 1e3);
2544
+ console.log(chalk20.yellow("\u26A0\uFE0F You have an active timer running:"));
2545
+ console.log(chalk20.white(` Problem: ${activeTimer.title}`));
2546
+ console.log(chalk20.white(` Elapsed: ${formatDuration(elapsed)}`));
2547
+ console.log();
2548
+ console.log(chalk20.gray("Use `leetcode timer --stop` to stop it first."));
2549
+ return;
2550
+ }
2551
+ const spinner = ora14("Fetching problem...").start();
2552
+ try {
2553
+ let problem;
2554
+ if (/^\d+$/.test(idOrSlug)) {
2555
+ problem = await leetcodeClient.getProblemById(idOrSlug);
2556
+ } else {
2557
+ problem = await leetcodeClient.getProblem(idOrSlug);
2558
+ }
2559
+ if (!problem) {
2560
+ spinner.fail(`Problem "${idOrSlug}" not found`);
2561
+ return;
2562
+ }
2563
+ spinner.stop();
2564
+ const durationMinutes = options.minutes ?? DEFAULT_TIMES[problem.difficulty] ?? 30;
2565
+ timerStorage.startTimer(
2566
+ problem.questionFrontendId,
2567
+ problem.title,
2568
+ problem.difficulty,
2569
+ durationMinutes
2570
+ );
2571
+ console.log();
2572
+ console.log(chalk20.bold.cyan("\u23F1\uFE0F Interview Mode Started!"));
2573
+ console.log(chalk20.gray("\u2500".repeat(50)));
2574
+ console.log();
2575
+ console.log(chalk20.white(`Problem: ${problem.questionFrontendId}. ${problem.title}`));
2576
+ console.log(chalk20.white(`Difficulty: ${chalk20.bold(problem.difficulty)}`));
2577
+ console.log(chalk20.white(`Time Limit: ${chalk20.bold.yellow(durationMinutes + " minutes")}`));
2578
+ console.log();
2579
+ console.log(chalk20.gray("\u2500".repeat(50)));
2580
+ console.log(chalk20.green("\u2713 Timer is running in background"));
2581
+ console.log(chalk20.gray(" When you submit successfully, your time will be recorded."));
2582
+ console.log(chalk20.gray(" Use `leetcode timer --stop` to cancel."));
2583
+ console.log();
2584
+ await pickCommand(idOrSlug, { open: true });
2585
+ } catch (error) {
2586
+ spinner.fail("Failed to start timer");
2587
+ if (error instanceof Error) {
2588
+ console.log(chalk20.red(error.message));
2589
+ }
2590
+ }
2591
+ }
2592
+ async function stopActiveTimer() {
2593
+ const result = timerStorage.stopTimer();
2594
+ if (!result) {
2595
+ console.log(chalk20.yellow("No active timer to stop."));
2596
+ return;
2597
+ }
2598
+ console.log(chalk20.green("\u23F1\uFE0F Timer stopped."));
2599
+ console.log(chalk20.gray(`Elapsed time: ${formatDuration(result.durationSeconds)}`));
2600
+ console.log(chalk20.gray("(Time not recorded since problem was not submitted)"));
2601
+ }
2602
+ async function showTimerStats(problemId) {
2603
+ if (problemId && /^\d+$/.test(problemId)) {
2604
+ const times = timerStorage.getSolveTimes(problemId);
2605
+ if (times.length === 0) {
2606
+ console.log(chalk20.yellow(`No solve times recorded for problem ${problemId}`));
2607
+ return;
2608
+ }
2609
+ console.log();
2610
+ console.log(chalk20.bold(`\u23F1\uFE0F Solve Times for Problem ${problemId}`));
2611
+ console.log(chalk20.gray("\u2500".repeat(40)));
2612
+ for (const entry of times) {
2613
+ const date = new Date(entry.solvedAt).toLocaleDateString();
2614
+ const duration = formatDuration(entry.durationSeconds);
2615
+ const limit = entry.timerMinutes;
2616
+ const withinLimit = entry.durationSeconds <= limit * 60;
2617
+ console.log(
2618
+ ` ${date} ${withinLimit ? chalk20.green(duration) : chalk20.red(duration)} (limit: ${limit}m)`
2619
+ );
2620
+ }
2621
+ } else {
2622
+ const stats = timerStorage.getStats();
2623
+ const allTimes = timerStorage.getAllSolveTimes();
2624
+ console.log();
2625
+ console.log(chalk20.bold("\u23F1\uFE0F Timer Statistics"));
2626
+ console.log(chalk20.gray("\u2500".repeat(40)));
2627
+ console.log();
2628
+ console.log(` Problems timed: ${chalk20.cyan(stats.totalProblems)}`);
2629
+ console.log(` Total time: ${chalk20.cyan(formatDuration(stats.totalTime))}`);
2630
+ console.log(` Average time: ${chalk20.cyan(formatDuration(stats.avgTime))}`);
2631
+ console.log();
2632
+ const recentSolves = [];
2633
+ for (const [id, times] of Object.entries(allTimes)) {
2634
+ for (const t of times) {
2635
+ recentSolves.push({
2636
+ problemId: id,
2637
+ title: t.title,
2638
+ duration: t.durationSeconds,
2639
+ date: t.solvedAt
2640
+ });
2641
+ }
2642
+ }
2643
+ if (recentSolves.length > 0) {
2644
+ recentSolves.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
2645
+ console.log(chalk20.bold(" Recent Solves:"));
2646
+ for (const solve of recentSolves.slice(0, 5)) {
2647
+ const date = new Date(solve.date).toLocaleDateString();
2648
+ console.log(
2649
+ chalk20.gray(` ${date} `) + chalk20.white(`${solve.problemId}. ${solve.title.substring(0, 25)}`) + chalk20.gray(" ") + chalk20.cyan(formatDuration(solve.duration))
2650
+ );
2651
+ }
2652
+ }
2653
+ console.log();
2654
+ }
2655
+ }
2656
+
2379
2657
  // src/index.ts
2380
2658
  var program = new Command();
2381
2659
  program.configureHelp({
2382
2660
  sortSubcommands: true,
2383
- subcommandTerm: (cmd) => chalk20.cyan(cmd.name()) + (cmd.alias() ? chalk20.gray(`|${cmd.alias()}`) : ""),
2384
- subcommandDescription: (cmd) => chalk20.white(cmd.description()),
2385
- optionTerm: (option) => chalk20.yellow(option.flags),
2386
- optionDescription: (option) => chalk20.white(option.description)
2661
+ subcommandTerm: (cmd) => chalk21.cyan(cmd.name()) + (cmd.alias() ? chalk21.gray(`|${cmd.alias()}`) : ""),
2662
+ subcommandDescription: (cmd) => chalk21.white(cmd.description()),
2663
+ optionTerm: (option) => chalk21.yellow(option.flags),
2664
+ optionDescription: (option) => chalk21.white(option.description)
2387
2665
  });
2388
- program.name("leetcode").usage("[command] [options]").description(chalk20.bold.cyan("\u{1F525} A modern LeetCode CLI built with TypeScript")).version("1.4.0", "-v, --version", "Output the version number").helpOption("-h, --help", "Display help for command").addHelpText("after", `
2389
- ${chalk20.yellow("Examples:")}
2390
- ${chalk20.cyan("$ leetcode login")} Login to LeetCode
2391
- ${chalk20.cyan("$ leetcode list -d easy")} List easy problems
2392
- ${chalk20.cyan("$ leetcode random -d medium")} Get random medium problem
2393
- ${chalk20.cyan("$ leetcode pick 1")} Start solving "Two Sum"
2394
- ${chalk20.cyan("$ leetcode test 1")} Test your solution
2395
- ${chalk20.cyan("$ leetcode submit 1")} Submit your solution
2666
+ program.name("leetcode").usage("[command] [options]").description(chalk21.bold.cyan("\u{1F525} A modern LeetCode CLI built with TypeScript")).version("1.5.0", "-v, --version", "Output the version number").helpOption("-h, --help", "Display help for command").addHelpText("after", `
2667
+ ${chalk21.yellow("Examples:")}
2668
+ ${chalk21.cyan("$ leetcode login")} Login to LeetCode
2669
+ ${chalk21.cyan("$ leetcode list -d easy")} List easy problems
2670
+ ${chalk21.cyan("$ leetcode random -d medium")} Get random medium problem
2671
+ ${chalk21.cyan("$ leetcode pick 1")} Start solving "Two Sum"
2672
+ ${chalk21.cyan("$ leetcode test 1")} Test your solution
2673
+ ${chalk21.cyan("$ leetcode submit 1")} Submit your solution
2396
2674
  `);
2397
2675
  program.command("login").description("Login to LeetCode with browser cookies").addHelpText("after", `
2398
- ${chalk20.yellow("How to login:")}
2399
- 1. Open ${chalk20.cyan("https://leetcode.com")} in your browser
2676
+ ${chalk21.yellow("How to login:")}
2677
+ 1. Open ${chalk21.cyan("https://leetcode.com")} in your browser
2400
2678
  2. Login to your account
2401
2679
  3. Open Developer Tools (F12) \u2192 Application \u2192 Cookies
2402
- 4. Copy values of ${chalk20.green("LEETCODE_SESSION")} and ${chalk20.green("csrftoken")}
2680
+ 4. Copy values of ${chalk21.green("LEETCODE_SESSION")} and ${chalk21.green("csrftoken")}
2403
2681
  5. Paste when prompted by this command
2404
2682
  `).action(loginCommand);
2405
2683
  program.command("logout").description("Clear stored credentials").action(logoutCommand);
2406
2684
  program.command("whoami").description("Check current login status").action(whoamiCommand);
2407
2685
  program.command("list").alias("l").description("List LeetCode problems").option("-d, --difficulty <level>", "Filter by difficulty (easy/medium/hard)").option("-s, --status <status>", "Filter by status (todo/solved/attempted)").option("-t, --tag <tags...>", "Filter by topic tags").option("-q, --search <keywords>", "Search by keywords").option("-n, --limit <number>", "Number of problems to show", "20").option("-p, --page <number>", "Page number", "1").addHelpText("after", `
2408
- ${chalk20.yellow("Examples:")}
2409
- ${chalk20.cyan("$ leetcode list")} List first 20 problems
2410
- ${chalk20.cyan("$ leetcode list -d easy")} List easy problems only
2411
- ${chalk20.cyan("$ leetcode list -s solved")} List your solved problems
2412
- ${chalk20.cyan("$ leetcode list -t array -t string")} Filter by multiple tags
2413
- ${chalk20.cyan('$ leetcode list -q "two sum"')} Search by keywords
2414
- ${chalk20.cyan("$ leetcode list -n 50 -p 2")} Show 50 problems, page 2
2686
+ ${chalk21.yellow("Examples:")}
2687
+ ${chalk21.cyan("$ leetcode list")} List first 20 problems
2688
+ ${chalk21.cyan("$ leetcode list -d easy")} List easy problems only
2689
+ ${chalk21.cyan("$ leetcode list -s solved")} List your solved problems
2690
+ ${chalk21.cyan("$ leetcode list -t array -t string")} Filter by multiple tags
2691
+ ${chalk21.cyan('$ leetcode list -q "two sum"')} Search by keywords
2692
+ ${chalk21.cyan("$ leetcode list -n 50 -p 2")} Show 50 problems, page 2
2415
2693
  `).action(listCommand);
2416
2694
  program.command("show <id>").alias("s").description("Show problem description").addHelpText("after", `
2417
- ${chalk20.yellow("Examples:")}
2418
- ${chalk20.cyan("$ leetcode show 1")} Show by problem ID
2419
- ${chalk20.cyan("$ leetcode show two-sum")} Show by problem slug
2420
- ${chalk20.cyan("$ leetcode s 412")} Short alias
2695
+ ${chalk21.yellow("Examples:")}
2696
+ ${chalk21.cyan("$ leetcode show 1")} Show by problem ID
2697
+ ${chalk21.cyan("$ leetcode show two-sum")} Show by problem slug
2698
+ ${chalk21.cyan("$ leetcode s 412")} Short alias
2421
2699
  `).action(showCommand);
2422
2700
  program.command("daily").alias("d").description("Show today's daily challenge").addHelpText("after", `
2423
- ${chalk20.yellow("Examples:")}
2424
- ${chalk20.cyan("$ leetcode daily")} Show today's challenge
2425
- ${chalk20.cyan("$ leetcode d")} Short alias
2701
+ ${chalk21.yellow("Examples:")}
2702
+ ${chalk21.cyan("$ leetcode daily")} Show today's challenge
2703
+ ${chalk21.cyan("$ leetcode d")} Short alias
2426
2704
  `).action(dailyCommand);
2427
2705
  program.command("random").alias("r").description("Get a random problem").option("-d, --difficulty <level>", "Filter by difficulty (easy/medium/hard)").option("-t, --tag <tag>", "Filter by topic tag").option("--pick", "Auto-generate solution file").option("--no-open", "Do not open file in editor").addHelpText("after", `
2428
- ${chalk20.yellow("Examples:")}
2429
- ${chalk20.cyan("$ leetcode random")} Get any random problem
2430
- ${chalk20.cyan("$ leetcode random -d medium")} Random medium problem
2431
- ${chalk20.cyan("$ leetcode random -t array")} Random array problem
2432
- ${chalk20.cyan("$ leetcode random --pick")} Random + create file
2433
- ${chalk20.cyan("$ leetcode r -d easy --pick")} Random easy + file
2706
+ ${chalk21.yellow("Examples:")}
2707
+ ${chalk21.cyan("$ leetcode random")} Get any random problem
2708
+ ${chalk21.cyan("$ leetcode random -d medium")} Random medium problem
2709
+ ${chalk21.cyan("$ leetcode random -t array")} Random array problem
2710
+ ${chalk21.cyan("$ leetcode random --pick")} Random + create file
2711
+ ${chalk21.cyan("$ leetcode r -d easy --pick")} Random easy + file
2434
2712
  `).action(randomCommand);
2435
2713
  program.command("pick <id>").alias("p").description("Generate solution file for a problem").option("-l, --lang <language>", "Programming language for the solution").option("--no-open", "Do not open file in editor").addHelpText("after", `
2436
- ${chalk20.yellow("Examples:")}
2437
- ${chalk20.cyan("$ leetcode pick 1")} Pick by problem ID
2438
- ${chalk20.cyan("$ leetcode pick two-sum")} Pick by problem slug
2439
- ${chalk20.cyan("$ leetcode pick 1 -l python3")} Pick with specific language
2440
- ${chalk20.cyan("$ leetcode pick 1 --no-open")} Create file without opening
2441
- ${chalk20.cyan("$ leetcode p 412")} Short alias
2714
+ ${chalk21.yellow("Examples:")}
2715
+ ${chalk21.cyan("$ leetcode pick 1")} Pick by problem ID
2716
+ ${chalk21.cyan("$ leetcode pick two-sum")} Pick by problem slug
2717
+ ${chalk21.cyan("$ leetcode pick 1 -l python3")} Pick with specific language
2718
+ ${chalk21.cyan("$ leetcode pick 1 --no-open")} Create file without opening
2719
+ ${chalk21.cyan("$ leetcode p 412")} Short alias
2442
2720
 
2443
- ${chalk20.gray("Files are organized by: workDir/Difficulty/Category/")}
2721
+ ${chalk21.gray("Files are organized by: workDir/Difficulty/Category/")}
2444
2722
  `).action(async (id, options) => {
2445
2723
  await pickCommand(id, options);
2446
2724
  });
2447
2725
  program.command("pick-batch <ids...>").description("Generate solution files for multiple problems").option("-l, --lang <language>", "Programming language for the solutions").addHelpText("after", `
2448
- ${chalk20.yellow("Examples:")}
2449
- ${chalk20.cyan("$ leetcode pick-batch 1 2 3")} Pick problems 1, 2, and 3
2450
- ${chalk20.cyan("$ leetcode pick-batch 1 2 3 -l py")} Pick with Python
2726
+ ${chalk21.yellow("Examples:")}
2727
+ ${chalk21.cyan("$ leetcode pick-batch 1 2 3")} Pick problems 1, 2, and 3
2728
+ ${chalk21.cyan("$ leetcode pick-batch 1 2 3 -l py")} Pick with Python
2451
2729
  `).action(batchPickCommand);
2452
2730
  program.command("test <file>").alias("t").description("Test solution against sample test cases").option("-c, --testcase <testcase>", "Custom test case").addHelpText("after", `
2453
- ${chalk20.yellow("Examples:")}
2454
- ${chalk20.cyan("$ leetcode test 1")} Test by problem ID
2455
- ${chalk20.cyan("$ leetcode test two-sum")} Test by problem slug
2456
- ${chalk20.cyan("$ leetcode test ./path/to/file.py")} Test by file path
2457
- ${chalk20.cyan('$ leetcode test 1 -c "[1,2]\\n3"')} Test with custom case
2458
- ${chalk20.cyan("$ leetcode t 412")} Short alias
2731
+ ${chalk21.yellow("Examples:")}
2732
+ ${chalk21.cyan("$ leetcode test 1")} Test by problem ID
2733
+ ${chalk21.cyan("$ leetcode test two-sum")} Test by problem slug
2734
+ ${chalk21.cyan("$ leetcode test ./path/to/file.py")} Test by file path
2735
+ ${chalk21.cyan('$ leetcode test 1 -c "[1,2]\\n3"')} Test with custom case
2736
+ ${chalk21.cyan("$ leetcode t 412")} Short alias
2459
2737
 
2460
- ${chalk20.gray("Testcases use \\n to separate multiple inputs.")}
2738
+ ${chalk21.gray("Testcases use \\n to separate multiple inputs.")}
2461
2739
  `).action(testCommand);
2462
2740
  program.command("submit <file>").alias("x").description("Submit solution to LeetCode").addHelpText("after", `
2463
- ${chalk20.yellow("Examples:")}
2464
- ${chalk20.cyan("$ leetcode submit 1")} Submit by problem ID
2465
- ${chalk20.cyan("$ leetcode submit two-sum")} Submit by problem slug
2466
- ${chalk20.cyan("$ leetcode submit ./path/to/file.py")} Submit by file path
2467
- ${chalk20.cyan("$ leetcode x 412")} Short alias
2741
+ ${chalk21.yellow("Examples:")}
2742
+ ${chalk21.cyan("$ leetcode submit 1")} Submit by problem ID
2743
+ ${chalk21.cyan("$ leetcode submit two-sum")} Submit by problem slug
2744
+ ${chalk21.cyan("$ leetcode submit ./path/to/file.py")} Submit by file path
2745
+ ${chalk21.cyan("$ leetcode x 412")} Short alias
2468
2746
  `).action(submitCommand);
2469
2747
  program.command("submissions <id>").description("View past submissions").option("-n, --limit <number>", "Number of submissions to show", "20").option("--last", "Show details of the last accepted submission").option("--download", "Download the last accepted submission code").addHelpText("after", `
2470
- ${chalk20.yellow("Examples:")}
2471
- ${chalk20.cyan("$ leetcode submissions 1")} View submissions for problem
2472
- ${chalk20.cyan("$ leetcode submissions 1 -n 5")} Show last 5 submissions
2473
- ${chalk20.cyan("$ leetcode submissions 1 --last")} Show last accepted submission
2474
- ${chalk20.cyan("$ leetcode submissions 1 --download")} Download last accepted code
2748
+ ${chalk21.yellow("Examples:")}
2749
+ ${chalk21.cyan("$ leetcode submissions 1")} View submissions for problem
2750
+ ${chalk21.cyan("$ leetcode submissions 1 -n 5")} Show last 5 submissions
2751
+ ${chalk21.cyan("$ leetcode submissions 1 --last")} Show last accepted submission
2752
+ ${chalk21.cyan("$ leetcode submissions 1 --download")} Download last accepted code
2475
2753
  `).action(submissionsCommand);
2476
2754
  program.command("stat [username]").description("Show user statistics and analytics").option("-c, --calendar", "Weekly activity summary (submissions & active days for last 12 weeks)").option("-s, --skills", "Skill breakdown (problems solved grouped by topic tags)").option("-t, --trend", "Daily trend chart (bar graph of submissions for last 7 days)").addHelpText("after", `
2477
- ${chalk20.yellow("Options Explained:")}
2478
- ${chalk20.cyan("-c, --calendar")} Shows a table of your weekly submissions and active days
2755
+ ${chalk21.yellow("Options Explained:")}
2756
+ ${chalk21.cyan("-c, --calendar")} Shows a table of your weekly submissions and active days
2479
2757
  for the past 12 weeks. Useful for tracking consistency.
2480
2758
 
2481
- ${chalk20.cyan("-s, --skills")} Shows how many problems you solved per topic tag,
2759
+ ${chalk21.cyan("-s, --skills")} Shows how many problems you solved per topic tag,
2482
2760
  grouped by difficulty (Fundamental/Intermediate/Advanced).
2483
2761
  Helps identify your strong and weak areas.
2484
2762
 
2485
- ${chalk20.cyan("-t, --trend")} Shows a bar chart of daily submissions for the past week.
2763
+ ${chalk21.cyan("-t, --trend")} Shows a bar chart of daily submissions for the past week.
2486
2764
  Visualizes your recent coding activity day by day.
2487
2765
 
2488
- ${chalk20.yellow("Examples:")}
2489
- ${chalk20.cyan("$ leetcode stat")} Show basic stats (solved count, rank)
2490
- ${chalk20.cyan("$ leetcode stat lee215")} Show another user's stats
2491
- ${chalk20.cyan("$ leetcode stat -c")} Weekly activity table
2492
- ${chalk20.cyan("$ leetcode stat -s")} Topic-wise breakdown
2493
- ${chalk20.cyan("$ leetcode stat -t")} 7-day trend chart
2766
+ ${chalk21.yellow("Examples:")}
2767
+ ${chalk21.cyan("$ leetcode stat")} Show basic stats (solved count, rank)
2768
+ ${chalk21.cyan("$ leetcode stat lee215")} Show another user's stats
2769
+ ${chalk21.cyan("$ leetcode stat -c")} Weekly activity table
2770
+ ${chalk21.cyan("$ leetcode stat -s")} Topic-wise breakdown
2771
+ ${chalk21.cyan("$ leetcode stat -t")} 7-day trend chart
2494
2772
  `).action((username, options) => statCommand(username, options));
2495
2773
  program.command("today").description("Show today's progress summary").addHelpText("after", `
2496
- ${chalk20.yellow("Examples:")}
2497
- ${chalk20.cyan("$ leetcode today")} Show streak, solved, and daily challenge
2774
+ ${chalk21.yellow("Examples:")}
2775
+ ${chalk21.cyan("$ leetcode today")} Show streak, solved, and daily challenge
2498
2776
  `).action(todayCommand);
2499
2777
  program.command("bookmark <action> [id]").description("Manage problem bookmarks").addHelpText("after", `
2500
- ${chalk20.yellow("Actions:")}
2501
- ${chalk20.cyan("add <id>")} Bookmark a problem
2502
- ${chalk20.cyan("remove <id>")} Remove a bookmark
2503
- ${chalk20.cyan("list")} List all bookmarks
2504
- ${chalk20.cyan("clear")} Clear all bookmarks
2778
+ ${chalk21.yellow("Actions:")}
2779
+ ${chalk21.cyan("add <id>")} Bookmark a problem
2780
+ ${chalk21.cyan("remove <id>")} Remove a bookmark
2781
+ ${chalk21.cyan("list")} List all bookmarks
2782
+ ${chalk21.cyan("clear")} Clear all bookmarks
2505
2783
 
2506
- ${chalk20.yellow("Examples:")}
2507
- ${chalk20.cyan("$ leetcode bookmark add 1")} Bookmark problem 1
2508
- ${chalk20.cyan("$ leetcode bookmark remove 1")} Remove bookmark
2509
- ${chalk20.cyan("$ leetcode bookmark list")} List all bookmarks
2784
+ ${chalk21.yellow("Examples:")}
2785
+ ${chalk21.cyan("$ leetcode bookmark add 1")} Bookmark problem 1
2786
+ ${chalk21.cyan("$ leetcode bookmark remove 1")} Remove bookmark
2787
+ ${chalk21.cyan("$ leetcode bookmark list")} List all bookmarks
2510
2788
  `).action(bookmarkCommand);
2511
2789
  program.command("note <id> [action]").description("View or edit notes for a problem").addHelpText("after", `
2512
- ${chalk20.yellow("Actions:")}
2513
- ${chalk20.cyan("edit")} Open notes in editor (default)
2514
- ${chalk20.cyan("view")} Display notes in terminal
2790
+ ${chalk21.yellow("Actions:")}
2791
+ ${chalk21.cyan("edit")} Open notes in editor (default)
2792
+ ${chalk21.cyan("view")} Display notes in terminal
2515
2793
 
2516
- ${chalk20.yellow("Examples:")}
2517
- ${chalk20.cyan("$ leetcode note 1")} Edit notes for problem 1
2518
- ${chalk20.cyan("$ leetcode note 1 edit")} Edit notes (explicit)
2519
- ${chalk20.cyan("$ leetcode note 1 view")} View notes in terminal
2794
+ ${chalk21.yellow("Examples:")}
2795
+ ${chalk21.cyan("$ leetcode note 1")} Edit notes for problem 1
2796
+ ${chalk21.cyan("$ leetcode note 1 edit")} Edit notes (explicit)
2797
+ ${chalk21.cyan("$ leetcode note 1 view")} View notes in terminal
2520
2798
  `).action(notesCommand);
2521
2799
  program.command("sync").description("Sync solutions to Git repository").addHelpText("after", `
2522
- ${chalk20.yellow("Examples:")}
2523
- ${chalk20.cyan("$ leetcode sync")} Sync all solutions to remote
2800
+ ${chalk21.yellow("Examples:")}
2801
+ ${chalk21.cyan("$ leetcode sync")} Sync all solutions to remote
2524
2802
  `).action(syncCommand);
2525
2803
  program.command("config").description("View or set configuration").option("-l, --lang <language>", "Set default programming language").option("-e, --editor <editor>", "Set editor command").option("-w, --workdir <path>", "Set working directory for solutions").option("-r, --repo <url>", "Set Git repository URL").option("-i, --interactive", "Interactive configuration").addHelpText("after", `
2526
- ${chalk20.yellow("Examples:")}
2527
- ${chalk20.cyan("$ leetcode config")} View current config
2528
- ${chalk20.cyan("$ leetcode config -l python3")} Set language to Python
2529
- ${chalk20.cyan('$ leetcode config -e "code"')} Set editor to VS Code
2530
- ${chalk20.cyan("$ leetcode config -w ~/leetcode")} Set solutions folder
2531
- ${chalk20.cyan("$ leetcode config -r https://...")} Set git repository
2532
- ${chalk20.cyan("$ leetcode config -i")} Interactive setup
2804
+ ${chalk21.yellow("Examples:")}
2805
+ ${chalk21.cyan("$ leetcode config")} View current config
2806
+ ${chalk21.cyan("$ leetcode config -l python3")} Set language to Python
2807
+ ${chalk21.cyan('$ leetcode config -e "code"')} Set editor to VS Code
2808
+ ${chalk21.cyan("$ leetcode config -w ~/leetcode")} Set solutions folder
2809
+ ${chalk21.cyan("$ leetcode config -r https://...")} Set git repository
2810
+ ${chalk21.cyan("$ leetcode config -i")} Interactive setup
2533
2811
 
2534
- ${chalk20.gray("Supported languages: typescript, javascript, python3, java, cpp, c, csharp, go, rust, kotlin, swift")}
2812
+ ${chalk21.gray("Supported languages: typescript, javascript, python3, java, cpp, c, csharp, go, rust, kotlin, swift")}
2535
2813
  `).action(async (options) => {
2536
2814
  if (options.interactive) {
2537
2815
  await configInteractiveCommand();
@@ -2539,12 +2817,24 @@ ${chalk20.gray("Supported languages: typescript, javascript, python3, java, cpp,
2539
2817
  await configCommand(options);
2540
2818
  }
2541
2819
  });
2820
+ program.command("timer [id]").description("Start interview mode with timer").option("-m, --minutes <minutes>", "Custom time limit in minutes").option("--stats", "Show solve time statistics").option("--stop", "Stop active timer").addHelpText("after", `
2821
+ ${chalk21.yellow("How it works:")}
2822
+ Start a problem with a countdown timer to simulate interview conditions.
2823
+ Default time limits: Easy (20 min), Medium (40 min), Hard (60 min).
2824
+ Your solve times are recorded when you submit successfully.
2825
+
2826
+ ${chalk21.yellow("Examples:")}
2827
+ ${chalk21.cyan("$ leetcode timer 1")} Start problem 1 with default time
2828
+ ${chalk21.cyan("$ leetcode timer 1 -m 30")} Start with 30 minute limit
2829
+ ${chalk21.cyan("$ leetcode timer --stats")} Show your solve time statistics
2830
+ ${chalk21.cyan("$ leetcode timer --stop")} Stop active timer
2831
+ `).action((id, options) => timerCommand(id, options));
2542
2832
  program.showHelpAfterError("(add --help for additional information)");
2543
2833
  program.parse();
2544
2834
  if (!process.argv.slice(2).length) {
2545
2835
  console.log();
2546
- console.log(chalk20.bold.cyan(" \u{1F525} LeetCode CLI"));
2547
- console.log(chalk20.gray(" A modern command-line interface for LeetCode"));
2836
+ console.log(chalk21.bold.cyan(" \u{1F525} LeetCode CLI"));
2837
+ console.log(chalk21.gray(" A modern command-line interface for LeetCode"));
2548
2838
  console.log();
2549
2839
  program.outputHelp();
2550
2840
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@night-slayer18/leetcode-cli",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "A modern LeetCode CLI built with TypeScript",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",