@night-slayer18/leetcode-cli 1.3.2 → 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 +45 -1
  2. package/dist/index.js +874 -204
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { Command } from "commander";
5
- import chalk18 from "chalk";
5
+ import chalk21 from "chalk";
6
6
 
7
7
  // src/commands/login.ts
8
8
  import inquirer from "inquirer";
@@ -111,7 +111,8 @@ var UserProfileSchema = z.object({
111
111
  }),
112
112
  userCalendar: z.object({
113
113
  streak: z.number(),
114
- totalActiveDays: z.number()
114
+ totalActiveDays: z.number(),
115
+ submissionCalendar: z.string().optional()
115
116
  })
116
117
  });
117
118
  var UserStatusSchema = z.object({
@@ -202,6 +203,30 @@ var USER_PROFILE_QUERY = `
202
203
  userCalendar {
203
204
  streak
204
205
  totalActiveDays
206
+ submissionCalendar
207
+ }
208
+ }
209
+ }
210
+ `;
211
+ var SKILL_STATS_QUERY = `
212
+ query skillStats($username: String!) {
213
+ matchedUser(username: $username) {
214
+ tagProblemCounts {
215
+ fundamental {
216
+ tagName
217
+ tagSlug
218
+ problemsSolved
219
+ }
220
+ intermediate {
221
+ tagName
222
+ tagSlug
223
+ problemsSolved
224
+ }
225
+ advanced {
226
+ tagName
227
+ tagSlug
228
+ problemsSolved
229
+ }
205
230
  }
206
231
  }
207
232
  }
@@ -381,9 +406,14 @@ var LeetCodeClient = class {
381
406
  ranking: validated.profile.ranking,
382
407
  acSubmissionNum: validated.submitStatsGlobal.acSubmissionNum,
383
408
  streak: validated.userCalendar.streak,
384
- totalActiveDays: validated.userCalendar.totalActiveDays
409
+ totalActiveDays: validated.userCalendar.totalActiveDays,
410
+ submissionCalendar: user.userCalendar.submissionCalendar
385
411
  };
386
412
  }
413
+ async getSkillStats(username) {
414
+ const data = await this.graphql(SKILL_STATS_QUERY, { username });
415
+ return data.matchedUser.tagProblemCounts;
416
+ }
387
417
  async getSubmissionList(slug, limit = 20, offset = 0) {
388
418
  const data = await this.graphql(SUBMISSION_LIST_QUERY, { questionSlug: slug, limit, offset });
389
419
  const validated = z2.array(SubmissionSchema).parse(data.questionSubmissionList.submissions);
@@ -449,7 +479,8 @@ var schema = {
449
479
  properties: {
450
480
  language: { type: "string", default: "typescript" },
451
481
  editor: { type: "string" },
452
- workDir: { type: "string", default: join(homedir(), "leetcode") }
482
+ workDir: { type: "string", default: join(homedir(), "leetcode") },
483
+ repo: { type: "string" }
453
484
  },
454
485
  default: {
455
486
  language: "typescript",
@@ -486,6 +517,12 @@ var config = {
486
517
  setWorkDir(workDir) {
487
518
  configStore.set("config.workDir", workDir);
488
519
  },
520
+ setRepo(repo) {
521
+ configStore.set("config.repo", repo);
522
+ },
523
+ deleteRepo() {
524
+ configStore.delete("config.repo");
525
+ },
489
526
  // Get specific config values
490
527
  getLanguage() {
491
528
  return configStore.get("config.language");
@@ -496,6 +533,9 @@ var config = {
496
533
  getWorkDir() {
497
534
  return configStore.get("config.workDir");
498
535
  },
536
+ getRepo() {
537
+ return configStore.get("config.repo");
538
+ },
499
539
  // Clear all config
500
540
  clear() {
501
541
  configStore.clear();
@@ -1238,12 +1278,16 @@ async function findSolutionFile(dir, problemId, currentDepth = 0) {
1238
1278
  if (currentDepth >= MAX_SEARCH_DEPTH) return null;
1239
1279
  const entries = await readdir(dir, { withFileTypes: true });
1240
1280
  for (const entry of entries) {
1281
+ if (entry.name.startsWith(".")) continue;
1241
1282
  const fullPath = join3(dir, entry.name);
1242
1283
  if (entry.isDirectory()) {
1243
1284
  const found = await findSolutionFile(fullPath, problemId, currentDepth + 1);
1244
1285
  if (found) return found;
1245
1286
  } else if (entry.name.startsWith(`${problemId}.`)) {
1246
- return fullPath;
1287
+ const ext = entry.name.split(".").pop()?.toLowerCase();
1288
+ if (ext && ext in EXT_TO_LANG_MAP) {
1289
+ return fullPath;
1290
+ }
1247
1291
  }
1248
1292
  }
1249
1293
  return null;
@@ -1263,6 +1307,19 @@ async function findFileByName(dir, fileName, currentDepth = 0) {
1263
1307
  }
1264
1308
  return null;
1265
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
+ };
1266
1323
  function getLangSlugFromExtension(ext) {
1267
1324
  const langMap = {
1268
1325
  ts: "typescript",
@@ -1358,6 +1415,82 @@ import { existsSync as existsSync4 } from "fs";
1358
1415
  import { basename as basename2 } from "path";
1359
1416
  import ora6 from "ora";
1360
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
1361
1494
  async function submitCommand(fileOrId) {
1362
1495
  const { authorized } = await requireAuth();
1363
1496
  if (!authorized) return;
@@ -1417,6 +1550,35 @@ async function submitCommand(fileOrId) {
1417
1550
  );
1418
1551
  spinner.stop();
1419
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
+ }
1420
1582
  } catch (error) {
1421
1583
  spinner.fail("Submission failed");
1422
1584
  if (error instanceof Error) {
@@ -1427,8 +1589,121 @@ async function submitCommand(fileOrId) {
1427
1589
 
1428
1590
  // src/commands/stat.ts
1429
1591
  import ora7 from "ora";
1592
+ import chalk11 from "chalk";
1593
+
1594
+ // src/utils/stats-display.ts
1430
1595
  import chalk10 from "chalk";
1431
- async function statCommand(username) {
1596
+ var MONTH_NAMES = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
1597
+ function renderHeatmap(calendarJson) {
1598
+ const data = JSON.parse(calendarJson);
1599
+ const now = /* @__PURE__ */ new Date();
1600
+ const weeks = [];
1601
+ for (let w = 11; w >= 0; w--) {
1602
+ const weekStart = new Date(now);
1603
+ weekStart.setDate(weekStart.getDate() - w * 7 - weekStart.getDay());
1604
+ let weekCount = 0;
1605
+ let activeDays = 0;
1606
+ for (let d = 0; d < 7; d++) {
1607
+ const day = new Date(weekStart);
1608
+ day.setDate(day.getDate() + d);
1609
+ if (day > now) break;
1610
+ const midnight = new Date(Date.UTC(day.getFullYear(), day.getMonth(), day.getDate()));
1611
+ const timestamp = Math.floor(midnight.getTime() / 1e3).toString();
1612
+ const count = data[timestamp] || 0;
1613
+ weekCount += count;
1614
+ if (count > 0) activeDays++;
1615
+ }
1616
+ const weekEnd = new Date(weekStart);
1617
+ weekEnd.setDate(weekEnd.getDate() + 6);
1618
+ weeks.push({
1619
+ start: `${MONTH_NAMES[weekStart.getMonth()]} ${weekStart.getDate()}`,
1620
+ end: `${MONTH_NAMES[weekEnd.getMonth()]} ${weekEnd.getDate()}`,
1621
+ count: weekCount,
1622
+ days: activeDays
1623
+ });
1624
+ }
1625
+ const totalSubmissions = weeks.reduce((sum, w) => sum + w.count, 0);
1626
+ const totalActiveDays = weeks.reduce((sum, w) => sum + w.days, 0);
1627
+ console.log();
1628
+ console.log(chalk10.bold("\u{1F4C5} Activity (Last 12 Weeks)"));
1629
+ console.log(chalk10.gray("How many problems you submitted and days you practiced."));
1630
+ console.log(chalk10.gray("\u2500".repeat(50)));
1631
+ console.log();
1632
+ for (const week of weeks) {
1633
+ const weekLabel = `${week.start} - ${week.end}`.padEnd(18);
1634
+ const bar = week.count > 0 ? chalk10.green("\u2588".repeat(Math.min(week.count, 10))).padEnd(10) : chalk10.gray("\xB7").padEnd(10);
1635
+ const countStr = week.count > 0 ? `${week.count} subs`.padEnd(10) : "".padEnd(10);
1636
+ const daysStr = week.days > 0 ? `${week.days}d active` : "";
1637
+ console.log(` ${chalk10.white(weekLabel)} ${bar} ${chalk10.cyan(countStr)} ${chalk10.yellow(daysStr)}`);
1638
+ }
1639
+ console.log(chalk10.gray("\u2500".repeat(50)));
1640
+ console.log(` ${chalk10.bold.white("Total:")} ${chalk10.cyan.bold(totalSubmissions + " submissions")}, ${chalk10.yellow.bold(totalActiveDays + " days active")}`);
1641
+ console.log();
1642
+ }
1643
+ function renderSkillStats(fundamental, intermediate, advanced) {
1644
+ console.log();
1645
+ console.log(chalk10.bold("\u{1F3AF} Skill Breakdown"));
1646
+ console.log(chalk10.gray("\u2500".repeat(45)));
1647
+ const renderSection = (title, stats, color) => {
1648
+ if (stats.length === 0) return;
1649
+ console.log();
1650
+ console.log(color.bold(` ${title}`));
1651
+ const sorted = [...stats].sort((a, b) => b.problemsSolved - a.problemsSolved);
1652
+ for (const stat of sorted.slice(0, 8)) {
1653
+ const name = stat.tagName.padEnd(22);
1654
+ const bar = color("\u2588".repeat(Math.min(stat.problemsSolved, 15)));
1655
+ console.log(` ${chalk10.white(name)} ${bar} ${chalk10.white(stat.problemsSolved)}`);
1656
+ }
1657
+ };
1658
+ renderSection("Fundamental", fundamental, chalk10.green);
1659
+ renderSection("Intermediate", intermediate, chalk10.yellow);
1660
+ renderSection("Advanced", advanced, chalk10.red);
1661
+ console.log();
1662
+ }
1663
+ function renderTrendChart(calendarJson) {
1664
+ const data = JSON.parse(calendarJson);
1665
+ const now = /* @__PURE__ */ new Date();
1666
+ const dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
1667
+ const days = [];
1668
+ for (let d = 6; d >= 0; d--) {
1669
+ const day = new Date(now);
1670
+ day.setDate(day.getDate() - d);
1671
+ const midnight = new Date(Date.UTC(day.getFullYear(), day.getMonth(), day.getDate()));
1672
+ const timestamp = Math.floor(midnight.getTime() / 1e3).toString();
1673
+ days.push({
1674
+ label: dayNames[day.getDay()],
1675
+ count: data[timestamp] || 0
1676
+ });
1677
+ }
1678
+ const maxVal = Math.max(...days.map((d) => d.count), 1);
1679
+ const chartHeight = 6;
1680
+ console.log();
1681
+ console.log(chalk10.bold("\u{1F4C8} Submission Trend (Last 7 Days)"));
1682
+ console.log(chalk10.gray("\u2500".repeat(35)));
1683
+ console.log();
1684
+ for (let row = chartHeight; row >= 1; row--) {
1685
+ let line = ` ${row === chartHeight ? maxVal.toString().padStart(2) : " "} \u2502`;
1686
+ for (const day of days) {
1687
+ const barHeight = Math.round(day.count / maxVal * chartHeight);
1688
+ if (barHeight >= row) {
1689
+ line += chalk10.green(" \u2588\u2588 ");
1690
+ } else {
1691
+ line += " ";
1692
+ }
1693
+ }
1694
+ console.log(line);
1695
+ }
1696
+ console.log(` 0 \u2514${"\u2500\u2500\u2500\u2500".repeat(7)}`);
1697
+ console.log(` ${days.map((d) => d.label.padEnd(4)).join("")}`);
1698
+ console.log(chalk10.gray(` ${days.map((d) => d.count.toString().padEnd(4)).join("")}`));
1699
+ const total = days.reduce((a, b) => a + b.count, 0);
1700
+ console.log();
1701
+ console.log(chalk10.white(` Total: ${total} submissions this week`));
1702
+ console.log();
1703
+ }
1704
+
1705
+ // src/commands/stat.ts
1706
+ async function statCommand(username, options = {}) {
1432
1707
  const { authorized, username: currentUser } = await requireAuth();
1433
1708
  if (!authorized) return;
1434
1709
  const spinner = ora7("Fetching statistics...").start();
@@ -1440,25 +1715,48 @@ async function statCommand(username) {
1440
1715
  }
1441
1716
  const profile = await leetcodeClient.getUserProfile(targetUsername);
1442
1717
  spinner.stop();
1443
- displayUserStats(
1444
- profile.username,
1445
- profile.realName,
1446
- profile.ranking,
1447
- profile.acSubmissionNum,
1448
- profile.streak,
1449
- profile.totalActiveDays
1450
- );
1718
+ if (!options.calendar && !options.skills && !options.trend) {
1719
+ displayUserStats(
1720
+ profile.username,
1721
+ profile.realName,
1722
+ profile.ranking,
1723
+ profile.acSubmissionNum,
1724
+ profile.streak,
1725
+ profile.totalActiveDays
1726
+ );
1727
+ return;
1728
+ }
1729
+ if (options.calendar) {
1730
+ if (profile.submissionCalendar) {
1731
+ renderHeatmap(profile.submissionCalendar);
1732
+ } else {
1733
+ console.log(chalk11.yellow("Calendar data not available."));
1734
+ }
1735
+ }
1736
+ if (options.trend) {
1737
+ if (profile.submissionCalendar) {
1738
+ renderTrendChart(profile.submissionCalendar);
1739
+ } else {
1740
+ console.log(chalk11.yellow("Calendar data not available."));
1741
+ }
1742
+ }
1743
+ if (options.skills) {
1744
+ spinner.start("Fetching skill stats...");
1745
+ const skills = await leetcodeClient.getSkillStats(targetUsername);
1746
+ spinner.stop();
1747
+ renderSkillStats(skills.fundamental, skills.intermediate, skills.advanced);
1748
+ }
1451
1749
  } catch (error) {
1452
1750
  spinner.fail("Failed to fetch statistics");
1453
1751
  if (error instanceof Error) {
1454
- console.log(chalk10.red(error.message));
1752
+ console.log(chalk11.red(error.message));
1455
1753
  }
1456
1754
  }
1457
1755
  }
1458
1756
 
1459
1757
  // src/commands/daily.ts
1460
1758
  import ora8 from "ora";
1461
- import chalk11 from "chalk";
1759
+ import chalk12 from "chalk";
1462
1760
  async function dailyCommand() {
1463
1761
  const { authorized } = await requireAuth();
1464
1762
  if (!authorized) return;
@@ -1468,19 +1766,19 @@ async function dailyCommand() {
1468
1766
  spinner.stop();
1469
1767
  displayDailyChallenge(daily.date, daily.question);
1470
1768
  console.log();
1471
- console.log(chalk11.gray("Run the following to start working on this problem:"));
1472
- console.log(chalk11.cyan(` leetcode pick ${daily.question.titleSlug}`));
1769
+ console.log(chalk12.gray("Run the following to start working on this problem:"));
1770
+ console.log(chalk12.cyan(` leetcode pick ${daily.question.titleSlug}`));
1473
1771
  } catch (error) {
1474
1772
  spinner.fail("Failed to fetch daily challenge");
1475
1773
  if (error instanceof Error) {
1476
- console.log(chalk11.red(error.message));
1774
+ console.log(chalk12.red(error.message));
1477
1775
  }
1478
1776
  }
1479
1777
  }
1480
1778
 
1481
1779
  // src/commands/random.ts
1482
1780
  import ora9 from "ora";
1483
- import chalk12 from "chalk";
1781
+ import chalk13 from "chalk";
1484
1782
  async function randomCommand(options) {
1485
1783
  const { authorized } = await requireAuth();
1486
1784
  if (!authorized) return;
@@ -1514,13 +1812,13 @@ async function randomCommand(options) {
1514
1812
  await pickCommand(titleSlug, { open: options.open ?? true });
1515
1813
  } else {
1516
1814
  await showCommand(titleSlug);
1517
- console.log(chalk12.gray("Run following to start solving:"));
1518
- console.log(chalk12.cyan(` leetcode pick ${titleSlug}`));
1815
+ console.log(chalk13.gray("Run following to start solving:"));
1816
+ console.log(chalk13.cyan(` leetcode pick ${titleSlug}`));
1519
1817
  }
1520
1818
  } catch (error) {
1521
1819
  spinner.fail("Failed to fetch random problem");
1522
1820
  if (error instanceof Error) {
1523
- console.log(chalk12.red(error.message));
1821
+ console.log(chalk13.red(error.message));
1524
1822
  }
1525
1823
  }
1526
1824
  }
@@ -1528,9 +1826,9 @@ async function randomCommand(options) {
1528
1826
  // src/commands/submissions.ts
1529
1827
  import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
1530
1828
  import { existsSync as existsSync5 } from "fs";
1531
- import { join as join4 } from "path";
1829
+ import { join as join5 } from "path";
1532
1830
  import ora10 from "ora";
1533
- import chalk13 from "chalk";
1831
+ import chalk14 from "chalk";
1534
1832
  async function submissionsCommand(idOrSlug, options) {
1535
1833
  const { authorized } = await requireAuth();
1536
1834
  if (!authorized) return;
@@ -1552,16 +1850,16 @@ async function submissionsCommand(idOrSlug, options) {
1552
1850
  const submissions = await leetcodeClient.getSubmissionList(slug, limit);
1553
1851
  spinner.stop();
1554
1852
  if (submissions.length === 0) {
1555
- console.log(chalk13.yellow("No submissions found."));
1853
+ console.log(chalk14.yellow("No submissions found."));
1556
1854
  return;
1557
1855
  }
1558
1856
  if (options.last) {
1559
1857
  const lastAC = submissions.find((s) => s.statusDisplay === "Accepted");
1560
1858
  if (lastAC) {
1561
- console.log(chalk13.bold("Last Accepted Submission:"));
1859
+ console.log(chalk14.bold("Last Accepted Submission:"));
1562
1860
  displaySubmissionsList([lastAC]);
1563
1861
  } else {
1564
- console.log(chalk13.yellow("No accepted submissions found in recent history."));
1862
+ console.log(chalk14.yellow("No accepted submissions found in recent history."));
1565
1863
  }
1566
1864
  } else {
1567
1865
  displaySubmissionsList(submissions);
@@ -1582,7 +1880,7 @@ async function submissionsCommand(idOrSlug, options) {
1582
1880
  const workDir = config.getWorkDir();
1583
1881
  const difficulty = problem.difficulty;
1584
1882
  const category = problem.topicTags.length > 0 ? problem.topicTags[0].name.replace(/[^\w\s-]/g, "").trim() : "Uncategorized";
1585
- const targetDir = join4(workDir, difficulty, category);
1883
+ const targetDir = join5(workDir, difficulty, category);
1586
1884
  if (!existsSync5(targetDir)) {
1587
1885
  await mkdir2(targetDir, { recursive: true });
1588
1886
  }
@@ -1590,22 +1888,22 @@ async function submissionsCommand(idOrSlug, options) {
1590
1888
  const supportedLang = LANG_SLUG_MAP[langSlug] ?? "txt";
1591
1889
  const ext = LANGUAGE_EXTENSIONS[supportedLang] ?? langSlug;
1592
1890
  const fileName = `${problem.questionFrontendId}.${problem.titleSlug}.submission-${lastAC.id}.${ext}`;
1593
- const filePath = join4(targetDir, fileName);
1891
+ const filePath = join5(targetDir, fileName);
1594
1892
  await writeFile2(filePath, details.code, "utf-8");
1595
- downloadSpinner.succeed(`Downloaded to ${chalk13.green(fileName)}`);
1596
- console.log(chalk13.gray(`Path: ${filePath}`));
1893
+ downloadSpinner.succeed(`Downloaded to ${chalk14.green(fileName)}`);
1894
+ console.log(chalk14.gray(`Path: ${filePath}`));
1597
1895
  }
1598
1896
  } catch (error) {
1599
1897
  spinner.fail("Failed to fetch submissions");
1600
1898
  if (error instanceof Error) {
1601
- console.log(chalk13.red(error.message));
1899
+ console.log(chalk14.red(error.message));
1602
1900
  }
1603
1901
  }
1604
1902
  }
1605
1903
 
1606
1904
  // src/commands/config.ts
1607
1905
  import inquirer2 from "inquirer";
1608
- import chalk14 from "chalk";
1906
+ import chalk15 from "chalk";
1609
1907
  var SUPPORTED_LANGUAGES = [
1610
1908
  "typescript",
1611
1909
  "javascript",
@@ -1627,21 +1925,30 @@ async function configCommand(options) {
1627
1925
  if (options.lang) {
1628
1926
  const langInput = options.lang.toLowerCase();
1629
1927
  if (!SUPPORTED_LANGUAGES.includes(langInput)) {
1630
- console.log(chalk14.red(`Unsupported language: ${options.lang}`));
1631
- console.log(chalk14.gray(`Supported: ${SUPPORTED_LANGUAGES.join(", ")}`));
1928
+ console.log(chalk15.red(`Unsupported language: ${options.lang}`));
1929
+ console.log(chalk15.gray(`Supported: ${SUPPORTED_LANGUAGES.join(", ")}`));
1632
1930
  return;
1633
1931
  }
1634
1932
  const lang = langInput;
1635
1933
  config.setLanguage(lang);
1636
- console.log(chalk14.green(`\u2713 Default language set to ${lang}`));
1934
+ console.log(chalk15.green(`\u2713 Default language set to ${lang}`));
1637
1935
  }
1638
1936
  if (options.editor) {
1639
1937
  config.setEditor(options.editor);
1640
- console.log(chalk14.green(`\u2713 Editor set to ${options.editor}`));
1938
+ console.log(chalk15.green(`\u2713 Editor set to ${options.editor}`));
1641
1939
  }
1642
1940
  if (options.workdir) {
1643
1941
  config.setWorkDir(options.workdir);
1644
- console.log(chalk14.green(`\u2713 Work directory set to ${options.workdir}`));
1942
+ console.log(chalk15.green(`\u2713 Work directory set to ${options.workdir}`));
1943
+ }
1944
+ if (options.repo !== void 0) {
1945
+ if (options.repo.trim() === "") {
1946
+ config.deleteRepo();
1947
+ console.log(chalk15.green("\u2713 Repository URL cleared"));
1948
+ } else {
1949
+ config.setRepo(options.repo);
1950
+ console.log(chalk15.green(`\u2713 Repository URL set to ${options.repo}`));
1951
+ }
1645
1952
  }
1646
1953
  }
1647
1954
  async function configInteractiveCommand() {
@@ -1665,38 +1972,50 @@ async function configInteractiveCommand() {
1665
1972
  name: "workDir",
1666
1973
  message: "Working directory for solution files:",
1667
1974
  default: currentConfig.workDir
1975
+ },
1976
+ {
1977
+ type: "input",
1978
+ name: "repo",
1979
+ message: "Git repository URL (optional):",
1980
+ default: currentConfig.repo
1668
1981
  }
1669
1982
  ]);
1670
1983
  config.setLanguage(answers.language);
1671
1984
  config.setEditor(answers.editor);
1672
1985
  config.setWorkDir(answers.workDir);
1986
+ if (answers.repo) {
1987
+ config.setRepo(answers.repo);
1988
+ } else {
1989
+ config.deleteRepo();
1990
+ }
1673
1991
  console.log();
1674
- console.log(chalk14.green("\u2713 Configuration saved"));
1992
+ console.log(chalk15.green("\u2713 Configuration saved"));
1675
1993
  showCurrentConfig();
1676
1994
  }
1677
1995
  function showCurrentConfig() {
1678
1996
  const currentConfig = config.getConfig();
1679
1997
  const credentials = config.getCredentials();
1680
1998
  console.log();
1681
- console.log(chalk14.bold("LeetCode CLI Configuration"));
1682
- console.log(chalk14.gray("\u2500".repeat(40)));
1999
+ console.log(chalk15.bold("LeetCode CLI Configuration"));
2000
+ console.log(chalk15.gray("\u2500".repeat(40)));
1683
2001
  console.log();
1684
- console.log(chalk14.gray("Config file:"), config.getPath());
2002
+ console.log(chalk15.gray("Config file:"), config.getPath());
1685
2003
  console.log();
1686
- console.log(chalk14.gray("Language: "), chalk14.white(currentConfig.language));
1687
- console.log(chalk14.gray("Editor: "), chalk14.white(currentConfig.editor ?? "(not set)"));
1688
- console.log(chalk14.gray("Work Dir: "), chalk14.white(currentConfig.workDir));
1689
- console.log(chalk14.gray("Logged in: "), credentials ? chalk14.green("Yes") : chalk14.yellow("No"));
2004
+ console.log(chalk15.gray("Language: "), chalk15.white(currentConfig.language));
2005
+ console.log(chalk15.gray("Editor: "), chalk15.white(currentConfig.editor ?? "(not set)"));
2006
+ console.log(chalk15.gray("Work Dir: "), chalk15.white(currentConfig.workDir));
2007
+ console.log(chalk15.gray("Repo URL: "), chalk15.white(currentConfig.repo ?? "(not set)"));
2008
+ console.log(chalk15.gray("Logged in: "), credentials ? chalk15.green("Yes") : chalk15.yellow("No"));
1690
2009
  }
1691
2010
 
1692
2011
  // src/commands/bookmark.ts
1693
- import chalk15 from "chalk";
2012
+ import chalk16 from "chalk";
1694
2013
  import Table2 from "cli-table3";
1695
2014
  import ora11 from "ora";
1696
2015
 
1697
2016
  // src/storage/bookmarks.ts
1698
- import Conf2 from "conf";
1699
- var bookmarksStore = new Conf2({
2017
+ import Conf3 from "conf";
2018
+ var bookmarksStore = new Conf3({
1700
2019
  projectName: "leetcode-cli-bookmarks",
1701
2020
  defaults: { bookmarks: [] }
1702
2021
  });
@@ -1735,36 +2054,36 @@ var bookmarks = {
1735
2054
  async function bookmarkCommand(action, id) {
1736
2055
  const validActions = ["add", "remove", "list", "clear"];
1737
2056
  if (!validActions.includes(action)) {
1738
- console.log(chalk15.red(`Invalid action: ${action}`));
1739
- console.log(chalk15.gray("Valid actions: add, remove, list, clear"));
2057
+ console.log(chalk16.red(`Invalid action: ${action}`));
2058
+ console.log(chalk16.gray("Valid actions: add, remove, list, clear"));
1740
2059
  return;
1741
2060
  }
1742
2061
  switch (action) {
1743
2062
  case "add":
1744
2063
  if (!id) {
1745
- console.log(chalk15.red("Please provide a problem ID to bookmark"));
2064
+ console.log(chalk16.red("Please provide a problem ID to bookmark"));
1746
2065
  return;
1747
2066
  }
1748
2067
  if (!isProblemId(id)) {
1749
- console.log(chalk15.red(`Invalid problem ID: ${id}`));
1750
- console.log(chalk15.gray("Problem ID must be a positive integer"));
2068
+ console.log(chalk16.red(`Invalid problem ID: ${id}`));
2069
+ console.log(chalk16.gray("Problem ID must be a positive integer"));
1751
2070
  return;
1752
2071
  }
1753
2072
  if (bookmarks.add(id)) {
1754
- console.log(chalk15.green(`\u2713 Bookmarked problem ${id}`));
2073
+ console.log(chalk16.green(`\u2713 Bookmarked problem ${id}`));
1755
2074
  } else {
1756
- console.log(chalk15.yellow(`Problem ${id} is already bookmarked`));
2075
+ console.log(chalk16.yellow(`Problem ${id} is already bookmarked`));
1757
2076
  }
1758
2077
  break;
1759
2078
  case "remove":
1760
2079
  if (!id) {
1761
- console.log(chalk15.red("Please provide a problem ID to remove"));
2080
+ console.log(chalk16.red("Please provide a problem ID to remove"));
1762
2081
  return;
1763
2082
  }
1764
2083
  if (bookmarks.remove(id)) {
1765
- console.log(chalk15.green(`\u2713 Removed bookmark for problem ${id}`));
2084
+ console.log(chalk16.green(`\u2713 Removed bookmark for problem ${id}`));
1766
2085
  } else {
1767
- console.log(chalk15.yellow(`Problem ${id} is not bookmarked`));
2086
+ console.log(chalk16.yellow(`Problem ${id} is not bookmarked`));
1768
2087
  }
1769
2088
  break;
1770
2089
  case "list":
@@ -1773,10 +2092,10 @@ async function bookmarkCommand(action, id) {
1773
2092
  case "clear":
1774
2093
  const count = bookmarks.count();
1775
2094
  if (count === 0) {
1776
- console.log(chalk15.yellow("No bookmarks to clear"));
2095
+ console.log(chalk16.yellow("No bookmarks to clear"));
1777
2096
  } else {
1778
2097
  bookmarks.clear();
1779
- console.log(chalk15.green(`\u2713 Cleared ${count} bookmark${count !== 1 ? "s" : ""}`));
2098
+ console.log(chalk16.green(`\u2713 Cleared ${count} bookmark${count !== 1 ? "s" : ""}`));
1780
2099
  }
1781
2100
  break;
1782
2101
  }
@@ -1784,12 +2103,12 @@ async function bookmarkCommand(action, id) {
1784
2103
  async function listBookmarks() {
1785
2104
  const bookmarkList = bookmarks.list();
1786
2105
  if (bookmarkList.length === 0) {
1787
- console.log(chalk15.yellow("\u{1F4CC} No bookmarked problems"));
1788
- console.log(chalk15.gray('Use "leetcode bookmark add <id>" to bookmark a problem'));
2106
+ console.log(chalk16.yellow("\u{1F4CC} No bookmarked problems"));
2107
+ console.log(chalk16.gray('Use "leetcode bookmark add <id>" to bookmark a problem'));
1789
2108
  return;
1790
2109
  }
1791
2110
  console.log();
1792
- console.log(chalk15.bold.cyan(`\u{1F4CC} Bookmarked Problems (${bookmarkList.length})`));
2111
+ console.log(chalk16.bold.cyan(`\u{1F4CC} Bookmarked Problems (${bookmarkList.length})`));
1793
2112
  console.log();
1794
2113
  const { authorized } = await requireAuth();
1795
2114
  if (authorized) {
@@ -1797,10 +2116,10 @@ async function listBookmarks() {
1797
2116
  try {
1798
2117
  const table = new Table2({
1799
2118
  head: [
1800
- chalk15.cyan("ID"),
1801
- chalk15.cyan("Title"),
1802
- chalk15.cyan("Difficulty"),
1803
- chalk15.cyan("Status")
2119
+ chalk16.cyan("ID"),
2120
+ chalk16.cyan("Title"),
2121
+ chalk16.cyan("Difficulty"),
2122
+ chalk16.cyan("Status")
1804
2123
  ],
1805
2124
  colWidths: [8, 45, 12, 10],
1806
2125
  style: { head: [], border: [] }
@@ -1813,35 +2132,35 @@ async function listBookmarks() {
1813
2132
  problem.questionFrontendId,
1814
2133
  problem.title.length > 42 ? problem.title.slice(0, 39) + "..." : problem.title,
1815
2134
  colorDifficulty2(problem.difficulty),
1816
- problem.status === "ac" ? chalk15.green("\u2713") : chalk15.gray("-")
2135
+ problem.status === "ac" ? chalk16.green("\u2713") : chalk16.gray("-")
1817
2136
  ]);
1818
2137
  } else {
1819
- table.push([id, chalk15.gray("(not found)"), "-", "-"]);
2138
+ table.push([id, chalk16.gray("(not found)"), "-", "-"]);
1820
2139
  }
1821
2140
  } catch {
1822
- table.push([id, chalk15.gray("(error fetching)"), "-", "-"]);
2141
+ table.push([id, chalk16.gray("(error fetching)"), "-", "-"]);
1823
2142
  }
1824
2143
  }
1825
2144
  spinner.stop();
1826
2145
  console.log(table.toString());
1827
2146
  } catch {
1828
2147
  spinner.stop();
1829
- console.log(chalk15.gray("IDs: ") + bookmarkList.join(", "));
2148
+ console.log(chalk16.gray("IDs: ") + bookmarkList.join(", "));
1830
2149
  }
1831
2150
  } else {
1832
- console.log(chalk15.gray("IDs: ") + bookmarkList.join(", "));
2151
+ console.log(chalk16.gray("IDs: ") + bookmarkList.join(", "));
1833
2152
  console.log();
1834
- console.log(chalk15.gray("Login to see problem details"));
2153
+ console.log(chalk16.gray("Login to see problem details"));
1835
2154
  }
1836
2155
  }
1837
2156
  function colorDifficulty2(difficulty) {
1838
2157
  switch (difficulty.toLowerCase()) {
1839
2158
  case "easy":
1840
- return chalk15.green(difficulty);
2159
+ return chalk16.green(difficulty);
1841
2160
  case "medium":
1842
- return chalk15.yellow(difficulty);
2161
+ return chalk16.yellow(difficulty);
1843
2162
  case "hard":
1844
- return chalk15.red(difficulty);
2163
+ return chalk16.red(difficulty);
1845
2164
  default:
1846
2165
  return difficulty;
1847
2166
  }
@@ -1849,18 +2168,18 @@ function colorDifficulty2(difficulty) {
1849
2168
 
1850
2169
  // src/commands/notes.ts
1851
2170
  import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
1852
- import { join as join5 } from "path";
2171
+ import { join as join6 } from "path";
1853
2172
  import { existsSync as existsSync6 } from "fs";
1854
- import chalk16 from "chalk";
2173
+ import chalk17 from "chalk";
1855
2174
  async function notesCommand(problemId, action) {
1856
2175
  if (!isProblemId(problemId)) {
1857
- console.log(chalk16.red(`Invalid problem ID: ${problemId}`));
1858
- console.log(chalk16.gray("Problem ID must be a positive integer"));
2176
+ console.log(chalk17.red(`Invalid problem ID: ${problemId}`));
2177
+ console.log(chalk17.gray("Problem ID must be a positive integer"));
1859
2178
  return;
1860
2179
  }
1861
2180
  const noteAction = action === "view" ? "view" : "edit";
1862
- const notesDir = join5(config.getWorkDir(), ".notes");
1863
- const notePath = join5(notesDir, `${problemId}.md`);
2181
+ const notesDir = join6(config.getWorkDir(), ".notes");
2182
+ const notePath = join6(notesDir, `${problemId}.md`);
1864
2183
  if (!existsSync6(notesDir)) {
1865
2184
  await mkdir3(notesDir, { recursive: true });
1866
2185
  }
@@ -1872,21 +2191,21 @@ async function notesCommand(problemId, action) {
1872
2191
  }
1873
2192
  async function viewNote(notePath, problemId) {
1874
2193
  if (!existsSync6(notePath)) {
1875
- console.log(chalk16.yellow(`No notes found for problem ${problemId}`));
1876
- console.log(chalk16.gray(`Use "leetcode note ${problemId} edit" to create notes`));
2194
+ console.log(chalk17.yellow(`No notes found for problem ${problemId}`));
2195
+ console.log(chalk17.gray(`Use "leetcode note ${problemId} edit" to create notes`));
1877
2196
  return;
1878
2197
  }
1879
2198
  try {
1880
2199
  const content = await readFile3(notePath, "utf-8");
1881
2200
  console.log();
1882
- console.log(chalk16.bold.cyan(`\u{1F4DD} Notes for Problem ${problemId}`));
1883
- console.log(chalk16.gray("\u2500".repeat(50)));
2201
+ console.log(chalk17.bold.cyan(`\u{1F4DD} Notes for Problem ${problemId}`));
2202
+ console.log(chalk17.gray("\u2500".repeat(50)));
1884
2203
  console.log();
1885
2204
  console.log(content);
1886
2205
  } catch (error) {
1887
- console.log(chalk16.red("Failed to read notes"));
2206
+ console.log(chalk17.red("Failed to read notes"));
1888
2207
  if (error instanceof Error) {
1889
- console.log(chalk16.gray(error.message));
2208
+ console.log(chalk17.gray(error.message));
1890
2209
  }
1891
2210
  }
1892
2211
  }
@@ -1894,9 +2213,9 @@ async function editNote(notePath, problemId) {
1894
2213
  if (!existsSync6(notePath)) {
1895
2214
  const template = await generateNoteTemplate(problemId);
1896
2215
  await writeFile3(notePath, template, "utf-8");
1897
- console.log(chalk16.green(`\u2713 Created notes file for problem ${problemId}`));
2216
+ console.log(chalk17.green(`\u2713 Created notes file for problem ${problemId}`));
1898
2217
  }
1899
- console.log(chalk16.gray(`Opening: ${notePath}`));
2218
+ console.log(chalk17.gray(`Opening: ${notePath}`));
1900
2219
  await openInEditor(notePath);
1901
2220
  }
1902
2221
  async function generateNoteTemplate(problemId) {
@@ -1953,7 +2272,7 @@ async function generateNoteTemplate(problemId) {
1953
2272
  }
1954
2273
 
1955
2274
  // src/commands/today.ts
1956
- import chalk17 from "chalk";
2275
+ import chalk18 from "chalk";
1957
2276
  import ora12 from "ora";
1958
2277
  async function todayCommand() {
1959
2278
  const { authorized, username } = await requireAuth();
@@ -1968,190 +2287,529 @@ async function todayCommand() {
1968
2287
  ]);
1969
2288
  spinner.stop();
1970
2289
  console.log();
1971
- console.log(chalk17.bold.cyan(`\u{1F4CA} Today's Summary`), chalk17.gray(`- ${username}`));
1972
- console.log(chalk17.gray("\u2500".repeat(50)));
2290
+ console.log(chalk18.bold.cyan(`\u{1F4CA} Today's Summary`), chalk18.gray(`- ${username}`));
2291
+ console.log(chalk18.gray("\u2500".repeat(50)));
1973
2292
  console.log();
1974
- console.log(chalk17.yellow(`\u{1F525} Current Streak: ${profile.streak} day${profile.streak !== 1 ? "s" : ""}`));
1975
- console.log(chalk17.gray(` Total Active Days: ${profile.totalActiveDays}`));
2293
+ console.log(chalk18.yellow(`\u{1F525} Current Streak: ${profile.streak} day${profile.streak !== 1 ? "s" : ""}`));
2294
+ console.log(chalk18.gray(` Total Active Days: ${profile.totalActiveDays}`));
1976
2295
  console.log();
1977
2296
  const total = profile.acSubmissionNum.find((s) => s.difficulty === "All");
1978
2297
  const easy = profile.acSubmissionNum.find((s) => s.difficulty === "Easy");
1979
2298
  const medium = profile.acSubmissionNum.find((s) => s.difficulty === "Medium");
1980
2299
  const hard = profile.acSubmissionNum.find((s) => s.difficulty === "Hard");
1981
- console.log(chalk17.white("\u{1F4C8} Problems Solved:"));
1982
- console.log(` ${chalk17.green("Easy")}: ${easy?.count ?? 0} | ${chalk17.yellow("Medium")}: ${medium?.count ?? 0} | ${chalk17.red("Hard")}: ${hard?.count ?? 0}`);
1983
- console.log(` ${chalk17.bold("Total")}: ${total?.count ?? 0}`);
2300
+ console.log(chalk18.white("\u{1F4C8} Problems Solved:"));
2301
+ console.log(` ${chalk18.green("Easy")}: ${easy?.count ?? 0} | ${chalk18.yellow("Medium")}: ${medium?.count ?? 0} | ${chalk18.red("Hard")}: ${hard?.count ?? 0}`);
2302
+ console.log(` ${chalk18.bold("Total")}: ${total?.count ?? 0}`);
1984
2303
  console.log();
1985
- console.log(chalk17.bold.yellow("\u{1F3AF} Today's Challenge:"));
2304
+ console.log(chalk18.bold.yellow("\u{1F3AF} Today's Challenge:"));
1986
2305
  console.log(` ${daily.question.questionFrontendId}. ${daily.question.title}`);
1987
2306
  console.log(` ${colorDifficulty3(daily.question.difficulty)}`);
1988
2307
  const status = daily.question.status;
1989
2308
  if (status === "ac") {
1990
- console.log(chalk17.green(" \u2713 Completed!"));
2309
+ console.log(chalk18.green(" \u2713 Completed!"));
1991
2310
  } else if (status === "notac") {
1992
- console.log(chalk17.yellow(" \u25CB Attempted"));
2311
+ console.log(chalk18.yellow(" \u25CB Attempted"));
1993
2312
  } else {
1994
- console.log(chalk17.gray(" - Not started"));
2313
+ console.log(chalk18.gray(" - Not started"));
1995
2314
  }
1996
2315
  console.log();
1997
- console.log(chalk17.gray(` leetcode pick ${daily.question.questionFrontendId} # Start working on it`));
2316
+ console.log(chalk18.gray(` leetcode pick ${daily.question.questionFrontendId} # Start working on it`));
1998
2317
  } catch (error) {
1999
2318
  spinner.fail("Failed to fetch progress");
2000
2319
  if (error instanceof Error) {
2001
- console.log(chalk17.red(error.message));
2320
+ console.log(chalk18.red(error.message));
2002
2321
  }
2003
2322
  }
2004
2323
  }
2005
2324
  function colorDifficulty3(difficulty) {
2006
2325
  switch (difficulty.toLowerCase()) {
2007
2326
  case "easy":
2008
- return chalk17.green(difficulty);
2327
+ return chalk18.green(difficulty);
2009
2328
  case "medium":
2010
- return chalk17.yellow(difficulty);
2329
+ return chalk18.yellow(difficulty);
2011
2330
  case "hard":
2012
- return chalk17.red(difficulty);
2331
+ return chalk18.red(difficulty);
2013
2332
  default:
2014
2333
  return difficulty;
2015
2334
  }
2016
2335
  }
2017
2336
 
2337
+ // src/commands/sync.ts
2338
+ import { execSync } from "child_process";
2339
+ import { existsSync as existsSync7 } from "fs";
2340
+ import chalk19 from "chalk";
2341
+ import inquirer3 from "inquirer";
2342
+ import ora13 from "ora";
2343
+ function isGitInstalled() {
2344
+ try {
2345
+ execSync("git --version", { stdio: "ignore" });
2346
+ return true;
2347
+ } catch (error) {
2348
+ return false;
2349
+ }
2350
+ }
2351
+ function isMapRepo(workDir) {
2352
+ try {
2353
+ execSync("git rev-parse --is-inside-work-tree", { cwd: workDir, stdio: "ignore" });
2354
+ return true;
2355
+ } catch (error) {
2356
+ return false;
2357
+ }
2358
+ }
2359
+ function isGhInstalled() {
2360
+ try {
2361
+ execSync("gh --version", { stdio: "ignore" });
2362
+ return true;
2363
+ } catch (error) {
2364
+ return false;
2365
+ }
2366
+ }
2367
+ function getRemoteUrl(workDir) {
2368
+ try {
2369
+ const url = execSync("git config --get remote.origin.url", { cwd: workDir, encoding: "utf-8" });
2370
+ return url.trim();
2371
+ } catch (error) {
2372
+ return null;
2373
+ }
2374
+ }
2375
+ async function setupGitRepo(workDir) {
2376
+ const { init } = await inquirer3.prompt([
2377
+ {
2378
+ type: "confirm",
2379
+ name: "init",
2380
+ message: "Work directory is not a git repository. Initialize?",
2381
+ default: true
2382
+ }
2383
+ ]);
2384
+ if (!init) {
2385
+ console.log(chalk19.yellow("Skipping basic git initialization."));
2386
+ return false;
2387
+ }
2388
+ const spinner = ora13("Initializing git repository...").start();
2389
+ try {
2390
+ execSync("git init", { cwd: workDir });
2391
+ spinner.succeed("Initialized git repository");
2392
+ return true;
2393
+ } catch (error) {
2394
+ spinner.fail("Failed to initialize git repository");
2395
+ throw error;
2396
+ }
2397
+ }
2398
+ async function setupRemote(workDir) {
2399
+ const spinner = ora13();
2400
+ let repoUrl = config.getRepo();
2401
+ if (!repoUrl) {
2402
+ if (isGhInstalled()) {
2403
+ const { createGh } = await inquirer3.prompt([
2404
+ {
2405
+ type: "confirm",
2406
+ name: "createGh",
2407
+ message: "Create a new private GitHub repository?",
2408
+ default: true
2409
+ }
2410
+ ]);
2411
+ if (createGh) {
2412
+ spinner.start("Creating GitHub repository...");
2413
+ try {
2414
+ const repoName = workDir.split("/").pop() || "leetcode-solutions";
2415
+ execSync(`gh repo create ${repoName} --private --source=. --remote=origin`, { cwd: workDir });
2416
+ spinner.succeed("Created and linked GitHub repository");
2417
+ repoUrl = getRemoteUrl(workDir) || "";
2418
+ if (repoUrl) {
2419
+ config.setRepo(repoUrl);
2420
+ }
2421
+ return repoUrl;
2422
+ } catch (error) {
2423
+ spinner.fail("Failed to create GitHub repository");
2424
+ console.log(chalk19.red(error));
2425
+ }
2426
+ }
2427
+ }
2428
+ if (!repoUrl) {
2429
+ console.log(chalk19.yellow("\nPlease create a new repository on your Git provider and copy the URL."));
2430
+ const { url } = await inquirer3.prompt([
2431
+ {
2432
+ type: "input",
2433
+ name: "url",
2434
+ message: "Enter remote repository URL:",
2435
+ validate: (input) => input.length > 0 ? true : "URL cannot be empty"
2436
+ }
2437
+ ]);
2438
+ repoUrl = url;
2439
+ }
2440
+ }
2441
+ if (repoUrl) {
2442
+ config.setRepo(repoUrl);
2443
+ }
2444
+ const currentRemote = getRemoteUrl(workDir);
2445
+ if (!currentRemote && repoUrl) {
2446
+ try {
2447
+ execSync(`git remote add origin ${repoUrl}`, { cwd: workDir });
2448
+ console.log(chalk19.green("\u2713 Added remote origin"));
2449
+ } catch (e) {
2450
+ console.log(chalk19.red("Failed to add remote origin"));
2451
+ }
2452
+ }
2453
+ return repoUrl || "";
2454
+ }
2455
+ async function syncCommand() {
2456
+ const workDir = config.getWorkDir();
2457
+ if (!existsSync7(workDir)) {
2458
+ console.log(chalk19.red(`Work directory does not exist: ${workDir}`));
2459
+ return;
2460
+ }
2461
+ if (!isGitInstalled()) {
2462
+ console.log(chalk19.red("Git is not installed. Please install Git to use command."));
2463
+ return;
2464
+ }
2465
+ if (!isMapRepo(workDir)) {
2466
+ const initialized = await setupGitRepo(workDir);
2467
+ if (!initialized) return;
2468
+ }
2469
+ await setupRemote(workDir);
2470
+ const spinner = ora13("Syncing solutions...").start();
2471
+ try {
2472
+ const status = execSync("git status --porcelain", { cwd: workDir, encoding: "utf-8" });
2473
+ if (!status) {
2474
+ spinner.info("No changes to sync");
2475
+ return;
2476
+ }
2477
+ execSync("git add .", { cwd: workDir });
2478
+ const lines = status.trim().split("\n");
2479
+ const count = lines.length;
2480
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").substring(0, 19);
2481
+ const message = `Sync: ${count} solutions - ${timestamp}`;
2482
+ execSync(`git commit -m "${message}"`, { cwd: workDir });
2483
+ try {
2484
+ execSync("git push -u origin main", { cwd: workDir, stdio: "ignore" });
2485
+ } catch {
2486
+ try {
2487
+ execSync("git push -u origin master", { cwd: workDir, stdio: "ignore" });
2488
+ } catch (e) {
2489
+ throw new Error("Failed to push to remote. Please check your git credentials and branch status.");
2490
+ }
2491
+ }
2492
+ spinner.succeed("Successfully synced solutions to remote");
2493
+ } catch (error) {
2494
+ spinner.fail("Sync failed");
2495
+ if (error.message) {
2496
+ console.log(chalk19.red(error.message));
2497
+ }
2498
+ }
2499
+ }
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
+
2018
2657
  // src/index.ts
2019
2658
  var program = new Command();
2020
2659
  program.configureHelp({
2021
2660
  sortSubcommands: true,
2022
- subcommandTerm: (cmd) => chalk18.cyan(cmd.name()) + (cmd.alias() ? chalk18.gray(`|${cmd.alias()}`) : ""),
2023
- subcommandDescription: (cmd) => chalk18.white(cmd.description()),
2024
- optionTerm: (option) => chalk18.yellow(option.flags),
2025
- optionDescription: (option) => chalk18.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)
2026
2665
  });
2027
- program.name("leetcode").usage("[command] [options]").description(chalk18.bold.cyan("\u{1F525} A modern LeetCode CLI built with TypeScript")).version("1.3.2", "-v, --version", "Output the version number").helpOption("-h, --help", "Display help for command").addHelpText("after", `
2028
- ${chalk18.yellow("Examples:")}
2029
- ${chalk18.cyan("$ leetcode login")} Login to LeetCode
2030
- ${chalk18.cyan("$ leetcode list -d easy")} List easy problems
2031
- ${chalk18.cyan("$ leetcode random -d medium")} Get random medium problem
2032
- ${chalk18.cyan("$ leetcode pick 1")} Start solving "Two Sum"
2033
- ${chalk18.cyan("$ leetcode test 1")} Test your solution
2034
- ${chalk18.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
2035
2674
  `);
2036
2675
  program.command("login").description("Login to LeetCode with browser cookies").addHelpText("after", `
2037
- ${chalk18.yellow("How to login:")}
2038
- 1. Open ${chalk18.cyan("https://leetcode.com")} in your browser
2676
+ ${chalk21.yellow("How to login:")}
2677
+ 1. Open ${chalk21.cyan("https://leetcode.com")} in your browser
2039
2678
  2. Login to your account
2040
2679
  3. Open Developer Tools (F12) \u2192 Application \u2192 Cookies
2041
- 4. Copy values of ${chalk18.green("LEETCODE_SESSION")} and ${chalk18.green("csrftoken")}
2680
+ 4. Copy values of ${chalk21.green("LEETCODE_SESSION")} and ${chalk21.green("csrftoken")}
2042
2681
  5. Paste when prompted by this command
2043
2682
  `).action(loginCommand);
2044
2683
  program.command("logout").description("Clear stored credentials").action(logoutCommand);
2045
2684
  program.command("whoami").description("Check current login status").action(whoamiCommand);
2046
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", `
2047
- ${chalk18.yellow("Examples:")}
2048
- ${chalk18.cyan("$ leetcode list")} List first 20 problems
2049
- ${chalk18.cyan("$ leetcode list -d easy")} List easy problems only
2050
- ${chalk18.cyan("$ leetcode list -s solved")} List your solved problems
2051
- ${chalk18.cyan("$ leetcode list -t array -t string")} Filter by multiple tags
2052
- ${chalk18.cyan('$ leetcode list -q "two sum"')} Search by keywords
2053
- ${chalk18.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
2054
2693
  `).action(listCommand);
2055
2694
  program.command("show <id>").alias("s").description("Show problem description").addHelpText("after", `
2056
- ${chalk18.yellow("Examples:")}
2057
- ${chalk18.cyan("$ leetcode show 1")} Show by problem ID
2058
- ${chalk18.cyan("$ leetcode show two-sum")} Show by problem slug
2059
- ${chalk18.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
2060
2699
  `).action(showCommand);
2061
2700
  program.command("daily").alias("d").description("Show today's daily challenge").addHelpText("after", `
2062
- ${chalk18.yellow("Examples:")}
2063
- ${chalk18.cyan("$ leetcode daily")} Show today's challenge
2064
- ${chalk18.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
2065
2704
  `).action(dailyCommand);
2066
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", `
2067
- ${chalk18.yellow("Examples:")}
2068
- ${chalk18.cyan("$ leetcode random")} Get any random problem
2069
- ${chalk18.cyan("$ leetcode random -d medium")} Random medium problem
2070
- ${chalk18.cyan("$ leetcode random -t array")} Random array problem
2071
- ${chalk18.cyan("$ leetcode random --pick")} Random + create file
2072
- ${chalk18.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
2073
2712
  `).action(randomCommand);
2074
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", `
2075
- ${chalk18.yellow("Examples:")}
2076
- ${chalk18.cyan("$ leetcode pick 1")} Pick by problem ID
2077
- ${chalk18.cyan("$ leetcode pick two-sum")} Pick by problem slug
2078
- ${chalk18.cyan("$ leetcode pick 1 -l python3")} Pick with specific language
2079
- ${chalk18.cyan("$ leetcode pick 1 --no-open")} Create file without opening
2080
- ${chalk18.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
2081
2720
 
2082
- ${chalk18.gray("Files are organized by: workDir/Difficulty/Category/")}
2721
+ ${chalk21.gray("Files are organized by: workDir/Difficulty/Category/")}
2083
2722
  `).action(async (id, options) => {
2084
2723
  await pickCommand(id, options);
2085
2724
  });
2086
2725
  program.command("pick-batch <ids...>").description("Generate solution files for multiple problems").option("-l, --lang <language>", "Programming language for the solutions").addHelpText("after", `
2087
- ${chalk18.yellow("Examples:")}
2088
- ${chalk18.cyan("$ leetcode pick-batch 1 2 3")} Pick problems 1, 2, and 3
2089
- ${chalk18.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
2090
2729
  `).action(batchPickCommand);
2091
2730
  program.command("test <file>").alias("t").description("Test solution against sample test cases").option("-c, --testcase <testcase>", "Custom test case").addHelpText("after", `
2092
- ${chalk18.yellow("Examples:")}
2093
- ${chalk18.cyan("$ leetcode test 1")} Test by problem ID
2094
- ${chalk18.cyan("$ leetcode test two-sum")} Test by problem slug
2095
- ${chalk18.cyan("$ leetcode test ./path/to/file.py")} Test by file path
2096
- ${chalk18.cyan('$ leetcode test 1 -c "[1,2]\\n3"')} Test with custom case
2097
- ${chalk18.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
2098
2737
 
2099
- ${chalk18.gray("Testcases use \\n to separate multiple inputs.")}
2738
+ ${chalk21.gray("Testcases use \\n to separate multiple inputs.")}
2100
2739
  `).action(testCommand);
2101
2740
  program.command("submit <file>").alias("x").description("Submit solution to LeetCode").addHelpText("after", `
2102
- ${chalk18.yellow("Examples:")}
2103
- ${chalk18.cyan("$ leetcode submit 1")} Submit by problem ID
2104
- ${chalk18.cyan("$ leetcode submit two-sum")} Submit by problem slug
2105
- ${chalk18.cyan("$ leetcode submit ./path/to/file.py")} Submit by file path
2106
- ${chalk18.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
2107
2746
  `).action(submitCommand);
2108
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", `
2109
- ${chalk18.yellow("Examples:")}
2110
- ${chalk18.cyan("$ leetcode submissions 1")} View submissions for problem
2111
- ${chalk18.cyan("$ leetcode submissions 1 -n 5")} Show last 5 submissions
2112
- ${chalk18.cyan("$ leetcode submissions 1 --last")} Show last accepted submission
2113
- ${chalk18.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
2114
2753
  `).action(submissionsCommand);
2115
- program.command("stat [username]").description("Show user statistics").addHelpText("after", `
2116
- ${chalk18.yellow("Examples:")}
2117
- ${chalk18.cyan("$ leetcode stat")} Show your statistics
2118
- ${chalk18.cyan("$ leetcode stat lee215")} Show another user's stats
2119
- `).action(statCommand);
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", `
2755
+ ${chalk21.yellow("Options Explained:")}
2756
+ ${chalk21.cyan("-c, --calendar")} Shows a table of your weekly submissions and active days
2757
+ for the past 12 weeks. Useful for tracking consistency.
2758
+
2759
+ ${chalk21.cyan("-s, --skills")} Shows how many problems you solved per topic tag,
2760
+ grouped by difficulty (Fundamental/Intermediate/Advanced).
2761
+ Helps identify your strong and weak areas.
2762
+
2763
+ ${chalk21.cyan("-t, --trend")} Shows a bar chart of daily submissions for the past week.
2764
+ Visualizes your recent coding activity day by day.
2765
+
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
2772
+ `).action((username, options) => statCommand(username, options));
2120
2773
  program.command("today").description("Show today's progress summary").addHelpText("after", `
2121
- ${chalk18.yellow("Examples:")}
2122
- ${chalk18.cyan("$ leetcode today")} Show streak, solved, and daily challenge
2774
+ ${chalk21.yellow("Examples:")}
2775
+ ${chalk21.cyan("$ leetcode today")} Show streak, solved, and daily challenge
2123
2776
  `).action(todayCommand);
2124
2777
  program.command("bookmark <action> [id]").description("Manage problem bookmarks").addHelpText("after", `
2125
- ${chalk18.yellow("Actions:")}
2126
- ${chalk18.cyan("add <id>")} Bookmark a problem
2127
- ${chalk18.cyan("remove <id>")} Remove a bookmark
2128
- ${chalk18.cyan("list")} List all bookmarks
2129
- ${chalk18.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
2130
2783
 
2131
- ${chalk18.yellow("Examples:")}
2132
- ${chalk18.cyan("$ leetcode bookmark add 1")} Bookmark problem 1
2133
- ${chalk18.cyan("$ leetcode bookmark remove 1")} Remove bookmark
2134
- ${chalk18.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
2135
2788
  `).action(bookmarkCommand);
2136
2789
  program.command("note <id> [action]").description("View or edit notes for a problem").addHelpText("after", `
2137
- ${chalk18.yellow("Actions:")}
2138
- ${chalk18.cyan("edit")} Open notes in editor (default)
2139
- ${chalk18.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
2140
2793
 
2141
- ${chalk18.yellow("Examples:")}
2142
- ${chalk18.cyan("$ leetcode note 1")} Edit notes for problem 1
2143
- ${chalk18.cyan("$ leetcode note 1 edit")} Edit notes (explicit)
2144
- ${chalk18.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
2145
2798
  `).action(notesCommand);
2146
- 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("-i, --interactive", "Interactive configuration").addHelpText("after", `
2147
- ${chalk18.yellow("Examples:")}
2148
- ${chalk18.cyan("$ leetcode config")} View current config
2149
- ${chalk18.cyan("$ leetcode config -l python3")} Set language to Python
2150
- ${chalk18.cyan('$ leetcode config -e "code"')} Set editor to VS Code
2151
- ${chalk18.cyan("$ leetcode config -w ~/leetcode")} Set solutions folder
2152
- ${chalk18.cyan("$ leetcode config -i")} Interactive setup
2799
+ program.command("sync").description("Sync solutions to Git repository").addHelpText("after", `
2800
+ ${chalk21.yellow("Examples:")}
2801
+ ${chalk21.cyan("$ leetcode sync")} Sync all solutions to remote
2802
+ `).action(syncCommand);
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", `
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
2153
2811
 
2154
- ${chalk18.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")}
2155
2813
  `).action(async (options) => {
2156
2814
  if (options.interactive) {
2157
2815
  await configInteractiveCommand();
@@ -2159,12 +2817,24 @@ ${chalk18.gray("Supported languages: typescript, javascript, python3, java, cpp,
2159
2817
  await configCommand(options);
2160
2818
  }
2161
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));
2162
2832
  program.showHelpAfterError("(add --help for additional information)");
2163
2833
  program.parse();
2164
2834
  if (!process.argv.slice(2).length) {
2165
2835
  console.log();
2166
- console.log(chalk18.bold.cyan(" \u{1F525} LeetCode CLI"));
2167
- console.log(chalk18.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"));
2168
2838
  console.log();
2169
2839
  program.outputHelp();
2170
2840
  }