@night-slayer18/leetcode-cli 1.5.0 → 2.0.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 +112 -0
  2. package/dist/index.js +1899 -555
  3. package/package.json +5 -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 chalk21 from "chalk";
5
+ import chalk26 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;
500
- },
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);
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 };
513
479
  },
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");
529
- },
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");
480
+ set(creds) {
481
+ credentialsStore.set("session", creds.session);
482
+ credentialsStore.set("csrfToken", creds.csrfToken);
538
483
  },
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");
@@ -628,18 +571,18 @@ async function whoamiCommand() {
628
571
 
629
572
  // src/commands/list.ts
630
573
  import ora2 from "ora";
631
- import chalk4 from "chalk";
574
+ import chalk5 from "chalk";
632
575
 
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"));
@@ -653,16 +596,227 @@ async function requireAuth() {
653
596
  }
654
597
 
655
598
  // src/utils/display.ts
656
- import chalk3 from "chalk";
599
+ import chalk4 from "chalk";
657
600
  import Table from "cli-table3";
601
+
602
+ // src/utils/visualize.ts
603
+ import chalk3 from "chalk";
604
+ var TAG_VISUALIZATION = {
605
+ "Linked List": "linkedlist",
606
+ "Doubly-Linked List": "linkedlist",
607
+ "Tree": "tree",
608
+ "Binary Tree": "tree",
609
+ "Binary Search Tree": "tree",
610
+ "Trie": "tree",
611
+ "Segment Tree": "tree",
612
+ "Binary Indexed Tree": "tree",
613
+ "Graph": "graph",
614
+ "Matrix": "matrix",
615
+ "Array": "array",
616
+ "Hash Table": "array",
617
+ "Stack": "array",
618
+ "Queue": "array",
619
+ "Monotonic Stack": "array",
620
+ "Monotonic Queue": "array",
621
+ "Heap (Priority Queue)": "array",
622
+ "String": "string"
623
+ };
624
+ function detectVisualizationType(tags) {
625
+ for (const tag of tags) {
626
+ const vizType = TAG_VISUALIZATION[tag.name];
627
+ if (vizType) return vizType;
628
+ }
629
+ return null;
630
+ }
631
+ function parseValue(value) {
632
+ try {
633
+ return JSON.parse(value);
634
+ } catch {
635
+ return value;
636
+ }
637
+ }
638
+ function isMatrix(value) {
639
+ return Array.isArray(value) && value.length > 0 && Array.isArray(value[0]);
640
+ }
641
+ function visualizeArray(arr, expected) {
642
+ if (!Array.isArray(arr) || arr.length === 0) {
643
+ return String(arr);
644
+ }
645
+ const maxLen = Math.max(...arr.map((v) => String(v).length), 1);
646
+ const cellWidth = Math.max(maxLen, 3);
647
+ const indices = arr.map((_, i) => `[${i}]`.padStart(cellWidth).padEnd(cellWidth)).join(" ");
648
+ const values = arr.map((v, i) => {
649
+ const valStr = String(v).padStart(cellWidth).padEnd(cellWidth);
650
+ if (expected && Array.isArray(expected) && expected[i] !== v) {
651
+ return chalk3.red.bold(valStr);
652
+ }
653
+ return valStr;
654
+ }).join(" ");
655
+ return `${chalk3.gray(indices)}
656
+ ${values}`;
657
+ }
658
+ function visualizeLinkedList(arr, expected) {
659
+ if (!Array.isArray(arr)) {
660
+ return String(arr);
661
+ }
662
+ if (arr.length === 0) {
663
+ return chalk3.gray("(empty)");
664
+ }
665
+ const parts = arr.map((v, i) => {
666
+ const valStr = String(v);
667
+ if (expected && Array.isArray(expected)) {
668
+ if (i >= expected.length || expected[i] !== v) {
669
+ return chalk3.red.bold(valStr);
670
+ }
671
+ }
672
+ return valStr;
673
+ });
674
+ return parts.join(chalk3.gray(" \u2192 "));
675
+ }
676
+ function visualizeTree(arr) {
677
+ if (!Array.isArray(arr) || arr.length === 0) {
678
+ return chalk3.gray("(empty tree)");
679
+ }
680
+ const lines = [];
681
+ const height = Math.floor(Math.log2(arr.length)) + 1;
682
+ const maxWidth = Math.pow(2, height) * 3;
683
+ function renderLevel(level, startIdx, endIdx, indent) {
684
+ if (startIdx > arr.length - 1) return;
685
+ const levelNodes = [];
686
+ const levelBranches = [];
687
+ const spacing = Math.floor(maxWidth / Math.pow(2, level + 1));
688
+ for (let i = startIdx; i <= endIdx && i < arr.length; i++) {
689
+ const val = arr[i];
690
+ const nodeStr = val === null ? " " : String(val);
691
+ levelNodes.push(nodeStr.padStart(spacing).padEnd(spacing));
692
+ const leftChild = 2 * i + 1;
693
+ const rightChild = 2 * i + 2;
694
+ const hasLeft = leftChild < arr.length && arr[leftChild] !== null;
695
+ const hasRight = rightChild < arr.length && arr[rightChild] !== null;
696
+ if (hasLeft || hasRight) {
697
+ let branch = "";
698
+ if (hasLeft) branch += "/";
699
+ else branch += " ";
700
+ branch += " ";
701
+ if (hasRight) branch += "\\";
702
+ else branch += " ";
703
+ levelBranches.push(branch.padStart(spacing).padEnd(spacing));
704
+ }
705
+ }
706
+ if (levelNodes.length > 0) {
707
+ lines.push(levelNodes.join(""));
708
+ if (levelBranches.length > 0 && level < height - 1) {
709
+ lines.push(levelBranches.join(""));
710
+ }
711
+ }
712
+ }
713
+ for (let level = 0; level < height; level++) {
714
+ const startIdx = Math.pow(2, level) - 1;
715
+ const endIdx = Math.pow(2, level + 1) - 2;
716
+ renderLevel(level, startIdx, endIdx, 0);
717
+ }
718
+ return lines.join("\n");
719
+ }
720
+ function visualizeMatrix(matrix, expected) {
721
+ if (!isMatrix(matrix) || matrix.length === 0) {
722
+ return String(matrix);
723
+ }
724
+ const rows = matrix.length;
725
+ const cols = matrix[0].length;
726
+ const cellWidth = 3;
727
+ const lines = [];
728
+ const colHeaders = " " + matrix[0].map((_, i) => String(i).padStart(cellWidth).padEnd(cellWidth)).join(" ");
729
+ lines.push(chalk3.gray(colHeaders));
730
+ lines.push(" \u250C" + Array(cols).fill("\u2500\u2500\u2500").join("\u252C") + "\u2510");
731
+ for (let r = 0; r < rows; r++) {
732
+ const rowContent = matrix[r].map((v, c) => {
733
+ const valStr = String(v).padStart(2);
734
+ if (expected && isMatrix(expected) && expected[r] && expected[r][c] !== v) {
735
+ return chalk3.red.bold(valStr);
736
+ }
737
+ return valStr;
738
+ }).join(" \u2502 ");
739
+ lines.push(chalk3.gray(` ${r} `) + `\u2502 ${rowContent} \u2502`);
740
+ if (r < rows - 1) {
741
+ lines.push(" \u251C" + Array(cols).fill("\u2500\u2500\u2500").join("\u253C") + "\u2524");
742
+ } else {
743
+ lines.push(" \u2514" + Array(cols).fill("\u2500\u2500\u2500").join("\u2534") + "\u2518");
744
+ }
745
+ }
746
+ return lines.join("\n");
747
+ }
748
+ function visualizeGraph(adjList) {
749
+ if (!isMatrix(adjList)) {
750
+ return String(adjList);
751
+ }
752
+ const lines = adjList.map((neighbors, node) => {
753
+ const neighborStr = Array.isArray(neighbors) ? neighbors.join(", ") : String(neighbors);
754
+ return ` ${chalk3.cyan(String(node))} \u2192 [${neighborStr}]`;
755
+ });
756
+ return lines.join("\n");
757
+ }
758
+ function visualizeTestOutput(output, expected, tags) {
759
+ const outputVal = parseValue(output);
760
+ const expectedVal = parseValue(expected);
761
+ const matches = output === expected;
762
+ const vizType = detectVisualizationType(tags);
763
+ let outputVis;
764
+ let expectedVis;
765
+ if (isMatrix(outputVal)) {
766
+ const expectedMatrix = isMatrix(expectedVal) ? expectedVal : void 0;
767
+ outputVis = visualizeMatrix(outputVal, expectedMatrix);
768
+ expectedVis = isMatrix(expectedVal) ? visualizeMatrix(expectedVal) : String(expected);
769
+ return { outputVis, expectedVis, matches };
770
+ }
771
+ if (vizType === null) {
772
+ return {
773
+ outputVis: String(output),
774
+ expectedVis: String(expected),
775
+ matches,
776
+ unsupported: true
777
+ };
778
+ }
779
+ switch (vizType) {
780
+ case "linkedlist":
781
+ outputVis = visualizeLinkedList(outputVal, expectedVal);
782
+ expectedVis = visualizeLinkedList(expectedVal);
783
+ break;
784
+ case "tree":
785
+ outputVis = visualizeTree(outputVal);
786
+ expectedVis = visualizeTree(expectedVal);
787
+ break;
788
+ case "graph":
789
+ outputVis = visualizeGraph(outputVal);
790
+ expectedVis = visualizeGraph(expectedVal);
791
+ break;
792
+ case "matrix":
793
+ const expMatrix = isMatrix(expectedVal) ? expectedVal : void 0;
794
+ outputVis = visualizeMatrix(outputVal, expMatrix);
795
+ expectedVis = isMatrix(expectedVal) ? visualizeMatrix(expectedVal) : String(expected);
796
+ break;
797
+ case "array":
798
+ case "string":
799
+ if (Array.isArray(outputVal)) {
800
+ outputVis = visualizeArray(outputVal, expectedVal);
801
+ expectedVis = Array.isArray(expectedVal) ? visualizeArray(expectedVal) : String(expected);
802
+ } else {
803
+ outputVis = matches ? String(output) : chalk3.red.bold(String(output));
804
+ expectedVis = String(expected);
805
+ }
806
+ break;
807
+ }
808
+ return { outputVis, expectedVis, matches };
809
+ }
810
+
811
+ // src/utils/display.ts
658
812
  function displayProblemList(problems, total) {
659
813
  const table = new Table({
660
814
  head: [
661
- chalk3.cyan("ID"),
662
- chalk3.cyan("Title"),
663
- chalk3.cyan("Difficulty"),
664
- chalk3.cyan("Rate"),
665
- chalk3.cyan("Status")
815
+ chalk4.cyan("ID"),
816
+ chalk4.cyan("Title"),
817
+ chalk4.cyan("Difficulty"),
818
+ chalk4.cyan("Rate"),
819
+ chalk4.cyan("Status")
666
820
  ],
667
821
  colWidths: [8, 45, 12, 10, 10],
668
822
  style: { head: [], border: [] }
@@ -681,34 +835,34 @@ function displayProblemList(problems, total) {
681
835
  ]);
682
836
  }
683
837
  console.log(table.toString());
684
- console.log(chalk3.gray(`
838
+ console.log(chalk4.gray(`
685
839
  Showing ${problems.length} of ${total} problems`));
686
840
  }
687
841
  function displayProblemDetail(problem) {
688
842
  console.log();
689
843
  const titlePrefix = problem.isPaidOnly ? "\u{1F512} " : "";
690
- console.log(chalk3.bold.cyan(` ${problem.questionFrontendId}. ${titlePrefix}${problem.title}`));
844
+ console.log(chalk4.bold.cyan(` ${problem.questionFrontendId}. ${titlePrefix}${problem.title}`));
691
845
  console.log(` ${colorDifficulty(problem.difficulty)}`);
692
- console.log(chalk3.gray(` https://leetcode.com/problems/${problem.titleSlug}/`));
846
+ console.log(chalk4.gray(` https://leetcode.com/problems/${problem.titleSlug}/`));
693
847
  console.log();
694
848
  if (problem.isPaidOnly) {
695
- console.log(chalk3.yellow(" \u26A0\uFE0F Premium Problem"));
696
- console.log(chalk3.gray(" This problem requires a LeetCode Premium subscription."));
697
- console.log(chalk3.gray(` Visit the URL above to view on LeetCode.`));
849
+ console.log(chalk4.yellow(" \u26A0\uFE0F Premium Problem"));
850
+ console.log(chalk4.gray(" This problem requires a LeetCode Premium subscription."));
851
+ console.log(chalk4.gray(` Visit the URL above to view on LeetCode.`));
698
852
  console.log();
699
853
  }
700
854
  if (problem.topicTags.length) {
701
- const tags = problem.topicTags.map((t) => chalk3.bgBlue.white(` ${t.name} `)).join(" ");
855
+ const tags = problem.topicTags.map((t) => chalk4.bgBlue.white(` ${t.name} `)).join(" ");
702
856
  console.log(` ${tags}`);
703
857
  console.log();
704
858
  }
705
- console.log(chalk3.gray("\u2500".repeat(60)));
859
+ console.log(chalk4.gray("\u2500".repeat(60)));
706
860
  console.log();
707
861
  let content = problem.content;
708
862
  if (!content) {
709
- console.log(chalk3.yellow(" \u{1F512} Premium Content"));
710
- console.log(chalk3.gray(" Problem description is not available directly."));
711
- console.log(chalk3.gray(" Please visit the URL above to view on LeetCode."));
863
+ console.log(chalk4.yellow(" \u{1F512} Premium Content"));
864
+ console.log(chalk4.gray(" Problem description is not available directly."));
865
+ console.log(chalk4.gray(" Please visit the URL above to view on LeetCode."));
712
866
  console.log();
713
867
  return;
714
868
  }
@@ -728,100 +882,128 @@ function displayProblemDetail(problem) {
728
882
  content = content.replace(/<[^>]+>/g, "");
729
883
  content = content.replace(/&nbsp;/g, " ").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&le;/g, "\u2264").replace(/&ge;/g, "\u2265").replace(/&#(\d+);/g, (_, code) => String.fromCharCode(parseInt(code, 10)));
730
884
  content = content.replace(/\n{3,}/g, "\n\n").trim();
731
- content = content.replace(/§EXAMPLE§(\d+)§/g, (_, num) => chalk3.green.bold(`\u{1F4CC} Example ${num}:`));
732
- content = content.replace(/§INPUT§/g, chalk3.yellow("Input:"));
733
- content = content.replace(/§OUTPUT§/g, chalk3.yellow("Output:"));
734
- content = content.replace(/§EXPLAIN§/g, chalk3.gray("Explanation:"));
735
- content = content.replace(/§CONSTRAINTS§/g, chalk3.cyan.bold("\n\u{1F4CB} Constraints:"));
736
- content = content.replace(/§FOLLOWUP§/g, chalk3.magenta.bold("\n\u{1F4A1} Follow-up:"));
885
+ content = content.replace(/§EXAMPLE§(\d+)§/g, (_, num) => chalk4.green.bold(`\u{1F4CC} Example ${num}:`));
886
+ content = content.replace(/§INPUT§/g, chalk4.yellow("Input:"));
887
+ content = content.replace(/§OUTPUT§/g, chalk4.yellow("Output:"));
888
+ content = content.replace(/§EXPLAIN§/g, chalk4.gray("Explanation:"));
889
+ content = content.replace(/§CONSTRAINTS§/g, chalk4.cyan.bold("\n\u{1F4CB} Constraints:"));
890
+ content = content.replace(/§FOLLOWUP§/g, chalk4.magenta.bold("\n\u{1F4A1} Follow-up:"));
737
891
  console.log(content);
738
892
  console.log();
739
893
  }
740
- function displayTestResult(result) {
894
+ function displayTestResult(result, topicTags) {
741
895
  console.log();
742
896
  if (result.compile_error) {
743
- console.log(chalk3.red.bold("\u274C Compile Error"));
744
- console.log(chalk3.red(result.compile_error));
897
+ console.log(chalk4.red.bold("\u274C Compile Error"));
898
+ console.log(chalk4.red(result.compile_error));
745
899
  return;
746
900
  }
747
901
  if (result.runtime_error) {
748
- console.log(chalk3.red.bold("\u274C Runtime Error"));
749
- console.log(chalk3.red(result.runtime_error));
902
+ console.log(chalk4.red.bold("\u274C Runtime Error"));
903
+ console.log(chalk4.red(result.runtime_error));
750
904
  return;
751
905
  }
752
906
  if (result.correct_answer) {
753
- console.log(chalk3.green.bold("\u2713 All test cases passed!"));
907
+ console.log(chalk4.green.bold("\u2713 All test cases passed!"));
754
908
  } else {
755
- console.log(chalk3.yellow.bold("\u2717 Some test cases failed"));
756
- }
757
- console.log();
758
- console.log(chalk3.gray("Your Output:"));
759
- for (const output of result.code_answer ?? []) {
760
- console.log(chalk3.white(` ${output}`));
909
+ console.log(chalk4.yellow.bold("\u2717 Some test cases failed"));
761
910
  }
762
- console.log();
763
- console.log(chalk3.gray("Expected Output:"));
764
- for (const output of result.expected_code_answer ?? []) {
765
- console.log(chalk3.white(` ${output}`));
911
+ const outputs = result.code_answer ?? [];
912
+ const expected = result.expected_code_answer ?? [];
913
+ if (topicTags && outputs.length > 0) {
914
+ console.log();
915
+ console.log(chalk4.gray.bold("\u2500".repeat(50)));
916
+ const validCases = outputs.map((out, i) => ({ out, exp: expected[i] ?? "" })).filter(({ out, exp }) => out !== "" || exp !== "");
917
+ for (let i = 0; i < validCases.length; i++) {
918
+ const { out, exp } = validCases[i];
919
+ const { outputVis, expectedVis, matches, unsupported } = visualizeTestOutput(out, exp, topicTags);
920
+ console.log();
921
+ console.log(chalk4.gray(`Test Case ${i + 1}:`));
922
+ if (unsupported) {
923
+ console.log(chalk4.yellow(" \u26A0 No visualization available for this problem type"));
924
+ console.log(chalk4.gray(` Tags: ${topicTags.map((t) => t.name).join(", ")}`));
925
+ }
926
+ console.log();
927
+ console.log(chalk4.cyan(" Your Output:"));
928
+ outputVis.split("\n").forEach((line) => console.log(` ${line}`));
929
+ console.log();
930
+ console.log(chalk4.cyan(" Expected:"));
931
+ expectedVis.split("\n").forEach((line) => console.log(` ${line}`));
932
+ console.log();
933
+ console.log(matches ? chalk4.green(" \u2713 Match") : chalk4.red(" \u2717 Mismatch"));
934
+ }
935
+ console.log(chalk4.gray.bold("\u2500".repeat(50)));
936
+ } else {
937
+ console.log();
938
+ console.log(chalk4.gray("Your Output:"));
939
+ for (const output of outputs) {
940
+ console.log(chalk4.white(` ${output}`));
941
+ }
942
+ console.log();
943
+ console.log(chalk4.gray("Expected Output:"));
944
+ for (const output of expected) {
945
+ console.log(chalk4.white(` ${output}`));
946
+ }
766
947
  }
767
- if (result.std_output_list?.length) {
948
+ const stdoutEntries = (result.std_output_list ?? []).filter((s) => s);
949
+ if (stdoutEntries.length > 0) {
768
950
  console.log();
769
- console.log(chalk3.gray("Stdout:"));
770
- for (const output of result.std_output_list) {
771
- if (output) console.log(chalk3.gray(` ${output}`));
951
+ console.log(chalk4.gray("Stdout:"));
952
+ for (const output of stdoutEntries) {
953
+ console.log(chalk4.gray(` ${output}`));
772
954
  }
773
955
  }
774
956
  }
775
957
  function displaySubmissionResult(result) {
776
958
  console.log();
777
959
  if (result.compile_error) {
778
- console.log(chalk3.red.bold("\u274C Compile Error"));
779
- console.log(chalk3.red(result.compile_error));
960
+ console.log(chalk4.red.bold("\u274C Compile Error"));
961
+ console.log(chalk4.red(result.compile_error));
780
962
  return;
781
963
  }
782
964
  if (result.runtime_error) {
783
- console.log(chalk3.red.bold("\u274C Runtime Error"));
784
- console.log(chalk3.red(result.runtime_error));
965
+ console.log(chalk4.red.bold("\u274C Runtime Error"));
966
+ console.log(chalk4.red(result.runtime_error));
785
967
  if (result.last_testcase) {
786
- console.log(chalk3.gray("Last testcase:"), result.last_testcase);
968
+ console.log(chalk4.gray("Last testcase:"), result.last_testcase);
787
969
  }
788
970
  return;
789
971
  }
790
972
  if (result.status_msg === "Accepted") {
791
- console.log(chalk3.green.bold("\u2713 Accepted!"));
973
+ console.log(chalk4.green.bold("\u2713 Accepted!"));
792
974
  console.log();
793
975
  console.log(
794
- chalk3.gray("Runtime:"),
795
- chalk3.white(result.status_runtime),
796
- chalk3.gray(`(beats ${result.runtime_percentile?.toFixed(1) ?? "N/A"}%)`)
976
+ chalk4.gray("Runtime:"),
977
+ chalk4.white(result.status_runtime),
978
+ chalk4.gray(`(beats ${result.runtime_percentile?.toFixed(1) ?? "N/A"}%)`)
797
979
  );
798
980
  console.log(
799
- chalk3.gray("Memory:"),
800
- chalk3.white(result.status_memory),
801
- chalk3.gray(`(beats ${result.memory_percentile?.toFixed(1) ?? "N/A"}%)`)
981
+ chalk4.gray("Memory:"),
982
+ chalk4.white(result.status_memory),
983
+ chalk4.gray(`(beats ${result.memory_percentile?.toFixed(1) ?? "N/A"}%)`)
802
984
  );
803
985
  } else {
804
- console.log(chalk3.red.bold(`\u274C ${result.status_msg}`));
986
+ console.log(chalk4.red.bold(`\u274C ${result.status_msg}`));
805
987
  console.log();
806
- console.log(chalk3.gray(`Passed ${result.total_correct}/${result.total_testcases} testcases`));
988
+ console.log(chalk4.gray(`Passed ${result.total_correct}/${result.total_testcases} testcases`));
807
989
  if (result.code_output) {
808
- console.log(chalk3.gray("Your Output:"), result.code_output);
990
+ console.log(chalk4.gray("Your Output:"), result.code_output);
809
991
  }
810
992
  if (result.expected_output) {
811
- console.log(chalk3.gray("Expected:"), result.expected_output);
993
+ console.log(chalk4.gray("Expected:"), result.expected_output);
812
994
  }
813
995
  if (result.last_testcase) {
814
- console.log(chalk3.gray("Failed testcase:"), result.last_testcase);
996
+ console.log(chalk4.gray("Failed testcase:"), result.last_testcase);
815
997
  }
816
998
  }
817
999
  }
818
1000
  function displayUserStats(username, realName, ranking, acStats, streak, totalActiveDays) {
819
1001
  console.log();
820
- console.log(chalk3.bold.white(`\u{1F464} ${username}`) + (realName ? chalk3.gray(` (${realName})`) : ""));
821
- console.log(chalk3.gray(`Ranking: #${ranking.toLocaleString()}`));
1002
+ console.log(chalk4.bold.white(`\u{1F464} ${username}`) + (realName ? chalk4.gray(` (${realName})`) : ""));
1003
+ console.log(chalk4.gray(`Ranking: #${ranking.toLocaleString()}`));
822
1004
  console.log();
823
1005
  const table = new Table({
824
- head: [chalk3.cyan("Difficulty"), chalk3.cyan("Solved")],
1006
+ head: [chalk4.cyan("Difficulty"), chalk4.cyan("Solved")],
825
1007
  style: { head: [], border: [] }
826
1008
  });
827
1009
  for (const stat of acStats) {
@@ -833,33 +1015,33 @@ function displayUserStats(username, realName, ranking, acStats, streak, totalAct
833
1015
  }
834
1016
  }
835
1017
  const total = acStats.find((s) => s.difficulty === "All")?.count ?? 0;
836
- table.push([chalk3.white.bold("Total"), chalk3.white.bold(total.toString())]);
1018
+ table.push([chalk4.white.bold("Total"), chalk4.white.bold(total.toString())]);
837
1019
  console.log(table.toString());
838
1020
  console.log();
839
- console.log(chalk3.gray("\u{1F525} Current streak:"), chalk3.hex("#FFA500")(streak.toString()), chalk3.gray("days"));
840
- console.log(chalk3.gray("\u{1F4C5} Total active days:"), chalk3.white(totalActiveDays.toString()));
1021
+ console.log(chalk4.gray("\u{1F525} Current streak:"), chalk4.hex("#FFA500")(streak.toString()), chalk4.gray("days"));
1022
+ console.log(chalk4.gray("\u{1F4C5} Total active days:"), chalk4.white(totalActiveDays.toString()));
841
1023
  }
842
1024
  function displayDailyChallenge(date, problem) {
843
1025
  console.log();
844
- console.log(chalk3.bold.yellow("\u{1F3AF} Daily Challenge"), chalk3.gray(`(${date})`));
1026
+ console.log(chalk4.bold.yellow("\u{1F3AF} Daily Challenge"), chalk4.gray(`(${date})`));
845
1027
  console.log();
846
- console.log(chalk3.white(`${problem.questionFrontendId}. ${problem.title}`));
1028
+ console.log(chalk4.white(`${problem.questionFrontendId}. ${problem.title}`));
847
1029
  console.log(colorDifficulty(problem.difficulty));
848
- console.log(chalk3.gray(`https://leetcode.com/problems/${problem.titleSlug}/`));
1030
+ console.log(chalk4.gray(`https://leetcode.com/problems/${problem.titleSlug}/`));
849
1031
  if (problem.topicTags.length) {
850
1032
  console.log();
851
- const tags = problem.topicTags.map((t) => chalk3.blue(t.name)).join(" ");
852
- console.log(chalk3.gray("Tags:"), tags);
1033
+ const tags = problem.topicTags.map((t) => chalk4.blue(t.name)).join(" ");
1034
+ console.log(chalk4.gray("Tags:"), tags);
853
1035
  }
854
1036
  }
855
1037
  function colorDifficulty(difficulty) {
856
1038
  switch (difficulty.toLowerCase()) {
857
1039
  case "easy":
858
- return chalk3.green(difficulty);
1040
+ return chalk4.green(difficulty);
859
1041
  case "medium":
860
- return chalk3.yellow(difficulty);
1042
+ return chalk4.yellow(difficulty);
861
1043
  case "hard":
862
- return chalk3.red(difficulty);
1044
+ return chalk4.red(difficulty);
863
1045
  default:
864
1046
  return difficulty;
865
1047
  }
@@ -867,22 +1049,22 @@ function colorDifficulty(difficulty) {
867
1049
  function formatStatus(status) {
868
1050
  switch (status) {
869
1051
  case "ac":
870
- return chalk3.green("\u2713");
1052
+ return chalk4.green("\u2713");
871
1053
  case "notac":
872
- return chalk3.yellow("\u25CB");
1054
+ return chalk4.yellow("\u25CB");
873
1055
  default:
874
- return chalk3.gray("-");
1056
+ return chalk4.gray("-");
875
1057
  }
876
1058
  }
877
1059
  function displaySubmissionsList(submissions) {
878
1060
  const table = new Table({
879
1061
  head: [
880
- chalk3.cyan("ID"),
881
- chalk3.cyan("Status"),
882
- chalk3.cyan("Lang"),
883
- chalk3.cyan("Runtime"),
884
- chalk3.cyan("Memory"),
885
- chalk3.cyan("Date")
1062
+ chalk4.cyan("ID"),
1063
+ chalk4.cyan("Status"),
1064
+ chalk4.cyan("Lang"),
1065
+ chalk4.cyan("Runtime"),
1066
+ chalk4.cyan("Memory"),
1067
+ chalk4.cyan("Date")
886
1068
  ],
887
1069
  colWidths: [12, 18, 15, 12, 12, 25],
888
1070
  style: { head: [], border: [] }
@@ -892,7 +1074,7 @@ function displaySubmissionsList(submissions) {
892
1074
  const cleanTime = new Date(parseInt(s.timestamp) * 1e3).toLocaleString();
893
1075
  table.push([
894
1076
  s.id,
895
- isAC ? chalk3.green(s.statusDisplay) : chalk3.red(s.statusDisplay),
1077
+ isAC ? chalk4.green(s.statusDisplay) : chalk4.red(s.statusDisplay),
896
1078
  s.lang,
897
1079
  s.runtime,
898
1080
  s.memory,
@@ -943,25 +1125,25 @@ async function listCommand(options) {
943
1125
  const { total, problems } = await leetcodeClient.getProblems(filters);
944
1126
  spinner.stop();
945
1127
  if (problems.length === 0) {
946
- console.log(chalk4.yellow("No problems found matching your criteria."));
1128
+ console.log(chalk5.yellow("No problems found matching your criteria."));
947
1129
  return;
948
1130
  }
949
1131
  displayProblemList(problems, total);
950
1132
  if (page * limit < total) {
951
- console.log(chalk4.gray(`
1133
+ console.log(chalk5.gray(`
952
1134
  Page ${page} of ${Math.ceil(total / limit)}. Use --page to navigate.`));
953
1135
  }
954
1136
  } catch (error) {
955
1137
  spinner.fail("Failed to fetch problems");
956
1138
  if (error instanceof Error) {
957
- console.log(chalk4.red(error.message));
1139
+ console.log(chalk5.red(error.message));
958
1140
  }
959
1141
  }
960
1142
  }
961
1143
 
962
1144
  // src/commands/show.ts
963
1145
  import ora3 from "ora";
964
- import chalk5 from "chalk";
1146
+ import chalk6 from "chalk";
965
1147
  async function showCommand(idOrSlug) {
966
1148
  const { authorized } = await requireAuth();
967
1149
  if (!authorized) return;
@@ -982,17 +1164,229 @@ async function showCommand(idOrSlug) {
982
1164
  } catch (error) {
983
1165
  spinner.fail("Failed to fetch problem");
984
1166
  if (error instanceof Error) {
985
- console.log(chalk5.red(error.message));
1167
+ console.log(chalk6.red(error.message));
986
1168
  }
987
1169
  }
988
1170
  }
989
1171
 
990
1172
  // src/commands/pick.ts
991
1173
  import { writeFile, mkdir } from "fs/promises";
992
- import { existsSync } from "fs";
993
- import { join as join2 } from "path";
1174
+ import { existsSync as existsSync2 } from "fs";
1175
+ import { join as join4 } from "path";
994
1176
  import ora4 from "ora";
995
- import chalk7 from "chalk";
1177
+ import chalk8 from "chalk";
1178
+
1179
+ // src/storage/config.ts
1180
+ import { join as join3 } from "path";
1181
+
1182
+ // src/storage/workspaces.ts
1183
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
1184
+ import { join as join2 } from "path";
1185
+ import { homedir as homedir2 } from "os";
1186
+ var LEETCODE_DIR = join2(homedir2(), ".leetcode");
1187
+ var WORKSPACES_FILE = join2(LEETCODE_DIR, "workspaces.json");
1188
+ var WORKSPACES_DIR = join2(LEETCODE_DIR, "workspaces");
1189
+ function ensureDir(dir) {
1190
+ if (!existsSync(dir)) {
1191
+ mkdirSync(dir, { recursive: true });
1192
+ }
1193
+ }
1194
+ function loadRegistry() {
1195
+ if (existsSync(WORKSPACES_FILE)) {
1196
+ return JSON.parse(readFileSync(WORKSPACES_FILE, "utf-8"));
1197
+ }
1198
+ return { active: "default", workspaces: [] };
1199
+ }
1200
+ function saveRegistry(registry) {
1201
+ ensureDir(LEETCODE_DIR);
1202
+ writeFileSync(WORKSPACES_FILE, JSON.stringify(registry, null, 2));
1203
+ }
1204
+ var workspaceStorage = {
1205
+ /**
1206
+ * Initialize workspaces on first run - migrate existing config to "default" workspace
1207
+ */
1208
+ ensureInitialized() {
1209
+ const registry = loadRegistry();
1210
+ if (registry.workspaces.length === 0) {
1211
+ this.create("default", {
1212
+ workDir: join2(homedir2(), "leetcode"),
1213
+ lang: "typescript"
1214
+ });
1215
+ registry.workspaces = ["default"];
1216
+ registry.active = "default";
1217
+ saveRegistry(registry);
1218
+ }
1219
+ },
1220
+ /**
1221
+ * Get the currently active workspace name
1222
+ */
1223
+ getActive() {
1224
+ this.ensureInitialized();
1225
+ return loadRegistry().active;
1226
+ },
1227
+ /**
1228
+ * Set the active workspace
1229
+ */
1230
+ setActive(name) {
1231
+ const registry = loadRegistry();
1232
+ if (!registry.workspaces.includes(name)) {
1233
+ return false;
1234
+ }
1235
+ registry.active = name;
1236
+ saveRegistry(registry);
1237
+ return true;
1238
+ },
1239
+ /**
1240
+ * List all workspace names
1241
+ */
1242
+ list() {
1243
+ this.ensureInitialized();
1244
+ return loadRegistry().workspaces;
1245
+ },
1246
+ /**
1247
+ * Check if a workspace exists
1248
+ */
1249
+ exists(name) {
1250
+ this.ensureInitialized();
1251
+ return loadRegistry().workspaces.includes(name);
1252
+ },
1253
+ /**
1254
+ * Create a new workspace
1255
+ */
1256
+ create(name, config2) {
1257
+ const registry = loadRegistry();
1258
+ if (registry.workspaces.includes(name)) {
1259
+ return false;
1260
+ }
1261
+ const wsDir = join2(WORKSPACES_DIR, name);
1262
+ ensureDir(wsDir);
1263
+ ensureDir(join2(wsDir, "snapshots"));
1264
+ const configPath = join2(wsDir, "config.json");
1265
+ writeFileSync(configPath, JSON.stringify(config2, null, 2));
1266
+ writeFileSync(join2(wsDir, "timer.json"), JSON.stringify({ solveTimes: {}, activeTimer: null }, null, 2));
1267
+ writeFileSync(join2(wsDir, "collab.json"), JSON.stringify({ session: null }, null, 2));
1268
+ registry.workspaces.push(name);
1269
+ saveRegistry(registry);
1270
+ return true;
1271
+ },
1272
+ /**
1273
+ * Delete a workspace
1274
+ */
1275
+ delete(name) {
1276
+ if (name === "default") {
1277
+ return false;
1278
+ }
1279
+ const registry = loadRegistry();
1280
+ if (!registry.workspaces.includes(name)) {
1281
+ return false;
1282
+ }
1283
+ registry.workspaces = registry.workspaces.filter((w) => w !== name);
1284
+ if (registry.active === name) {
1285
+ registry.active = "default";
1286
+ }
1287
+ saveRegistry(registry);
1288
+ return true;
1289
+ },
1290
+ /**
1291
+ * Get the directory path for a workspace
1292
+ */
1293
+ getWorkspaceDir(name) {
1294
+ const wsName = name ?? this.getActive();
1295
+ return join2(WORKSPACES_DIR, wsName);
1296
+ },
1297
+ /**
1298
+ * Get config for a workspace
1299
+ */
1300
+ getConfig(name) {
1301
+ const wsName = name ?? this.getActive();
1302
+ const configPath = join2(WORKSPACES_DIR, wsName, "config.json");
1303
+ if (existsSync(configPath)) {
1304
+ return JSON.parse(readFileSync(configPath, "utf-8"));
1305
+ }
1306
+ return {
1307
+ workDir: join2(homedir2(), "leetcode"),
1308
+ lang: "typescript"
1309
+ };
1310
+ },
1311
+ /**
1312
+ * Update config for a workspace
1313
+ */
1314
+ setConfig(config2, name) {
1315
+ const wsName = name ?? this.getActive();
1316
+ const wsDir = join2(WORKSPACES_DIR, wsName);
1317
+ ensureDir(wsDir);
1318
+ const currentConfig = this.getConfig(wsName);
1319
+ const newConfig = { ...currentConfig, ...config2 };
1320
+ writeFileSync(join2(wsDir, "config.json"), JSON.stringify(newConfig, null, 2));
1321
+ },
1322
+ /**
1323
+ * Get snapshots directory for active workspace
1324
+ */
1325
+ getSnapshotsDir() {
1326
+ return join2(this.getWorkspaceDir(), "snapshots");
1327
+ },
1328
+ /**
1329
+ * Get timer file path for active workspace
1330
+ */
1331
+ getTimerPath() {
1332
+ return join2(this.getWorkspaceDir(), "timer.json");
1333
+ },
1334
+ /**
1335
+ * Get collab file path for active workspace
1336
+ */
1337
+ getCollabPath() {
1338
+ return join2(this.getWorkspaceDir(), "collab.json");
1339
+ }
1340
+ };
1341
+
1342
+ // src/storage/config.ts
1343
+ var config = {
1344
+ getConfig() {
1345
+ const wsConfig = workspaceStorage.getConfig();
1346
+ return {
1347
+ language: wsConfig.lang,
1348
+ editor: wsConfig.editor,
1349
+ workDir: wsConfig.workDir,
1350
+ repo: wsConfig.syncRepo
1351
+ };
1352
+ },
1353
+ setLanguage(language) {
1354
+ workspaceStorage.setConfig({ lang: language });
1355
+ },
1356
+ setEditor(editor) {
1357
+ workspaceStorage.setConfig({ editor });
1358
+ },
1359
+ setWorkDir(workDir) {
1360
+ workspaceStorage.setConfig({ workDir });
1361
+ },
1362
+ setRepo(repo) {
1363
+ workspaceStorage.setConfig({ syncRepo: repo });
1364
+ },
1365
+ deleteRepo() {
1366
+ const wsConfig = workspaceStorage.getConfig();
1367
+ delete wsConfig.syncRepo;
1368
+ workspaceStorage.setConfig(wsConfig);
1369
+ },
1370
+ getLanguage() {
1371
+ return workspaceStorage.getConfig().lang;
1372
+ },
1373
+ getEditor() {
1374
+ return workspaceStorage.getConfig().editor;
1375
+ },
1376
+ getWorkDir() {
1377
+ return workspaceStorage.getConfig().workDir;
1378
+ },
1379
+ getRepo() {
1380
+ return workspaceStorage.getConfig().syncRepo;
1381
+ },
1382
+ getPath() {
1383
+ return join3(workspaceStorage.getWorkspaceDir(), "config.json");
1384
+ },
1385
+ // New workspace-aware methods
1386
+ getActiveWorkspace() {
1387
+ return workspaceStorage.getActive();
1388
+ }
1389
+ };
996
1390
 
997
1391
  // src/utils/templates.ts
998
1392
  var LANGUAGE_EXTENSIONS = {
@@ -1116,7 +1510,7 @@ function getSolutionFileName(problemId, titleSlug, language) {
1116
1510
  // src/utils/editor.ts
1117
1511
  import { spawn } from "child_process";
1118
1512
  import open from "open";
1119
- import chalk6 from "chalk";
1513
+ import chalk7 from "chalk";
1120
1514
  var TERMINAL_EDITORS = ["vim", "nvim", "vi", "nano", "emacs", "micro", "helix"];
1121
1515
  var VSCODE_EDITORS = ["code", "code-insiders", "cursor", "codium", "vscodium"];
1122
1516
  async function openInEditor(filePath, workDir) {
@@ -1124,7 +1518,7 @@ async function openInEditor(filePath, workDir) {
1124
1518
  const workspace = workDir ?? config.getWorkDir();
1125
1519
  if (TERMINAL_EDITORS.includes(editor)) {
1126
1520
  console.log();
1127
- console.log(chalk6.gray(`Open with: ${editor} ${filePath}`));
1521
+ console.log(chalk7.gray(`Open with: ${editor} ${filePath}`));
1128
1522
  return;
1129
1523
  }
1130
1524
  try {
@@ -1164,13 +1558,13 @@ async function pickCommand(idOrSlug, options) {
1164
1558
  const template = getCodeTemplate(snippets, language);
1165
1559
  let code;
1166
1560
  if (snippets.length === 0) {
1167
- spinner.warn(chalk7.yellow("Premium Problem (No code snippets available)"));
1168
- console.log(chalk7.gray("Generating placeholder file with problem info..."));
1561
+ spinner.warn(chalk8.yellow("Premium Problem (No code snippets available)"));
1562
+ console.log(chalk8.gray("Generating placeholder file with problem info..."));
1169
1563
  code = `// \u{1F512} Premium Problem - ${problem.title}
1170
1564
  // Solution stub not available - visit LeetCode to view`;
1171
1565
  } else if (!template) {
1172
1566
  spinner.fail(`No code template available for ${language}`);
1173
- console.log(chalk7.gray(`Available languages: ${snippets.map((s) => s.langSlug).join(", ")}`));
1567
+ console.log(chalk8.gray(`Available languages: ${snippets.map((s) => s.langSlug).join(", ")}`));
1174
1568
  return false;
1175
1569
  } else {
1176
1570
  code = template.code;
@@ -1187,26 +1581,26 @@ async function pickCommand(idOrSlug, options) {
1187
1581
  const workDir = config.getWorkDir();
1188
1582
  const difficulty = problem.difficulty;
1189
1583
  const category = problem.topicTags.length > 0 ? problem.topicTags[0].name.replace(/[^\w\s-]/g, "").trim() : "Uncategorized";
1190
- const targetDir = join2(workDir, difficulty, category);
1191
- if (!existsSync(targetDir)) {
1584
+ const targetDir = join4(workDir, difficulty, category);
1585
+ if (!existsSync2(targetDir)) {
1192
1586
  await mkdir(targetDir, { recursive: true });
1193
1587
  }
1194
1588
  const fileName = getSolutionFileName(problem.questionFrontendId, problem.titleSlug, language);
1195
- const filePath = join2(targetDir, fileName);
1196
- if (existsSync(filePath)) {
1589
+ const filePath = join4(targetDir, fileName);
1590
+ if (existsSync2(filePath)) {
1197
1591
  spinner.warn(`File already exists: ${fileName}`);
1198
- console.log(chalk7.gray(`Path: ${filePath}`));
1592
+ console.log(chalk8.gray(`Path: ${filePath}`));
1199
1593
  if (options.open !== false) {
1200
1594
  await openInEditor(filePath);
1201
1595
  }
1202
1596
  return true;
1203
1597
  }
1204
1598
  await writeFile(filePath, content, "utf-8");
1205
- spinner.succeed(`Created ${chalk7.green(fileName)}`);
1206
- console.log(chalk7.gray(`Path: ${filePath}`));
1599
+ spinner.succeed(`Created ${chalk8.green(fileName)}`);
1600
+ console.log(chalk8.gray(`Path: ${filePath}`));
1207
1601
  console.log();
1208
- console.log(chalk7.cyan(`${problem.questionFrontendId}. ${problem.title}`));
1209
- console.log(chalk7.gray(`Difficulty: ${problem.difficulty} | Category: ${category}`));
1602
+ console.log(chalk8.cyan(`${problem.questionFrontendId}. ${problem.title}`));
1603
+ console.log(chalk8.gray(`Difficulty: ${problem.difficulty} | Category: ${category}`));
1210
1604
  if (options.open !== false) {
1211
1605
  await openInEditor(filePath);
1212
1606
  }
@@ -1215,17 +1609,17 @@ async function pickCommand(idOrSlug, options) {
1215
1609
  spinner.fail("Failed to fetch problem");
1216
1610
  if (error instanceof Error) {
1217
1611
  if (error.message.includes("expected object, received null")) {
1218
- console.log(chalk7.red(`Problem "${idOrSlug}" not found`));
1612
+ console.log(chalk8.red(`Problem "${idOrSlug}" not found`));
1219
1613
  } else {
1220
1614
  try {
1221
1615
  const zodError = JSON.parse(error.message);
1222
1616
  if (Array.isArray(zodError)) {
1223
- console.log(chalk7.red("API Response Validation Failed"));
1617
+ console.log(chalk8.red("API Response Validation Failed"));
1224
1618
  } else {
1225
- console.log(chalk7.red(error.message));
1619
+ console.log(chalk8.red(error.message));
1226
1620
  }
1227
1621
  } catch {
1228
- console.log(chalk7.red(error.message));
1622
+ console.log(chalk8.red(error.message));
1229
1623
  }
1230
1624
  }
1231
1625
  }
@@ -1234,12 +1628,12 @@ async function pickCommand(idOrSlug, options) {
1234
1628
  }
1235
1629
  async function batchPickCommand(ids, options) {
1236
1630
  if (ids.length === 0) {
1237
- console.log(chalk7.yellow("Please provide at least one problem ID"));
1631
+ console.log(chalk8.yellow("Please provide at least one problem ID"));
1238
1632
  return;
1239
1633
  }
1240
1634
  const { authorized } = await requireAuth();
1241
1635
  if (!authorized) return;
1242
- console.log(chalk7.cyan(`\u{1F4E6} Picking ${ids.length} problem${ids.length !== 1 ? "s" : ""}...`));
1636
+ console.log(chalk8.cyan(`\u{1F4E6} Picking ${ids.length} problem${ids.length !== 1 ? "s" : ""}...`));
1243
1637
  console.log();
1244
1638
  console.log();
1245
1639
  let succeeded = 0;
@@ -1253,33 +1647,33 @@ async function batchPickCommand(ids, options) {
1253
1647
  }
1254
1648
  console.log();
1255
1649
  }
1256
- console.log(chalk7.gray("\u2500".repeat(50)));
1650
+ console.log(chalk8.gray("\u2500".repeat(50)));
1257
1651
  console.log(
1258
- chalk7.bold(
1259
- `Done! ${chalk7.green(`${succeeded} succeeded`)}${failed > 0 ? `, ${chalk7.red(`${failed} failed`)}` : ""}`
1652
+ chalk8.bold(
1653
+ `Done! ${chalk8.green(`${succeeded} succeeded`)}${failed > 0 ? `, ${chalk8.red(`${failed} failed`)}` : ""}`
1260
1654
  )
1261
1655
  );
1262
1656
  }
1263
1657
 
1264
1658
  // src/commands/test.ts
1265
1659
  import { readFile } from "fs/promises";
1266
- import { existsSync as existsSync3 } from "fs";
1660
+ import { existsSync as existsSync4 } from "fs";
1267
1661
  import { basename } from "path";
1268
1662
  import ora5 from "ora";
1269
- import chalk8 from "chalk";
1663
+ import chalk9 from "chalk";
1270
1664
 
1271
1665
  // src/utils/fileUtils.ts
1272
1666
  import { readdir } from "fs/promises";
1273
- import { existsSync as existsSync2 } from "fs";
1274
- import { join as join3 } from "path";
1667
+ import { existsSync as existsSync3 } from "fs";
1668
+ import { join as join5 } from "path";
1275
1669
  var MAX_SEARCH_DEPTH = 5;
1276
1670
  async function findSolutionFile(dir, problemId, currentDepth = 0) {
1277
- if (!existsSync2(dir)) return null;
1671
+ if (!existsSync3(dir)) return null;
1278
1672
  if (currentDepth >= MAX_SEARCH_DEPTH) return null;
1279
1673
  const entries = await readdir(dir, { withFileTypes: true });
1280
1674
  for (const entry of entries) {
1281
1675
  if (entry.name.startsWith(".")) continue;
1282
- const fullPath = join3(dir, entry.name);
1676
+ const fullPath = join5(dir, entry.name);
1283
1677
  if (entry.isDirectory()) {
1284
1678
  const found = await findSolutionFile(fullPath, problemId, currentDepth + 1);
1285
1679
  if (found) return found;
@@ -1293,11 +1687,11 @@ async function findSolutionFile(dir, problemId, currentDepth = 0) {
1293
1687
  return null;
1294
1688
  }
1295
1689
  async function findFileByName(dir, fileName, currentDepth = 0) {
1296
- if (!existsSync2(dir)) return null;
1690
+ if (!existsSync3(dir)) return null;
1297
1691
  if (currentDepth >= MAX_SEARCH_DEPTH) return null;
1298
1692
  const entries = await readdir(dir, { withFileTypes: true });
1299
1693
  for (const entry of entries) {
1300
- const fullPath = join3(dir, entry.name);
1694
+ const fullPath = join5(dir, entry.name);
1301
1695
  if (entry.isDirectory()) {
1302
1696
  const found = await findFileByName(fullPath, fileName, currentDepth + 1);
1303
1697
  if (found) return found;
@@ -1354,26 +1748,26 @@ async function testCommand(fileOrId, options) {
1354
1748
  const workDir = config.getWorkDir();
1355
1749
  const found = await findSolutionFile(workDir, fileOrId);
1356
1750
  if (!found) {
1357
- console.log(chalk8.red(`No solution file found for problem ${fileOrId}`));
1358
- console.log(chalk8.gray(`Looking in: ${workDir}`));
1359
- console.log(chalk8.gray(`Run "leetcode pick ${fileOrId}" first to create a solution file.`));
1751
+ console.log(chalk9.red(`No solution file found for problem ${fileOrId}`));
1752
+ console.log(chalk9.gray(`Looking in: ${workDir}`));
1753
+ console.log(chalk9.gray(`Run "leetcode pick ${fileOrId}" first to create a solution file.`));
1360
1754
  return;
1361
1755
  }
1362
1756
  filePath = found;
1363
- console.log(chalk8.gray(`Found: ${filePath}`));
1757
+ console.log(chalk9.gray(`Found: ${filePath}`));
1364
1758
  } else if (isFileName(fileOrId)) {
1365
1759
  const workDir = config.getWorkDir();
1366
1760
  const found = await findFileByName(workDir, fileOrId);
1367
1761
  if (!found) {
1368
- console.log(chalk8.red(`File not found: ${fileOrId}`));
1369
- console.log(chalk8.gray(`Looking in: ${workDir}`));
1762
+ console.log(chalk9.red(`File not found: ${fileOrId}`));
1763
+ console.log(chalk9.gray(`Looking in: ${workDir}`));
1370
1764
  return;
1371
1765
  }
1372
1766
  filePath = found;
1373
- console.log(chalk8.gray(`Found: ${filePath}`));
1767
+ console.log(chalk9.gray(`Found: ${filePath}`));
1374
1768
  }
1375
- if (!existsSync3(filePath)) {
1376
- console.log(chalk8.red(`File not found: ${filePath}`));
1769
+ if (!existsSync4(filePath)) {
1770
+ console.log(chalk9.red(`File not found: ${filePath}`));
1377
1771
  return;
1378
1772
  }
1379
1773
  const spinner = ora5({ text: "Reading solution file...", spinner: "dots" }).start();
@@ -1382,8 +1776,8 @@ async function testCommand(fileOrId, options) {
1382
1776
  const match = fileName.match(/^(\d+)\.([^.]+)\./);
1383
1777
  if (!match) {
1384
1778
  spinner.fail("Invalid filename format");
1385
- console.log(chalk8.gray("Expected format: {id}.{title-slug}.{ext}"));
1386
- console.log(chalk8.gray("Example: 1.two-sum.ts"));
1779
+ console.log(chalk9.gray("Expected format: {id}.{title-slug}.{ext}"));
1780
+ console.log(chalk9.gray("Example: 1.two-sum.ts"));
1387
1781
  return;
1388
1782
  }
1389
1783
  const [, problemId, titleSlug] = match;
@@ -1400,62 +1794,75 @@ async function testCommand(fileOrId, options) {
1400
1794
  spinner.text = "Running tests...";
1401
1795
  const result = await leetcodeClient.testSolution(titleSlug, code, lang, testcases, problem.questionId);
1402
1796
  spinner.stop();
1403
- displayTestResult(result);
1797
+ displayTestResult(result, options.visualize ? problem.topicTags : void 0);
1404
1798
  } catch (error) {
1405
1799
  spinner.fail("Test failed");
1406
1800
  if (error instanceof Error) {
1407
- console.log(chalk8.red(error.message));
1801
+ console.log(chalk9.red(error.message));
1408
1802
  }
1409
1803
  }
1410
1804
  }
1411
1805
 
1412
1806
  // src/commands/submit.ts
1413
1807
  import { readFile as readFile2 } from "fs/promises";
1414
- import { existsSync as existsSync4 } from "fs";
1808
+ import { existsSync as existsSync6 } from "fs";
1415
1809
  import { basename as basename2 } from "path";
1416
1810
  import ora6 from "ora";
1417
- import chalk9 from "chalk";
1811
+ import chalk10 from "chalk";
1418
1812
 
1419
1813
  // 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
1814
+ import { existsSync as existsSync5, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
1815
+ import { dirname } from "path";
1816
+ function getTimerPath() {
1817
+ return workspaceStorage.getTimerPath();
1818
+ }
1819
+ function loadTimer() {
1820
+ const path = getTimerPath();
1821
+ if (existsSync5(path)) {
1822
+ return JSON.parse(readFileSync2(path, "utf-8"));
1429
1823
  }
1430
- });
1824
+ return { solveTimes: {}, activeTimer: null };
1825
+ }
1826
+ function saveTimer(data) {
1827
+ const timerPath = getTimerPath();
1828
+ const dir = dirname(timerPath);
1829
+ if (!existsSync5(dir)) {
1830
+ mkdirSync2(dir, { recursive: true });
1831
+ }
1832
+ writeFileSync2(timerPath, JSON.stringify(data, null, 2));
1833
+ }
1431
1834
  var timerStorage = {
1432
1835
  startTimer(problemId, title, difficulty, durationMinutes) {
1433
- timerStore.set("activeTimer", {
1836
+ const data = loadTimer();
1837
+ data.activeTimer = {
1434
1838
  problemId,
1435
1839
  title,
1436
1840
  difficulty,
1437
1841
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
1438
1842
  durationMinutes
1439
- });
1843
+ };
1844
+ saveTimer(data);
1440
1845
  },
1441
1846
  getActiveTimer() {
1442
- return timerStore.get("activeTimer");
1847
+ return loadTimer().activeTimer;
1443
1848
  },
1444
1849
  stopTimer() {
1445
- const active = timerStore.get("activeTimer");
1850
+ const data = loadTimer();
1851
+ const active = data.activeTimer;
1446
1852
  if (!active) return null;
1447
1853
  const startedAt = new Date(active.startedAt);
1448
1854
  const now = /* @__PURE__ */ new Date();
1449
1855
  const durationSeconds = Math.floor((now.getTime() - startedAt.getTime()) / 1e3);
1450
- timerStore.set("activeTimer", null);
1856
+ data.activeTimer = null;
1857
+ saveTimer(data);
1451
1858
  return { durationSeconds };
1452
1859
  },
1453
1860
  recordSolveTime(problemId, title, difficulty, durationSeconds, timerMinutes) {
1454
- const solveTimes = timerStore.get("solveTimes") ?? {};
1455
- if (!solveTimes[problemId]) {
1456
- solveTimes[problemId] = [];
1861
+ const data = loadTimer();
1862
+ if (!data.solveTimes[problemId]) {
1863
+ data.solveTimes[problemId] = [];
1457
1864
  }
1458
- solveTimes[problemId].push({
1865
+ data.solveTimes[problemId].push({
1459
1866
  problemId,
1460
1867
  title,
1461
1868
  difficulty,
@@ -1463,17 +1870,17 @@ var timerStorage = {
1463
1870
  durationSeconds,
1464
1871
  timerMinutes
1465
1872
  });
1466
- timerStore.set("solveTimes", solveTimes);
1873
+ saveTimer(data);
1467
1874
  },
1468
1875
  getSolveTimes(problemId) {
1469
- const solveTimes = timerStore.get("solveTimes") ?? {};
1470
- return solveTimes[problemId] ?? [];
1876
+ const data = loadTimer();
1877
+ return data.solveTimes[problemId] ?? [];
1471
1878
  },
1472
1879
  getAllSolveTimes() {
1473
- return timerStore.get("solveTimes") ?? {};
1880
+ return loadTimer().solveTimes ?? {};
1474
1881
  },
1475
1882
  getStats() {
1476
- const solveTimes = timerStore.get("solveTimes") ?? {};
1883
+ const solveTimes = loadTimer().solveTimes ?? {};
1477
1884
  let totalProblems = 0;
1478
1885
  let totalTime = 0;
1479
1886
  for (const times of Object.values(solveTimes)) {
@@ -1499,26 +1906,26 @@ async function submitCommand(fileOrId) {
1499
1906
  const workDir = config.getWorkDir();
1500
1907
  const found = await findSolutionFile(workDir, fileOrId);
1501
1908
  if (!found) {
1502
- console.log(chalk9.red(`No solution file found for problem ${fileOrId}`));
1503
- console.log(chalk9.gray(`Looking in: ${workDir}`));
1504
- console.log(chalk9.gray(`Run "leetcode pick ${fileOrId}" first to create a solution file.`));
1909
+ console.log(chalk10.red(`No solution file found for problem ${fileOrId}`));
1910
+ console.log(chalk10.gray(`Looking in: ${workDir}`));
1911
+ console.log(chalk10.gray(`Run "leetcode pick ${fileOrId}" first to create a solution file.`));
1505
1912
  return;
1506
1913
  }
1507
1914
  filePath = found;
1508
- console.log(chalk9.gray(`Found: ${filePath}`));
1915
+ console.log(chalk10.gray(`Found: ${filePath}`));
1509
1916
  } else if (isFileName(fileOrId)) {
1510
1917
  const workDir = config.getWorkDir();
1511
1918
  const found = await findFileByName(workDir, fileOrId);
1512
1919
  if (!found) {
1513
- console.log(chalk9.red(`File not found: ${fileOrId}`));
1514
- console.log(chalk9.gray(`Looking in: ${workDir}`));
1920
+ console.log(chalk10.red(`File not found: ${fileOrId}`));
1921
+ console.log(chalk10.gray(`Looking in: ${workDir}`));
1515
1922
  return;
1516
1923
  }
1517
1924
  filePath = found;
1518
- console.log(chalk9.gray(`Found: ${filePath}`));
1925
+ console.log(chalk10.gray(`Found: ${filePath}`));
1519
1926
  }
1520
- if (!existsSync4(filePath)) {
1521
- console.log(chalk9.red(`File not found: ${filePath}`));
1927
+ if (!existsSync6(filePath)) {
1928
+ console.log(chalk10.red(`File not found: ${filePath}`));
1522
1929
  return;
1523
1930
  }
1524
1931
  const spinner = ora6({ text: "Reading solution file...", spinner: "dots" }).start();
@@ -1527,8 +1934,8 @@ async function submitCommand(fileOrId) {
1527
1934
  const match = fileName.match(/^(\d+)\.([^.]+)\./);
1528
1935
  if (!match) {
1529
1936
  spinner.fail("Invalid filename format");
1530
- console.log(chalk9.gray("Expected format: {id}.{title-slug}.{ext}"));
1531
- console.log(chalk9.gray("Example: 1.two-sum.ts"));
1937
+ console.log(chalk10.gray("Expected format: {id}.{title-slug}.{ext}"));
1938
+ console.log(chalk10.gray("Example: 1.two-sum.ts"));
1532
1939
  return;
1533
1940
  }
1534
1941
  const [, problemId, titleSlug] = match;
@@ -1567,14 +1974,14 @@ async function submitCommand(fileOrId) {
1567
1974
  const timeStr = `${mins}m ${secs}s`;
1568
1975
  const withinLimit = timerResult.durationSeconds <= activeTimer.durationMinutes * 60;
1569
1976
  console.log();
1570
- console.log(chalk9.bold("\u23F1\uFE0F Timer Result:"));
1977
+ console.log(chalk10.bold("\u23F1\uFE0F Timer Result:"));
1571
1978
  console.log(
1572
- ` Solved in ${withinLimit ? chalk9.green(timeStr) : chalk9.yellow(timeStr)} (limit: ${activeTimer.durationMinutes}m)`
1979
+ ` Solved in ${withinLimit ? chalk10.green(timeStr) : chalk10.yellow(timeStr)} (limit: ${activeTimer.durationMinutes}m)`
1573
1980
  );
1574
1981
  if (withinLimit) {
1575
- console.log(chalk9.green(" \u2713 Within time limit!"));
1982
+ console.log(chalk10.green(" \u2713 Within time limit!"));
1576
1983
  } else {
1577
- console.log(chalk9.yellow(" \u26A0 Exceeded time limit"));
1984
+ console.log(chalk10.yellow(" \u26A0 Exceeded time limit"));
1578
1985
  }
1579
1986
  }
1580
1987
  }
@@ -1582,17 +1989,17 @@ async function submitCommand(fileOrId) {
1582
1989
  } catch (error) {
1583
1990
  spinner.fail("Submission failed");
1584
1991
  if (error instanceof Error) {
1585
- console.log(chalk9.red(error.message));
1992
+ console.log(chalk10.red(error.message));
1586
1993
  }
1587
1994
  }
1588
1995
  }
1589
1996
 
1590
1997
  // src/commands/stat.ts
1591
1998
  import ora7 from "ora";
1592
- import chalk11 from "chalk";
1999
+ import chalk12 from "chalk";
1593
2000
 
1594
2001
  // src/utils/stats-display.ts
1595
- import chalk10 from "chalk";
2002
+ import chalk11 from "chalk";
1596
2003
  var MONTH_NAMES = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
1597
2004
  function renderHeatmap(calendarJson) {
1598
2005
  const data = JSON.parse(calendarJson);
@@ -1625,25 +2032,25 @@ function renderHeatmap(calendarJson) {
1625
2032
  const totalSubmissions = weeks.reduce((sum, w) => sum + w.count, 0);
1626
2033
  const totalActiveDays = weeks.reduce((sum, w) => sum + w.days, 0);
1627
2034
  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)));
2035
+ console.log(chalk11.bold("\u{1F4C5} Activity (Last 12 Weeks)"));
2036
+ console.log(chalk11.gray("How many problems you submitted and days you practiced."));
2037
+ console.log(chalk11.gray("\u2500".repeat(50)));
1631
2038
  console.log();
1632
2039
  for (const week of weeks) {
1633
2040
  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);
2041
+ const bar = week.count > 0 ? chalk11.green("\u2588".repeat(Math.min(week.count, 10))).padEnd(10) : chalk11.gray("\xB7").padEnd(10);
1635
2042
  const countStr = week.count > 0 ? `${week.count} subs`.padEnd(10) : "".padEnd(10);
1636
2043
  const daysStr = week.days > 0 ? `${week.days}d active` : "";
1637
- console.log(` ${chalk10.white(weekLabel)} ${bar} ${chalk10.cyan(countStr)} ${chalk10.yellow(daysStr)}`);
2044
+ console.log(` ${chalk11.white(weekLabel)} ${bar} ${chalk11.cyan(countStr)} ${chalk11.yellow(daysStr)}`);
1638
2045
  }
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")}`);
2046
+ console.log(chalk11.gray("\u2500".repeat(50)));
2047
+ console.log(` ${chalk11.bold.white("Total:")} ${chalk11.cyan.bold(totalSubmissions + " submissions")}, ${chalk11.yellow.bold(totalActiveDays + " days active")}`);
1641
2048
  console.log();
1642
2049
  }
1643
2050
  function renderSkillStats(fundamental, intermediate, advanced) {
1644
2051
  console.log();
1645
- console.log(chalk10.bold("\u{1F3AF} Skill Breakdown"));
1646
- console.log(chalk10.gray("\u2500".repeat(45)));
2052
+ console.log(chalk11.bold("\u{1F3AF} Skill Breakdown"));
2053
+ console.log(chalk11.gray("\u2500".repeat(45)));
1647
2054
  const renderSection = (title, stats, color) => {
1648
2055
  if (stats.length === 0) return;
1649
2056
  console.log();
@@ -1652,12 +2059,12 @@ function renderSkillStats(fundamental, intermediate, advanced) {
1652
2059
  for (const stat of sorted.slice(0, 8)) {
1653
2060
  const name = stat.tagName.padEnd(22);
1654
2061
  const bar = color("\u2588".repeat(Math.min(stat.problemsSolved, 15)));
1655
- console.log(` ${chalk10.white(name)} ${bar} ${chalk10.white(stat.problemsSolved)}`);
2062
+ console.log(` ${chalk11.white(name)} ${bar} ${chalk11.white(stat.problemsSolved)}`);
1656
2063
  }
1657
2064
  };
1658
- renderSection("Fundamental", fundamental, chalk10.green);
1659
- renderSection("Intermediate", intermediate, chalk10.yellow);
1660
- renderSection("Advanced", advanced, chalk10.red);
2065
+ renderSection("Fundamental", fundamental, chalk11.green);
2066
+ renderSection("Intermediate", intermediate, chalk11.yellow);
2067
+ renderSection("Advanced", advanced, chalk11.red);
1661
2068
  console.log();
1662
2069
  }
1663
2070
  function renderTrendChart(calendarJson) {
@@ -1678,15 +2085,15 @@ function renderTrendChart(calendarJson) {
1678
2085
  const maxVal = Math.max(...days.map((d) => d.count), 1);
1679
2086
  const chartHeight = 6;
1680
2087
  console.log();
1681
- console.log(chalk10.bold("\u{1F4C8} Submission Trend (Last 7 Days)"));
1682
- console.log(chalk10.gray("\u2500".repeat(35)));
2088
+ console.log(chalk11.bold("\u{1F4C8} Submission Trend (Last 7 Days)"));
2089
+ console.log(chalk11.gray("\u2500".repeat(35)));
1683
2090
  console.log();
1684
2091
  for (let row = chartHeight; row >= 1; row--) {
1685
2092
  let line = ` ${row === chartHeight ? maxVal.toString().padStart(2) : " "} \u2502`;
1686
2093
  for (const day of days) {
1687
2094
  const barHeight = Math.round(day.count / maxVal * chartHeight);
1688
2095
  if (barHeight >= row) {
1689
- line += chalk10.green(" \u2588\u2588 ");
2096
+ line += chalk11.green(" \u2588\u2588 ");
1690
2097
  } else {
1691
2098
  line += " ";
1692
2099
  }
@@ -1695,10 +2102,10 @@ function renderTrendChart(calendarJson) {
1695
2102
  }
1696
2103
  console.log(` 0 \u2514${"\u2500\u2500\u2500\u2500".repeat(7)}`);
1697
2104
  console.log(` ${days.map((d) => d.label.padEnd(4)).join("")}`);
1698
- console.log(chalk10.gray(` ${days.map((d) => d.count.toString().padEnd(4)).join("")}`));
2105
+ console.log(chalk11.gray(` ${days.map((d) => d.count.toString().padEnd(4)).join("")}`));
1699
2106
  const total = days.reduce((a, b) => a + b.count, 0);
1700
2107
  console.log();
1701
- console.log(chalk10.white(` Total: ${total} submissions this week`));
2108
+ console.log(chalk11.white(` Total: ${total} submissions this week`));
1702
2109
  console.log();
1703
2110
  }
1704
2111
 
@@ -1730,14 +2137,14 @@ async function statCommand(username, options = {}) {
1730
2137
  if (profile.submissionCalendar) {
1731
2138
  renderHeatmap(profile.submissionCalendar);
1732
2139
  } else {
1733
- console.log(chalk11.yellow("Calendar data not available."));
2140
+ console.log(chalk12.yellow("Calendar data not available."));
1734
2141
  }
1735
2142
  }
1736
2143
  if (options.trend) {
1737
2144
  if (profile.submissionCalendar) {
1738
2145
  renderTrendChart(profile.submissionCalendar);
1739
2146
  } else {
1740
- console.log(chalk11.yellow("Calendar data not available."));
2147
+ console.log(chalk12.yellow("Calendar data not available."));
1741
2148
  }
1742
2149
  }
1743
2150
  if (options.skills) {
@@ -1749,14 +2156,14 @@ async function statCommand(username, options = {}) {
1749
2156
  } catch (error) {
1750
2157
  spinner.fail("Failed to fetch statistics");
1751
2158
  if (error instanceof Error) {
1752
- console.log(chalk11.red(error.message));
2159
+ console.log(chalk12.red(error.message));
1753
2160
  }
1754
2161
  }
1755
2162
  }
1756
2163
 
1757
2164
  // src/commands/daily.ts
1758
2165
  import ora8 from "ora";
1759
- import chalk12 from "chalk";
2166
+ import chalk13 from "chalk";
1760
2167
  async function dailyCommand() {
1761
2168
  const { authorized } = await requireAuth();
1762
2169
  if (!authorized) return;
@@ -1766,19 +2173,19 @@ async function dailyCommand() {
1766
2173
  spinner.stop();
1767
2174
  displayDailyChallenge(daily.date, daily.question);
1768
2175
  console.log();
1769
- console.log(chalk12.gray("Run the following to start working on this problem:"));
1770
- console.log(chalk12.cyan(` leetcode pick ${daily.question.titleSlug}`));
2176
+ console.log(chalk13.gray("Run the following to start working on this problem:"));
2177
+ console.log(chalk13.cyan(` leetcode pick ${daily.question.titleSlug}`));
1771
2178
  } catch (error) {
1772
2179
  spinner.fail("Failed to fetch daily challenge");
1773
2180
  if (error instanceof Error) {
1774
- console.log(chalk12.red(error.message));
2181
+ console.log(chalk13.red(error.message));
1775
2182
  }
1776
2183
  }
1777
2184
  }
1778
2185
 
1779
2186
  // src/commands/random.ts
1780
2187
  import ora9 from "ora";
1781
- import chalk13 from "chalk";
2188
+ import chalk14 from "chalk";
1782
2189
  async function randomCommand(options) {
1783
2190
  const { authorized } = await requireAuth();
1784
2191
  if (!authorized) return;
@@ -1812,23 +2219,23 @@ async function randomCommand(options) {
1812
2219
  await pickCommand(titleSlug, { open: options.open ?? true });
1813
2220
  } else {
1814
2221
  await showCommand(titleSlug);
1815
- console.log(chalk13.gray("Run following to start solving:"));
1816
- console.log(chalk13.cyan(` leetcode pick ${titleSlug}`));
2222
+ console.log(chalk14.gray("Run following to start solving:"));
2223
+ console.log(chalk14.cyan(` leetcode pick ${titleSlug}`));
1817
2224
  }
1818
2225
  } catch (error) {
1819
2226
  spinner.fail("Failed to fetch random problem");
1820
2227
  if (error instanceof Error) {
1821
- console.log(chalk13.red(error.message));
2228
+ console.log(chalk14.red(error.message));
1822
2229
  }
1823
2230
  }
1824
2231
  }
1825
2232
 
1826
2233
  // src/commands/submissions.ts
1827
2234
  import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
1828
- import { existsSync as existsSync5 } from "fs";
1829
- import { join as join5 } from "path";
2235
+ import { existsSync as existsSync7 } from "fs";
2236
+ import { join as join6 } from "path";
1830
2237
  import ora10 from "ora";
1831
- import chalk14 from "chalk";
2238
+ import chalk15 from "chalk";
1832
2239
  async function submissionsCommand(idOrSlug, options) {
1833
2240
  const { authorized } = await requireAuth();
1834
2241
  if (!authorized) return;
@@ -1850,16 +2257,16 @@ async function submissionsCommand(idOrSlug, options) {
1850
2257
  const submissions = await leetcodeClient.getSubmissionList(slug, limit);
1851
2258
  spinner.stop();
1852
2259
  if (submissions.length === 0) {
1853
- console.log(chalk14.yellow("No submissions found."));
2260
+ console.log(chalk15.yellow("No submissions found."));
1854
2261
  return;
1855
2262
  }
1856
2263
  if (options.last) {
1857
2264
  const lastAC = submissions.find((s) => s.statusDisplay === "Accepted");
1858
2265
  if (lastAC) {
1859
- console.log(chalk14.bold("Last Accepted Submission:"));
2266
+ console.log(chalk15.bold("Last Accepted Submission:"));
1860
2267
  displaySubmissionsList([lastAC]);
1861
2268
  } else {
1862
- console.log(chalk14.yellow("No accepted submissions found in recent history."));
2269
+ console.log(chalk15.yellow("No accepted submissions found in recent history."));
1863
2270
  }
1864
2271
  } else {
1865
2272
  displaySubmissionsList(submissions);
@@ -1880,30 +2287,30 @@ async function submissionsCommand(idOrSlug, options) {
1880
2287
  const workDir = config.getWorkDir();
1881
2288
  const difficulty = problem.difficulty;
1882
2289
  const category = problem.topicTags.length > 0 ? problem.topicTags[0].name.replace(/[^\w\s-]/g, "").trim() : "Uncategorized";
1883
- const targetDir = join5(workDir, difficulty, category);
1884
- if (!existsSync5(targetDir)) {
2290
+ const targetDir = join6(workDir, difficulty, category);
2291
+ if (!existsSync7(targetDir)) {
1885
2292
  await mkdir2(targetDir, { recursive: true });
1886
2293
  }
1887
2294
  const langSlug = details.lang.name;
1888
2295
  const supportedLang = LANG_SLUG_MAP[langSlug] ?? "txt";
1889
2296
  const ext = LANGUAGE_EXTENSIONS[supportedLang] ?? langSlug;
1890
2297
  const fileName = `${problem.questionFrontendId}.${problem.titleSlug}.submission-${lastAC.id}.${ext}`;
1891
- const filePath = join5(targetDir, fileName);
2298
+ const filePath = join6(targetDir, fileName);
1892
2299
  await writeFile2(filePath, details.code, "utf-8");
1893
- downloadSpinner.succeed(`Downloaded to ${chalk14.green(fileName)}`);
1894
- console.log(chalk14.gray(`Path: ${filePath}`));
2300
+ downloadSpinner.succeed(`Downloaded to ${chalk15.green(fileName)}`);
2301
+ console.log(chalk15.gray(`Path: ${filePath}`));
1895
2302
  }
1896
2303
  } catch (error) {
1897
2304
  spinner.fail("Failed to fetch submissions");
1898
2305
  if (error instanceof Error) {
1899
- console.log(chalk14.red(error.message));
2306
+ console.log(chalk15.red(error.message));
1900
2307
  }
1901
2308
  }
1902
2309
  }
1903
2310
 
1904
2311
  // src/commands/config.ts
1905
2312
  import inquirer2 from "inquirer";
1906
- import chalk15 from "chalk";
2313
+ import chalk16 from "chalk";
1907
2314
  var SUPPORTED_LANGUAGES = [
1908
2315
  "typescript",
1909
2316
  "javascript",
@@ -1925,34 +2332,38 @@ async function configCommand(options) {
1925
2332
  if (options.lang) {
1926
2333
  const langInput = options.lang.toLowerCase();
1927
2334
  if (!SUPPORTED_LANGUAGES.includes(langInput)) {
1928
- console.log(chalk15.red(`Unsupported language: ${options.lang}`));
1929
- console.log(chalk15.gray(`Supported: ${SUPPORTED_LANGUAGES.join(", ")}`));
2335
+ console.log(chalk16.red(`Unsupported language: ${options.lang}`));
2336
+ console.log(chalk16.gray(`Supported: ${SUPPORTED_LANGUAGES.join(", ")}`));
1930
2337
  return;
1931
2338
  }
1932
2339
  const lang = langInput;
1933
2340
  config.setLanguage(lang);
1934
- console.log(chalk15.green(`\u2713 Default language set to ${lang}`));
2341
+ console.log(chalk16.green(`\u2713 Default language set to ${lang}`));
1935
2342
  }
1936
2343
  if (options.editor) {
1937
2344
  config.setEditor(options.editor);
1938
- console.log(chalk15.green(`\u2713 Editor set to ${options.editor}`));
2345
+ console.log(chalk16.green(`\u2713 Editor set to ${options.editor}`));
1939
2346
  }
1940
2347
  if (options.workdir) {
1941
2348
  config.setWorkDir(options.workdir);
1942
- console.log(chalk15.green(`\u2713 Work directory set to ${options.workdir}`));
2349
+ console.log(chalk16.green(`\u2713 Work directory set to ${options.workdir}`));
1943
2350
  }
1944
2351
  if (options.repo !== void 0) {
1945
2352
  if (options.repo.trim() === "") {
1946
2353
  config.deleteRepo();
1947
- console.log(chalk15.green("\u2713 Repository URL cleared"));
2354
+ console.log(chalk16.green("\u2713 Repository URL cleared"));
1948
2355
  } else {
1949
2356
  config.setRepo(options.repo);
1950
- console.log(chalk15.green(`\u2713 Repository URL set to ${options.repo}`));
2357
+ console.log(chalk16.green(`\u2713 Repository URL set to ${options.repo}`));
1951
2358
  }
1952
2359
  }
1953
2360
  }
1954
2361
  async function configInteractiveCommand() {
1955
2362
  const currentConfig = config.getConfig();
2363
+ const workspace = config.getActiveWorkspace();
2364
+ console.log();
2365
+ console.log(chalk16.bold.cyan(`\u{1F4C1} Configuring workspace: ${workspace}`));
2366
+ console.log(chalk16.gray("\u2500".repeat(40)));
1956
2367
  const answers = await inquirer2.prompt([
1957
2368
  {
1958
2369
  type: "list",
@@ -1989,33 +2400,34 @@ async function configInteractiveCommand() {
1989
2400
  config.deleteRepo();
1990
2401
  }
1991
2402
  console.log();
1992
- console.log(chalk15.green("\u2713 Configuration saved"));
2403
+ console.log(chalk16.green("\u2713 Configuration saved"));
1993
2404
  showCurrentConfig();
1994
2405
  }
1995
2406
  function showCurrentConfig() {
1996
2407
  const currentConfig = config.getConfig();
1997
- const credentials = config.getCredentials();
2408
+ const creds = credentials.get();
2409
+ const workspace = config.getActiveWorkspace();
1998
2410
  console.log();
1999
- console.log(chalk15.bold("LeetCode CLI Configuration"));
2000
- console.log(chalk15.gray("\u2500".repeat(40)));
2411
+ console.log(chalk16.bold.cyan(`\u{1F4C1} Workspace: ${workspace}`));
2412
+ console.log(chalk16.gray("\u2500".repeat(40)));
2001
2413
  console.log();
2002
- console.log(chalk15.gray("Config file:"), config.getPath());
2414
+ console.log(chalk16.gray("Config file:"), config.getPath());
2003
2415
  console.log();
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"));
2416
+ console.log(chalk16.gray("Language: "), chalk16.white(currentConfig.language));
2417
+ console.log(chalk16.gray("Editor: "), chalk16.white(currentConfig.editor ?? "(not set)"));
2418
+ console.log(chalk16.gray("Work Dir: "), chalk16.white(currentConfig.workDir));
2419
+ console.log(chalk16.gray("Repo URL: "), chalk16.white(currentConfig.repo ?? "(not set)"));
2420
+ console.log(chalk16.gray("Logged in: "), creds ? chalk16.green("Yes") : chalk16.yellow("No"));
2009
2421
  }
2010
2422
 
2011
2423
  // src/commands/bookmark.ts
2012
- import chalk16 from "chalk";
2424
+ import chalk17 from "chalk";
2013
2425
  import Table2 from "cli-table3";
2014
2426
  import ora11 from "ora";
2015
2427
 
2016
2428
  // src/storage/bookmarks.ts
2017
- import Conf3 from "conf";
2018
- var bookmarksStore = new Conf3({
2429
+ import Conf2 from "conf";
2430
+ var bookmarksStore = new Conf2({
2019
2431
  projectName: "leetcode-cli-bookmarks",
2020
2432
  defaults: { bookmarks: [] }
2021
2433
  });
@@ -2054,36 +2466,36 @@ var bookmarks = {
2054
2466
  async function bookmarkCommand(action, id) {
2055
2467
  const validActions = ["add", "remove", "list", "clear"];
2056
2468
  if (!validActions.includes(action)) {
2057
- console.log(chalk16.red(`Invalid action: ${action}`));
2058
- console.log(chalk16.gray("Valid actions: add, remove, list, clear"));
2469
+ console.log(chalk17.red(`Invalid action: ${action}`));
2470
+ console.log(chalk17.gray("Valid actions: add, remove, list, clear"));
2059
2471
  return;
2060
2472
  }
2061
2473
  switch (action) {
2062
2474
  case "add":
2063
2475
  if (!id) {
2064
- console.log(chalk16.red("Please provide a problem ID to bookmark"));
2476
+ console.log(chalk17.red("Please provide a problem ID to bookmark"));
2065
2477
  return;
2066
2478
  }
2067
2479
  if (!isProblemId(id)) {
2068
- console.log(chalk16.red(`Invalid problem ID: ${id}`));
2069
- console.log(chalk16.gray("Problem ID must be a positive integer"));
2480
+ console.log(chalk17.red(`Invalid problem ID: ${id}`));
2481
+ console.log(chalk17.gray("Problem ID must be a positive integer"));
2070
2482
  return;
2071
2483
  }
2072
2484
  if (bookmarks.add(id)) {
2073
- console.log(chalk16.green(`\u2713 Bookmarked problem ${id}`));
2485
+ console.log(chalk17.green(`\u2713 Bookmarked problem ${id}`));
2074
2486
  } else {
2075
- console.log(chalk16.yellow(`Problem ${id} is already bookmarked`));
2487
+ console.log(chalk17.yellow(`Problem ${id} is already bookmarked`));
2076
2488
  }
2077
2489
  break;
2078
2490
  case "remove":
2079
2491
  if (!id) {
2080
- console.log(chalk16.red("Please provide a problem ID to remove"));
2492
+ console.log(chalk17.red("Please provide a problem ID to remove"));
2081
2493
  return;
2082
2494
  }
2083
2495
  if (bookmarks.remove(id)) {
2084
- console.log(chalk16.green(`\u2713 Removed bookmark for problem ${id}`));
2496
+ console.log(chalk17.green(`\u2713 Removed bookmark for problem ${id}`));
2085
2497
  } else {
2086
- console.log(chalk16.yellow(`Problem ${id} is not bookmarked`));
2498
+ console.log(chalk17.yellow(`Problem ${id} is not bookmarked`));
2087
2499
  }
2088
2500
  break;
2089
2501
  case "list":
@@ -2092,10 +2504,10 @@ async function bookmarkCommand(action, id) {
2092
2504
  case "clear":
2093
2505
  const count = bookmarks.count();
2094
2506
  if (count === 0) {
2095
- console.log(chalk16.yellow("No bookmarks to clear"));
2507
+ console.log(chalk17.yellow("No bookmarks to clear"));
2096
2508
  } else {
2097
2509
  bookmarks.clear();
2098
- console.log(chalk16.green(`\u2713 Cleared ${count} bookmark${count !== 1 ? "s" : ""}`));
2510
+ console.log(chalk17.green(`\u2713 Cleared ${count} bookmark${count !== 1 ? "s" : ""}`));
2099
2511
  }
2100
2512
  break;
2101
2513
  }
@@ -2103,12 +2515,12 @@ async function bookmarkCommand(action, id) {
2103
2515
  async function listBookmarks() {
2104
2516
  const bookmarkList = bookmarks.list();
2105
2517
  if (bookmarkList.length === 0) {
2106
- console.log(chalk16.yellow("\u{1F4CC} No bookmarked problems"));
2107
- console.log(chalk16.gray('Use "leetcode bookmark add <id>" to bookmark a problem'));
2518
+ console.log(chalk17.yellow("\u{1F4CC} No bookmarked problems"));
2519
+ console.log(chalk17.gray('Use "leetcode bookmark add <id>" to bookmark a problem'));
2108
2520
  return;
2109
2521
  }
2110
2522
  console.log();
2111
- console.log(chalk16.bold.cyan(`\u{1F4CC} Bookmarked Problems (${bookmarkList.length})`));
2523
+ console.log(chalk17.bold.cyan(`\u{1F4CC} Bookmarked Problems (${bookmarkList.length})`));
2112
2524
  console.log();
2113
2525
  const { authorized } = await requireAuth();
2114
2526
  if (authorized) {
@@ -2116,10 +2528,10 @@ async function listBookmarks() {
2116
2528
  try {
2117
2529
  const table = new Table2({
2118
2530
  head: [
2119
- chalk16.cyan("ID"),
2120
- chalk16.cyan("Title"),
2121
- chalk16.cyan("Difficulty"),
2122
- chalk16.cyan("Status")
2531
+ chalk17.cyan("ID"),
2532
+ chalk17.cyan("Title"),
2533
+ chalk17.cyan("Difficulty"),
2534
+ chalk17.cyan("Status")
2123
2535
  ],
2124
2536
  colWidths: [8, 45, 12, 10],
2125
2537
  style: { head: [], border: [] }
@@ -2132,35 +2544,35 @@ async function listBookmarks() {
2132
2544
  problem.questionFrontendId,
2133
2545
  problem.title.length > 42 ? problem.title.slice(0, 39) + "..." : problem.title,
2134
2546
  colorDifficulty2(problem.difficulty),
2135
- problem.status === "ac" ? chalk16.green("\u2713") : chalk16.gray("-")
2547
+ problem.status === "ac" ? chalk17.green("\u2713") : chalk17.gray("-")
2136
2548
  ]);
2137
2549
  } else {
2138
- table.push([id, chalk16.gray("(not found)"), "-", "-"]);
2550
+ table.push([id, chalk17.gray("(not found)"), "-", "-"]);
2139
2551
  }
2140
2552
  } catch {
2141
- table.push([id, chalk16.gray("(error fetching)"), "-", "-"]);
2553
+ table.push([id, chalk17.gray("(error fetching)"), "-", "-"]);
2142
2554
  }
2143
2555
  }
2144
2556
  spinner.stop();
2145
2557
  console.log(table.toString());
2146
2558
  } catch {
2147
2559
  spinner.stop();
2148
- console.log(chalk16.gray("IDs: ") + bookmarkList.join(", "));
2560
+ console.log(chalk17.gray("IDs: ") + bookmarkList.join(", "));
2149
2561
  }
2150
2562
  } else {
2151
- console.log(chalk16.gray("IDs: ") + bookmarkList.join(", "));
2563
+ console.log(chalk17.gray("IDs: ") + bookmarkList.join(", "));
2152
2564
  console.log();
2153
- console.log(chalk16.gray("Login to see problem details"));
2565
+ console.log(chalk17.gray("Login to see problem details"));
2154
2566
  }
2155
2567
  }
2156
2568
  function colorDifficulty2(difficulty) {
2157
2569
  switch (difficulty.toLowerCase()) {
2158
2570
  case "easy":
2159
- return chalk16.green(difficulty);
2571
+ return chalk17.green(difficulty);
2160
2572
  case "medium":
2161
- return chalk16.yellow(difficulty);
2573
+ return chalk17.yellow(difficulty);
2162
2574
  case "hard":
2163
- return chalk16.red(difficulty);
2575
+ return chalk17.red(difficulty);
2164
2576
  default:
2165
2577
  return difficulty;
2166
2578
  }
@@ -2168,19 +2580,19 @@ function colorDifficulty2(difficulty) {
2168
2580
 
2169
2581
  // src/commands/notes.ts
2170
2582
  import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
2171
- import { join as join6 } from "path";
2172
- import { existsSync as existsSync6 } from "fs";
2173
- import chalk17 from "chalk";
2583
+ import { join as join7 } from "path";
2584
+ import { existsSync as existsSync8 } from "fs";
2585
+ import chalk18 from "chalk";
2174
2586
  async function notesCommand(problemId, action) {
2175
2587
  if (!isProblemId(problemId)) {
2176
- console.log(chalk17.red(`Invalid problem ID: ${problemId}`));
2177
- console.log(chalk17.gray("Problem ID must be a positive integer"));
2588
+ console.log(chalk18.red(`Invalid problem ID: ${problemId}`));
2589
+ console.log(chalk18.gray("Problem ID must be a positive integer"));
2178
2590
  return;
2179
2591
  }
2180
2592
  const noteAction = action === "view" ? "view" : "edit";
2181
- const notesDir = join6(config.getWorkDir(), ".notes");
2182
- const notePath = join6(notesDir, `${problemId}.md`);
2183
- if (!existsSync6(notesDir)) {
2593
+ const notesDir = join7(config.getWorkDir(), ".notes");
2594
+ const notePath = join7(notesDir, `${problemId}.md`);
2595
+ if (!existsSync8(notesDir)) {
2184
2596
  await mkdir3(notesDir, { recursive: true });
2185
2597
  }
2186
2598
  if (noteAction === "view") {
@@ -2190,32 +2602,32 @@ async function notesCommand(problemId, action) {
2190
2602
  }
2191
2603
  }
2192
2604
  async function viewNote(notePath, problemId) {
2193
- if (!existsSync6(notePath)) {
2194
- console.log(chalk17.yellow(`No notes found for problem ${problemId}`));
2195
- console.log(chalk17.gray(`Use "leetcode note ${problemId} edit" to create notes`));
2605
+ if (!existsSync8(notePath)) {
2606
+ console.log(chalk18.yellow(`No notes found for problem ${problemId}`));
2607
+ console.log(chalk18.gray(`Use "leetcode note ${problemId} edit" to create notes`));
2196
2608
  return;
2197
2609
  }
2198
2610
  try {
2199
2611
  const content = await readFile3(notePath, "utf-8");
2200
2612
  console.log();
2201
- console.log(chalk17.bold.cyan(`\u{1F4DD} Notes for Problem ${problemId}`));
2202
- console.log(chalk17.gray("\u2500".repeat(50)));
2613
+ console.log(chalk18.bold.cyan(`\u{1F4DD} Notes for Problem ${problemId}`));
2614
+ console.log(chalk18.gray("\u2500".repeat(50)));
2203
2615
  console.log();
2204
2616
  console.log(content);
2205
2617
  } catch (error) {
2206
- console.log(chalk17.red("Failed to read notes"));
2618
+ console.log(chalk18.red("Failed to read notes"));
2207
2619
  if (error instanceof Error) {
2208
- console.log(chalk17.gray(error.message));
2620
+ console.log(chalk18.gray(error.message));
2209
2621
  }
2210
2622
  }
2211
2623
  }
2212
2624
  async function editNote(notePath, problemId) {
2213
- if (!existsSync6(notePath)) {
2625
+ if (!existsSync8(notePath)) {
2214
2626
  const template = await generateNoteTemplate(problemId);
2215
2627
  await writeFile3(notePath, template, "utf-8");
2216
- console.log(chalk17.green(`\u2713 Created notes file for problem ${problemId}`));
2628
+ console.log(chalk18.green(`\u2713 Created notes file for problem ${problemId}`));
2217
2629
  }
2218
- console.log(chalk17.gray(`Opening: ${notePath}`));
2630
+ console.log(chalk18.gray(`Opening: ${notePath}`));
2219
2631
  await openInEditor(notePath);
2220
2632
  }
2221
2633
  async function generateNoteTemplate(problemId) {
@@ -2272,7 +2684,7 @@ async function generateNoteTemplate(problemId) {
2272
2684
  }
2273
2685
 
2274
2686
  // src/commands/today.ts
2275
- import chalk18 from "chalk";
2687
+ import chalk19 from "chalk";
2276
2688
  import ora12 from "ora";
2277
2689
  async function todayCommand() {
2278
2690
  const { authorized, username } = await requireAuth();
@@ -2287,48 +2699,48 @@ async function todayCommand() {
2287
2699
  ]);
2288
2700
  spinner.stop();
2289
2701
  console.log();
2290
- console.log(chalk18.bold.cyan(`\u{1F4CA} Today's Summary`), chalk18.gray(`- ${username}`));
2291
- console.log(chalk18.gray("\u2500".repeat(50)));
2702
+ console.log(chalk19.bold.cyan(`\u{1F4CA} Today's Summary`), chalk19.gray(`- ${username}`));
2703
+ console.log(chalk19.gray("\u2500".repeat(50)));
2292
2704
  console.log();
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}`));
2705
+ console.log(chalk19.yellow(`\u{1F525} Current Streak: ${profile.streak} day${profile.streak !== 1 ? "s" : ""}`));
2706
+ console.log(chalk19.gray(` Total Active Days: ${profile.totalActiveDays}`));
2295
2707
  console.log();
2296
2708
  const total = profile.acSubmissionNum.find((s) => s.difficulty === "All");
2297
2709
  const easy = profile.acSubmissionNum.find((s) => s.difficulty === "Easy");
2298
2710
  const medium = profile.acSubmissionNum.find((s) => s.difficulty === "Medium");
2299
2711
  const hard = profile.acSubmissionNum.find((s) => s.difficulty === "Hard");
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}`);
2712
+ console.log(chalk19.white("\u{1F4C8} Problems Solved:"));
2713
+ console.log(` ${chalk19.green("Easy")}: ${easy?.count ?? 0} | ${chalk19.yellow("Medium")}: ${medium?.count ?? 0} | ${chalk19.red("Hard")}: ${hard?.count ?? 0}`);
2714
+ console.log(` ${chalk19.bold("Total")}: ${total?.count ?? 0}`);
2303
2715
  console.log();
2304
- console.log(chalk18.bold.yellow("\u{1F3AF} Today's Challenge:"));
2716
+ console.log(chalk19.bold.yellow("\u{1F3AF} Today's Challenge:"));
2305
2717
  console.log(` ${daily.question.questionFrontendId}. ${daily.question.title}`);
2306
2718
  console.log(` ${colorDifficulty3(daily.question.difficulty)}`);
2307
2719
  const status = daily.question.status;
2308
2720
  if (status === "ac") {
2309
- console.log(chalk18.green(" \u2713 Completed!"));
2721
+ console.log(chalk19.green(" \u2713 Completed!"));
2310
2722
  } else if (status === "notac") {
2311
- console.log(chalk18.yellow(" \u25CB Attempted"));
2723
+ console.log(chalk19.yellow(" \u25CB Attempted"));
2312
2724
  } else {
2313
- console.log(chalk18.gray(" - Not started"));
2725
+ console.log(chalk19.gray(" - Not started"));
2314
2726
  }
2315
2727
  console.log();
2316
- console.log(chalk18.gray(` leetcode pick ${daily.question.questionFrontendId} # Start working on it`));
2728
+ console.log(chalk19.gray(` leetcode pick ${daily.question.questionFrontendId} # Start working on it`));
2317
2729
  } catch (error) {
2318
2730
  spinner.fail("Failed to fetch progress");
2319
2731
  if (error instanceof Error) {
2320
- console.log(chalk18.red(error.message));
2732
+ console.log(chalk19.red(error.message));
2321
2733
  }
2322
2734
  }
2323
2735
  }
2324
2736
  function colorDifficulty3(difficulty) {
2325
2737
  switch (difficulty.toLowerCase()) {
2326
2738
  case "easy":
2327
- return chalk18.green(difficulty);
2739
+ return chalk19.green(difficulty);
2328
2740
  case "medium":
2329
- return chalk18.yellow(difficulty);
2741
+ return chalk19.yellow(difficulty);
2330
2742
  case "hard":
2331
- return chalk18.red(difficulty);
2743
+ return chalk19.red(difficulty);
2332
2744
  default:
2333
2745
  return difficulty;
2334
2746
  }
@@ -2336,8 +2748,8 @@ function colorDifficulty3(difficulty) {
2336
2748
 
2337
2749
  // src/commands/sync.ts
2338
2750
  import { execSync } from "child_process";
2339
- import { existsSync as existsSync7 } from "fs";
2340
- import chalk19 from "chalk";
2751
+ import { existsSync as existsSync9 } from "fs";
2752
+ import chalk20 from "chalk";
2341
2753
  import inquirer3 from "inquirer";
2342
2754
  import ora13 from "ora";
2343
2755
  function isGitInstalled() {
@@ -2382,7 +2794,7 @@ async function setupGitRepo(workDir) {
2382
2794
  }
2383
2795
  ]);
2384
2796
  if (!init) {
2385
- console.log(chalk19.yellow("Skipping basic git initialization."));
2797
+ console.log(chalk20.yellow("Skipping basic git initialization."));
2386
2798
  return false;
2387
2799
  }
2388
2800
  const spinner = ora13("Initializing git repository...").start();
@@ -2421,12 +2833,12 @@ async function setupRemote(workDir) {
2421
2833
  return repoUrl;
2422
2834
  } catch (error) {
2423
2835
  spinner.fail("Failed to create GitHub repository");
2424
- console.log(chalk19.red(error));
2836
+ console.log(chalk20.red(error));
2425
2837
  }
2426
2838
  }
2427
2839
  }
2428
2840
  if (!repoUrl) {
2429
- console.log(chalk19.yellow("\nPlease create a new repository on your Git provider and copy the URL."));
2841
+ console.log(chalk20.yellow("\nPlease create a new repository on your Git provider and copy the URL."));
2430
2842
  const { url } = await inquirer3.prompt([
2431
2843
  {
2432
2844
  type: "input",
@@ -2445,21 +2857,21 @@ async function setupRemote(workDir) {
2445
2857
  if (!currentRemote && repoUrl) {
2446
2858
  try {
2447
2859
  execSync(`git remote add origin ${repoUrl}`, { cwd: workDir });
2448
- console.log(chalk19.green("\u2713 Added remote origin"));
2860
+ console.log(chalk20.green("\u2713 Added remote origin"));
2449
2861
  } catch (e) {
2450
- console.log(chalk19.red("Failed to add remote origin"));
2862
+ console.log(chalk20.red("Failed to add remote origin"));
2451
2863
  }
2452
2864
  }
2453
2865
  return repoUrl || "";
2454
2866
  }
2455
2867
  async function syncCommand() {
2456
2868
  const workDir = config.getWorkDir();
2457
- if (!existsSync7(workDir)) {
2458
- console.log(chalk19.red(`Work directory does not exist: ${workDir}`));
2869
+ if (!existsSync9(workDir)) {
2870
+ console.log(chalk20.red(`Work directory does not exist: ${workDir}`));
2459
2871
  return;
2460
2872
  }
2461
2873
  if (!isGitInstalled()) {
2462
- console.log(chalk19.red("Git is not installed. Please install Git to use command."));
2874
+ console.log(chalk20.red("Git is not installed. Please install Git to use command."));
2463
2875
  return;
2464
2876
  }
2465
2877
  if (!isMapRepo(workDir)) {
@@ -2493,14 +2905,14 @@ async function syncCommand() {
2493
2905
  } catch (error) {
2494
2906
  spinner.fail("Sync failed");
2495
2907
  if (error.message) {
2496
- console.log(chalk19.red(error.message));
2908
+ console.log(chalk20.red(error.message));
2497
2909
  }
2498
2910
  }
2499
2911
  }
2500
2912
 
2501
2913
  // src/commands/timer.ts
2502
2914
  import ora14 from "ora";
2503
- import chalk20 from "chalk";
2915
+ import chalk21 from "chalk";
2504
2916
  var DEFAULT_TIMES = {
2505
2917
  Easy: 20,
2506
2918
  Medium: 40,
@@ -2529,10 +2941,10 @@ async function timerCommand(idOrSlug, options) {
2529
2941
  return;
2530
2942
  }
2531
2943
  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"));
2944
+ console.log(chalk21.yellow("Please provide a problem ID to start the timer."));
2945
+ console.log(chalk21.gray("Usage: leetcode timer <id>"));
2946
+ console.log(chalk21.gray(" leetcode timer --stats"));
2947
+ console.log(chalk21.gray(" leetcode timer --stop"));
2536
2948
  return;
2537
2949
  }
2538
2950
  const { authorized } = await requireAuth();
@@ -2541,11 +2953,11 @@ async function timerCommand(idOrSlug, options) {
2541
2953
  if (activeTimer) {
2542
2954
  const startedAt = new Date(activeTimer.startedAt);
2543
2955
  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)}`));
2956
+ console.log(chalk21.yellow("\u26A0\uFE0F You have an active timer running:"));
2957
+ console.log(chalk21.white(` Problem: ${activeTimer.title}`));
2958
+ console.log(chalk21.white(` Elapsed: ${formatDuration(elapsed)}`));
2547
2959
  console.log();
2548
- console.log(chalk20.gray("Use `leetcode timer --stop` to stop it first."));
2960
+ console.log(chalk21.gray("Use `leetcode timer --stop` to stop it first."));
2549
2961
  return;
2550
2962
  }
2551
2963
  const spinner = ora14("Fetching problem...").start();
@@ -2569,65 +2981,65 @@ async function timerCommand(idOrSlug, options) {
2569
2981
  durationMinutes
2570
2982
  );
2571
2983
  console.log();
2572
- console.log(chalk20.bold.cyan("\u23F1\uFE0F Interview Mode Started!"));
2573
- console.log(chalk20.gray("\u2500".repeat(50)));
2984
+ console.log(chalk21.bold.cyan("\u23F1\uFE0F Interview Mode Started!"));
2985
+ console.log(chalk21.gray("\u2500".repeat(50)));
2574
2986
  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")}`));
2987
+ console.log(chalk21.white(`Problem: ${problem.questionFrontendId}. ${problem.title}`));
2988
+ console.log(chalk21.white(`Difficulty: ${chalk21.bold(problem.difficulty)}`));
2989
+ console.log(chalk21.white(`Time Limit: ${chalk21.bold.yellow(durationMinutes + " minutes")}`));
2578
2990
  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."));
2991
+ console.log(chalk21.gray("\u2500".repeat(50)));
2992
+ console.log(chalk21.green("\u2713 Timer is running in background"));
2993
+ console.log(chalk21.gray(" When you submit successfully, your time will be recorded."));
2994
+ console.log(chalk21.gray(" Use `leetcode timer --stop` to cancel."));
2583
2995
  console.log();
2584
2996
  await pickCommand(idOrSlug, { open: true });
2585
2997
  } catch (error) {
2586
2998
  spinner.fail("Failed to start timer");
2587
2999
  if (error instanceof Error) {
2588
- console.log(chalk20.red(error.message));
3000
+ console.log(chalk21.red(error.message));
2589
3001
  }
2590
3002
  }
2591
3003
  }
2592
3004
  async function stopActiveTimer() {
2593
3005
  const result = timerStorage.stopTimer();
2594
3006
  if (!result) {
2595
- console.log(chalk20.yellow("No active timer to stop."));
3007
+ console.log(chalk21.yellow("No active timer to stop."));
2596
3008
  return;
2597
3009
  }
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)"));
3010
+ console.log(chalk21.green("\u23F1\uFE0F Timer stopped."));
3011
+ console.log(chalk21.gray(`Elapsed time: ${formatDuration(result.durationSeconds)}`));
3012
+ console.log(chalk21.gray("(Time not recorded since problem was not submitted)"));
2601
3013
  }
2602
3014
  async function showTimerStats(problemId) {
2603
3015
  if (problemId && /^\d+$/.test(problemId)) {
2604
3016
  const times = timerStorage.getSolveTimes(problemId);
2605
3017
  if (times.length === 0) {
2606
- console.log(chalk20.yellow(`No solve times recorded for problem ${problemId}`));
3018
+ console.log(chalk21.yellow(`No solve times recorded for problem ${problemId}`));
2607
3019
  return;
2608
3020
  }
2609
3021
  console.log();
2610
- console.log(chalk20.bold(`\u23F1\uFE0F Solve Times for Problem ${problemId}`));
2611
- console.log(chalk20.gray("\u2500".repeat(40)));
3022
+ console.log(chalk21.bold(`\u23F1\uFE0F Solve Times for Problem ${problemId}`));
3023
+ console.log(chalk21.gray("\u2500".repeat(40)));
2612
3024
  for (const entry of times) {
2613
3025
  const date = new Date(entry.solvedAt).toLocaleDateString();
2614
3026
  const duration = formatDuration(entry.durationSeconds);
2615
3027
  const limit = entry.timerMinutes;
2616
3028
  const withinLimit = entry.durationSeconds <= limit * 60;
2617
3029
  console.log(
2618
- ` ${date} ${withinLimit ? chalk20.green(duration) : chalk20.red(duration)} (limit: ${limit}m)`
3030
+ ` ${date} ${withinLimit ? chalk21.green(duration) : chalk21.red(duration)} (limit: ${limit}m)`
2619
3031
  );
2620
3032
  }
2621
3033
  } else {
2622
3034
  const stats = timerStorage.getStats();
2623
3035
  const allTimes = timerStorage.getAllSolveTimes();
2624
3036
  console.log();
2625
- console.log(chalk20.bold("\u23F1\uFE0F Timer Statistics"));
2626
- console.log(chalk20.gray("\u2500".repeat(40)));
3037
+ console.log(chalk21.bold("\u23F1\uFE0F Timer Statistics"));
3038
+ console.log(chalk21.gray("\u2500".repeat(40)));
2627
3039
  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))}`);
3040
+ console.log(` Problems timed: ${chalk21.cyan(stats.totalProblems)}`);
3041
+ console.log(` Total time: ${chalk21.cyan(formatDuration(stats.totalTime))}`);
3042
+ console.log(` Average time: ${chalk21.cyan(formatDuration(stats.avgTime))}`);
2631
3043
  console.log();
2632
3044
  const recentSolves = [];
2633
3045
  for (const [id, times] of Object.entries(allTimes)) {
@@ -2642,11 +3054,11 @@ async function showTimerStats(problemId) {
2642
3054
  }
2643
3055
  if (recentSolves.length > 0) {
2644
3056
  recentSolves.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
2645
- console.log(chalk20.bold(" Recent Solves:"));
3057
+ console.log(chalk21.bold(" Recent Solves:"));
2646
3058
  for (const solve of recentSolves.slice(0, 5)) {
2647
3059
  const date = new Date(solve.date).toLocaleDateString();
2648
3060
  console.log(
2649
- chalk20.gray(` ${date} `) + chalk20.white(`${solve.problemId}. ${solve.title.substring(0, 25)}`) + chalk20.gray(" ") + chalk20.cyan(formatDuration(solve.duration))
3061
+ chalk21.gray(` ${date} `) + chalk21.white(`${solve.problemId}. ${solve.title.substring(0, 25)}`) + chalk21.gray(" ") + chalk21.cyan(formatDuration(solve.duration))
2650
3062
  );
2651
3063
  }
2652
3064
  }
@@ -2654,162 +3066,1048 @@ async function showTimerStats(problemId) {
2654
3066
  }
2655
3067
  }
2656
3068
 
3069
+ // src/commands/collab.ts
3070
+ import chalk22 from "chalk";
3071
+ import ora15 from "ora";
3072
+ import { readFile as readFile4 } from "fs/promises";
3073
+
3074
+ // src/services/supabase.ts
3075
+ import { createClient } from "@supabase/supabase-js";
3076
+ var SUPABASE_URL = "https://abagrmwdpvnfyuqizyym.supabase.co";
3077
+ var SUPABASE_ANON_KEY = "sb_publishable_indrKu8VJmASdyLp7w8Hog_OyqT17cV";
3078
+ var supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
3079
+
3080
+ // src/storage/collab.ts
3081
+ import { existsSync as existsSync10, readFileSync as readFileSync3, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3 } from "fs";
3082
+ import { dirname as dirname2 } from "path";
3083
+ function getCollabPath() {
3084
+ return workspaceStorage.getCollabPath();
3085
+ }
3086
+ function loadCollab() {
3087
+ const path = getCollabPath();
3088
+ if (existsSync10(path)) {
3089
+ return JSON.parse(readFileSync3(path, "utf-8"));
3090
+ }
3091
+ return { session: null };
3092
+ }
3093
+ function saveCollab(data) {
3094
+ const collabPath = getCollabPath();
3095
+ const dir = dirname2(collabPath);
3096
+ if (!existsSync10(dir)) {
3097
+ mkdirSync3(dir, { recursive: true });
3098
+ }
3099
+ writeFileSync4(collabPath, JSON.stringify(data, null, 2));
3100
+ }
3101
+ var collabStorage = {
3102
+ getSession() {
3103
+ return loadCollab().session;
3104
+ },
3105
+ setSession(session) {
3106
+ saveCollab({ session });
3107
+ },
3108
+ getPath() {
3109
+ return getCollabPath();
3110
+ }
3111
+ };
3112
+
3113
+ // src/services/collab.ts
3114
+ function generateRoomCode() {
3115
+ const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
3116
+ let code = "";
3117
+ for (let i = 0; i < 6; i++) {
3118
+ code += chars.charAt(Math.floor(Math.random() * chars.length));
3119
+ }
3120
+ return code;
3121
+ }
3122
+ var collabService = {
3123
+ getSession() {
3124
+ return collabStorage.getSession();
3125
+ },
3126
+ async createRoom(problemId, username) {
3127
+ const roomCode = generateRoomCode();
3128
+ const { error } = await supabase.from("collab_rooms").insert({
3129
+ room_code: roomCode,
3130
+ problem_id: problemId,
3131
+ host_username: username,
3132
+ host_code: "",
3133
+ guest_username: null,
3134
+ guest_code: null
3135
+ });
3136
+ if (error) {
3137
+ return { error: error.message };
3138
+ }
3139
+ collabStorage.setSession({
3140
+ roomCode,
3141
+ problemId,
3142
+ isHost: true,
3143
+ username
3144
+ });
3145
+ return { roomCode };
3146
+ },
3147
+ async joinRoom(roomCode, username) {
3148
+ const { data: room, error: fetchError } = await supabase.from("collab_rooms").select("*").eq("room_code", roomCode.toUpperCase()).single();
3149
+ if (fetchError || !room) {
3150
+ return { error: "Room not found" };
3151
+ }
3152
+ const { error: updateError } = await supabase.from("collab_rooms").update({ guest_username: username }).eq("room_code", roomCode.toUpperCase());
3153
+ if (updateError) {
3154
+ return { error: updateError.message };
3155
+ }
3156
+ collabStorage.setSession({
3157
+ roomCode: roomCode.toUpperCase(),
3158
+ problemId: room.problem_id,
3159
+ isHost: false,
3160
+ username
3161
+ });
3162
+ return { problemId: room.problem_id };
3163
+ },
3164
+ async syncCode(code) {
3165
+ const session = collabStorage.getSession();
3166
+ if (!session) {
3167
+ return { success: false, error: "No active session" };
3168
+ }
3169
+ const column = session.isHost ? "host_code" : "guest_code";
3170
+ const { error } = await supabase.from("collab_rooms").update({ [column]: code }).eq("room_code", session.roomCode);
3171
+ if (error) {
3172
+ return { success: false, error: error.message };
3173
+ }
3174
+ return { success: true };
3175
+ },
3176
+ async getPartnerCode() {
3177
+ const session = collabStorage.getSession();
3178
+ if (!session) {
3179
+ return { error: "No active session" };
3180
+ }
3181
+ const { data: room, error } = await supabase.from("collab_rooms").select("*").eq("room_code", session.roomCode).single();
3182
+ if (error || !room) {
3183
+ return { error: "Room not found" };
3184
+ }
3185
+ if (session.isHost) {
3186
+ return {
3187
+ code: room.guest_code || "",
3188
+ username: room.guest_username || "Partner"
3189
+ };
3190
+ } else {
3191
+ return {
3192
+ code: room.host_code || "",
3193
+ username: room.host_username || "Host"
3194
+ };
3195
+ }
3196
+ },
3197
+ async getRoomStatus() {
3198
+ const session = collabStorage.getSession();
3199
+ if (!session) {
3200
+ return { error: "No active session" };
3201
+ }
3202
+ const { data: room, error } = await supabase.from("collab_rooms").select("*").eq("room_code", session.roomCode).single();
3203
+ if (error || !room) {
3204
+ return { error: "Room not found" };
3205
+ }
3206
+ return {
3207
+ host: room.host_username,
3208
+ guest: room.guest_username,
3209
+ hasHostCode: !!room.host_code,
3210
+ hasGuestCode: !!room.guest_code
3211
+ };
3212
+ },
3213
+ async leaveRoom() {
3214
+ const session = collabStorage.getSession();
3215
+ if (session) {
3216
+ if (session.isHost) {
3217
+ await supabase.from("collab_rooms").delete().eq("room_code", session.roomCode);
3218
+ }
3219
+ }
3220
+ collabStorage.setSession(null);
3221
+ }
3222
+ };
3223
+
3224
+ // src/commands/collab.ts
3225
+ async function collabHostCommand(problemId) {
3226
+ const { authorized, username } = await requireAuth();
3227
+ if (!authorized || !username) return;
3228
+ const spinner = ora15("Creating collaboration room...").start();
3229
+ try {
3230
+ const result = await collabService.createRoom(problemId, username);
3231
+ if ("error" in result) {
3232
+ spinner.fail(result.error);
3233
+ return;
3234
+ }
3235
+ spinner.succeed("Room created!");
3236
+ console.log();
3237
+ console.log(chalk22.bold.cyan("\u{1F465} Collaborative Coding Session"));
3238
+ console.log(chalk22.gray("\u2500".repeat(50)));
3239
+ console.log();
3240
+ console.log(chalk22.white(`Room Code: ${chalk22.bold.green(result.roomCode)}`));
3241
+ console.log(chalk22.white(`Problem: ${problemId}`));
3242
+ console.log();
3243
+ console.log(chalk22.gray("Share this code with your partner:"));
3244
+ console.log(chalk22.yellow(` leetcode collab join ${result.roomCode}`));
3245
+ console.log();
3246
+ console.log(chalk22.gray("\u2500".repeat(50)));
3247
+ console.log(chalk22.gray("After solving, sync and compare:"));
3248
+ console.log(chalk22.gray(" leetcode collab sync - Upload your solution"));
3249
+ console.log(chalk22.gray(" leetcode collab compare - See both solutions"));
3250
+ console.log(chalk22.gray(" leetcode collab status - Check room status"));
3251
+ console.log(chalk22.gray(" leetcode collab leave - End session"));
3252
+ console.log();
3253
+ await pickCommand(problemId, { open: true });
3254
+ } catch (error) {
3255
+ spinner.fail("Failed to create room");
3256
+ if (error instanceof Error) {
3257
+ console.log(chalk22.red(error.message));
3258
+ }
3259
+ }
3260
+ }
3261
+ async function collabJoinCommand(roomCode) {
3262
+ const { authorized, username } = await requireAuth();
3263
+ if (!authorized || !username) return;
3264
+ const spinner = ora15(`Joining room ${roomCode}...`).start();
3265
+ try {
3266
+ const result = await collabService.joinRoom(roomCode.toUpperCase(), username);
3267
+ if ("error" in result) {
3268
+ spinner.fail(result.error);
3269
+ return;
3270
+ }
3271
+ spinner.succeed("Joined room!");
3272
+ console.log();
3273
+ console.log(chalk22.bold.cyan("\u{1F465} Collaborative Coding Session"));
3274
+ console.log(chalk22.gray("\u2500".repeat(50)));
3275
+ console.log();
3276
+ console.log(chalk22.white(`Room Code: ${chalk22.bold.green(roomCode.toUpperCase())}`));
3277
+ console.log(chalk22.white(`Problem: ${result.problemId}`));
3278
+ console.log();
3279
+ console.log(chalk22.gray("\u2500".repeat(50)));
3280
+ console.log(chalk22.gray("After solving, sync and compare:"));
3281
+ console.log(chalk22.gray(" leetcode collab sync - Upload your solution"));
3282
+ console.log(chalk22.gray(" leetcode collab compare - See both solutions"));
3283
+ console.log(chalk22.gray(" leetcode collab status - Check room status"));
3284
+ console.log(chalk22.gray(" leetcode collab leave - End session"));
3285
+ console.log();
3286
+ await pickCommand(result.problemId, { open: true });
3287
+ } catch (error) {
3288
+ spinner.fail("Failed to join room");
3289
+ if (error instanceof Error) {
3290
+ console.log(chalk22.red(error.message));
3291
+ }
3292
+ }
3293
+ }
3294
+ async function collabSyncCommand() {
3295
+ const session = collabService.getSession();
3296
+ if (!session) {
3297
+ console.log(chalk22.yellow("No active collaboration session."));
3298
+ console.log(chalk22.gray("Use `leetcode collab host <id>` or `leetcode collab join <code>` first."));
3299
+ return;
3300
+ }
3301
+ const spinner = ora15("Syncing your code...").start();
3302
+ const workDir = config.getWorkDir();
3303
+ const filePath = await findSolutionFile(workDir, session.problemId);
3304
+ if (!filePath) {
3305
+ spinner.fail(`No solution file found for problem ${session.problemId}`);
3306
+ return;
3307
+ }
3308
+ const code = await readFile4(filePath, "utf-8");
3309
+ const result = await collabService.syncCode(code);
3310
+ if (result.success) {
3311
+ spinner.succeed("Code synced successfully!");
3312
+ console.log(chalk22.gray(`Uploaded ${code.split("\n").length} lines from ${filePath}`));
3313
+ } else {
3314
+ spinner.fail(result.error || "Sync failed");
3315
+ }
3316
+ }
3317
+ async function collabCompareCommand() {
3318
+ const session = collabService.getSession();
3319
+ if (!session) {
3320
+ console.log(chalk22.yellow("No active collaboration session."));
3321
+ console.log(chalk22.gray("Use `leetcode collab host <id>` or `leetcode collab join <code>` first."));
3322
+ return;
3323
+ }
3324
+ const spinner = ora15("Fetching solutions...").start();
3325
+ const workDir = config.getWorkDir();
3326
+ const filePath = await findSolutionFile(workDir, session.problemId);
3327
+ if (!filePath) {
3328
+ spinner.fail(`No solution file found for problem ${session.problemId}`);
3329
+ return;
3330
+ }
3331
+ const myCode = await readFile4(filePath, "utf-8");
3332
+ const partnerResult = await collabService.getPartnerCode();
3333
+ if ("error" in partnerResult) {
3334
+ spinner.fail(partnerResult.error);
3335
+ return;
3336
+ }
3337
+ spinner.stop();
3338
+ if (!partnerResult.code) {
3339
+ console.log(chalk22.yellow("Partner has not synced their code yet."));
3340
+ console.log(chalk22.gray("Ask them to run `leetcode collab sync`."));
3341
+ return;
3342
+ }
3343
+ console.log();
3344
+ console.log(chalk22.bold.cyan("\u{1F4CA} Solution Comparison"));
3345
+ console.log(chalk22.gray("\u2500".repeat(60)));
3346
+ console.log();
3347
+ console.log(chalk22.bold.green(`\u25B8 Your Solution (${session.username})`));
3348
+ console.log(chalk22.gray("\u2500".repeat(60)));
3349
+ const myLines = myCode.split("\n");
3350
+ for (let i = 0; i < myLines.length; i++) {
3351
+ const lineNum = String(i + 1).padStart(3, " ");
3352
+ console.log(`${chalk22.gray(lineNum)} ${myLines[i]}`);
3353
+ }
3354
+ console.log();
3355
+ console.log(chalk22.bold.blue(`\u25B8 ${partnerResult.username}'s Solution`));
3356
+ console.log(chalk22.gray("\u2500".repeat(60)));
3357
+ const partnerLines = partnerResult.code.split("\n");
3358
+ for (let i = 0; i < partnerLines.length; i++) {
3359
+ const lineNum = String(i + 1).padStart(3, " ");
3360
+ console.log(`${chalk22.gray(lineNum)} ${partnerLines[i]}`);
3361
+ }
3362
+ console.log();
3363
+ console.log(chalk22.gray("\u2500".repeat(60)));
3364
+ console.log(chalk22.gray(`Your code: ${myLines.length} lines | Partner: ${partnerLines.length} lines`));
3365
+ console.log();
3366
+ }
3367
+ async function collabLeaveCommand() {
3368
+ const session = collabService.getSession();
3369
+ if (!session) {
3370
+ console.log(chalk22.yellow("No active collaboration session."));
3371
+ return;
3372
+ }
3373
+ await collabService.leaveRoom();
3374
+ console.log(chalk22.green("\u2713 Left the collaboration session."));
3375
+ }
3376
+ async function collabStatusCommand() {
3377
+ const session = collabService.getSession();
3378
+ if (!session) {
3379
+ console.log(chalk22.yellow("No active collaboration session."));
3380
+ console.log(chalk22.gray("Use `leetcode collab host <id>` or `leetcode collab join <code>` to start."));
3381
+ return;
3382
+ }
3383
+ const status = await collabService.getRoomStatus();
3384
+ if ("error" in status) {
3385
+ console.log(chalk22.red(status.error));
3386
+ return;
3387
+ }
3388
+ console.log();
3389
+ console.log(chalk22.bold.cyan("\u{1F465} Collaboration Status"));
3390
+ console.log(chalk22.gray("\u2500".repeat(40)));
3391
+ console.log(` Room: ${chalk22.green(session.roomCode)}`);
3392
+ console.log(` Problem: ${session.problemId}`);
3393
+ console.log(` Role: ${session.isHost ? "Host" : "Guest"}`);
3394
+ console.log();
3395
+ console.log(chalk22.bold(" Participants:"));
3396
+ console.log(` Host: ${status.host} ${status.hasHostCode ? chalk22.green("\u2713 synced") : chalk22.gray("pending")}`);
3397
+ console.log(` Guest: ${status.guest || chalk22.gray("(waiting...)")} ${status.hasGuestCode ? chalk22.green("\u2713 synced") : chalk22.gray("pending")}`);
3398
+ console.log();
3399
+ }
3400
+
3401
+ // src/commands/snapshot.ts
3402
+ import chalk23 from "chalk";
3403
+
3404
+ // src/storage/snapshots.ts
3405
+ import { existsSync as existsSync11, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync5, unlinkSync } from "fs";
3406
+ import { join as join8 } from "path";
3407
+ function getSnapshotsDir() {
3408
+ return workspaceStorage.getSnapshotsDir();
3409
+ }
3410
+ function getSnapshotDir(problemId) {
3411
+ return join8(getSnapshotsDir(), problemId);
3412
+ }
3413
+ function getMetaPath(problemId) {
3414
+ return join8(getSnapshotDir(problemId), "meta.json");
3415
+ }
3416
+ function getFilesDir(problemId) {
3417
+ return join8(getSnapshotDir(problemId), "files");
3418
+ }
3419
+ function ensureSnapshotDir(problemId) {
3420
+ const dir = getFilesDir(problemId);
3421
+ if (!existsSync11(dir)) {
3422
+ mkdirSync4(dir, { recursive: true });
3423
+ }
3424
+ }
3425
+ function loadMeta(problemId) {
3426
+ const metaPath = getMetaPath(problemId);
3427
+ if (existsSync11(metaPath)) {
3428
+ return JSON.parse(readFileSync4(metaPath, "utf-8"));
3429
+ }
3430
+ return {
3431
+ problemId,
3432
+ problemTitle: "",
3433
+ snapshots: []
3434
+ };
3435
+ }
3436
+ function saveMeta(problemId, meta) {
3437
+ ensureSnapshotDir(problemId);
3438
+ writeFileSync5(getMetaPath(problemId), JSON.stringify(meta, null, 2));
3439
+ }
3440
+ var snapshotStorage = {
3441
+ /**
3442
+ * Save a snapshot of the solution
3443
+ */
3444
+ save(problemId, problemTitle, code, language, name) {
3445
+ ensureSnapshotDir(problemId);
3446
+ const meta = loadMeta(problemId);
3447
+ if (problemTitle) {
3448
+ meta.problemTitle = problemTitle;
3449
+ }
3450
+ const nextId = meta.snapshots.length > 0 ? Math.max(...meta.snapshots.map((s) => s.id)) + 1 : 1;
3451
+ const snapshotName = name || `snapshot-${nextId}`;
3452
+ const existing = meta.snapshots.find((s) => s.name === snapshotName);
3453
+ if (existing) {
3454
+ return { error: `Snapshot with name "${snapshotName}" already exists (ID: ${existing.id})` };
3455
+ }
3456
+ const ext = LANGUAGE_EXTENSIONS[language] || language;
3457
+ const fileName = `${nextId}_${snapshotName}.${ext}`;
3458
+ const filePath = join8(getFilesDir(problemId), fileName);
3459
+ writeFileSync5(filePath, code, "utf-8");
3460
+ const snapshot = {
3461
+ id: nextId,
3462
+ name: snapshotName,
3463
+ fileName,
3464
+ language,
3465
+ lines: code.split("\n").length,
3466
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
3467
+ };
3468
+ meta.snapshots.push(snapshot);
3469
+ saveMeta(problemId, meta);
3470
+ return snapshot;
3471
+ },
3472
+ /**
3473
+ * Get all snapshots for a problem
3474
+ */
3475
+ list(problemId) {
3476
+ const meta = loadMeta(problemId);
3477
+ return meta.snapshots;
3478
+ },
3479
+ /**
3480
+ * Get snapshot metadata
3481
+ */
3482
+ getMeta(problemId) {
3483
+ return loadMeta(problemId);
3484
+ },
3485
+ /**
3486
+ * Get a specific snapshot by ID or name
3487
+ */
3488
+ get(problemId, idOrName) {
3489
+ const meta = loadMeta(problemId);
3490
+ const byId = meta.snapshots.find((s) => s.id === parseInt(idOrName, 10));
3491
+ if (byId) return byId;
3492
+ const byName = meta.snapshots.find((s) => s.name === idOrName);
3493
+ return byName || null;
3494
+ },
3495
+ /**
3496
+ * Get snapshot code content
3497
+ */
3498
+ getCode(problemId, snapshot) {
3499
+ const filePath = join8(getFilesDir(problemId), snapshot.fileName);
3500
+ if (!existsSync11(filePath)) {
3501
+ throw new Error(`Snapshot file not found: ${snapshot.fileName}`);
3502
+ }
3503
+ return readFileSync4(filePath, "utf-8");
3504
+ },
3505
+ /**
3506
+ * Delete a snapshot
3507
+ */
3508
+ delete(problemId, idOrName) {
3509
+ const meta = loadMeta(problemId);
3510
+ const snapshot = this.get(problemId, idOrName);
3511
+ if (!snapshot) {
3512
+ return false;
3513
+ }
3514
+ const filePath = join8(getFilesDir(problemId), snapshot.fileName);
3515
+ if (existsSync11(filePath)) {
3516
+ unlinkSync(filePath);
3517
+ }
3518
+ meta.snapshots = meta.snapshots.filter((s) => s.id !== snapshot.id);
3519
+ saveMeta(problemId, meta);
3520
+ return true;
3521
+ },
3522
+ /**
3523
+ * Check if snapshots exist for a problem
3524
+ */
3525
+ hasSnapshots(problemId) {
3526
+ return this.list(problemId).length > 0;
3527
+ }
3528
+ };
3529
+
3530
+ // src/commands/snapshot.ts
3531
+ import { readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
3532
+ import { extname, basename as basename3 } from "path";
3533
+ import { diffLines } from "diff";
3534
+ function formatTimeAgo(dateStr) {
3535
+ const date = new Date(dateStr);
3536
+ const now = /* @__PURE__ */ new Date();
3537
+ const diffMs = now.getTime() - date.getTime();
3538
+ const diffMins = Math.floor(diffMs / 6e4);
3539
+ const diffHours = Math.floor(diffMins / 60);
3540
+ const diffDays = Math.floor(diffHours / 24);
3541
+ if (diffMins < 1) return "just now";
3542
+ if (diffMins < 60) return `${diffMins}m ago`;
3543
+ if (diffHours < 24) return `${diffHours}h ago`;
3544
+ return `${diffDays}d ago`;
3545
+ }
3546
+ async function snapshotSaveCommand(problemId, name) {
3547
+ const workDir = config.getWorkDir();
3548
+ try {
3549
+ const filePath = await findSolutionFile(workDir, problemId);
3550
+ if (!filePath) {
3551
+ console.log(chalk23.red(`No solution file found for problem ${problemId}`));
3552
+ console.log(chalk23.gray("Run `leetcode pick " + problemId + "` first to create a solution file."));
3553
+ return;
3554
+ }
3555
+ const code = await readFile5(filePath, "utf-8");
3556
+ const ext = extname(filePath).slice(1);
3557
+ const lang = getLangSlugFromExtension(ext) || ext;
3558
+ const fileName = basename3(filePath);
3559
+ const titleMatch = fileName.match(/^\d+\.(.+)\.\w+$/);
3560
+ const title = titleMatch ? titleMatch[1] : "";
3561
+ const result = snapshotStorage.save(problemId, title, code, lang, name);
3562
+ if ("error" in result) {
3563
+ console.log(chalk23.red("\u2717 " + result.error));
3564
+ return;
3565
+ }
3566
+ const snapshot = result;
3567
+ console.log(chalk23.green("\u2713 Snapshot saved!"));
3568
+ console.log();
3569
+ console.log(` ID: ${chalk23.cyan(snapshot.id)}`);
3570
+ console.log(` Name: ${chalk23.white(snapshot.name)}`);
3571
+ console.log(` Lines: ${chalk23.gray(snapshot.lines)}`);
3572
+ console.log(` File: ${chalk23.gray(filePath)}`);
3573
+ } catch (error) {
3574
+ console.log(chalk23.red("Failed to save snapshot"));
3575
+ if (error instanceof Error) {
3576
+ console.log(chalk23.gray(error.message));
3577
+ }
3578
+ }
3579
+ }
3580
+ async function snapshotListCommand(problemId) {
3581
+ const snapshots = snapshotStorage.list(problemId);
3582
+ if (snapshots.length === 0) {
3583
+ console.log(chalk23.yellow(`No snapshots found for problem ${problemId}`));
3584
+ console.log(chalk23.gray("Use `leetcode snapshot save " + problemId + "` to create one."));
3585
+ return;
3586
+ }
3587
+ const meta = snapshotStorage.getMeta(problemId);
3588
+ console.log();
3589
+ console.log(chalk23.bold(`\u{1F4F8} Snapshots for Problem ${problemId}`));
3590
+ if (meta.problemTitle) {
3591
+ console.log(chalk23.gray(` ${meta.problemTitle}`));
3592
+ }
3593
+ console.log(chalk23.gray("\u2500".repeat(50)));
3594
+ console.log();
3595
+ for (const snap of snapshots) {
3596
+ const timeAgo = formatTimeAgo(snap.createdAt);
3597
+ console.log(
3598
+ ` ${chalk23.cyan(snap.id.toString().padStart(2))}. ${chalk23.white(snap.name.padEnd(25))} ${chalk23.gray(snap.lines + " lines")} ${chalk23.gray("\xB7")} ${chalk23.gray(timeAgo)}`
3599
+ );
3600
+ }
3601
+ console.log();
3602
+ console.log(chalk23.gray("Commands:"));
3603
+ console.log(chalk23.gray(` restore: leetcode snapshot restore ${problemId} <id|name>`));
3604
+ console.log(chalk23.gray(` diff: leetcode snapshot diff ${problemId} <id1> <id2>`));
3605
+ console.log(chalk23.gray(` delete: leetcode snapshot delete ${problemId} <id|name>`));
3606
+ }
3607
+ async function snapshotRestoreCommand(problemId, idOrName) {
3608
+ const workDir = config.getWorkDir();
3609
+ try {
3610
+ const snapshot = snapshotStorage.get(problemId, idOrName);
3611
+ if (!snapshot) {
3612
+ console.log(chalk23.red(`Snapshot "${idOrName}" not found for problem ${problemId}`));
3613
+ console.log(chalk23.gray("Run `leetcode snapshot list " + problemId + "` to see available snapshots."));
3614
+ return;
3615
+ }
3616
+ const filePath = await findSolutionFile(workDir, problemId);
3617
+ if (!filePath) {
3618
+ console.log(chalk23.red(`No solution file found for problem ${problemId}`));
3619
+ return;
3620
+ }
3621
+ const currentCode = await readFile5(filePath, "utf-8");
3622
+ const backupName = `backup-before-restore-${Date.now()}`;
3623
+ const ext = extname(filePath).slice(1);
3624
+ const lang = getLangSlugFromExtension(ext) || ext;
3625
+ snapshotStorage.save(problemId, "", currentCode, lang, backupName);
3626
+ const snapshotCode = snapshotStorage.getCode(problemId, snapshot);
3627
+ await writeFile4(filePath, snapshotCode, "utf-8");
3628
+ console.log(chalk23.green("\u2713 Snapshot restored!"));
3629
+ console.log();
3630
+ console.log(` Restored: ${chalk23.cyan(snapshot.name)} (${snapshot.lines} lines)`);
3631
+ console.log(` File: ${chalk23.gray(filePath)}`);
3632
+ console.log(` Backup: ${chalk23.gray(backupName)}`);
3633
+ } catch (error) {
3634
+ console.log(chalk23.red("Failed to restore snapshot"));
3635
+ if (error instanceof Error) {
3636
+ console.log(chalk23.gray(error.message));
3637
+ }
3638
+ }
3639
+ }
3640
+ async function snapshotDiffCommand(problemId, idOrName1, idOrName2) {
3641
+ try {
3642
+ const snap1 = snapshotStorage.get(problemId, idOrName1);
3643
+ const snap2 = snapshotStorage.get(problemId, idOrName2);
3644
+ if (!snap1) {
3645
+ console.log(chalk23.red(`Snapshot "${idOrName1}" not found`));
3646
+ return;
3647
+ }
3648
+ if (!snap2) {
3649
+ console.log(chalk23.red(`Snapshot "${idOrName2}" not found`));
3650
+ return;
3651
+ }
3652
+ const code1 = snapshotStorage.getCode(problemId, snap1);
3653
+ const code2 = snapshotStorage.getCode(problemId, snap2);
3654
+ console.log();
3655
+ console.log(chalk23.bold(`\u{1F4CA} Diff: ${snap1.name} \u2192 ${snap2.name}`));
3656
+ console.log(chalk23.gray("\u2500".repeat(50)));
3657
+ console.log();
3658
+ const diff = diffLines(code1, code2);
3659
+ let added = 0;
3660
+ let removed = 0;
3661
+ for (const part of diff) {
3662
+ const lines = part.value.split("\n").filter((l) => l !== "");
3663
+ if (part.added) {
3664
+ added += lines.length;
3665
+ for (const line of lines) {
3666
+ console.log(chalk23.green("+ " + line));
3667
+ }
3668
+ } else if (part.removed) {
3669
+ removed += lines.length;
3670
+ for (const line of lines) {
3671
+ console.log(chalk23.red("- " + line));
3672
+ }
3673
+ } else {
3674
+ if (lines.length <= 4) {
3675
+ for (const line of lines) {
3676
+ console.log(chalk23.gray(" " + line));
3677
+ }
3678
+ } else {
3679
+ console.log(chalk23.gray(" " + lines[0]));
3680
+ console.log(chalk23.gray(" " + lines[1]));
3681
+ console.log(chalk23.gray(` ... (${lines.length - 4} more lines)`));
3682
+ console.log(chalk23.gray(" " + lines[lines.length - 2]));
3683
+ console.log(chalk23.gray(" " + lines[lines.length - 1]));
3684
+ }
3685
+ }
3686
+ }
3687
+ console.log();
3688
+ console.log(chalk23.gray("\u2500".repeat(50)));
3689
+ console.log(
3690
+ `${chalk23.green("+" + added + " added")} ${chalk23.gray("\xB7")} ${chalk23.red("-" + removed + " removed")} ${chalk23.gray("\xB7")} ${chalk23.gray(snap1.lines + " \u2192 " + snap2.lines + " lines")}`
3691
+ );
3692
+ } catch (error) {
3693
+ console.log(chalk23.red("Failed to diff snapshots"));
3694
+ if (error instanceof Error) {
3695
+ console.log(chalk23.gray(error.message));
3696
+ }
3697
+ }
3698
+ }
3699
+ async function snapshotDeleteCommand(problemId, idOrName) {
3700
+ const snapshot = snapshotStorage.get(problemId, idOrName);
3701
+ if (!snapshot) {
3702
+ console.log(chalk23.red(`Snapshot "${idOrName}" not found for problem ${problemId}`));
3703
+ return;
3704
+ }
3705
+ const deleted = snapshotStorage.delete(problemId, idOrName);
3706
+ if (deleted) {
3707
+ console.log(chalk23.green(`\u2713 Deleted snapshot: ${snapshot.name}`));
3708
+ } else {
3709
+ console.log(chalk23.red("Failed to delete snapshot"));
3710
+ }
3711
+ }
3712
+
3713
+ // src/commands/diff.ts
3714
+ import ora16 from "ora";
3715
+ import chalk24 from "chalk";
3716
+ import { readFile as readFile6 } from "fs/promises";
3717
+ import { existsSync as existsSync12 } from "fs";
3718
+ import { diffLines as diffLines2 } from "diff";
3719
+ function showCodeBlock(code, label) {
3720
+ const lines = code.split("\n");
3721
+ const lineCount = lines.length;
3722
+ console.log();
3723
+ console.log(chalk24.bold.cyan(`=== ${label} (${lineCount} lines) ===`));
3724
+ console.log(chalk24.gray("\u2500".repeat(60)));
3725
+ lines.forEach((line, i) => {
3726
+ const lineNum = (i + 1).toString().padStart(3);
3727
+ console.log(chalk24.gray(lineNum + " \u2502 ") + line);
3728
+ });
3729
+ }
3730
+ function showUnifiedDiff(sourceCode, targetCode, sourceLabel, targetLabel) {
3731
+ const sourceLines = sourceCode.split("\n").length;
3732
+ const targetLines = targetCode.split("\n").length;
3733
+ console.log();
3734
+ console.log(chalk24.bold(`\u{1F4CA} Unified Diff: ${sourceLabel} \u2192 ${targetLabel}`));
3735
+ console.log(chalk24.gray("\u2500".repeat(60)));
3736
+ console.log();
3737
+ const diff = diffLines2(sourceCode, targetCode);
3738
+ let added = 0;
3739
+ let removed = 0;
3740
+ for (const part of diff) {
3741
+ const lines = part.value.split("\n").filter((l) => l !== "");
3742
+ if (part.added) {
3743
+ added += lines.length;
3744
+ for (const line of lines) {
3745
+ console.log(chalk24.green("+ " + line));
3746
+ }
3747
+ } else if (part.removed) {
3748
+ removed += lines.length;
3749
+ for (const line of lines) {
3750
+ console.log(chalk24.red("- " + line));
3751
+ }
3752
+ } else {
3753
+ if (lines.length <= 6) {
3754
+ for (const line of lines) {
3755
+ console.log(chalk24.gray(" " + line));
3756
+ }
3757
+ } else {
3758
+ console.log(chalk24.gray(" " + lines[0]));
3759
+ console.log(chalk24.gray(" " + lines[1]));
3760
+ console.log(chalk24.dim(` ... (${lines.length - 4} unchanged lines)`));
3761
+ console.log(chalk24.gray(" " + lines[lines.length - 2]));
3762
+ console.log(chalk24.gray(" " + lines[lines.length - 1]));
3763
+ }
3764
+ }
3765
+ }
3766
+ console.log();
3767
+ console.log(chalk24.gray("\u2500".repeat(60)));
3768
+ console.log(
3769
+ `${chalk24.green("+" + added + " added")} ${chalk24.gray("\xB7")} ${chalk24.red("-" + removed + " removed")} ${chalk24.gray("\xB7")} ${chalk24.gray(sourceLines + " \u2192 " + targetLines + " lines")}`
3770
+ );
3771
+ }
3772
+ function showComparison(sourceCode, targetCode, sourceLabel, targetLabel, unified) {
3773
+ if (unified) {
3774
+ showUnifiedDiff(sourceCode, targetCode, sourceLabel, targetLabel);
3775
+ } else {
3776
+ showCodeBlock(sourceCode, sourceLabel);
3777
+ showCodeBlock(targetCode, targetLabel);
3778
+ const diff = diffLines2(sourceCode, targetCode);
3779
+ let added = 0;
3780
+ let removed = 0;
3781
+ for (const part of diff) {
3782
+ const lines = part.value.split("\n").filter((l) => l !== "");
3783
+ if (part.added) added += lines.length;
3784
+ else if (part.removed) removed += lines.length;
3785
+ }
3786
+ console.log();
3787
+ console.log(chalk24.gray("\u2500".repeat(60)));
3788
+ console.log(
3789
+ `${chalk24.bold("Summary:")} ${chalk24.green("+" + added + " added")} ${chalk24.gray("\xB7")} ${chalk24.red("-" + removed + " removed")}`
3790
+ );
3791
+ console.log(chalk24.gray("Tip: Use --unified for line-by-line diff"));
3792
+ }
3793
+ }
3794
+ async function diffCommand(problemId, options) {
3795
+ const { authorized } = await requireAuth();
3796
+ if (!authorized) return;
3797
+ const workDir = config.getWorkDir();
3798
+ const spinner = ora16("Finding solution file...").start();
3799
+ try {
3800
+ const filePath = await findSolutionFile(workDir, problemId);
3801
+ if (!filePath) {
3802
+ spinner.fail(`No solution file found for problem ${problemId}`);
3803
+ console.log(chalk24.gray("Run `leetcode pick " + problemId + "` first to create a solution file."));
3804
+ return;
3805
+ }
3806
+ const currentCode = await readFile6(filePath, "utf-8");
3807
+ spinner.text = "Fetching comparison target...";
3808
+ if (options.file) {
3809
+ spinner.stop();
3810
+ if (!existsSync12(options.file)) {
3811
+ console.log(chalk24.red(`File not found: ${options.file}`));
3812
+ return;
3813
+ }
3814
+ const otherCode = await readFile6(options.file, "utf-8");
3815
+ showComparison(currentCode, otherCode, "Your Solution", options.file, options.unified ?? false);
3816
+ return;
3817
+ }
3818
+ const problem = await leetcodeClient.getProblemById(problemId);
3819
+ if (!problem) {
3820
+ spinner.fail(`Problem ${problemId} not found`);
3821
+ return;
3822
+ }
3823
+ if (options.submission) {
3824
+ const submissionId = parseInt(options.submission, 10);
3825
+ const submission = await leetcodeClient.getSubmissionDetails(submissionId);
3826
+ spinner.stop();
3827
+ showComparison(currentCode, submission.code, "Your Solution", `Submission #${submissionId}`, options.unified ?? false);
3828
+ return;
3829
+ }
3830
+ const submissions = await leetcodeClient.getSubmissionList(problem.titleSlug, 50);
3831
+ const accepted = submissions.find((s) => s.statusDisplay === "Accepted");
3832
+ if (!accepted) {
3833
+ spinner.fail("No accepted submissions found for this problem");
3834
+ console.log(chalk24.gray("Tip: Use --file to compare with a local file instead"));
3835
+ return;
3836
+ }
3837
+ const acceptedDetails = await leetcodeClient.getSubmissionDetails(parseInt(accepted.id, 10));
3838
+ spinner.stop();
3839
+ showComparison(currentCode, acceptedDetails.code, "Your Solution", "Last Accepted", options.unified ?? false);
3840
+ } catch (error) {
3841
+ spinner.fail("Failed to diff");
3842
+ if (error instanceof Error) {
3843
+ console.log(chalk24.red(error.message));
3844
+ }
3845
+ }
3846
+ }
3847
+
3848
+ // src/commands/workspace.ts
3849
+ import chalk25 from "chalk";
3850
+ import inquirer4 from "inquirer";
3851
+ import { homedir as homedir3 } from "os";
3852
+ import { join as join9 } from "path";
3853
+ async function workspaceCurrentCommand() {
3854
+ const active = workspaceStorage.getActive();
3855
+ const config2 = workspaceStorage.getConfig(active);
3856
+ console.log();
3857
+ console.log(chalk25.bold.cyan(`\u{1F4C1} Active Workspace: ${active}`));
3858
+ console.log(chalk25.gray("\u2500".repeat(40)));
3859
+ console.log(` workDir: ${chalk25.white(config2.workDir)}`);
3860
+ console.log(` lang: ${chalk25.white(config2.lang)}`);
3861
+ if (config2.editor) console.log(` editor: ${chalk25.white(config2.editor)}`);
3862
+ if (config2.syncRepo) console.log(` syncRepo: ${chalk25.white(config2.syncRepo)}`);
3863
+ console.log();
3864
+ }
3865
+ async function workspaceListCommand() {
3866
+ const workspaces = workspaceStorage.list();
3867
+ const active = workspaceStorage.getActive();
3868
+ console.log();
3869
+ console.log(chalk25.bold("Workspaces:"));
3870
+ console.log();
3871
+ for (const ws of workspaces) {
3872
+ const config2 = workspaceStorage.getConfig(ws);
3873
+ const marker = ws === active ? chalk25.green("\u25B8 ") : " ";
3874
+ const name = ws === active ? chalk25.green.bold(ws) : ws;
3875
+ console.log(`${marker}${name}`);
3876
+ console.log(` ${chalk25.gray(config2.workDir)}`);
3877
+ }
3878
+ console.log();
3879
+ }
3880
+ async function workspaceCreateCommand(name, options) {
3881
+ if (workspaceStorage.exists(name)) {
3882
+ console.log(chalk25.red(`Workspace "${name}" already exists`));
3883
+ return;
3884
+ }
3885
+ const workDir = options.workdir ?? join9(homedir3(), "leetcode", name);
3886
+ const config2 = {
3887
+ workDir,
3888
+ lang: "typescript"
3889
+ };
3890
+ const success = workspaceStorage.create(name, config2);
3891
+ if (success) {
3892
+ console.log(chalk25.green(`\u2713 Created workspace "${name}"`));
3893
+ console.log(` workDir: ${chalk25.gray(workDir)}`);
3894
+ console.log();
3895
+ console.log(chalk25.gray(`Switch to it: leetcode workspace use ${name}`));
3896
+ } else {
3897
+ console.log(chalk25.red("Failed to create workspace"));
3898
+ }
3899
+ }
3900
+ async function workspaceUseCommand(name) {
3901
+ if (!workspaceStorage.exists(name)) {
3902
+ console.log(chalk25.red(`Workspace "${name}" not found`));
3903
+ console.log(chalk25.gray("Use `leetcode workspace list` to see available workspaces"));
3904
+ return;
3905
+ }
3906
+ const success = workspaceStorage.setActive(name);
3907
+ if (success) {
3908
+ const config2 = workspaceStorage.getConfig(name);
3909
+ console.log(chalk25.green(`\u2713 Switched to workspace "${name}"`));
3910
+ console.log(` workDir: ${chalk25.gray(config2.workDir)}`);
3911
+ } else {
3912
+ console.log(chalk25.red("Failed to switch workspace"));
3913
+ }
3914
+ }
3915
+ async function workspaceDeleteCommand(name) {
3916
+ if (name === "default") {
3917
+ console.log(chalk25.red("Cannot delete the default workspace"));
3918
+ return;
3919
+ }
3920
+ if (!workspaceStorage.exists(name)) {
3921
+ console.log(chalk25.red(`Workspace "${name}" not found`));
3922
+ return;
3923
+ }
3924
+ const { confirmed } = await inquirer4.prompt([{
3925
+ type: "confirm",
3926
+ name: "confirmed",
3927
+ message: `Delete workspace "${name}"? (files in workDir will NOT be deleted)`,
3928
+ default: false
3929
+ }]);
3930
+ if (!confirmed) {
3931
+ console.log(chalk25.gray("Cancelled"));
3932
+ return;
3933
+ }
3934
+ const success = workspaceStorage.delete(name);
3935
+ if (success) {
3936
+ console.log(chalk25.green(`\u2713 Deleted workspace "${name}"`));
3937
+ } else {
3938
+ console.log(chalk25.red("Failed to delete workspace"));
3939
+ }
3940
+ }
3941
+
2657
3942
  // src/index.ts
2658
3943
  var program = new Command();
2659
3944
  program.configureHelp({
2660
3945
  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)
3946
+ subcommandTerm: (cmd) => {
3947
+ const name = cmd.name();
3948
+ const alias = cmd.alias();
3949
+ const term = alias ? `${name}|${alias}` : name;
3950
+ return chalk26.cyan(term.padEnd(16));
3951
+ },
3952
+ subcommandDescription: (cmd) => chalk26.white(cmd.description()),
3953
+ optionTerm: (option) => chalk26.yellow(option.flags),
3954
+ optionDescription: (option) => chalk26.white(option.description)
2665
3955
  });
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
3956
+ program.name("leetcode").usage("[command] [options]").description(chalk26.bold.cyan("\u{1F525} A modern LeetCode CLI built with TypeScript")).version("2.0.0", "-v, --version", "Output the version number").helpOption("-h, --help", "Display help for command").addHelpText("after", `
3957
+ ${chalk26.yellow("Examples:")}
3958
+ ${chalk26.cyan("$ leetcode login")} Login to LeetCode
3959
+ ${chalk26.cyan("$ leetcode list -d easy")} List easy problems
3960
+ ${chalk26.cyan("$ leetcode random -d medium")} Get random medium problem
3961
+ ${chalk26.cyan("$ leetcode pick 1")} Start solving "Two Sum"
3962
+ ${chalk26.cyan("$ leetcode test 1")} Test your solution
3963
+ ${chalk26.cyan("$ leetcode submit 1")} Submit your solution
2674
3964
  `);
2675
3965
  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
3966
+ ${chalk26.yellow("How to login:")}
3967
+ 1. Open ${chalk26.cyan("https://leetcode.com")} in your browser
2678
3968
  2. Login to your account
2679
3969
  3. Open Developer Tools (F12) \u2192 Application \u2192 Cookies
2680
- 4. Copy values of ${chalk21.green("LEETCODE_SESSION")} and ${chalk21.green("csrftoken")}
3970
+ 4. Copy values of ${chalk26.green("LEETCODE_SESSION")} and ${chalk26.green("csrftoken")}
2681
3971
  5. Paste when prompted by this command
2682
3972
  `).action(loginCommand);
2683
3973
  program.command("logout").description("Clear stored credentials").action(logoutCommand);
2684
3974
  program.command("whoami").description("Check current login status").action(whoamiCommand);
2685
3975
  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
3976
+ ${chalk26.yellow("Examples:")}
3977
+ ${chalk26.cyan("$ leetcode list")} List first 20 problems
3978
+ ${chalk26.cyan("$ leetcode list -d easy")} List easy problems only
3979
+ ${chalk26.cyan("$ leetcode list -s solved")} List your solved problems
3980
+ ${chalk26.cyan("$ leetcode list -t array -t string")} Filter by multiple tags
3981
+ ${chalk26.cyan('$ leetcode list -q "two sum"')} Search by keywords
3982
+ ${chalk26.cyan("$ leetcode list -n 50 -p 2")} Show 50 problems, page 2
2693
3983
  `).action(listCommand);
2694
3984
  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
3985
+ ${chalk26.yellow("Examples:")}
3986
+ ${chalk26.cyan("$ leetcode show 1")} Show by problem ID
3987
+ ${chalk26.cyan("$ leetcode show two-sum")} Show by problem slug
3988
+ ${chalk26.cyan("$ leetcode s 412")} Short alias
2699
3989
  `).action(showCommand);
2700
3990
  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
3991
+ ${chalk26.yellow("Examples:")}
3992
+ ${chalk26.cyan("$ leetcode daily")} Show today's challenge
3993
+ ${chalk26.cyan("$ leetcode d")} Short alias
2704
3994
  `).action(dailyCommand);
2705
3995
  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
3996
+ ${chalk26.yellow("Examples:")}
3997
+ ${chalk26.cyan("$ leetcode random")} Get any random problem
3998
+ ${chalk26.cyan("$ leetcode random -d medium")} Random medium problem
3999
+ ${chalk26.cyan("$ leetcode random -t array")} Random array problem
4000
+ ${chalk26.cyan("$ leetcode random --pick")} Random + create file
4001
+ ${chalk26.cyan("$ leetcode r -d easy --pick")} Random easy + file
2712
4002
  `).action(randomCommand);
2713
4003
  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/")}
4004
+ ${chalk26.yellow("Examples:")}
4005
+ ${chalk26.cyan("$ leetcode pick 1")} Pick by problem ID
4006
+ ${chalk26.cyan("$ leetcode pick two-sum")} Pick by problem slug
4007
+ ${chalk26.cyan("$ leetcode pick 1 -l python3")} Pick with specific language
4008
+ ${chalk26.cyan("$ leetcode pick 1 --no-open")} Create file without opening
4009
+ ${chalk26.cyan("$ leetcode p 412")} Short alias
4010
+
4011
+ ${chalk26.gray("Files are organized by: workDir/Difficulty/Category/")}
2722
4012
  `).action(async (id, options) => {
2723
4013
  await pickCommand(id, options);
2724
4014
  });
2725
4015
  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
4016
+ ${chalk26.yellow("Examples:")}
4017
+ ${chalk26.cyan("$ leetcode pick-batch 1 2 3")} Pick problems 1, 2, and 3
4018
+ ${chalk26.cyan("$ leetcode pick-batch 1 2 3 -l py")} Pick with Python
2729
4019
  `).action(batchPickCommand);
2730
- 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.")}
4020
+ program.command("test <file>").alias("t").description("Test solution against sample test cases").option("-c, --testcase <testcase>", "Custom test case").option("-V, --visualize", "Visual output for data structures (arrays, trees, etc.)").addHelpText("after", `
4021
+ ${chalk26.yellow("Examples:")}
4022
+ ${chalk26.cyan("$ leetcode test 1")} Test by problem ID
4023
+ ${chalk26.cyan("$ leetcode test two-sum")} Test by problem slug
4024
+ ${chalk26.cyan("$ leetcode test ./path/to/file.py")} Test by file path
4025
+ ${chalk26.cyan('$ leetcode test 1 -c "[1,2]\\n3"')} Test with custom case
4026
+ ${chalk26.cyan("$ leetcode test 1 --visualize")} Visual mode for debugging
4027
+ ${chalk26.cyan("$ leetcode t 412")} Short alias
4028
+
4029
+ ${chalk26.gray("Testcases use \\n to separate multiple inputs.")}
2739
4030
  `).action(testCommand);
2740
4031
  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
4032
+ ${chalk26.yellow("Examples:")}
4033
+ ${chalk26.cyan("$ leetcode submit 1")} Submit by problem ID
4034
+ ${chalk26.cyan("$ leetcode submit two-sum")} Submit by problem slug
4035
+ ${chalk26.cyan("$ leetcode submit ./path/to/file.py")} Submit by file path
4036
+ ${chalk26.cyan("$ leetcode x 412")} Short alias
2746
4037
  `).action(submitCommand);
4038
+ program.command("diff <id>").description("Compare solution with past submissions").option("-s, --submission <id>", "Compare with specific submission ID").option("-f, --file <path>", "Compare with a local file").option("-u, --unified", "Show unified diff (line-by-line changes)").addHelpText("after", `
4039
+ ${chalk26.yellow("Examples:")}
4040
+ ${chalk26.cyan("$ leetcode diff 1")} Compare with last accepted
4041
+ ${chalk26.cyan("$ leetcode diff 1 -u")} Show unified diff
4042
+ ${chalk26.cyan("$ leetcode diff 1 -s 12345")} Compare with specific submission
4043
+ ${chalk26.cyan("$ leetcode diff 1 -f other.py")} Compare with local file
4044
+ `).action(diffCommand);
2747
4045
  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
4046
+ ${chalk26.yellow("Examples:")}
4047
+ ${chalk26.cyan("$ leetcode submissions 1")} View submissions for problem
4048
+ ${chalk26.cyan("$ leetcode submissions 1 -n 5")} Show last 5 submissions
4049
+ ${chalk26.cyan("$ leetcode submissions 1 --last")} Show last accepted submission
4050
+ ${chalk26.cyan("$ leetcode submissions 1 --download")} Download last accepted code
2753
4051
  `).action(submissionsCommand);
2754
4052
  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
4053
+ ${chalk26.yellow("Options Explained:")}
4054
+ ${chalk26.cyan("-c, --calendar")} Shows a table of your weekly submissions and active days
2757
4055
  for the past 12 weeks. Useful for tracking consistency.
2758
4056
 
2759
- ${chalk21.cyan("-s, --skills")} Shows how many problems you solved per topic tag,
4057
+ ${chalk26.cyan("-s, --skills")} Shows how many problems you solved per topic tag,
2760
4058
  grouped by difficulty (Fundamental/Intermediate/Advanced).
2761
4059
  Helps identify your strong and weak areas.
2762
4060
 
2763
- ${chalk21.cyan("-t, --trend")} Shows a bar chart of daily submissions for the past week.
4061
+ ${chalk26.cyan("-t, --trend")} Shows a bar chart of daily submissions for the past week.
2764
4062
  Visualizes your recent coding activity day by day.
2765
4063
 
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
4064
+ ${chalk26.yellow("Examples:")}
4065
+ ${chalk26.cyan("$ leetcode stat")} Show basic stats (solved count, rank)
4066
+ ${chalk26.cyan("$ leetcode stat lee215")} Show another user's stats
4067
+ ${chalk26.cyan("$ leetcode stat -c")} Weekly activity table
4068
+ ${chalk26.cyan("$ leetcode stat -s")} Topic-wise breakdown
4069
+ ${chalk26.cyan("$ leetcode stat -t")} 7-day trend chart
2772
4070
  `).action((username, options) => statCommand(username, options));
2773
4071
  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
4072
+ ${chalk26.yellow("Examples:")}
4073
+ ${chalk26.cyan("$ leetcode today")} Show streak, solved, and daily challenge
2776
4074
  `).action(todayCommand);
2777
4075
  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
4076
+ ${chalk26.yellow("Actions:")}
4077
+ ${chalk26.cyan("add <id>")} Bookmark a problem
4078
+ ${chalk26.cyan("remove <id>")} Remove a bookmark
4079
+ ${chalk26.cyan("list")} List all bookmarks
4080
+ ${chalk26.cyan("clear")} Clear all bookmarks
4081
+
4082
+ ${chalk26.yellow("Examples:")}
4083
+ ${chalk26.cyan("$ leetcode bookmark add 1")} Bookmark problem 1
4084
+ ${chalk26.cyan("$ leetcode bookmark remove 1")} Remove bookmark
4085
+ ${chalk26.cyan("$ leetcode bookmark list")} List all bookmarks
2788
4086
  `).action(bookmarkCommand);
2789
4087
  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
4088
+ ${chalk26.yellow("Actions:")}
4089
+ ${chalk26.cyan("edit")} Open notes in editor (default)
4090
+ ${chalk26.cyan("view")} Display notes in terminal
4091
+
4092
+ ${chalk26.yellow("Examples:")}
4093
+ ${chalk26.cyan("$ leetcode note 1")} Edit notes for problem 1
4094
+ ${chalk26.cyan("$ leetcode note 1 edit")} Edit notes (explicit)
4095
+ ${chalk26.cyan("$ leetcode note 1 view")} View notes in terminal
2798
4096
  `).action(notesCommand);
2799
4097
  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
4098
+ ${chalk26.yellow("Examples:")}
4099
+ ${chalk26.cyan("$ leetcode sync")} Sync all solutions to remote
2802
4100
  `).action(syncCommand);
2803
4101
  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")}
4102
+ ${chalk26.yellow("Examples:")}
4103
+ ${chalk26.cyan("$ leetcode config")} View current config
4104
+ ${chalk26.cyan("$ leetcode config -l python3")} Set language to Python
4105
+ ${chalk26.cyan('$ leetcode config -e "code"')} Set editor to VS Code
4106
+ ${chalk26.cyan("$ leetcode config -w ~/leetcode")} Set solutions folder
4107
+ ${chalk26.cyan("$ leetcode config -r https://...")} Set git repository
4108
+ ${chalk26.cyan("$ leetcode config -i")} Interactive setup
4109
+
4110
+ ${chalk26.gray("Supported languages: typescript, javascript, python3, java, cpp, c, csharp, go, rust, kotlin, swift")}
2813
4111
  `).action(async (options) => {
2814
4112
  if (options.interactive) {
2815
4113
  await configInteractiveCommand();
@@ -2818,23 +4116,69 @@ ${chalk21.gray("Supported languages: typescript, javascript, python3, java, cpp,
2818
4116
  }
2819
4117
  });
2820
4118
  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:")}
4119
+ ${chalk26.yellow("How it works:")}
2822
4120
  Start a problem with a countdown timer to simulate interview conditions.
2823
4121
  Default time limits: Easy (20 min), Medium (40 min), Hard (60 min).
2824
4122
  Your solve times are recorded when you submit successfully.
2825
4123
 
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
4124
+ ${chalk26.yellow("Examples:")}
4125
+ ${chalk26.cyan("$ leetcode timer 1")} Start problem 1 with default time
4126
+ ${chalk26.cyan("$ leetcode timer 1 -m 30")} Start with 30 minute limit
4127
+ ${chalk26.cyan("$ leetcode timer --stats")} Show your solve time statistics
4128
+ ${chalk26.cyan("$ leetcode timer --stop")} Stop active timer
2831
4129
  `).action((id, options) => timerCommand(id, options));
4130
+ var workspaceCmd = program.command("workspace").description("Manage workspaces for different contexts");
4131
+ workspaceCmd.command("current").description("Show current workspace").action(workspaceCurrentCommand);
4132
+ workspaceCmd.command("list").description("List all workspaces").action(workspaceListCommand);
4133
+ workspaceCmd.command("create <name>").description("Create a new workspace").option("-w, --workdir <path>", "Set working directory for this workspace").action(workspaceCreateCommand);
4134
+ workspaceCmd.command("use <name>").description("Switch to a workspace").action(workspaceUseCommand);
4135
+ workspaceCmd.command("delete <name>").description("Delete a workspace").action(workspaceDeleteCommand);
4136
+ var collabCmd = program.command("collab").description("Collaborative coding with a partner").addHelpText("after", `
4137
+ ${chalk26.yellow("Subcommands:")}
4138
+ ${chalk26.cyan("host <id>")} Create a room and get a code to share
4139
+ ${chalk26.cyan("join <code>")} Join a room with the shared code
4140
+ ${chalk26.cyan("sync")} Upload your solution to the room
4141
+ ${chalk26.cyan("compare")} View both solutions side by side
4142
+ ${chalk26.cyan("status")} Check room and sync status
4143
+ ${chalk26.cyan("leave")} End the collaboration session
4144
+
4145
+ ${chalk26.yellow("Examples:")}
4146
+ ${chalk26.gray("$ leetcode collab host 1")} Start a session for Two Sum
4147
+ ${chalk26.gray("$ leetcode collab join ABC123")} Join your partner's session
4148
+ ${chalk26.gray("$ leetcode collab sync")} Upload your code after solving
4149
+ ${chalk26.gray("$ leetcode collab compare")} Compare solutions
4150
+ `);
4151
+ collabCmd.command("host <problemId>").description("Host a collaboration session").action(collabHostCommand);
4152
+ collabCmd.command("join <roomCode>").description("Join a collaboration session").action(collabJoinCommand);
4153
+ collabCmd.command("sync").description("Sync your code with partner").action(collabSyncCommand);
4154
+ collabCmd.command("compare").description("Compare your solution with partner").action(collabCompareCommand);
4155
+ collabCmd.command("leave").description("Leave the collaboration session").action(collabLeaveCommand);
4156
+ collabCmd.command("status").description("Show collaboration status").action(collabStatusCommand);
4157
+ var snapshotCmd = program.command("snapshot").description("Save and restore solution versions").addHelpText("after", `
4158
+ ${chalk26.yellow("Subcommands:")}
4159
+ ${chalk26.cyan("save <id> [name]")} Save current solution as a snapshot
4160
+ ${chalk26.cyan("list <id>")} List all snapshots for a problem
4161
+ ${chalk26.cyan("restore <id> <snapshot>")} Restore a snapshot
4162
+ ${chalk26.cyan("diff <id> <s1> <s2>")} Compare two snapshots
4163
+ ${chalk26.cyan("delete <id> <snapshot>")} Delete a snapshot
4164
+
4165
+ ${chalk26.yellow("Examples:")}
4166
+ ${chalk26.gray('$ leetcode snapshot save 1 "brute-force"')} Save current solution
4167
+ ${chalk26.gray("$ leetcode snapshot list 1")} List snapshots
4168
+ ${chalk26.gray("$ leetcode snapshot restore 1 2")} Restore snapshot #2
4169
+ ${chalk26.gray("$ leetcode snapshot diff 1 1 2")} Compare snapshots
4170
+ `);
4171
+ snapshotCmd.command("save <id> [name]").description("Save current solution as a snapshot").action(snapshotSaveCommand);
4172
+ snapshotCmd.command("list <id>").description("List all snapshots for a problem").action(snapshotListCommand);
4173
+ snapshotCmd.command("restore <id> <snapshot>").description("Restore a snapshot").action(snapshotRestoreCommand);
4174
+ snapshotCmd.command("diff <id> <snap1> <snap2>").description("Compare two snapshots").action(snapshotDiffCommand);
4175
+ snapshotCmd.command("delete <id> <snapshot>").description("Delete a snapshot").action(snapshotDeleteCommand);
2832
4176
  program.showHelpAfterError("(add --help for additional information)");
2833
4177
  program.parse();
2834
4178
  if (!process.argv.slice(2).length) {
2835
4179
  console.log();
2836
- console.log(chalk21.bold.cyan(" \u{1F525} LeetCode CLI"));
2837
- console.log(chalk21.gray(" A modern command-line interface for LeetCode"));
4180
+ console.log(chalk26.bold.cyan(" \u{1F525} LeetCode CLI"));
4181
+ console.log(chalk26.gray(" A modern command-line interface for LeetCode"));
2838
4182
  console.log();
2839
4183
  program.outputHelp();
2840
4184
  }