@night-slayer18/leetcode-cli 1.5.0 → 1.6.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 +26 -0
  2. package/dist/index.js +568 -225
  3. package/package.json +2 -1
package/README.md CHANGED
@@ -16,6 +16,8 @@ A modern, feature-rich LeetCode CLI built with TypeScript.
16
16
  - 📤 **Submit solutions** - Submit directly to LeetCode
17
17
  - 📊 **View statistics** - Track your progress
18
18
  - 🎯 **Daily challenge** - Get today's problem
19
+ - ⏱️ **Interview timer** - Timed practice with solve time tracking
20
+ - 👥 **Collaborative coding** - Solve problems with a partner
19
21
  - ⚙️ **Configurable** - Set language, editor, and working directory
20
22
  - 📂 **Smart file discovery** - Use problem ID, filename, or full path
21
23
  - 🔄 **Git Sync** - Auto-sync solutions to GitHub/GitLab
@@ -72,6 +74,7 @@ leetcode submit 1
72
74
  | `submissions <id>` | View past submissions |
73
75
  | `stat [username]` | Show user statistics |
74
76
  | `timer <id>` | Interview mode with timer |
77
+ | `collab <cmd>` | Collaborative coding with a partner |
75
78
  | `config` | View or set configuration |
76
79
  | `sync` | Sync solutions to Git repository |
77
80
 
@@ -222,6 +225,29 @@ leetcode timer --stats
222
225
 
223
226
  # Stop active timer
224
227
  leetcode timer --stop
228
+ ```
229
+
230
+ ### Collaborative Coding
231
+
232
+ ```bash
233
+ # Host a collaboration session
234
+ leetcode collab host 1
235
+
236
+ # Share the room code with your partner
237
+ # Partner joins with:
238
+ leetcode collab join ABC123
239
+
240
+ # Both solve the problem, then sync
241
+ leetcode collab sync
242
+
243
+ # Compare solutions
244
+ leetcode collab compare
245
+
246
+ # Check session status
247
+ leetcode collab status
248
+
249
+ # Leave session
250
+ leetcode collab leave
225
251
  ```
226
252
 
227
253
  ### Configuration
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { Command } from "commander";
5
- import chalk21 from "chalk";
5
+ import chalk22 from "chalk";
6
6
 
7
7
  // src/commands/login.ts
8
8
  import inquirer from "inquirer";
@@ -308,12 +308,12 @@ var LeetCodeClient = class {
308
308
  retry: { limit: 2 }
309
309
  });
310
310
  }
311
- setCredentials(credentials) {
312
- this.credentials = credentials;
311
+ setCredentials(credentials2) {
312
+ this.credentials = credentials2;
313
313
  this.client = this.client.extend({
314
314
  headers: {
315
- "Cookie": `LEETCODE_SESSION=${credentials.session}; csrftoken=${credentials.csrfToken}`,
316
- "X-CSRFToken": credentials.csrfToken
315
+ "Cookie": `LEETCODE_SESSION=${credentials2.session}; csrftoken=${credentials2.csrfToken}`,
316
+ "X-CSRFToken": credentials2.csrfToken
317
317
  }
318
318
  });
319
319
  }
@@ -445,14 +445,14 @@ var LeetCodeClient = class {
445
445
  }).json();
446
446
  return this.pollSubmission(response.submission_id.toString(), "submission", SubmissionResultSchema);
447
447
  }
448
- async pollSubmission(id, _type, schema2) {
448
+ async pollSubmission(id, _type, schema) {
449
449
  const endpoint = `submissions/detail/${id}/check/`;
450
450
  const maxAttempts = 30;
451
451
  const delay = 1e3;
452
452
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
453
453
  const result = await this.client.get(endpoint).json();
454
454
  if (result.state === "SUCCESS" || result.state === "FAILURE") {
455
- return schema2.parse(result);
455
+ return schema.parse(result);
456
456
  }
457
457
  await new Promise((resolve) => setTimeout(resolve, delay));
458
458
  }
@@ -461,88 +461,31 @@ var LeetCodeClient = class {
461
461
  };
462
462
  var leetcodeClient = new LeetCodeClient();
463
463
 
464
- // src/storage/config.ts
464
+ // src/storage/credentials.ts
465
465
  import Conf from "conf";
466
466
  import { homedir } from "os";
467
467
  import { join } from "path";
468
- var schema = {
469
- credentials: {
470
- type: "object",
471
- nullable: true,
472
- properties: {
473
- csrfToken: { type: "string" },
474
- session: { type: "string" }
475
- }
476
- },
477
- config: {
478
- type: "object",
479
- properties: {
480
- language: { type: "string", default: "typescript" },
481
- editor: { type: "string" },
482
- workDir: { type: "string", default: join(homedir(), "leetcode") },
483
- repo: { type: "string" }
484
- },
485
- default: {
486
- language: "typescript",
487
- workDir: join(homedir(), "leetcode")
488
- }
489
- }
490
- };
491
- var configStore = new Conf({
492
- projectName: "leetcode-cli",
468
+ var credentialsStore = new Conf({
469
+ configName: "credentials",
493
470
  cwd: join(homedir(), ".leetcode"),
494
- schema
471
+ defaults: {}
495
472
  });
496
- var config = {
497
- // Credentials
498
- getCredentials() {
499
- return configStore.get("credentials") ?? null;
473
+ var credentials = {
474
+ get() {
475
+ const session = credentialsStore.get("session");
476
+ const csrfToken = credentialsStore.get("csrfToken");
477
+ if (!session || !csrfToken) return null;
478
+ return { session, csrfToken };
500
479
  },
501
- setCredentials(credentials) {
502
- configStore.set("credentials", credentials);
503
- },
504
- clearCredentials() {
505
- configStore.delete("credentials");
506
- },
507
- // User Config
508
- getConfig() {
509
- return configStore.get("config");
510
- },
511
- setLanguage(language) {
512
- configStore.set("config.language", language);
513
- },
514
- setEditor(editor) {
515
- configStore.set("config.editor", editor);
516
- },
517
- setWorkDir(workDir) {
518
- configStore.set("config.workDir", workDir);
519
- },
520
- setRepo(repo) {
521
- configStore.set("config.repo", repo);
522
- },
523
- deleteRepo() {
524
- configStore.delete("config.repo");
525
- },
526
- // Get specific config values
527
- getLanguage() {
528
- return configStore.get("config.language");
480
+ set(creds) {
481
+ credentialsStore.set("session", creds.session);
482
+ credentialsStore.set("csrfToken", creds.csrfToken);
529
483
  },
530
- getEditor() {
531
- return configStore.get("config.editor");
532
- },
533
- getWorkDir() {
534
- return configStore.get("config.workDir");
535
- },
536
- getRepo() {
537
- return configStore.get("config.repo");
538
- },
539
- // Clear all config
540
484
  clear() {
541
- configStore.clear();
485
+ credentialsStore.clear();
542
486
  },
543
- // Get config file path (for debugging)
544
487
  getPath() {
545
- return configStore.path;
488
+ return credentialsStore.path;
546
489
  }
547
490
  };
548
491
 
@@ -574,23 +517,23 @@ async function loginCommand() {
574
517
  validate: (input) => input.length > 0 || "CSRF token is required"
575
518
  }
576
519
  ]);
577
- const credentials = {
520
+ const creds = {
578
521
  session: answers.session.trim(),
579
522
  csrfToken: answers.csrfToken.trim()
580
523
  };
581
524
  const spinner = ora("Verifying credentials...").start();
582
525
  try {
583
- leetcodeClient.setCredentials(credentials);
526
+ leetcodeClient.setCredentials(creds);
584
527
  const { isSignedIn, username } = await leetcodeClient.checkAuth();
585
528
  if (!isSignedIn || !username) {
586
529
  spinner.fail("Invalid credentials");
587
530
  console.log(chalk.red("Please check your session cookies and try again."));
588
531
  return;
589
532
  }
590
- config.setCredentials(credentials);
533
+ credentials.set(creds);
591
534
  spinner.succeed(`Logged in as ${chalk.green(username)}`);
592
535
  console.log();
593
- console.log(chalk.gray(`Credentials saved to ${config.getPath()}`));
536
+ console.log(chalk.gray(`Credentials saved to ${credentials.getPath()}`));
594
537
  } catch (error) {
595
538
  spinner.fail("Authentication failed");
596
539
  if (error instanceof Error) {
@@ -599,18 +542,18 @@ async function loginCommand() {
599
542
  }
600
543
  }
601
544
  async function logoutCommand() {
602
- config.clearCredentials();
545
+ credentials.clear();
603
546
  console.log(chalk.green("\u2713 Logged out successfully"));
604
547
  }
605
548
  async function whoamiCommand() {
606
- const credentials = config.getCredentials();
607
- if (!credentials) {
549
+ const creds = credentials.get();
550
+ if (!creds) {
608
551
  console.log(chalk.yellow('Not logged in. Run "leetcode login" to authenticate.'));
609
552
  return;
610
553
  }
611
554
  const spinner = ora("Checking session...").start();
612
555
  try {
613
- leetcodeClient.setCredentials(credentials);
556
+ leetcodeClient.setCredentials(creds);
614
557
  const { isSignedIn, username } = await leetcodeClient.checkAuth();
615
558
  if (!isSignedIn || !username) {
616
559
  spinner.fail("Session expired");
@@ -633,13 +576,13 @@ import chalk4 from "chalk";
633
576
  // src/utils/auth.ts
634
577
  import chalk2 from "chalk";
635
578
  async function requireAuth() {
636
- const credentials = config.getCredentials();
637
- if (!credentials) {
579
+ const creds = credentials.get();
580
+ if (!creds) {
638
581
  console.log(chalk2.yellow("\u26A0\uFE0F Please login first: leetcode login"));
639
582
  return { authorized: false };
640
583
  }
641
584
  try {
642
- leetcodeClient.setCredentials(credentials);
585
+ leetcodeClient.setCredentials(creds);
643
586
  const { isSignedIn, username } = await leetcodeClient.checkAuth();
644
587
  if (!isSignedIn) {
645
588
  console.log(chalk2.yellow("\u26A0\uFE0F Session expired. Please run: leetcode login"));
@@ -990,10 +933,63 @@ async function showCommand(idOrSlug) {
990
933
  // src/commands/pick.ts
991
934
  import { writeFile, mkdir } from "fs/promises";
992
935
  import { existsSync } from "fs";
993
- import { join as join2 } from "path";
936
+ import { join as join3 } from "path";
994
937
  import ora4 from "ora";
995
938
  import chalk7 from "chalk";
996
939
 
940
+ // src/storage/config.ts
941
+ import Conf2 from "conf";
942
+ import { homedir as homedir2 } from "os";
943
+ import { join as join2 } from "path";
944
+ var configStore = new Conf2({
945
+ configName: "config",
946
+ cwd: join2(homedir2(), ".leetcode"),
947
+ defaults: {
948
+ language: "typescript",
949
+ workDir: join2(homedir2(), "leetcode")
950
+ }
951
+ });
952
+ var config = {
953
+ getConfig() {
954
+ return {
955
+ language: configStore.get("language"),
956
+ editor: configStore.get("editor"),
957
+ workDir: configStore.get("workDir"),
958
+ repo: configStore.get("repo")
959
+ };
960
+ },
961
+ setLanguage(language) {
962
+ configStore.set("language", language);
963
+ },
964
+ setEditor(editor) {
965
+ configStore.set("editor", editor);
966
+ },
967
+ setWorkDir(workDir) {
968
+ configStore.set("workDir", workDir);
969
+ },
970
+ setRepo(repo) {
971
+ configStore.set("repo", repo);
972
+ },
973
+ deleteRepo() {
974
+ configStore.delete("repo");
975
+ },
976
+ getLanguage() {
977
+ return configStore.get("language");
978
+ },
979
+ getEditor() {
980
+ return configStore.get("editor");
981
+ },
982
+ getWorkDir() {
983
+ return configStore.get("workDir");
984
+ },
985
+ getRepo() {
986
+ return configStore.get("repo");
987
+ },
988
+ getPath() {
989
+ return configStore.path;
990
+ }
991
+ };
992
+
997
993
  // src/utils/templates.ts
998
994
  var LANGUAGE_EXTENSIONS = {
999
995
  typescript: "ts",
@@ -1187,12 +1183,12 @@ async function pickCommand(idOrSlug, options) {
1187
1183
  const workDir = config.getWorkDir();
1188
1184
  const difficulty = problem.difficulty;
1189
1185
  const category = problem.topicTags.length > 0 ? problem.topicTags[0].name.replace(/[^\w\s-]/g, "").trim() : "Uncategorized";
1190
- const targetDir = join2(workDir, difficulty, category);
1186
+ const targetDir = join3(workDir, difficulty, category);
1191
1187
  if (!existsSync(targetDir)) {
1192
1188
  await mkdir(targetDir, { recursive: true });
1193
1189
  }
1194
1190
  const fileName = getSolutionFileName(problem.questionFrontendId, problem.titleSlug, language);
1195
- const filePath = join2(targetDir, fileName);
1191
+ const filePath = join3(targetDir, fileName);
1196
1192
  if (existsSync(filePath)) {
1197
1193
  spinner.warn(`File already exists: ${fileName}`);
1198
1194
  console.log(chalk7.gray(`Path: ${filePath}`));
@@ -1271,7 +1267,7 @@ import chalk8 from "chalk";
1271
1267
  // src/utils/fileUtils.ts
1272
1268
  import { readdir } from "fs/promises";
1273
1269
  import { existsSync as existsSync2 } from "fs";
1274
- import { join as join3 } from "path";
1270
+ import { join as join4 } from "path";
1275
1271
  var MAX_SEARCH_DEPTH = 5;
1276
1272
  async function findSolutionFile(dir, problemId, currentDepth = 0) {
1277
1273
  if (!existsSync2(dir)) return null;
@@ -1279,7 +1275,7 @@ async function findSolutionFile(dir, problemId, currentDepth = 0) {
1279
1275
  const entries = await readdir(dir, { withFileTypes: true });
1280
1276
  for (const entry of entries) {
1281
1277
  if (entry.name.startsWith(".")) continue;
1282
- const fullPath = join3(dir, entry.name);
1278
+ const fullPath = join4(dir, entry.name);
1283
1279
  if (entry.isDirectory()) {
1284
1280
  const found = await findSolutionFile(fullPath, problemId, currentDepth + 1);
1285
1281
  if (found) return found;
@@ -1297,7 +1293,7 @@ async function findFileByName(dir, fileName, currentDepth = 0) {
1297
1293
  if (currentDepth >= MAX_SEARCH_DEPTH) return null;
1298
1294
  const entries = await readdir(dir, { withFileTypes: true });
1299
1295
  for (const entry of entries) {
1300
- const fullPath = join3(dir, entry.name);
1296
+ const fullPath = join4(dir, entry.name);
1301
1297
  if (entry.isDirectory()) {
1302
1298
  const found = await findFileByName(fullPath, fileName, currentDepth + 1);
1303
1299
  if (found) return found;
@@ -1417,12 +1413,12 @@ import ora6 from "ora";
1417
1413
  import chalk9 from "chalk";
1418
1414
 
1419
1415
  // 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"),
1416
+ import Conf3 from "conf";
1417
+ import { homedir as homedir3 } from "os";
1418
+ import { join as join5 } from "path";
1419
+ var timerStore = new Conf3({
1420
+ configName: "timer",
1421
+ cwd: join5(homedir3(), ".leetcode"),
1426
1422
  defaults: {
1427
1423
  solveTimes: {},
1428
1424
  activeTimer: null
@@ -1826,7 +1822,7 @@ async function randomCommand(options) {
1826
1822
  // src/commands/submissions.ts
1827
1823
  import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
1828
1824
  import { existsSync as existsSync5 } from "fs";
1829
- import { join as join5 } from "path";
1825
+ import { join as join6 } from "path";
1830
1826
  import ora10 from "ora";
1831
1827
  import chalk14 from "chalk";
1832
1828
  async function submissionsCommand(idOrSlug, options) {
@@ -1880,7 +1876,7 @@ async function submissionsCommand(idOrSlug, options) {
1880
1876
  const workDir = config.getWorkDir();
1881
1877
  const difficulty = problem.difficulty;
1882
1878
  const category = problem.topicTags.length > 0 ? problem.topicTags[0].name.replace(/[^\w\s-]/g, "").trim() : "Uncategorized";
1883
- const targetDir = join5(workDir, difficulty, category);
1879
+ const targetDir = join6(workDir, difficulty, category);
1884
1880
  if (!existsSync5(targetDir)) {
1885
1881
  await mkdir2(targetDir, { recursive: true });
1886
1882
  }
@@ -1888,7 +1884,7 @@ async function submissionsCommand(idOrSlug, options) {
1888
1884
  const supportedLang = LANG_SLUG_MAP[langSlug] ?? "txt";
1889
1885
  const ext = LANGUAGE_EXTENSIONS[supportedLang] ?? langSlug;
1890
1886
  const fileName = `${problem.questionFrontendId}.${problem.titleSlug}.submission-${lastAC.id}.${ext}`;
1891
- const filePath = join5(targetDir, fileName);
1887
+ const filePath = join6(targetDir, fileName);
1892
1888
  await writeFile2(filePath, details.code, "utf-8");
1893
1889
  downloadSpinner.succeed(`Downloaded to ${chalk14.green(fileName)}`);
1894
1890
  console.log(chalk14.gray(`Path: ${filePath}`));
@@ -1994,7 +1990,7 @@ async function configInteractiveCommand() {
1994
1990
  }
1995
1991
  function showCurrentConfig() {
1996
1992
  const currentConfig = config.getConfig();
1997
- const credentials = config.getCredentials();
1993
+ const creds = credentials.get();
1998
1994
  console.log();
1999
1995
  console.log(chalk15.bold("LeetCode CLI Configuration"));
2000
1996
  console.log(chalk15.gray("\u2500".repeat(40)));
@@ -2005,7 +2001,7 @@ function showCurrentConfig() {
2005
2001
  console.log(chalk15.gray("Editor: "), chalk15.white(currentConfig.editor ?? "(not set)"));
2006
2002
  console.log(chalk15.gray("Work Dir: "), chalk15.white(currentConfig.workDir));
2007
2003
  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"));
2004
+ console.log(chalk15.gray("Logged in: "), creds ? chalk15.green("Yes") : chalk15.yellow("No"));
2009
2005
  }
2010
2006
 
2011
2007
  // src/commands/bookmark.ts
@@ -2014,8 +2010,8 @@ import Table2 from "cli-table3";
2014
2010
  import ora11 from "ora";
2015
2011
 
2016
2012
  // src/storage/bookmarks.ts
2017
- import Conf3 from "conf";
2018
- var bookmarksStore = new Conf3({
2013
+ import Conf4 from "conf";
2014
+ var bookmarksStore = new Conf4({
2019
2015
  projectName: "leetcode-cli-bookmarks",
2020
2016
  defaults: { bookmarks: [] }
2021
2017
  });
@@ -2168,7 +2164,7 @@ function colorDifficulty2(difficulty) {
2168
2164
 
2169
2165
  // src/commands/notes.ts
2170
2166
  import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
2171
- import { join as join6 } from "path";
2167
+ import { join as join7 } from "path";
2172
2168
  import { existsSync as existsSync6 } from "fs";
2173
2169
  import chalk17 from "chalk";
2174
2170
  async function notesCommand(problemId, action) {
@@ -2178,8 +2174,8 @@ async function notesCommand(problemId, action) {
2178
2174
  return;
2179
2175
  }
2180
2176
  const noteAction = action === "view" ? "view" : "edit";
2181
- const notesDir = join6(config.getWorkDir(), ".notes");
2182
- const notePath = join6(notesDir, `${problemId}.md`);
2177
+ const notesDir = join7(config.getWorkDir(), ".notes");
2178
+ const notePath = join7(notesDir, `${problemId}.md`);
2183
2179
  if (!existsSync6(notesDir)) {
2184
2180
  await mkdir3(notesDir, { recursive: true });
2185
2181
  }
@@ -2654,162 +2650,488 @@ async function showTimerStats(problemId) {
2654
2650
  }
2655
2651
  }
2656
2652
 
2653
+ // src/commands/collab.ts
2654
+ import chalk21 from "chalk";
2655
+ import ora15 from "ora";
2656
+ import { readFile as readFile4 } from "fs/promises";
2657
+
2658
+ // src/services/supabase.ts
2659
+ import { createClient } from "@supabase/supabase-js";
2660
+ var SUPABASE_URL = "https://abagrmwdpvnfyuqizyym.supabase.co";
2661
+ var SUPABASE_ANON_KEY = "sb_publishable_indrKu8VJmASdyLp7w8Hog_OyqT17cV";
2662
+ var supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
2663
+
2664
+ // src/storage/collab.ts
2665
+ import Conf5 from "conf";
2666
+ import { homedir as homedir4 } from "os";
2667
+ import { join as join8 } from "path";
2668
+ var collabStore = new Conf5({
2669
+ configName: "collab",
2670
+ cwd: join8(homedir4(), ".leetcode"),
2671
+ defaults: {
2672
+ session: null
2673
+ }
2674
+ });
2675
+ var collabStorage = {
2676
+ getSession() {
2677
+ return collabStore.get("session");
2678
+ },
2679
+ setSession(session) {
2680
+ if (session) {
2681
+ collabStore.set("session", session);
2682
+ } else {
2683
+ collabStore.delete("session");
2684
+ }
2685
+ },
2686
+ getPath() {
2687
+ return collabStore.path;
2688
+ }
2689
+ };
2690
+
2691
+ // src/services/collab.ts
2692
+ function generateRoomCode() {
2693
+ const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
2694
+ let code = "";
2695
+ for (let i = 0; i < 6; i++) {
2696
+ code += chars.charAt(Math.floor(Math.random() * chars.length));
2697
+ }
2698
+ return code;
2699
+ }
2700
+ var collabService = {
2701
+ getSession() {
2702
+ return collabStorage.getSession();
2703
+ },
2704
+ async createRoom(problemId, username) {
2705
+ const roomCode = generateRoomCode();
2706
+ const { error } = await supabase.from("collab_rooms").insert({
2707
+ room_code: roomCode,
2708
+ problem_id: problemId,
2709
+ host_username: username,
2710
+ host_code: "",
2711
+ guest_username: null,
2712
+ guest_code: null
2713
+ });
2714
+ if (error) {
2715
+ return { error: error.message };
2716
+ }
2717
+ collabStorage.setSession({
2718
+ roomCode,
2719
+ problemId,
2720
+ isHost: true,
2721
+ username
2722
+ });
2723
+ return { roomCode };
2724
+ },
2725
+ async joinRoom(roomCode, username) {
2726
+ const { data: room, error: fetchError } = await supabase.from("collab_rooms").select("*").eq("room_code", roomCode.toUpperCase()).single();
2727
+ if (fetchError || !room) {
2728
+ return { error: "Room not found" };
2729
+ }
2730
+ const { error: updateError } = await supabase.from("collab_rooms").update({ guest_username: username }).eq("room_code", roomCode.toUpperCase());
2731
+ if (updateError) {
2732
+ return { error: updateError.message };
2733
+ }
2734
+ collabStorage.setSession({
2735
+ roomCode: roomCode.toUpperCase(),
2736
+ problemId: room.problem_id,
2737
+ isHost: false,
2738
+ username
2739
+ });
2740
+ return { problemId: room.problem_id };
2741
+ },
2742
+ async syncCode(code) {
2743
+ const session = collabStorage.getSession();
2744
+ if (!session) {
2745
+ return { success: false, error: "No active session" };
2746
+ }
2747
+ const column = session.isHost ? "host_code" : "guest_code";
2748
+ const { error } = await supabase.from("collab_rooms").update({ [column]: code }).eq("room_code", session.roomCode);
2749
+ if (error) {
2750
+ return { success: false, error: error.message };
2751
+ }
2752
+ return { success: true };
2753
+ },
2754
+ async getPartnerCode() {
2755
+ const session = collabStorage.getSession();
2756
+ if (!session) {
2757
+ return { error: "No active session" };
2758
+ }
2759
+ const { data: room, error } = await supabase.from("collab_rooms").select("*").eq("room_code", session.roomCode).single();
2760
+ if (error || !room) {
2761
+ return { error: "Room not found" };
2762
+ }
2763
+ if (session.isHost) {
2764
+ return {
2765
+ code: room.guest_code || "",
2766
+ username: room.guest_username || "Partner"
2767
+ };
2768
+ } else {
2769
+ return {
2770
+ code: room.host_code || "",
2771
+ username: room.host_username || "Host"
2772
+ };
2773
+ }
2774
+ },
2775
+ async getRoomStatus() {
2776
+ const session = collabStorage.getSession();
2777
+ if (!session) {
2778
+ return { error: "No active session" };
2779
+ }
2780
+ const { data: room, error } = await supabase.from("collab_rooms").select("*").eq("room_code", session.roomCode).single();
2781
+ if (error || !room) {
2782
+ return { error: "Room not found" };
2783
+ }
2784
+ return {
2785
+ host: room.host_username,
2786
+ guest: room.guest_username,
2787
+ hasHostCode: !!room.host_code,
2788
+ hasGuestCode: !!room.guest_code
2789
+ };
2790
+ },
2791
+ async leaveRoom() {
2792
+ const session = collabStorage.getSession();
2793
+ if (session) {
2794
+ if (session.isHost) {
2795
+ await supabase.from("collab_rooms").delete().eq("room_code", session.roomCode);
2796
+ }
2797
+ }
2798
+ collabStorage.setSession(null);
2799
+ }
2800
+ };
2801
+
2802
+ // src/commands/collab.ts
2803
+ async function collabHostCommand(problemId) {
2804
+ const { authorized, username } = await requireAuth();
2805
+ if (!authorized || !username) return;
2806
+ const spinner = ora15("Creating collaboration room...").start();
2807
+ try {
2808
+ const result = await collabService.createRoom(problemId, username);
2809
+ if ("error" in result) {
2810
+ spinner.fail(result.error);
2811
+ return;
2812
+ }
2813
+ spinner.succeed("Room created!");
2814
+ console.log();
2815
+ console.log(chalk21.bold.cyan("\u{1F465} Collaborative Coding Session"));
2816
+ console.log(chalk21.gray("\u2500".repeat(50)));
2817
+ console.log();
2818
+ console.log(chalk21.white(`Room Code: ${chalk21.bold.green(result.roomCode)}`));
2819
+ console.log(chalk21.white(`Problem: ${problemId}`));
2820
+ console.log();
2821
+ console.log(chalk21.gray("Share this code with your partner:"));
2822
+ console.log(chalk21.yellow(` leetcode collab join ${result.roomCode}`));
2823
+ console.log();
2824
+ console.log(chalk21.gray("\u2500".repeat(50)));
2825
+ console.log(chalk21.gray("After solving, sync and compare:"));
2826
+ console.log(chalk21.gray(" leetcode collab sync - Upload your solution"));
2827
+ console.log(chalk21.gray(" leetcode collab compare - See both solutions"));
2828
+ console.log(chalk21.gray(" leetcode collab status - Check room status"));
2829
+ console.log(chalk21.gray(" leetcode collab leave - End session"));
2830
+ console.log();
2831
+ await pickCommand(problemId, { open: true });
2832
+ } catch (error) {
2833
+ spinner.fail("Failed to create room");
2834
+ if (error instanceof Error) {
2835
+ console.log(chalk21.red(error.message));
2836
+ }
2837
+ }
2838
+ }
2839
+ async function collabJoinCommand(roomCode) {
2840
+ const { authorized, username } = await requireAuth();
2841
+ if (!authorized || !username) return;
2842
+ const spinner = ora15(`Joining room ${roomCode}...`).start();
2843
+ try {
2844
+ const result = await collabService.joinRoom(roomCode.toUpperCase(), username);
2845
+ if ("error" in result) {
2846
+ spinner.fail(result.error);
2847
+ return;
2848
+ }
2849
+ spinner.succeed("Joined room!");
2850
+ console.log();
2851
+ console.log(chalk21.bold.cyan("\u{1F465} Collaborative Coding Session"));
2852
+ console.log(chalk21.gray("\u2500".repeat(50)));
2853
+ console.log();
2854
+ console.log(chalk21.white(`Room Code: ${chalk21.bold.green(roomCode.toUpperCase())}`));
2855
+ console.log(chalk21.white(`Problem: ${result.problemId}`));
2856
+ console.log();
2857
+ console.log(chalk21.gray("\u2500".repeat(50)));
2858
+ console.log(chalk21.gray("After solving, sync and compare:"));
2859
+ console.log(chalk21.gray(" leetcode collab sync - Upload your solution"));
2860
+ console.log(chalk21.gray(" leetcode collab compare - See both solutions"));
2861
+ console.log(chalk21.gray(" leetcode collab status - Check room status"));
2862
+ console.log(chalk21.gray(" leetcode collab leave - End session"));
2863
+ console.log();
2864
+ await pickCommand(result.problemId, { open: true });
2865
+ } catch (error) {
2866
+ spinner.fail("Failed to join room");
2867
+ if (error instanceof Error) {
2868
+ console.log(chalk21.red(error.message));
2869
+ }
2870
+ }
2871
+ }
2872
+ async function collabSyncCommand() {
2873
+ const session = collabService.getSession();
2874
+ if (!session) {
2875
+ console.log(chalk21.yellow("No active collaboration session."));
2876
+ console.log(chalk21.gray("Use `leetcode collab host <id>` or `leetcode collab join <code>` first."));
2877
+ return;
2878
+ }
2879
+ const spinner = ora15("Syncing your code...").start();
2880
+ const workDir = config.getWorkDir();
2881
+ const filePath = await findSolutionFile(workDir, session.problemId);
2882
+ if (!filePath) {
2883
+ spinner.fail(`No solution file found for problem ${session.problemId}`);
2884
+ return;
2885
+ }
2886
+ const code = await readFile4(filePath, "utf-8");
2887
+ const result = await collabService.syncCode(code);
2888
+ if (result.success) {
2889
+ spinner.succeed("Code synced successfully!");
2890
+ console.log(chalk21.gray(`Uploaded ${code.split("\n").length} lines from ${filePath}`));
2891
+ } else {
2892
+ spinner.fail(result.error || "Sync failed");
2893
+ }
2894
+ }
2895
+ async function collabCompareCommand() {
2896
+ const session = collabService.getSession();
2897
+ if (!session) {
2898
+ console.log(chalk21.yellow("No active collaboration session."));
2899
+ console.log(chalk21.gray("Use `leetcode collab host <id>` or `leetcode collab join <code>` first."));
2900
+ return;
2901
+ }
2902
+ const spinner = ora15("Fetching solutions...").start();
2903
+ const workDir = config.getWorkDir();
2904
+ const filePath = await findSolutionFile(workDir, session.problemId);
2905
+ if (!filePath) {
2906
+ spinner.fail(`No solution file found for problem ${session.problemId}`);
2907
+ return;
2908
+ }
2909
+ const myCode = await readFile4(filePath, "utf-8");
2910
+ const partnerResult = await collabService.getPartnerCode();
2911
+ if ("error" in partnerResult) {
2912
+ spinner.fail(partnerResult.error);
2913
+ return;
2914
+ }
2915
+ spinner.stop();
2916
+ if (!partnerResult.code) {
2917
+ console.log(chalk21.yellow("Partner has not synced their code yet."));
2918
+ console.log(chalk21.gray("Ask them to run `leetcode collab sync`."));
2919
+ return;
2920
+ }
2921
+ console.log();
2922
+ console.log(chalk21.bold.cyan("\u{1F4CA} Solution Comparison"));
2923
+ console.log(chalk21.gray("\u2500".repeat(60)));
2924
+ console.log();
2925
+ console.log(chalk21.bold.green(`\u25B8 Your Solution (${session.username})`));
2926
+ console.log(chalk21.gray("\u2500".repeat(60)));
2927
+ const myLines = myCode.split("\n");
2928
+ for (let i = 0; i < myLines.length; i++) {
2929
+ const lineNum = String(i + 1).padStart(3, " ");
2930
+ console.log(`${chalk21.gray(lineNum)} ${myLines[i]}`);
2931
+ }
2932
+ console.log();
2933
+ console.log(chalk21.bold.blue(`\u25B8 ${partnerResult.username}'s Solution`));
2934
+ console.log(chalk21.gray("\u2500".repeat(60)));
2935
+ const partnerLines = partnerResult.code.split("\n");
2936
+ for (let i = 0; i < partnerLines.length; i++) {
2937
+ const lineNum = String(i + 1).padStart(3, " ");
2938
+ console.log(`${chalk21.gray(lineNum)} ${partnerLines[i]}`);
2939
+ }
2940
+ console.log();
2941
+ console.log(chalk21.gray("\u2500".repeat(60)));
2942
+ console.log(chalk21.gray(`Your code: ${myLines.length} lines | Partner: ${partnerLines.length} lines`));
2943
+ console.log();
2944
+ }
2945
+ async function collabLeaveCommand() {
2946
+ const session = collabService.getSession();
2947
+ if (!session) {
2948
+ console.log(chalk21.yellow("No active collaboration session."));
2949
+ return;
2950
+ }
2951
+ await collabService.leaveRoom();
2952
+ console.log(chalk21.green("\u2713 Left the collaboration session."));
2953
+ }
2954
+ async function collabStatusCommand() {
2955
+ const session = collabService.getSession();
2956
+ if (!session) {
2957
+ console.log(chalk21.yellow("No active collaboration session."));
2958
+ console.log(chalk21.gray("Use `leetcode collab host <id>` or `leetcode collab join <code>` to start."));
2959
+ return;
2960
+ }
2961
+ const status = await collabService.getRoomStatus();
2962
+ if ("error" in status) {
2963
+ console.log(chalk21.red(status.error));
2964
+ return;
2965
+ }
2966
+ console.log();
2967
+ console.log(chalk21.bold.cyan("\u{1F465} Collaboration Status"));
2968
+ console.log(chalk21.gray("\u2500".repeat(40)));
2969
+ console.log(` Room: ${chalk21.green(session.roomCode)}`);
2970
+ console.log(` Problem: ${session.problemId}`);
2971
+ console.log(` Role: ${session.isHost ? "Host" : "Guest"}`);
2972
+ console.log();
2973
+ console.log(chalk21.bold(" Participants:"));
2974
+ console.log(` Host: ${status.host} ${status.hasHostCode ? chalk21.green("\u2713 synced") : chalk21.gray("pending")}`);
2975
+ console.log(` Guest: ${status.guest || chalk21.gray("(waiting...)")} ${status.hasGuestCode ? chalk21.green("\u2713 synced") : chalk21.gray("pending")}`);
2976
+ console.log();
2977
+ }
2978
+
2657
2979
  // src/index.ts
2658
2980
  var program = new Command();
2659
2981
  program.configureHelp({
2660
2982
  sortSubcommands: true,
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)
2983
+ subcommandTerm: (cmd) => chalk22.cyan(cmd.name()) + (cmd.alias() ? chalk22.gray(`|${cmd.alias()}`) : ""),
2984
+ subcommandDescription: (cmd) => chalk22.white(cmd.description()),
2985
+ optionTerm: (option) => chalk22.yellow(option.flags),
2986
+ optionDescription: (option) => chalk22.white(option.description)
2665
2987
  });
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
2988
+ program.name("leetcode").usage("[command] [options]").description(chalk22.bold.cyan("\u{1F525} A modern LeetCode CLI built with TypeScript")).version("1.6.0", "-v, --version", "Output the version number").helpOption("-h, --help", "Display help for command").addHelpText("after", `
2989
+ ${chalk22.yellow("Examples:")}
2990
+ ${chalk22.cyan("$ leetcode login")} Login to LeetCode
2991
+ ${chalk22.cyan("$ leetcode list -d easy")} List easy problems
2992
+ ${chalk22.cyan("$ leetcode random -d medium")} Get random medium problem
2993
+ ${chalk22.cyan("$ leetcode pick 1")} Start solving "Two Sum"
2994
+ ${chalk22.cyan("$ leetcode test 1")} Test your solution
2995
+ ${chalk22.cyan("$ leetcode submit 1")} Submit your solution
2674
2996
  `);
2675
2997
  program.command("login").description("Login to LeetCode with browser cookies").addHelpText("after", `
2676
- ${chalk21.yellow("How to login:")}
2677
- 1. Open ${chalk21.cyan("https://leetcode.com")} in your browser
2998
+ ${chalk22.yellow("How to login:")}
2999
+ 1. Open ${chalk22.cyan("https://leetcode.com")} in your browser
2678
3000
  2. Login to your account
2679
3001
  3. Open Developer Tools (F12) \u2192 Application \u2192 Cookies
2680
- 4. Copy values of ${chalk21.green("LEETCODE_SESSION")} and ${chalk21.green("csrftoken")}
3002
+ 4. Copy values of ${chalk22.green("LEETCODE_SESSION")} and ${chalk22.green("csrftoken")}
2681
3003
  5. Paste when prompted by this command
2682
3004
  `).action(loginCommand);
2683
3005
  program.command("logout").description("Clear stored credentials").action(logoutCommand);
2684
3006
  program.command("whoami").description("Check current login status").action(whoamiCommand);
2685
3007
  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", `
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
3008
+ ${chalk22.yellow("Examples:")}
3009
+ ${chalk22.cyan("$ leetcode list")} List first 20 problems
3010
+ ${chalk22.cyan("$ leetcode list -d easy")} List easy problems only
3011
+ ${chalk22.cyan("$ leetcode list -s solved")} List your solved problems
3012
+ ${chalk22.cyan("$ leetcode list -t array -t string")} Filter by multiple tags
3013
+ ${chalk22.cyan('$ leetcode list -q "two sum"')} Search by keywords
3014
+ ${chalk22.cyan("$ leetcode list -n 50 -p 2")} Show 50 problems, page 2
2693
3015
  `).action(listCommand);
2694
3016
  program.command("show <id>").alias("s").description("Show problem description").addHelpText("after", `
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
3017
+ ${chalk22.yellow("Examples:")}
3018
+ ${chalk22.cyan("$ leetcode show 1")} Show by problem ID
3019
+ ${chalk22.cyan("$ leetcode show two-sum")} Show by problem slug
3020
+ ${chalk22.cyan("$ leetcode s 412")} Short alias
2699
3021
  `).action(showCommand);
2700
3022
  program.command("daily").alias("d").description("Show today's daily challenge").addHelpText("after", `
2701
- ${chalk21.yellow("Examples:")}
2702
- ${chalk21.cyan("$ leetcode daily")} Show today's challenge
2703
- ${chalk21.cyan("$ leetcode d")} Short alias
3023
+ ${chalk22.yellow("Examples:")}
3024
+ ${chalk22.cyan("$ leetcode daily")} Show today's challenge
3025
+ ${chalk22.cyan("$ leetcode d")} Short alias
2704
3026
  `).action(dailyCommand);
2705
3027
  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", `
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
3028
+ ${chalk22.yellow("Examples:")}
3029
+ ${chalk22.cyan("$ leetcode random")} Get any random problem
3030
+ ${chalk22.cyan("$ leetcode random -d medium")} Random medium problem
3031
+ ${chalk22.cyan("$ leetcode random -t array")} Random array problem
3032
+ ${chalk22.cyan("$ leetcode random --pick")} Random + create file
3033
+ ${chalk22.cyan("$ leetcode r -d easy --pick")} Random easy + file
2712
3034
  `).action(randomCommand);
2713
3035
  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", `
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
2720
-
2721
- ${chalk21.gray("Files are organized by: workDir/Difficulty/Category/")}
3036
+ ${chalk22.yellow("Examples:")}
3037
+ ${chalk22.cyan("$ leetcode pick 1")} Pick by problem ID
3038
+ ${chalk22.cyan("$ leetcode pick two-sum")} Pick by problem slug
3039
+ ${chalk22.cyan("$ leetcode pick 1 -l python3")} Pick with specific language
3040
+ ${chalk22.cyan("$ leetcode pick 1 --no-open")} Create file without opening
3041
+ ${chalk22.cyan("$ leetcode p 412")} Short alias
3042
+
3043
+ ${chalk22.gray("Files are organized by: workDir/Difficulty/Category/")}
2722
3044
  `).action(async (id, options) => {
2723
3045
  await pickCommand(id, options);
2724
3046
  });
2725
3047
  program.command("pick-batch <ids...>").description("Generate solution files for multiple problems").option("-l, --lang <language>", "Programming language for the solutions").addHelpText("after", `
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
3048
+ ${chalk22.yellow("Examples:")}
3049
+ ${chalk22.cyan("$ leetcode pick-batch 1 2 3")} Pick problems 1, 2, and 3
3050
+ ${chalk22.cyan("$ leetcode pick-batch 1 2 3 -l py")} Pick with Python
2729
3051
  `).action(batchPickCommand);
2730
3052
  program.command("test <file>").alias("t").description("Test solution against sample test cases").option("-c, --testcase <testcase>", "Custom test case").addHelpText("after", `
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
2737
-
2738
- ${chalk21.gray("Testcases use \\n to separate multiple inputs.")}
3053
+ ${chalk22.yellow("Examples:")}
3054
+ ${chalk22.cyan("$ leetcode test 1")} Test by problem ID
3055
+ ${chalk22.cyan("$ leetcode test two-sum")} Test by problem slug
3056
+ ${chalk22.cyan("$ leetcode test ./path/to/file.py")} Test by file path
3057
+ ${chalk22.cyan('$ leetcode test 1 -c "[1,2]\\n3"')} Test with custom case
3058
+ ${chalk22.cyan("$ leetcode t 412")} Short alias
3059
+
3060
+ ${chalk22.gray("Testcases use \\n to separate multiple inputs.")}
2739
3061
  `).action(testCommand);
2740
3062
  program.command("submit <file>").alias("x").description("Submit solution to LeetCode").addHelpText("after", `
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
3063
+ ${chalk22.yellow("Examples:")}
3064
+ ${chalk22.cyan("$ leetcode submit 1")} Submit by problem ID
3065
+ ${chalk22.cyan("$ leetcode submit two-sum")} Submit by problem slug
3066
+ ${chalk22.cyan("$ leetcode submit ./path/to/file.py")} Submit by file path
3067
+ ${chalk22.cyan("$ leetcode x 412")} Short alias
2746
3068
  `).action(submitCommand);
2747
3069
  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", `
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
3070
+ ${chalk22.yellow("Examples:")}
3071
+ ${chalk22.cyan("$ leetcode submissions 1")} View submissions for problem
3072
+ ${chalk22.cyan("$ leetcode submissions 1 -n 5")} Show last 5 submissions
3073
+ ${chalk22.cyan("$ leetcode submissions 1 --last")} Show last accepted submission
3074
+ ${chalk22.cyan("$ leetcode submissions 1 --download")} Download last accepted code
2753
3075
  `).action(submissionsCommand);
2754
3076
  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
3077
+ ${chalk22.yellow("Options Explained:")}
3078
+ ${chalk22.cyan("-c, --calendar")} Shows a table of your weekly submissions and active days
2757
3079
  for the past 12 weeks. Useful for tracking consistency.
2758
3080
 
2759
- ${chalk21.cyan("-s, --skills")} Shows how many problems you solved per topic tag,
3081
+ ${chalk22.cyan("-s, --skills")} Shows how many problems you solved per topic tag,
2760
3082
  grouped by difficulty (Fundamental/Intermediate/Advanced).
2761
3083
  Helps identify your strong and weak areas.
2762
3084
 
2763
- ${chalk21.cyan("-t, --trend")} Shows a bar chart of daily submissions for the past week.
3085
+ ${chalk22.cyan("-t, --trend")} Shows a bar chart of daily submissions for the past week.
2764
3086
  Visualizes your recent coding activity day by day.
2765
3087
 
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
3088
+ ${chalk22.yellow("Examples:")}
3089
+ ${chalk22.cyan("$ leetcode stat")} Show basic stats (solved count, rank)
3090
+ ${chalk22.cyan("$ leetcode stat lee215")} Show another user's stats
3091
+ ${chalk22.cyan("$ leetcode stat -c")} Weekly activity table
3092
+ ${chalk22.cyan("$ leetcode stat -s")} Topic-wise breakdown
3093
+ ${chalk22.cyan("$ leetcode stat -t")} 7-day trend chart
2772
3094
  `).action((username, options) => statCommand(username, options));
2773
3095
  program.command("today").description("Show today's progress summary").addHelpText("after", `
2774
- ${chalk21.yellow("Examples:")}
2775
- ${chalk21.cyan("$ leetcode today")} Show streak, solved, and daily challenge
3096
+ ${chalk22.yellow("Examples:")}
3097
+ ${chalk22.cyan("$ leetcode today")} Show streak, solved, and daily challenge
2776
3098
  `).action(todayCommand);
2777
3099
  program.command("bookmark <action> [id]").description("Manage problem bookmarks").addHelpText("after", `
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
2783
-
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
3100
+ ${chalk22.yellow("Actions:")}
3101
+ ${chalk22.cyan("add <id>")} Bookmark a problem
3102
+ ${chalk22.cyan("remove <id>")} Remove a bookmark
3103
+ ${chalk22.cyan("list")} List all bookmarks
3104
+ ${chalk22.cyan("clear")} Clear all bookmarks
3105
+
3106
+ ${chalk22.yellow("Examples:")}
3107
+ ${chalk22.cyan("$ leetcode bookmark add 1")} Bookmark problem 1
3108
+ ${chalk22.cyan("$ leetcode bookmark remove 1")} Remove bookmark
3109
+ ${chalk22.cyan("$ leetcode bookmark list")} List all bookmarks
2788
3110
  `).action(bookmarkCommand);
2789
3111
  program.command("note <id> [action]").description("View or edit notes for a problem").addHelpText("after", `
2790
- ${chalk21.yellow("Actions:")}
2791
- ${chalk21.cyan("edit")} Open notes in editor (default)
2792
- ${chalk21.cyan("view")} Display notes in terminal
2793
-
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
3112
+ ${chalk22.yellow("Actions:")}
3113
+ ${chalk22.cyan("edit")} Open notes in editor (default)
3114
+ ${chalk22.cyan("view")} Display notes in terminal
3115
+
3116
+ ${chalk22.yellow("Examples:")}
3117
+ ${chalk22.cyan("$ leetcode note 1")} Edit notes for problem 1
3118
+ ${chalk22.cyan("$ leetcode note 1 edit")} Edit notes (explicit)
3119
+ ${chalk22.cyan("$ leetcode note 1 view")} View notes in terminal
2798
3120
  `).action(notesCommand);
2799
3121
  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
3122
+ ${chalk22.yellow("Examples:")}
3123
+ ${chalk22.cyan("$ leetcode sync")} Sync all solutions to remote
2802
3124
  `).action(syncCommand);
2803
3125
  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
2811
-
2812
- ${chalk21.gray("Supported languages: typescript, javascript, python3, java, cpp, c, csharp, go, rust, kotlin, swift")}
3126
+ ${chalk22.yellow("Examples:")}
3127
+ ${chalk22.cyan("$ leetcode config")} View current config
3128
+ ${chalk22.cyan("$ leetcode config -l python3")} Set language to Python
3129
+ ${chalk22.cyan('$ leetcode config -e "code"')} Set editor to VS Code
3130
+ ${chalk22.cyan("$ leetcode config -w ~/leetcode")} Set solutions folder
3131
+ ${chalk22.cyan("$ leetcode config -r https://...")} Set git repository
3132
+ ${chalk22.cyan("$ leetcode config -i")} Interactive setup
3133
+
3134
+ ${chalk22.gray("Supported languages: typescript, javascript, python3, java, cpp, c, csharp, go, rust, kotlin, swift")}
2813
3135
  `).action(async (options) => {
2814
3136
  if (options.interactive) {
2815
3137
  await configInteractiveCommand();
@@ -2818,23 +3140,44 @@ ${chalk21.gray("Supported languages: typescript, javascript, python3, java, cpp,
2818
3140
  }
2819
3141
  });
2820
3142
  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:")}
3143
+ ${chalk22.yellow("How it works:")}
2822
3144
  Start a problem with a countdown timer to simulate interview conditions.
2823
3145
  Default time limits: Easy (20 min), Medium (40 min), Hard (60 min).
2824
3146
  Your solve times are recorded when you submit successfully.
2825
3147
 
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
3148
+ ${chalk22.yellow("Examples:")}
3149
+ ${chalk22.cyan("$ leetcode timer 1")} Start problem 1 with default time
3150
+ ${chalk22.cyan("$ leetcode timer 1 -m 30")} Start with 30 minute limit
3151
+ ${chalk22.cyan("$ leetcode timer --stats")} Show your solve time statistics
3152
+ ${chalk22.cyan("$ leetcode timer --stop")} Stop active timer
2831
3153
  `).action((id, options) => timerCommand(id, options));
3154
+ var collabCmd = program.command("collab").description("Collaborative coding with a partner").addHelpText("after", `
3155
+ ${chalk22.yellow("Subcommands:")}
3156
+ ${chalk22.cyan("host <id>")} Create a room and get a code to share
3157
+ ${chalk22.cyan("join <code>")} Join a room with the shared code
3158
+ ${chalk22.cyan("sync")} Upload your solution to the room
3159
+ ${chalk22.cyan("compare")} View both solutions side by side
3160
+ ${chalk22.cyan("status")} Check room and sync status
3161
+ ${chalk22.cyan("leave")} End the collaboration session
3162
+
3163
+ ${chalk22.yellow("Examples:")}
3164
+ ${chalk22.gray("$ leetcode collab host 1")} Start a session for Two Sum
3165
+ ${chalk22.gray("$ leetcode collab join ABC123")} Join your partner's session
3166
+ ${chalk22.gray("$ leetcode collab sync")} Upload your code after solving
3167
+ ${chalk22.gray("$ leetcode collab compare")} Compare solutions
3168
+ `);
3169
+ collabCmd.command("host <problemId>").description("Host a collaboration session").action(collabHostCommand);
3170
+ collabCmd.command("join <roomCode>").description("Join a collaboration session").action(collabJoinCommand);
3171
+ collabCmd.command("sync").description("Sync your code with partner").action(collabSyncCommand);
3172
+ collabCmd.command("compare").description("Compare your solution with partner").action(collabCompareCommand);
3173
+ collabCmd.command("leave").description("Leave the collaboration session").action(collabLeaveCommand);
3174
+ collabCmd.command("status").description("Show collaboration status").action(collabStatusCommand);
2832
3175
  program.showHelpAfterError("(add --help for additional information)");
2833
3176
  program.parse();
2834
3177
  if (!process.argv.slice(2).length) {
2835
3178
  console.log();
2836
- console.log(chalk21.bold.cyan(" \u{1F525} LeetCode CLI"));
2837
- console.log(chalk21.gray(" A modern command-line interface for LeetCode"));
3179
+ console.log(chalk22.bold.cyan(" \u{1F525} LeetCode CLI"));
3180
+ console.log(chalk22.gray(" A modern command-line interface for LeetCode"));
2838
3181
  console.log();
2839
3182
  program.outputHelp();
2840
3183
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@night-slayer18/leetcode-cli",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "A modern LeetCode CLI built with TypeScript",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -29,6 +29,7 @@
29
29
  "author": "night-slayer18",
30
30
  "license": "Apache-2.0",
31
31
  "dependencies": {
32
+ "@supabase/supabase-js": "^2.90.1",
32
33
  "chalk": "^5.3.0",
33
34
  "cli-table3": "^0.6.5",
34
35
  "commander": "^12.1.0",