@night-slayer18/leetcode-cli 1.6.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.
- package/README.md +86 -0
- package/dist/index.js +1565 -564
- package/package.json +4 -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
|
|
5
|
+
import chalk26 from "chalk";
|
|
6
6
|
|
|
7
7
|
// src/commands/login.ts
|
|
8
8
|
import inquirer from "inquirer";
|
|
@@ -571,7 +571,7 @@ async function whoamiCommand() {
|
|
|
571
571
|
|
|
572
572
|
// src/commands/list.ts
|
|
573
573
|
import ora2 from "ora";
|
|
574
|
-
import
|
|
574
|
+
import chalk5 from "chalk";
|
|
575
575
|
|
|
576
576
|
// src/utils/auth.ts
|
|
577
577
|
import chalk2 from "chalk";
|
|
@@ -596,16 +596,227 @@ async function requireAuth() {
|
|
|
596
596
|
}
|
|
597
597
|
|
|
598
598
|
// src/utils/display.ts
|
|
599
|
-
import
|
|
599
|
+
import chalk4 from "chalk";
|
|
600
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
|
|
601
812
|
function displayProblemList(problems, total) {
|
|
602
813
|
const table = new Table({
|
|
603
814
|
head: [
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
815
|
+
chalk4.cyan("ID"),
|
|
816
|
+
chalk4.cyan("Title"),
|
|
817
|
+
chalk4.cyan("Difficulty"),
|
|
818
|
+
chalk4.cyan("Rate"),
|
|
819
|
+
chalk4.cyan("Status")
|
|
609
820
|
],
|
|
610
821
|
colWidths: [8, 45, 12, 10, 10],
|
|
611
822
|
style: { head: [], border: [] }
|
|
@@ -624,34 +835,34 @@ function displayProblemList(problems, total) {
|
|
|
624
835
|
]);
|
|
625
836
|
}
|
|
626
837
|
console.log(table.toString());
|
|
627
|
-
console.log(
|
|
838
|
+
console.log(chalk4.gray(`
|
|
628
839
|
Showing ${problems.length} of ${total} problems`));
|
|
629
840
|
}
|
|
630
841
|
function displayProblemDetail(problem) {
|
|
631
842
|
console.log();
|
|
632
843
|
const titlePrefix = problem.isPaidOnly ? "\u{1F512} " : "";
|
|
633
|
-
console.log(
|
|
844
|
+
console.log(chalk4.bold.cyan(` ${problem.questionFrontendId}. ${titlePrefix}${problem.title}`));
|
|
634
845
|
console.log(` ${colorDifficulty(problem.difficulty)}`);
|
|
635
|
-
console.log(
|
|
846
|
+
console.log(chalk4.gray(` https://leetcode.com/problems/${problem.titleSlug}/`));
|
|
636
847
|
console.log();
|
|
637
848
|
if (problem.isPaidOnly) {
|
|
638
|
-
console.log(
|
|
639
|
-
console.log(
|
|
640
|
-
console.log(
|
|
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.`));
|
|
641
852
|
console.log();
|
|
642
853
|
}
|
|
643
854
|
if (problem.topicTags.length) {
|
|
644
|
-
const tags = problem.topicTags.map((t) =>
|
|
855
|
+
const tags = problem.topicTags.map((t) => chalk4.bgBlue.white(` ${t.name} `)).join(" ");
|
|
645
856
|
console.log(` ${tags}`);
|
|
646
857
|
console.log();
|
|
647
858
|
}
|
|
648
|
-
console.log(
|
|
859
|
+
console.log(chalk4.gray("\u2500".repeat(60)));
|
|
649
860
|
console.log();
|
|
650
861
|
let content = problem.content;
|
|
651
862
|
if (!content) {
|
|
652
|
-
console.log(
|
|
653
|
-
console.log(
|
|
654
|
-
console.log(
|
|
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."));
|
|
655
866
|
console.log();
|
|
656
867
|
return;
|
|
657
868
|
}
|
|
@@ -671,100 +882,128 @@ function displayProblemDetail(problem) {
|
|
|
671
882
|
content = content.replace(/<[^>]+>/g, "");
|
|
672
883
|
content = content.replace(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/"/g, '"').replace(/'/g, "'").replace(/≤/g, "\u2264").replace(/≥/g, "\u2265").replace(/&#(\d+);/g, (_, code) => String.fromCharCode(parseInt(code, 10)));
|
|
673
884
|
content = content.replace(/\n{3,}/g, "\n\n").trim();
|
|
674
|
-
content = content.replace(/§EXAMPLE§(\d+)§/g, (_, num) =>
|
|
675
|
-
content = content.replace(/§INPUT§/g,
|
|
676
|
-
content = content.replace(/§OUTPUT§/g,
|
|
677
|
-
content = content.replace(/§EXPLAIN§/g,
|
|
678
|
-
content = content.replace(/§CONSTRAINTS§/g,
|
|
679
|
-
content = content.replace(/§FOLLOWUP§/g,
|
|
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:"));
|
|
680
891
|
console.log(content);
|
|
681
892
|
console.log();
|
|
682
893
|
}
|
|
683
|
-
function displayTestResult(result) {
|
|
894
|
+
function displayTestResult(result, topicTags) {
|
|
684
895
|
console.log();
|
|
685
896
|
if (result.compile_error) {
|
|
686
|
-
console.log(
|
|
687
|
-
console.log(
|
|
897
|
+
console.log(chalk4.red.bold("\u274C Compile Error"));
|
|
898
|
+
console.log(chalk4.red(result.compile_error));
|
|
688
899
|
return;
|
|
689
900
|
}
|
|
690
901
|
if (result.runtime_error) {
|
|
691
|
-
console.log(
|
|
692
|
-
console.log(
|
|
902
|
+
console.log(chalk4.red.bold("\u274C Runtime Error"));
|
|
903
|
+
console.log(chalk4.red(result.runtime_error));
|
|
693
904
|
return;
|
|
694
905
|
}
|
|
695
906
|
if (result.correct_answer) {
|
|
696
|
-
console.log(
|
|
907
|
+
console.log(chalk4.green.bold("\u2713 All test cases passed!"));
|
|
697
908
|
} else {
|
|
698
|
-
console.log(
|
|
699
|
-
}
|
|
700
|
-
console.log();
|
|
701
|
-
console.log(chalk3.gray("Your Output:"));
|
|
702
|
-
for (const output of result.code_answer ?? []) {
|
|
703
|
-
console.log(chalk3.white(` ${output}`));
|
|
909
|
+
console.log(chalk4.yellow.bold("\u2717 Some test cases failed"));
|
|
704
910
|
}
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
console.log(
|
|
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
|
+
}
|
|
709
947
|
}
|
|
710
|
-
|
|
948
|
+
const stdoutEntries = (result.std_output_list ?? []).filter((s) => s);
|
|
949
|
+
if (stdoutEntries.length > 0) {
|
|
711
950
|
console.log();
|
|
712
|
-
console.log(
|
|
713
|
-
for (const output of
|
|
714
|
-
|
|
951
|
+
console.log(chalk4.gray("Stdout:"));
|
|
952
|
+
for (const output of stdoutEntries) {
|
|
953
|
+
console.log(chalk4.gray(` ${output}`));
|
|
715
954
|
}
|
|
716
955
|
}
|
|
717
956
|
}
|
|
718
957
|
function displaySubmissionResult(result) {
|
|
719
958
|
console.log();
|
|
720
959
|
if (result.compile_error) {
|
|
721
|
-
console.log(
|
|
722
|
-
console.log(
|
|
960
|
+
console.log(chalk4.red.bold("\u274C Compile Error"));
|
|
961
|
+
console.log(chalk4.red(result.compile_error));
|
|
723
962
|
return;
|
|
724
963
|
}
|
|
725
964
|
if (result.runtime_error) {
|
|
726
|
-
console.log(
|
|
727
|
-
console.log(
|
|
965
|
+
console.log(chalk4.red.bold("\u274C Runtime Error"));
|
|
966
|
+
console.log(chalk4.red(result.runtime_error));
|
|
728
967
|
if (result.last_testcase) {
|
|
729
|
-
console.log(
|
|
968
|
+
console.log(chalk4.gray("Last testcase:"), result.last_testcase);
|
|
730
969
|
}
|
|
731
970
|
return;
|
|
732
971
|
}
|
|
733
972
|
if (result.status_msg === "Accepted") {
|
|
734
|
-
console.log(
|
|
973
|
+
console.log(chalk4.green.bold("\u2713 Accepted!"));
|
|
735
974
|
console.log();
|
|
736
975
|
console.log(
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
976
|
+
chalk4.gray("Runtime:"),
|
|
977
|
+
chalk4.white(result.status_runtime),
|
|
978
|
+
chalk4.gray(`(beats ${result.runtime_percentile?.toFixed(1) ?? "N/A"}%)`)
|
|
740
979
|
);
|
|
741
980
|
console.log(
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
981
|
+
chalk4.gray("Memory:"),
|
|
982
|
+
chalk4.white(result.status_memory),
|
|
983
|
+
chalk4.gray(`(beats ${result.memory_percentile?.toFixed(1) ?? "N/A"}%)`)
|
|
745
984
|
);
|
|
746
985
|
} else {
|
|
747
|
-
console.log(
|
|
986
|
+
console.log(chalk4.red.bold(`\u274C ${result.status_msg}`));
|
|
748
987
|
console.log();
|
|
749
|
-
console.log(
|
|
988
|
+
console.log(chalk4.gray(`Passed ${result.total_correct}/${result.total_testcases} testcases`));
|
|
750
989
|
if (result.code_output) {
|
|
751
|
-
console.log(
|
|
990
|
+
console.log(chalk4.gray("Your Output:"), result.code_output);
|
|
752
991
|
}
|
|
753
992
|
if (result.expected_output) {
|
|
754
|
-
console.log(
|
|
993
|
+
console.log(chalk4.gray("Expected:"), result.expected_output);
|
|
755
994
|
}
|
|
756
995
|
if (result.last_testcase) {
|
|
757
|
-
console.log(
|
|
996
|
+
console.log(chalk4.gray("Failed testcase:"), result.last_testcase);
|
|
758
997
|
}
|
|
759
998
|
}
|
|
760
999
|
}
|
|
761
1000
|
function displayUserStats(username, realName, ranking, acStats, streak, totalActiveDays) {
|
|
762
1001
|
console.log();
|
|
763
|
-
console.log(
|
|
764
|
-
console.log(
|
|
1002
|
+
console.log(chalk4.bold.white(`\u{1F464} ${username}`) + (realName ? chalk4.gray(` (${realName})`) : ""));
|
|
1003
|
+
console.log(chalk4.gray(`Ranking: #${ranking.toLocaleString()}`));
|
|
765
1004
|
console.log();
|
|
766
1005
|
const table = new Table({
|
|
767
|
-
head: [
|
|
1006
|
+
head: [chalk4.cyan("Difficulty"), chalk4.cyan("Solved")],
|
|
768
1007
|
style: { head: [], border: [] }
|
|
769
1008
|
});
|
|
770
1009
|
for (const stat of acStats) {
|
|
@@ -776,33 +1015,33 @@ function displayUserStats(username, realName, ranking, acStats, streak, totalAct
|
|
|
776
1015
|
}
|
|
777
1016
|
}
|
|
778
1017
|
const total = acStats.find((s) => s.difficulty === "All")?.count ?? 0;
|
|
779
|
-
table.push([
|
|
1018
|
+
table.push([chalk4.white.bold("Total"), chalk4.white.bold(total.toString())]);
|
|
780
1019
|
console.log(table.toString());
|
|
781
1020
|
console.log();
|
|
782
|
-
console.log(
|
|
783
|
-
console.log(
|
|
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()));
|
|
784
1023
|
}
|
|
785
1024
|
function displayDailyChallenge(date, problem) {
|
|
786
1025
|
console.log();
|
|
787
|
-
console.log(
|
|
1026
|
+
console.log(chalk4.bold.yellow("\u{1F3AF} Daily Challenge"), chalk4.gray(`(${date})`));
|
|
788
1027
|
console.log();
|
|
789
|
-
console.log(
|
|
1028
|
+
console.log(chalk4.white(`${problem.questionFrontendId}. ${problem.title}`));
|
|
790
1029
|
console.log(colorDifficulty(problem.difficulty));
|
|
791
|
-
console.log(
|
|
1030
|
+
console.log(chalk4.gray(`https://leetcode.com/problems/${problem.titleSlug}/`));
|
|
792
1031
|
if (problem.topicTags.length) {
|
|
793
1032
|
console.log();
|
|
794
|
-
const tags = problem.topicTags.map((t) =>
|
|
795
|
-
console.log(
|
|
1033
|
+
const tags = problem.topicTags.map((t) => chalk4.blue(t.name)).join(" ");
|
|
1034
|
+
console.log(chalk4.gray("Tags:"), tags);
|
|
796
1035
|
}
|
|
797
1036
|
}
|
|
798
1037
|
function colorDifficulty(difficulty) {
|
|
799
1038
|
switch (difficulty.toLowerCase()) {
|
|
800
1039
|
case "easy":
|
|
801
|
-
return
|
|
1040
|
+
return chalk4.green(difficulty);
|
|
802
1041
|
case "medium":
|
|
803
|
-
return
|
|
1042
|
+
return chalk4.yellow(difficulty);
|
|
804
1043
|
case "hard":
|
|
805
|
-
return
|
|
1044
|
+
return chalk4.red(difficulty);
|
|
806
1045
|
default:
|
|
807
1046
|
return difficulty;
|
|
808
1047
|
}
|
|
@@ -810,22 +1049,22 @@ function colorDifficulty(difficulty) {
|
|
|
810
1049
|
function formatStatus(status) {
|
|
811
1050
|
switch (status) {
|
|
812
1051
|
case "ac":
|
|
813
|
-
return
|
|
1052
|
+
return chalk4.green("\u2713");
|
|
814
1053
|
case "notac":
|
|
815
|
-
return
|
|
1054
|
+
return chalk4.yellow("\u25CB");
|
|
816
1055
|
default:
|
|
817
|
-
return
|
|
1056
|
+
return chalk4.gray("-");
|
|
818
1057
|
}
|
|
819
1058
|
}
|
|
820
1059
|
function displaySubmissionsList(submissions) {
|
|
821
1060
|
const table = new Table({
|
|
822
1061
|
head: [
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
1062
|
+
chalk4.cyan("ID"),
|
|
1063
|
+
chalk4.cyan("Status"),
|
|
1064
|
+
chalk4.cyan("Lang"),
|
|
1065
|
+
chalk4.cyan("Runtime"),
|
|
1066
|
+
chalk4.cyan("Memory"),
|
|
1067
|
+
chalk4.cyan("Date")
|
|
829
1068
|
],
|
|
830
1069
|
colWidths: [12, 18, 15, 12, 12, 25],
|
|
831
1070
|
style: { head: [], border: [] }
|
|
@@ -835,7 +1074,7 @@ function displaySubmissionsList(submissions) {
|
|
|
835
1074
|
const cleanTime = new Date(parseInt(s.timestamp) * 1e3).toLocaleString();
|
|
836
1075
|
table.push([
|
|
837
1076
|
s.id,
|
|
838
|
-
isAC ?
|
|
1077
|
+
isAC ? chalk4.green(s.statusDisplay) : chalk4.red(s.statusDisplay),
|
|
839
1078
|
s.lang,
|
|
840
1079
|
s.runtime,
|
|
841
1080
|
s.memory,
|
|
@@ -886,25 +1125,25 @@ async function listCommand(options) {
|
|
|
886
1125
|
const { total, problems } = await leetcodeClient.getProblems(filters);
|
|
887
1126
|
spinner.stop();
|
|
888
1127
|
if (problems.length === 0) {
|
|
889
|
-
console.log(
|
|
1128
|
+
console.log(chalk5.yellow("No problems found matching your criteria."));
|
|
890
1129
|
return;
|
|
891
1130
|
}
|
|
892
1131
|
displayProblemList(problems, total);
|
|
893
1132
|
if (page * limit < total) {
|
|
894
|
-
console.log(
|
|
1133
|
+
console.log(chalk5.gray(`
|
|
895
1134
|
Page ${page} of ${Math.ceil(total / limit)}. Use --page to navigate.`));
|
|
896
1135
|
}
|
|
897
1136
|
} catch (error) {
|
|
898
1137
|
spinner.fail("Failed to fetch problems");
|
|
899
1138
|
if (error instanceof Error) {
|
|
900
|
-
console.log(
|
|
1139
|
+
console.log(chalk5.red(error.message));
|
|
901
1140
|
}
|
|
902
1141
|
}
|
|
903
1142
|
}
|
|
904
1143
|
|
|
905
1144
|
// src/commands/show.ts
|
|
906
1145
|
import ora3 from "ora";
|
|
907
|
-
import
|
|
1146
|
+
import chalk6 from "chalk";
|
|
908
1147
|
async function showCommand(idOrSlug) {
|
|
909
1148
|
const { authorized } = await requireAuth();
|
|
910
1149
|
if (!authorized) return;
|
|
@@ -925,68 +1164,227 @@ async function showCommand(idOrSlug) {
|
|
|
925
1164
|
} catch (error) {
|
|
926
1165
|
spinner.fail("Failed to fetch problem");
|
|
927
1166
|
if (error instanceof Error) {
|
|
928
|
-
console.log(
|
|
1167
|
+
console.log(chalk6.red(error.message));
|
|
929
1168
|
}
|
|
930
1169
|
}
|
|
931
1170
|
}
|
|
932
1171
|
|
|
933
1172
|
// src/commands/pick.ts
|
|
934
1173
|
import { writeFile, mkdir } from "fs/promises";
|
|
935
|
-
import { existsSync } from "fs";
|
|
936
|
-
import { join as
|
|
1174
|
+
import { existsSync as existsSync2 } from "fs";
|
|
1175
|
+
import { join as join4 } from "path";
|
|
937
1176
|
import ora4 from "ora";
|
|
938
|
-
import
|
|
1177
|
+
import chalk8 from "chalk";
|
|
939
1178
|
|
|
940
1179
|
// src/storage/config.ts
|
|
941
|
-
import
|
|
942
|
-
|
|
1180
|
+
import { join as join3 } from "path";
|
|
1181
|
+
|
|
1182
|
+
// src/storage/workspaces.ts
|
|
1183
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
943
1184
|
import { join as join2 } from "path";
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
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 });
|
|
950
1192
|
}
|
|
951
|
-
}
|
|
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
|
|
952
1343
|
var config = {
|
|
953
1344
|
getConfig() {
|
|
1345
|
+
const wsConfig = workspaceStorage.getConfig();
|
|
954
1346
|
return {
|
|
955
|
-
language:
|
|
956
|
-
editor:
|
|
957
|
-
workDir:
|
|
958
|
-
repo:
|
|
1347
|
+
language: wsConfig.lang,
|
|
1348
|
+
editor: wsConfig.editor,
|
|
1349
|
+
workDir: wsConfig.workDir,
|
|
1350
|
+
repo: wsConfig.syncRepo
|
|
959
1351
|
};
|
|
960
1352
|
},
|
|
961
1353
|
setLanguage(language) {
|
|
962
|
-
|
|
1354
|
+
workspaceStorage.setConfig({ lang: language });
|
|
963
1355
|
},
|
|
964
1356
|
setEditor(editor) {
|
|
965
|
-
|
|
1357
|
+
workspaceStorage.setConfig({ editor });
|
|
966
1358
|
},
|
|
967
1359
|
setWorkDir(workDir) {
|
|
968
|
-
|
|
1360
|
+
workspaceStorage.setConfig({ workDir });
|
|
969
1361
|
},
|
|
970
1362
|
setRepo(repo) {
|
|
971
|
-
|
|
1363
|
+
workspaceStorage.setConfig({ syncRepo: repo });
|
|
972
1364
|
},
|
|
973
1365
|
deleteRepo() {
|
|
974
|
-
|
|
1366
|
+
const wsConfig = workspaceStorage.getConfig();
|
|
1367
|
+
delete wsConfig.syncRepo;
|
|
1368
|
+
workspaceStorage.setConfig(wsConfig);
|
|
975
1369
|
},
|
|
976
1370
|
getLanguage() {
|
|
977
|
-
return
|
|
1371
|
+
return workspaceStorage.getConfig().lang;
|
|
978
1372
|
},
|
|
979
1373
|
getEditor() {
|
|
980
|
-
return
|
|
1374
|
+
return workspaceStorage.getConfig().editor;
|
|
981
1375
|
},
|
|
982
1376
|
getWorkDir() {
|
|
983
|
-
return
|
|
1377
|
+
return workspaceStorage.getConfig().workDir;
|
|
984
1378
|
},
|
|
985
1379
|
getRepo() {
|
|
986
|
-
return
|
|
1380
|
+
return workspaceStorage.getConfig().syncRepo;
|
|
987
1381
|
},
|
|
988
1382
|
getPath() {
|
|
989
|
-
return
|
|
1383
|
+
return join3(workspaceStorage.getWorkspaceDir(), "config.json");
|
|
1384
|
+
},
|
|
1385
|
+
// New workspace-aware methods
|
|
1386
|
+
getActiveWorkspace() {
|
|
1387
|
+
return workspaceStorage.getActive();
|
|
990
1388
|
}
|
|
991
1389
|
};
|
|
992
1390
|
|
|
@@ -1112,7 +1510,7 @@ function getSolutionFileName(problemId, titleSlug, language) {
|
|
|
1112
1510
|
// src/utils/editor.ts
|
|
1113
1511
|
import { spawn } from "child_process";
|
|
1114
1512
|
import open from "open";
|
|
1115
|
-
import
|
|
1513
|
+
import chalk7 from "chalk";
|
|
1116
1514
|
var TERMINAL_EDITORS = ["vim", "nvim", "vi", "nano", "emacs", "micro", "helix"];
|
|
1117
1515
|
var VSCODE_EDITORS = ["code", "code-insiders", "cursor", "codium", "vscodium"];
|
|
1118
1516
|
async function openInEditor(filePath, workDir) {
|
|
@@ -1120,7 +1518,7 @@ async function openInEditor(filePath, workDir) {
|
|
|
1120
1518
|
const workspace = workDir ?? config.getWorkDir();
|
|
1121
1519
|
if (TERMINAL_EDITORS.includes(editor)) {
|
|
1122
1520
|
console.log();
|
|
1123
|
-
console.log(
|
|
1521
|
+
console.log(chalk7.gray(`Open with: ${editor} ${filePath}`));
|
|
1124
1522
|
return;
|
|
1125
1523
|
}
|
|
1126
1524
|
try {
|
|
@@ -1160,13 +1558,13 @@ async function pickCommand(idOrSlug, options) {
|
|
|
1160
1558
|
const template = getCodeTemplate(snippets, language);
|
|
1161
1559
|
let code;
|
|
1162
1560
|
if (snippets.length === 0) {
|
|
1163
|
-
spinner.warn(
|
|
1164
|
-
console.log(
|
|
1561
|
+
spinner.warn(chalk8.yellow("Premium Problem (No code snippets available)"));
|
|
1562
|
+
console.log(chalk8.gray("Generating placeholder file with problem info..."));
|
|
1165
1563
|
code = `// \u{1F512} Premium Problem - ${problem.title}
|
|
1166
1564
|
// Solution stub not available - visit LeetCode to view`;
|
|
1167
1565
|
} else if (!template) {
|
|
1168
1566
|
spinner.fail(`No code template available for ${language}`);
|
|
1169
|
-
console.log(
|
|
1567
|
+
console.log(chalk8.gray(`Available languages: ${snippets.map((s) => s.langSlug).join(", ")}`));
|
|
1170
1568
|
return false;
|
|
1171
1569
|
} else {
|
|
1172
1570
|
code = template.code;
|
|
@@ -1183,26 +1581,26 @@ async function pickCommand(idOrSlug, options) {
|
|
|
1183
1581
|
const workDir = config.getWorkDir();
|
|
1184
1582
|
const difficulty = problem.difficulty;
|
|
1185
1583
|
const category = problem.topicTags.length > 0 ? problem.topicTags[0].name.replace(/[^\w\s-]/g, "").trim() : "Uncategorized";
|
|
1186
|
-
const targetDir =
|
|
1187
|
-
if (!
|
|
1584
|
+
const targetDir = join4(workDir, difficulty, category);
|
|
1585
|
+
if (!existsSync2(targetDir)) {
|
|
1188
1586
|
await mkdir(targetDir, { recursive: true });
|
|
1189
1587
|
}
|
|
1190
1588
|
const fileName = getSolutionFileName(problem.questionFrontendId, problem.titleSlug, language);
|
|
1191
|
-
const filePath =
|
|
1192
|
-
if (
|
|
1589
|
+
const filePath = join4(targetDir, fileName);
|
|
1590
|
+
if (existsSync2(filePath)) {
|
|
1193
1591
|
spinner.warn(`File already exists: ${fileName}`);
|
|
1194
|
-
console.log(
|
|
1592
|
+
console.log(chalk8.gray(`Path: ${filePath}`));
|
|
1195
1593
|
if (options.open !== false) {
|
|
1196
1594
|
await openInEditor(filePath);
|
|
1197
1595
|
}
|
|
1198
1596
|
return true;
|
|
1199
1597
|
}
|
|
1200
1598
|
await writeFile(filePath, content, "utf-8");
|
|
1201
|
-
spinner.succeed(`Created ${
|
|
1202
|
-
console.log(
|
|
1599
|
+
spinner.succeed(`Created ${chalk8.green(fileName)}`);
|
|
1600
|
+
console.log(chalk8.gray(`Path: ${filePath}`));
|
|
1203
1601
|
console.log();
|
|
1204
|
-
console.log(
|
|
1205
|
-
console.log(
|
|
1602
|
+
console.log(chalk8.cyan(`${problem.questionFrontendId}. ${problem.title}`));
|
|
1603
|
+
console.log(chalk8.gray(`Difficulty: ${problem.difficulty} | Category: ${category}`));
|
|
1206
1604
|
if (options.open !== false) {
|
|
1207
1605
|
await openInEditor(filePath);
|
|
1208
1606
|
}
|
|
@@ -1211,17 +1609,17 @@ async function pickCommand(idOrSlug, options) {
|
|
|
1211
1609
|
spinner.fail("Failed to fetch problem");
|
|
1212
1610
|
if (error instanceof Error) {
|
|
1213
1611
|
if (error.message.includes("expected object, received null")) {
|
|
1214
|
-
console.log(
|
|
1612
|
+
console.log(chalk8.red(`Problem "${idOrSlug}" not found`));
|
|
1215
1613
|
} else {
|
|
1216
1614
|
try {
|
|
1217
1615
|
const zodError = JSON.parse(error.message);
|
|
1218
1616
|
if (Array.isArray(zodError)) {
|
|
1219
|
-
console.log(
|
|
1617
|
+
console.log(chalk8.red("API Response Validation Failed"));
|
|
1220
1618
|
} else {
|
|
1221
|
-
console.log(
|
|
1619
|
+
console.log(chalk8.red(error.message));
|
|
1222
1620
|
}
|
|
1223
1621
|
} catch {
|
|
1224
|
-
console.log(
|
|
1622
|
+
console.log(chalk8.red(error.message));
|
|
1225
1623
|
}
|
|
1226
1624
|
}
|
|
1227
1625
|
}
|
|
@@ -1230,12 +1628,12 @@ async function pickCommand(idOrSlug, options) {
|
|
|
1230
1628
|
}
|
|
1231
1629
|
async function batchPickCommand(ids, options) {
|
|
1232
1630
|
if (ids.length === 0) {
|
|
1233
|
-
console.log(
|
|
1631
|
+
console.log(chalk8.yellow("Please provide at least one problem ID"));
|
|
1234
1632
|
return;
|
|
1235
1633
|
}
|
|
1236
1634
|
const { authorized } = await requireAuth();
|
|
1237
1635
|
if (!authorized) return;
|
|
1238
|
-
console.log(
|
|
1636
|
+
console.log(chalk8.cyan(`\u{1F4E6} Picking ${ids.length} problem${ids.length !== 1 ? "s" : ""}...`));
|
|
1239
1637
|
console.log();
|
|
1240
1638
|
console.log();
|
|
1241
1639
|
let succeeded = 0;
|
|
@@ -1249,33 +1647,33 @@ async function batchPickCommand(ids, options) {
|
|
|
1249
1647
|
}
|
|
1250
1648
|
console.log();
|
|
1251
1649
|
}
|
|
1252
|
-
console.log(
|
|
1650
|
+
console.log(chalk8.gray("\u2500".repeat(50)));
|
|
1253
1651
|
console.log(
|
|
1254
|
-
|
|
1255
|
-
`Done! ${
|
|
1652
|
+
chalk8.bold(
|
|
1653
|
+
`Done! ${chalk8.green(`${succeeded} succeeded`)}${failed > 0 ? `, ${chalk8.red(`${failed} failed`)}` : ""}`
|
|
1256
1654
|
)
|
|
1257
1655
|
);
|
|
1258
1656
|
}
|
|
1259
1657
|
|
|
1260
1658
|
// src/commands/test.ts
|
|
1261
1659
|
import { readFile } from "fs/promises";
|
|
1262
|
-
import { existsSync as
|
|
1660
|
+
import { existsSync as existsSync4 } from "fs";
|
|
1263
1661
|
import { basename } from "path";
|
|
1264
1662
|
import ora5 from "ora";
|
|
1265
|
-
import
|
|
1663
|
+
import chalk9 from "chalk";
|
|
1266
1664
|
|
|
1267
1665
|
// src/utils/fileUtils.ts
|
|
1268
1666
|
import { readdir } from "fs/promises";
|
|
1269
|
-
import { existsSync as
|
|
1270
|
-
import { join as
|
|
1667
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1668
|
+
import { join as join5 } from "path";
|
|
1271
1669
|
var MAX_SEARCH_DEPTH = 5;
|
|
1272
1670
|
async function findSolutionFile(dir, problemId, currentDepth = 0) {
|
|
1273
|
-
if (!
|
|
1671
|
+
if (!existsSync3(dir)) return null;
|
|
1274
1672
|
if (currentDepth >= MAX_SEARCH_DEPTH) return null;
|
|
1275
1673
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
1276
1674
|
for (const entry of entries) {
|
|
1277
1675
|
if (entry.name.startsWith(".")) continue;
|
|
1278
|
-
const fullPath =
|
|
1676
|
+
const fullPath = join5(dir, entry.name);
|
|
1279
1677
|
if (entry.isDirectory()) {
|
|
1280
1678
|
const found = await findSolutionFile(fullPath, problemId, currentDepth + 1);
|
|
1281
1679
|
if (found) return found;
|
|
@@ -1289,11 +1687,11 @@ async function findSolutionFile(dir, problemId, currentDepth = 0) {
|
|
|
1289
1687
|
return null;
|
|
1290
1688
|
}
|
|
1291
1689
|
async function findFileByName(dir, fileName, currentDepth = 0) {
|
|
1292
|
-
if (!
|
|
1690
|
+
if (!existsSync3(dir)) return null;
|
|
1293
1691
|
if (currentDepth >= MAX_SEARCH_DEPTH) return null;
|
|
1294
1692
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
1295
1693
|
for (const entry of entries) {
|
|
1296
|
-
const fullPath =
|
|
1694
|
+
const fullPath = join5(dir, entry.name);
|
|
1297
1695
|
if (entry.isDirectory()) {
|
|
1298
1696
|
const found = await findFileByName(fullPath, fileName, currentDepth + 1);
|
|
1299
1697
|
if (found) return found;
|
|
@@ -1350,26 +1748,26 @@ async function testCommand(fileOrId, options) {
|
|
|
1350
1748
|
const workDir = config.getWorkDir();
|
|
1351
1749
|
const found = await findSolutionFile(workDir, fileOrId);
|
|
1352
1750
|
if (!found) {
|
|
1353
|
-
console.log(
|
|
1354
|
-
console.log(
|
|
1355
|
-
console.log(
|
|
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.`));
|
|
1356
1754
|
return;
|
|
1357
1755
|
}
|
|
1358
1756
|
filePath = found;
|
|
1359
|
-
console.log(
|
|
1757
|
+
console.log(chalk9.gray(`Found: ${filePath}`));
|
|
1360
1758
|
} else if (isFileName(fileOrId)) {
|
|
1361
1759
|
const workDir = config.getWorkDir();
|
|
1362
1760
|
const found = await findFileByName(workDir, fileOrId);
|
|
1363
1761
|
if (!found) {
|
|
1364
|
-
console.log(
|
|
1365
|
-
console.log(
|
|
1762
|
+
console.log(chalk9.red(`File not found: ${fileOrId}`));
|
|
1763
|
+
console.log(chalk9.gray(`Looking in: ${workDir}`));
|
|
1366
1764
|
return;
|
|
1367
1765
|
}
|
|
1368
1766
|
filePath = found;
|
|
1369
|
-
console.log(
|
|
1767
|
+
console.log(chalk9.gray(`Found: ${filePath}`));
|
|
1370
1768
|
}
|
|
1371
|
-
if (!
|
|
1372
|
-
console.log(
|
|
1769
|
+
if (!existsSync4(filePath)) {
|
|
1770
|
+
console.log(chalk9.red(`File not found: ${filePath}`));
|
|
1373
1771
|
return;
|
|
1374
1772
|
}
|
|
1375
1773
|
const spinner = ora5({ text: "Reading solution file...", spinner: "dots" }).start();
|
|
@@ -1378,8 +1776,8 @@ async function testCommand(fileOrId, options) {
|
|
|
1378
1776
|
const match = fileName.match(/^(\d+)\.([^.]+)\./);
|
|
1379
1777
|
if (!match) {
|
|
1380
1778
|
spinner.fail("Invalid filename format");
|
|
1381
|
-
console.log(
|
|
1382
|
-
console.log(
|
|
1779
|
+
console.log(chalk9.gray("Expected format: {id}.{title-slug}.{ext}"));
|
|
1780
|
+
console.log(chalk9.gray("Example: 1.two-sum.ts"));
|
|
1383
1781
|
return;
|
|
1384
1782
|
}
|
|
1385
1783
|
const [, problemId, titleSlug] = match;
|
|
@@ -1396,62 +1794,75 @@ async function testCommand(fileOrId, options) {
|
|
|
1396
1794
|
spinner.text = "Running tests...";
|
|
1397
1795
|
const result = await leetcodeClient.testSolution(titleSlug, code, lang, testcases, problem.questionId);
|
|
1398
1796
|
spinner.stop();
|
|
1399
|
-
displayTestResult(result);
|
|
1797
|
+
displayTestResult(result, options.visualize ? problem.topicTags : void 0);
|
|
1400
1798
|
} catch (error) {
|
|
1401
1799
|
spinner.fail("Test failed");
|
|
1402
1800
|
if (error instanceof Error) {
|
|
1403
|
-
console.log(
|
|
1801
|
+
console.log(chalk9.red(error.message));
|
|
1404
1802
|
}
|
|
1405
1803
|
}
|
|
1406
1804
|
}
|
|
1407
1805
|
|
|
1408
1806
|
// src/commands/submit.ts
|
|
1409
1807
|
import { readFile as readFile2 } from "fs/promises";
|
|
1410
|
-
import { existsSync as
|
|
1808
|
+
import { existsSync as existsSync6 } from "fs";
|
|
1411
1809
|
import { basename as basename2 } from "path";
|
|
1412
1810
|
import ora6 from "ora";
|
|
1413
|
-
import
|
|
1811
|
+
import chalk10 from "chalk";
|
|
1414
1812
|
|
|
1415
1813
|
// src/storage/timer.ts
|
|
1416
|
-
import
|
|
1417
|
-
import {
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
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"));
|
|
1425
1823
|
}
|
|
1426
|
-
}
|
|
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
|
+
}
|
|
1427
1834
|
var timerStorage = {
|
|
1428
1835
|
startTimer(problemId, title, difficulty, durationMinutes) {
|
|
1429
|
-
|
|
1836
|
+
const data = loadTimer();
|
|
1837
|
+
data.activeTimer = {
|
|
1430
1838
|
problemId,
|
|
1431
1839
|
title,
|
|
1432
1840
|
difficulty,
|
|
1433
1841
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1434
1842
|
durationMinutes
|
|
1435
|
-
}
|
|
1843
|
+
};
|
|
1844
|
+
saveTimer(data);
|
|
1436
1845
|
},
|
|
1437
1846
|
getActiveTimer() {
|
|
1438
|
-
return
|
|
1847
|
+
return loadTimer().activeTimer;
|
|
1439
1848
|
},
|
|
1440
1849
|
stopTimer() {
|
|
1441
|
-
const
|
|
1850
|
+
const data = loadTimer();
|
|
1851
|
+
const active = data.activeTimer;
|
|
1442
1852
|
if (!active) return null;
|
|
1443
1853
|
const startedAt = new Date(active.startedAt);
|
|
1444
1854
|
const now = /* @__PURE__ */ new Date();
|
|
1445
1855
|
const durationSeconds = Math.floor((now.getTime() - startedAt.getTime()) / 1e3);
|
|
1446
|
-
|
|
1856
|
+
data.activeTimer = null;
|
|
1857
|
+
saveTimer(data);
|
|
1447
1858
|
return { durationSeconds };
|
|
1448
1859
|
},
|
|
1449
1860
|
recordSolveTime(problemId, title, difficulty, durationSeconds, timerMinutes) {
|
|
1450
|
-
const
|
|
1451
|
-
if (!solveTimes[problemId]) {
|
|
1452
|
-
solveTimes[problemId] = [];
|
|
1861
|
+
const data = loadTimer();
|
|
1862
|
+
if (!data.solveTimes[problemId]) {
|
|
1863
|
+
data.solveTimes[problemId] = [];
|
|
1453
1864
|
}
|
|
1454
|
-
solveTimes[problemId].push({
|
|
1865
|
+
data.solveTimes[problemId].push({
|
|
1455
1866
|
problemId,
|
|
1456
1867
|
title,
|
|
1457
1868
|
difficulty,
|
|
@@ -1459,17 +1870,17 @@ var timerStorage = {
|
|
|
1459
1870
|
durationSeconds,
|
|
1460
1871
|
timerMinutes
|
|
1461
1872
|
});
|
|
1462
|
-
|
|
1873
|
+
saveTimer(data);
|
|
1463
1874
|
},
|
|
1464
1875
|
getSolveTimes(problemId) {
|
|
1465
|
-
const
|
|
1466
|
-
return solveTimes[problemId] ?? [];
|
|
1876
|
+
const data = loadTimer();
|
|
1877
|
+
return data.solveTimes[problemId] ?? [];
|
|
1467
1878
|
},
|
|
1468
1879
|
getAllSolveTimes() {
|
|
1469
|
-
return
|
|
1880
|
+
return loadTimer().solveTimes ?? {};
|
|
1470
1881
|
},
|
|
1471
1882
|
getStats() {
|
|
1472
|
-
const solveTimes =
|
|
1883
|
+
const solveTimes = loadTimer().solveTimes ?? {};
|
|
1473
1884
|
let totalProblems = 0;
|
|
1474
1885
|
let totalTime = 0;
|
|
1475
1886
|
for (const times of Object.values(solveTimes)) {
|
|
@@ -1495,26 +1906,26 @@ async function submitCommand(fileOrId) {
|
|
|
1495
1906
|
const workDir = config.getWorkDir();
|
|
1496
1907
|
const found = await findSolutionFile(workDir, fileOrId);
|
|
1497
1908
|
if (!found) {
|
|
1498
|
-
console.log(
|
|
1499
|
-
console.log(
|
|
1500
|
-
console.log(
|
|
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.`));
|
|
1501
1912
|
return;
|
|
1502
1913
|
}
|
|
1503
1914
|
filePath = found;
|
|
1504
|
-
console.log(
|
|
1915
|
+
console.log(chalk10.gray(`Found: ${filePath}`));
|
|
1505
1916
|
} else if (isFileName(fileOrId)) {
|
|
1506
1917
|
const workDir = config.getWorkDir();
|
|
1507
1918
|
const found = await findFileByName(workDir, fileOrId);
|
|
1508
1919
|
if (!found) {
|
|
1509
|
-
console.log(
|
|
1510
|
-
console.log(
|
|
1920
|
+
console.log(chalk10.red(`File not found: ${fileOrId}`));
|
|
1921
|
+
console.log(chalk10.gray(`Looking in: ${workDir}`));
|
|
1511
1922
|
return;
|
|
1512
1923
|
}
|
|
1513
1924
|
filePath = found;
|
|
1514
|
-
console.log(
|
|
1925
|
+
console.log(chalk10.gray(`Found: ${filePath}`));
|
|
1515
1926
|
}
|
|
1516
|
-
if (!
|
|
1517
|
-
console.log(
|
|
1927
|
+
if (!existsSync6(filePath)) {
|
|
1928
|
+
console.log(chalk10.red(`File not found: ${filePath}`));
|
|
1518
1929
|
return;
|
|
1519
1930
|
}
|
|
1520
1931
|
const spinner = ora6({ text: "Reading solution file...", spinner: "dots" }).start();
|
|
@@ -1523,8 +1934,8 @@ async function submitCommand(fileOrId) {
|
|
|
1523
1934
|
const match = fileName.match(/^(\d+)\.([^.]+)\./);
|
|
1524
1935
|
if (!match) {
|
|
1525
1936
|
spinner.fail("Invalid filename format");
|
|
1526
|
-
console.log(
|
|
1527
|
-
console.log(
|
|
1937
|
+
console.log(chalk10.gray("Expected format: {id}.{title-slug}.{ext}"));
|
|
1938
|
+
console.log(chalk10.gray("Example: 1.two-sum.ts"));
|
|
1528
1939
|
return;
|
|
1529
1940
|
}
|
|
1530
1941
|
const [, problemId, titleSlug] = match;
|
|
@@ -1563,14 +1974,14 @@ async function submitCommand(fileOrId) {
|
|
|
1563
1974
|
const timeStr = `${mins}m ${secs}s`;
|
|
1564
1975
|
const withinLimit = timerResult.durationSeconds <= activeTimer.durationMinutes * 60;
|
|
1565
1976
|
console.log();
|
|
1566
|
-
console.log(
|
|
1977
|
+
console.log(chalk10.bold("\u23F1\uFE0F Timer Result:"));
|
|
1567
1978
|
console.log(
|
|
1568
|
-
` Solved in ${withinLimit ?
|
|
1979
|
+
` Solved in ${withinLimit ? chalk10.green(timeStr) : chalk10.yellow(timeStr)} (limit: ${activeTimer.durationMinutes}m)`
|
|
1569
1980
|
);
|
|
1570
1981
|
if (withinLimit) {
|
|
1571
|
-
console.log(
|
|
1982
|
+
console.log(chalk10.green(" \u2713 Within time limit!"));
|
|
1572
1983
|
} else {
|
|
1573
|
-
console.log(
|
|
1984
|
+
console.log(chalk10.yellow(" \u26A0 Exceeded time limit"));
|
|
1574
1985
|
}
|
|
1575
1986
|
}
|
|
1576
1987
|
}
|
|
@@ -1578,17 +1989,17 @@ async function submitCommand(fileOrId) {
|
|
|
1578
1989
|
} catch (error) {
|
|
1579
1990
|
spinner.fail("Submission failed");
|
|
1580
1991
|
if (error instanceof Error) {
|
|
1581
|
-
console.log(
|
|
1992
|
+
console.log(chalk10.red(error.message));
|
|
1582
1993
|
}
|
|
1583
1994
|
}
|
|
1584
1995
|
}
|
|
1585
1996
|
|
|
1586
1997
|
// src/commands/stat.ts
|
|
1587
1998
|
import ora7 from "ora";
|
|
1588
|
-
import
|
|
1999
|
+
import chalk12 from "chalk";
|
|
1589
2000
|
|
|
1590
2001
|
// src/utils/stats-display.ts
|
|
1591
|
-
import
|
|
2002
|
+
import chalk11 from "chalk";
|
|
1592
2003
|
var MONTH_NAMES = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
1593
2004
|
function renderHeatmap(calendarJson) {
|
|
1594
2005
|
const data = JSON.parse(calendarJson);
|
|
@@ -1621,25 +2032,25 @@ function renderHeatmap(calendarJson) {
|
|
|
1621
2032
|
const totalSubmissions = weeks.reduce((sum, w) => sum + w.count, 0);
|
|
1622
2033
|
const totalActiveDays = weeks.reduce((sum, w) => sum + w.days, 0);
|
|
1623
2034
|
console.log();
|
|
1624
|
-
console.log(
|
|
1625
|
-
console.log(
|
|
1626
|
-
console.log(
|
|
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)));
|
|
1627
2038
|
console.log();
|
|
1628
2039
|
for (const week of weeks) {
|
|
1629
2040
|
const weekLabel = `${week.start} - ${week.end}`.padEnd(18);
|
|
1630
|
-
const bar = week.count > 0 ?
|
|
2041
|
+
const bar = week.count > 0 ? chalk11.green("\u2588".repeat(Math.min(week.count, 10))).padEnd(10) : chalk11.gray("\xB7").padEnd(10);
|
|
1631
2042
|
const countStr = week.count > 0 ? `${week.count} subs`.padEnd(10) : "".padEnd(10);
|
|
1632
2043
|
const daysStr = week.days > 0 ? `${week.days}d active` : "";
|
|
1633
|
-
console.log(` ${
|
|
2044
|
+
console.log(` ${chalk11.white(weekLabel)} ${bar} ${chalk11.cyan(countStr)} ${chalk11.yellow(daysStr)}`);
|
|
1634
2045
|
}
|
|
1635
|
-
console.log(
|
|
1636
|
-
console.log(` ${
|
|
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")}`);
|
|
1637
2048
|
console.log();
|
|
1638
2049
|
}
|
|
1639
2050
|
function renderSkillStats(fundamental, intermediate, advanced) {
|
|
1640
2051
|
console.log();
|
|
1641
|
-
console.log(
|
|
1642
|
-
console.log(
|
|
2052
|
+
console.log(chalk11.bold("\u{1F3AF} Skill Breakdown"));
|
|
2053
|
+
console.log(chalk11.gray("\u2500".repeat(45)));
|
|
1643
2054
|
const renderSection = (title, stats, color) => {
|
|
1644
2055
|
if (stats.length === 0) return;
|
|
1645
2056
|
console.log();
|
|
@@ -1648,12 +2059,12 @@ function renderSkillStats(fundamental, intermediate, advanced) {
|
|
|
1648
2059
|
for (const stat of sorted.slice(0, 8)) {
|
|
1649
2060
|
const name = stat.tagName.padEnd(22);
|
|
1650
2061
|
const bar = color("\u2588".repeat(Math.min(stat.problemsSolved, 15)));
|
|
1651
|
-
console.log(` ${
|
|
2062
|
+
console.log(` ${chalk11.white(name)} ${bar} ${chalk11.white(stat.problemsSolved)}`);
|
|
1652
2063
|
}
|
|
1653
2064
|
};
|
|
1654
|
-
renderSection("Fundamental", fundamental,
|
|
1655
|
-
renderSection("Intermediate", intermediate,
|
|
1656
|
-
renderSection("Advanced", advanced,
|
|
2065
|
+
renderSection("Fundamental", fundamental, chalk11.green);
|
|
2066
|
+
renderSection("Intermediate", intermediate, chalk11.yellow);
|
|
2067
|
+
renderSection("Advanced", advanced, chalk11.red);
|
|
1657
2068
|
console.log();
|
|
1658
2069
|
}
|
|
1659
2070
|
function renderTrendChart(calendarJson) {
|
|
@@ -1674,15 +2085,15 @@ function renderTrendChart(calendarJson) {
|
|
|
1674
2085
|
const maxVal = Math.max(...days.map((d) => d.count), 1);
|
|
1675
2086
|
const chartHeight = 6;
|
|
1676
2087
|
console.log();
|
|
1677
|
-
console.log(
|
|
1678
|
-
console.log(
|
|
2088
|
+
console.log(chalk11.bold("\u{1F4C8} Submission Trend (Last 7 Days)"));
|
|
2089
|
+
console.log(chalk11.gray("\u2500".repeat(35)));
|
|
1679
2090
|
console.log();
|
|
1680
2091
|
for (let row = chartHeight; row >= 1; row--) {
|
|
1681
2092
|
let line = ` ${row === chartHeight ? maxVal.toString().padStart(2) : " "} \u2502`;
|
|
1682
2093
|
for (const day of days) {
|
|
1683
2094
|
const barHeight = Math.round(day.count / maxVal * chartHeight);
|
|
1684
2095
|
if (barHeight >= row) {
|
|
1685
|
-
line +=
|
|
2096
|
+
line += chalk11.green(" \u2588\u2588 ");
|
|
1686
2097
|
} else {
|
|
1687
2098
|
line += " ";
|
|
1688
2099
|
}
|
|
@@ -1691,10 +2102,10 @@ function renderTrendChart(calendarJson) {
|
|
|
1691
2102
|
}
|
|
1692
2103
|
console.log(` 0 \u2514${"\u2500\u2500\u2500\u2500".repeat(7)}`);
|
|
1693
2104
|
console.log(` ${days.map((d) => d.label.padEnd(4)).join("")}`);
|
|
1694
|
-
console.log(
|
|
2105
|
+
console.log(chalk11.gray(` ${days.map((d) => d.count.toString().padEnd(4)).join("")}`));
|
|
1695
2106
|
const total = days.reduce((a, b) => a + b.count, 0);
|
|
1696
2107
|
console.log();
|
|
1697
|
-
console.log(
|
|
2108
|
+
console.log(chalk11.white(` Total: ${total} submissions this week`));
|
|
1698
2109
|
console.log();
|
|
1699
2110
|
}
|
|
1700
2111
|
|
|
@@ -1726,14 +2137,14 @@ async function statCommand(username, options = {}) {
|
|
|
1726
2137
|
if (profile.submissionCalendar) {
|
|
1727
2138
|
renderHeatmap(profile.submissionCalendar);
|
|
1728
2139
|
} else {
|
|
1729
|
-
console.log(
|
|
2140
|
+
console.log(chalk12.yellow("Calendar data not available."));
|
|
1730
2141
|
}
|
|
1731
2142
|
}
|
|
1732
2143
|
if (options.trend) {
|
|
1733
2144
|
if (profile.submissionCalendar) {
|
|
1734
2145
|
renderTrendChart(profile.submissionCalendar);
|
|
1735
2146
|
} else {
|
|
1736
|
-
console.log(
|
|
2147
|
+
console.log(chalk12.yellow("Calendar data not available."));
|
|
1737
2148
|
}
|
|
1738
2149
|
}
|
|
1739
2150
|
if (options.skills) {
|
|
@@ -1745,14 +2156,14 @@ async function statCommand(username, options = {}) {
|
|
|
1745
2156
|
} catch (error) {
|
|
1746
2157
|
spinner.fail("Failed to fetch statistics");
|
|
1747
2158
|
if (error instanceof Error) {
|
|
1748
|
-
console.log(
|
|
2159
|
+
console.log(chalk12.red(error.message));
|
|
1749
2160
|
}
|
|
1750
2161
|
}
|
|
1751
2162
|
}
|
|
1752
2163
|
|
|
1753
2164
|
// src/commands/daily.ts
|
|
1754
2165
|
import ora8 from "ora";
|
|
1755
|
-
import
|
|
2166
|
+
import chalk13 from "chalk";
|
|
1756
2167
|
async function dailyCommand() {
|
|
1757
2168
|
const { authorized } = await requireAuth();
|
|
1758
2169
|
if (!authorized) return;
|
|
@@ -1762,19 +2173,19 @@ async function dailyCommand() {
|
|
|
1762
2173
|
spinner.stop();
|
|
1763
2174
|
displayDailyChallenge(daily.date, daily.question);
|
|
1764
2175
|
console.log();
|
|
1765
|
-
console.log(
|
|
1766
|
-
console.log(
|
|
2176
|
+
console.log(chalk13.gray("Run the following to start working on this problem:"));
|
|
2177
|
+
console.log(chalk13.cyan(` leetcode pick ${daily.question.titleSlug}`));
|
|
1767
2178
|
} catch (error) {
|
|
1768
2179
|
spinner.fail("Failed to fetch daily challenge");
|
|
1769
2180
|
if (error instanceof Error) {
|
|
1770
|
-
console.log(
|
|
2181
|
+
console.log(chalk13.red(error.message));
|
|
1771
2182
|
}
|
|
1772
2183
|
}
|
|
1773
2184
|
}
|
|
1774
2185
|
|
|
1775
2186
|
// src/commands/random.ts
|
|
1776
2187
|
import ora9 from "ora";
|
|
1777
|
-
import
|
|
2188
|
+
import chalk14 from "chalk";
|
|
1778
2189
|
async function randomCommand(options) {
|
|
1779
2190
|
const { authorized } = await requireAuth();
|
|
1780
2191
|
if (!authorized) return;
|
|
@@ -1808,23 +2219,23 @@ async function randomCommand(options) {
|
|
|
1808
2219
|
await pickCommand(titleSlug, { open: options.open ?? true });
|
|
1809
2220
|
} else {
|
|
1810
2221
|
await showCommand(titleSlug);
|
|
1811
|
-
console.log(
|
|
1812
|
-
console.log(
|
|
2222
|
+
console.log(chalk14.gray("Run following to start solving:"));
|
|
2223
|
+
console.log(chalk14.cyan(` leetcode pick ${titleSlug}`));
|
|
1813
2224
|
}
|
|
1814
2225
|
} catch (error) {
|
|
1815
2226
|
spinner.fail("Failed to fetch random problem");
|
|
1816
2227
|
if (error instanceof Error) {
|
|
1817
|
-
console.log(
|
|
2228
|
+
console.log(chalk14.red(error.message));
|
|
1818
2229
|
}
|
|
1819
2230
|
}
|
|
1820
2231
|
}
|
|
1821
2232
|
|
|
1822
2233
|
// src/commands/submissions.ts
|
|
1823
2234
|
import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
1824
|
-
import { existsSync as
|
|
2235
|
+
import { existsSync as existsSync7 } from "fs";
|
|
1825
2236
|
import { join as join6 } from "path";
|
|
1826
2237
|
import ora10 from "ora";
|
|
1827
|
-
import
|
|
2238
|
+
import chalk15 from "chalk";
|
|
1828
2239
|
async function submissionsCommand(idOrSlug, options) {
|
|
1829
2240
|
const { authorized } = await requireAuth();
|
|
1830
2241
|
if (!authorized) return;
|
|
@@ -1846,16 +2257,16 @@ async function submissionsCommand(idOrSlug, options) {
|
|
|
1846
2257
|
const submissions = await leetcodeClient.getSubmissionList(slug, limit);
|
|
1847
2258
|
spinner.stop();
|
|
1848
2259
|
if (submissions.length === 0) {
|
|
1849
|
-
console.log(
|
|
2260
|
+
console.log(chalk15.yellow("No submissions found."));
|
|
1850
2261
|
return;
|
|
1851
2262
|
}
|
|
1852
2263
|
if (options.last) {
|
|
1853
2264
|
const lastAC = submissions.find((s) => s.statusDisplay === "Accepted");
|
|
1854
2265
|
if (lastAC) {
|
|
1855
|
-
console.log(
|
|
2266
|
+
console.log(chalk15.bold("Last Accepted Submission:"));
|
|
1856
2267
|
displaySubmissionsList([lastAC]);
|
|
1857
2268
|
} else {
|
|
1858
|
-
console.log(
|
|
2269
|
+
console.log(chalk15.yellow("No accepted submissions found in recent history."));
|
|
1859
2270
|
}
|
|
1860
2271
|
} else {
|
|
1861
2272
|
displaySubmissionsList(submissions);
|
|
@@ -1877,7 +2288,7 @@ async function submissionsCommand(idOrSlug, options) {
|
|
|
1877
2288
|
const difficulty = problem.difficulty;
|
|
1878
2289
|
const category = problem.topicTags.length > 0 ? problem.topicTags[0].name.replace(/[^\w\s-]/g, "").trim() : "Uncategorized";
|
|
1879
2290
|
const targetDir = join6(workDir, difficulty, category);
|
|
1880
|
-
if (!
|
|
2291
|
+
if (!existsSync7(targetDir)) {
|
|
1881
2292
|
await mkdir2(targetDir, { recursive: true });
|
|
1882
2293
|
}
|
|
1883
2294
|
const langSlug = details.lang.name;
|
|
@@ -1886,20 +2297,20 @@ async function submissionsCommand(idOrSlug, options) {
|
|
|
1886
2297
|
const fileName = `${problem.questionFrontendId}.${problem.titleSlug}.submission-${lastAC.id}.${ext}`;
|
|
1887
2298
|
const filePath = join6(targetDir, fileName);
|
|
1888
2299
|
await writeFile2(filePath, details.code, "utf-8");
|
|
1889
|
-
downloadSpinner.succeed(`Downloaded to ${
|
|
1890
|
-
console.log(
|
|
2300
|
+
downloadSpinner.succeed(`Downloaded to ${chalk15.green(fileName)}`);
|
|
2301
|
+
console.log(chalk15.gray(`Path: ${filePath}`));
|
|
1891
2302
|
}
|
|
1892
2303
|
} catch (error) {
|
|
1893
2304
|
spinner.fail("Failed to fetch submissions");
|
|
1894
2305
|
if (error instanceof Error) {
|
|
1895
|
-
console.log(
|
|
2306
|
+
console.log(chalk15.red(error.message));
|
|
1896
2307
|
}
|
|
1897
2308
|
}
|
|
1898
2309
|
}
|
|
1899
2310
|
|
|
1900
2311
|
// src/commands/config.ts
|
|
1901
2312
|
import inquirer2 from "inquirer";
|
|
1902
|
-
import
|
|
2313
|
+
import chalk16 from "chalk";
|
|
1903
2314
|
var SUPPORTED_LANGUAGES = [
|
|
1904
2315
|
"typescript",
|
|
1905
2316
|
"javascript",
|
|
@@ -1921,34 +2332,38 @@ async function configCommand(options) {
|
|
|
1921
2332
|
if (options.lang) {
|
|
1922
2333
|
const langInput = options.lang.toLowerCase();
|
|
1923
2334
|
if (!SUPPORTED_LANGUAGES.includes(langInput)) {
|
|
1924
|
-
console.log(
|
|
1925
|
-
console.log(
|
|
2335
|
+
console.log(chalk16.red(`Unsupported language: ${options.lang}`));
|
|
2336
|
+
console.log(chalk16.gray(`Supported: ${SUPPORTED_LANGUAGES.join(", ")}`));
|
|
1926
2337
|
return;
|
|
1927
2338
|
}
|
|
1928
2339
|
const lang = langInput;
|
|
1929
2340
|
config.setLanguage(lang);
|
|
1930
|
-
console.log(
|
|
2341
|
+
console.log(chalk16.green(`\u2713 Default language set to ${lang}`));
|
|
1931
2342
|
}
|
|
1932
2343
|
if (options.editor) {
|
|
1933
2344
|
config.setEditor(options.editor);
|
|
1934
|
-
console.log(
|
|
2345
|
+
console.log(chalk16.green(`\u2713 Editor set to ${options.editor}`));
|
|
1935
2346
|
}
|
|
1936
2347
|
if (options.workdir) {
|
|
1937
2348
|
config.setWorkDir(options.workdir);
|
|
1938
|
-
console.log(
|
|
2349
|
+
console.log(chalk16.green(`\u2713 Work directory set to ${options.workdir}`));
|
|
1939
2350
|
}
|
|
1940
2351
|
if (options.repo !== void 0) {
|
|
1941
2352
|
if (options.repo.trim() === "") {
|
|
1942
2353
|
config.deleteRepo();
|
|
1943
|
-
console.log(
|
|
2354
|
+
console.log(chalk16.green("\u2713 Repository URL cleared"));
|
|
1944
2355
|
} else {
|
|
1945
2356
|
config.setRepo(options.repo);
|
|
1946
|
-
console.log(
|
|
2357
|
+
console.log(chalk16.green(`\u2713 Repository URL set to ${options.repo}`));
|
|
1947
2358
|
}
|
|
1948
2359
|
}
|
|
1949
2360
|
}
|
|
1950
2361
|
async function configInteractiveCommand() {
|
|
1951
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)));
|
|
1952
2367
|
const answers = await inquirer2.prompt([
|
|
1953
2368
|
{
|
|
1954
2369
|
type: "list",
|
|
@@ -1985,33 +2400,34 @@ async function configInteractiveCommand() {
|
|
|
1985
2400
|
config.deleteRepo();
|
|
1986
2401
|
}
|
|
1987
2402
|
console.log();
|
|
1988
|
-
console.log(
|
|
2403
|
+
console.log(chalk16.green("\u2713 Configuration saved"));
|
|
1989
2404
|
showCurrentConfig();
|
|
1990
2405
|
}
|
|
1991
2406
|
function showCurrentConfig() {
|
|
1992
2407
|
const currentConfig = config.getConfig();
|
|
1993
2408
|
const creds = credentials.get();
|
|
2409
|
+
const workspace = config.getActiveWorkspace();
|
|
1994
2410
|
console.log();
|
|
1995
|
-
console.log(
|
|
1996
|
-
console.log(
|
|
2411
|
+
console.log(chalk16.bold.cyan(`\u{1F4C1} Workspace: ${workspace}`));
|
|
2412
|
+
console.log(chalk16.gray("\u2500".repeat(40)));
|
|
1997
2413
|
console.log();
|
|
1998
|
-
console.log(
|
|
2414
|
+
console.log(chalk16.gray("Config file:"), config.getPath());
|
|
1999
2415
|
console.log();
|
|
2000
|
-
console.log(
|
|
2001
|
-
console.log(
|
|
2002
|
-
console.log(
|
|
2003
|
-
console.log(
|
|
2004
|
-
console.log(
|
|
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"));
|
|
2005
2421
|
}
|
|
2006
2422
|
|
|
2007
2423
|
// src/commands/bookmark.ts
|
|
2008
|
-
import
|
|
2424
|
+
import chalk17 from "chalk";
|
|
2009
2425
|
import Table2 from "cli-table3";
|
|
2010
2426
|
import ora11 from "ora";
|
|
2011
2427
|
|
|
2012
2428
|
// src/storage/bookmarks.ts
|
|
2013
|
-
import
|
|
2014
|
-
var bookmarksStore = new
|
|
2429
|
+
import Conf2 from "conf";
|
|
2430
|
+
var bookmarksStore = new Conf2({
|
|
2015
2431
|
projectName: "leetcode-cli-bookmarks",
|
|
2016
2432
|
defaults: { bookmarks: [] }
|
|
2017
2433
|
});
|
|
@@ -2050,36 +2466,36 @@ var bookmarks = {
|
|
|
2050
2466
|
async function bookmarkCommand(action, id) {
|
|
2051
2467
|
const validActions = ["add", "remove", "list", "clear"];
|
|
2052
2468
|
if (!validActions.includes(action)) {
|
|
2053
|
-
console.log(
|
|
2054
|
-
console.log(
|
|
2469
|
+
console.log(chalk17.red(`Invalid action: ${action}`));
|
|
2470
|
+
console.log(chalk17.gray("Valid actions: add, remove, list, clear"));
|
|
2055
2471
|
return;
|
|
2056
2472
|
}
|
|
2057
2473
|
switch (action) {
|
|
2058
2474
|
case "add":
|
|
2059
2475
|
if (!id) {
|
|
2060
|
-
console.log(
|
|
2476
|
+
console.log(chalk17.red("Please provide a problem ID to bookmark"));
|
|
2061
2477
|
return;
|
|
2062
2478
|
}
|
|
2063
2479
|
if (!isProblemId(id)) {
|
|
2064
|
-
console.log(
|
|
2065
|
-
console.log(
|
|
2480
|
+
console.log(chalk17.red(`Invalid problem ID: ${id}`));
|
|
2481
|
+
console.log(chalk17.gray("Problem ID must be a positive integer"));
|
|
2066
2482
|
return;
|
|
2067
2483
|
}
|
|
2068
2484
|
if (bookmarks.add(id)) {
|
|
2069
|
-
console.log(
|
|
2485
|
+
console.log(chalk17.green(`\u2713 Bookmarked problem ${id}`));
|
|
2070
2486
|
} else {
|
|
2071
|
-
console.log(
|
|
2487
|
+
console.log(chalk17.yellow(`Problem ${id} is already bookmarked`));
|
|
2072
2488
|
}
|
|
2073
2489
|
break;
|
|
2074
2490
|
case "remove":
|
|
2075
2491
|
if (!id) {
|
|
2076
|
-
console.log(
|
|
2492
|
+
console.log(chalk17.red("Please provide a problem ID to remove"));
|
|
2077
2493
|
return;
|
|
2078
2494
|
}
|
|
2079
2495
|
if (bookmarks.remove(id)) {
|
|
2080
|
-
console.log(
|
|
2496
|
+
console.log(chalk17.green(`\u2713 Removed bookmark for problem ${id}`));
|
|
2081
2497
|
} else {
|
|
2082
|
-
console.log(
|
|
2498
|
+
console.log(chalk17.yellow(`Problem ${id} is not bookmarked`));
|
|
2083
2499
|
}
|
|
2084
2500
|
break;
|
|
2085
2501
|
case "list":
|
|
@@ -2088,10 +2504,10 @@ async function bookmarkCommand(action, id) {
|
|
|
2088
2504
|
case "clear":
|
|
2089
2505
|
const count = bookmarks.count();
|
|
2090
2506
|
if (count === 0) {
|
|
2091
|
-
console.log(
|
|
2507
|
+
console.log(chalk17.yellow("No bookmarks to clear"));
|
|
2092
2508
|
} else {
|
|
2093
2509
|
bookmarks.clear();
|
|
2094
|
-
console.log(
|
|
2510
|
+
console.log(chalk17.green(`\u2713 Cleared ${count} bookmark${count !== 1 ? "s" : ""}`));
|
|
2095
2511
|
}
|
|
2096
2512
|
break;
|
|
2097
2513
|
}
|
|
@@ -2099,12 +2515,12 @@ async function bookmarkCommand(action, id) {
|
|
|
2099
2515
|
async function listBookmarks() {
|
|
2100
2516
|
const bookmarkList = bookmarks.list();
|
|
2101
2517
|
if (bookmarkList.length === 0) {
|
|
2102
|
-
console.log(
|
|
2103
|
-
console.log(
|
|
2518
|
+
console.log(chalk17.yellow("\u{1F4CC} No bookmarked problems"));
|
|
2519
|
+
console.log(chalk17.gray('Use "leetcode bookmark add <id>" to bookmark a problem'));
|
|
2104
2520
|
return;
|
|
2105
2521
|
}
|
|
2106
2522
|
console.log();
|
|
2107
|
-
console.log(
|
|
2523
|
+
console.log(chalk17.bold.cyan(`\u{1F4CC} Bookmarked Problems (${bookmarkList.length})`));
|
|
2108
2524
|
console.log();
|
|
2109
2525
|
const { authorized } = await requireAuth();
|
|
2110
2526
|
if (authorized) {
|
|
@@ -2112,10 +2528,10 @@ async function listBookmarks() {
|
|
|
2112
2528
|
try {
|
|
2113
2529
|
const table = new Table2({
|
|
2114
2530
|
head: [
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2531
|
+
chalk17.cyan("ID"),
|
|
2532
|
+
chalk17.cyan("Title"),
|
|
2533
|
+
chalk17.cyan("Difficulty"),
|
|
2534
|
+
chalk17.cyan("Status")
|
|
2119
2535
|
],
|
|
2120
2536
|
colWidths: [8, 45, 12, 10],
|
|
2121
2537
|
style: { head: [], border: [] }
|
|
@@ -2128,35 +2544,35 @@ async function listBookmarks() {
|
|
|
2128
2544
|
problem.questionFrontendId,
|
|
2129
2545
|
problem.title.length > 42 ? problem.title.slice(0, 39) + "..." : problem.title,
|
|
2130
2546
|
colorDifficulty2(problem.difficulty),
|
|
2131
|
-
problem.status === "ac" ?
|
|
2547
|
+
problem.status === "ac" ? chalk17.green("\u2713") : chalk17.gray("-")
|
|
2132
2548
|
]);
|
|
2133
2549
|
} else {
|
|
2134
|
-
table.push([id,
|
|
2550
|
+
table.push([id, chalk17.gray("(not found)"), "-", "-"]);
|
|
2135
2551
|
}
|
|
2136
2552
|
} catch {
|
|
2137
|
-
table.push([id,
|
|
2553
|
+
table.push([id, chalk17.gray("(error fetching)"), "-", "-"]);
|
|
2138
2554
|
}
|
|
2139
2555
|
}
|
|
2140
2556
|
spinner.stop();
|
|
2141
2557
|
console.log(table.toString());
|
|
2142
2558
|
} catch {
|
|
2143
2559
|
spinner.stop();
|
|
2144
|
-
console.log(
|
|
2560
|
+
console.log(chalk17.gray("IDs: ") + bookmarkList.join(", "));
|
|
2145
2561
|
}
|
|
2146
2562
|
} else {
|
|
2147
|
-
console.log(
|
|
2563
|
+
console.log(chalk17.gray("IDs: ") + bookmarkList.join(", "));
|
|
2148
2564
|
console.log();
|
|
2149
|
-
console.log(
|
|
2565
|
+
console.log(chalk17.gray("Login to see problem details"));
|
|
2150
2566
|
}
|
|
2151
2567
|
}
|
|
2152
2568
|
function colorDifficulty2(difficulty) {
|
|
2153
2569
|
switch (difficulty.toLowerCase()) {
|
|
2154
2570
|
case "easy":
|
|
2155
|
-
return
|
|
2571
|
+
return chalk17.green(difficulty);
|
|
2156
2572
|
case "medium":
|
|
2157
|
-
return
|
|
2573
|
+
return chalk17.yellow(difficulty);
|
|
2158
2574
|
case "hard":
|
|
2159
|
-
return
|
|
2575
|
+
return chalk17.red(difficulty);
|
|
2160
2576
|
default:
|
|
2161
2577
|
return difficulty;
|
|
2162
2578
|
}
|
|
@@ -2165,18 +2581,18 @@ function colorDifficulty2(difficulty) {
|
|
|
2165
2581
|
// src/commands/notes.ts
|
|
2166
2582
|
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
|
|
2167
2583
|
import { join as join7 } from "path";
|
|
2168
|
-
import { existsSync as
|
|
2169
|
-
import
|
|
2584
|
+
import { existsSync as existsSync8 } from "fs";
|
|
2585
|
+
import chalk18 from "chalk";
|
|
2170
2586
|
async function notesCommand(problemId, action) {
|
|
2171
2587
|
if (!isProblemId(problemId)) {
|
|
2172
|
-
console.log(
|
|
2173
|
-
console.log(
|
|
2588
|
+
console.log(chalk18.red(`Invalid problem ID: ${problemId}`));
|
|
2589
|
+
console.log(chalk18.gray("Problem ID must be a positive integer"));
|
|
2174
2590
|
return;
|
|
2175
2591
|
}
|
|
2176
2592
|
const noteAction = action === "view" ? "view" : "edit";
|
|
2177
2593
|
const notesDir = join7(config.getWorkDir(), ".notes");
|
|
2178
2594
|
const notePath = join7(notesDir, `${problemId}.md`);
|
|
2179
|
-
if (!
|
|
2595
|
+
if (!existsSync8(notesDir)) {
|
|
2180
2596
|
await mkdir3(notesDir, { recursive: true });
|
|
2181
2597
|
}
|
|
2182
2598
|
if (noteAction === "view") {
|
|
@@ -2186,32 +2602,32 @@ async function notesCommand(problemId, action) {
|
|
|
2186
2602
|
}
|
|
2187
2603
|
}
|
|
2188
2604
|
async function viewNote(notePath, problemId) {
|
|
2189
|
-
if (!
|
|
2190
|
-
console.log(
|
|
2191
|
-
console.log(
|
|
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`));
|
|
2192
2608
|
return;
|
|
2193
2609
|
}
|
|
2194
2610
|
try {
|
|
2195
2611
|
const content = await readFile3(notePath, "utf-8");
|
|
2196
2612
|
console.log();
|
|
2197
|
-
console.log(
|
|
2198
|
-
console.log(
|
|
2613
|
+
console.log(chalk18.bold.cyan(`\u{1F4DD} Notes for Problem ${problemId}`));
|
|
2614
|
+
console.log(chalk18.gray("\u2500".repeat(50)));
|
|
2199
2615
|
console.log();
|
|
2200
2616
|
console.log(content);
|
|
2201
2617
|
} catch (error) {
|
|
2202
|
-
console.log(
|
|
2618
|
+
console.log(chalk18.red("Failed to read notes"));
|
|
2203
2619
|
if (error instanceof Error) {
|
|
2204
|
-
console.log(
|
|
2620
|
+
console.log(chalk18.gray(error.message));
|
|
2205
2621
|
}
|
|
2206
2622
|
}
|
|
2207
2623
|
}
|
|
2208
2624
|
async function editNote(notePath, problemId) {
|
|
2209
|
-
if (!
|
|
2625
|
+
if (!existsSync8(notePath)) {
|
|
2210
2626
|
const template = await generateNoteTemplate(problemId);
|
|
2211
2627
|
await writeFile3(notePath, template, "utf-8");
|
|
2212
|
-
console.log(
|
|
2628
|
+
console.log(chalk18.green(`\u2713 Created notes file for problem ${problemId}`));
|
|
2213
2629
|
}
|
|
2214
|
-
console.log(
|
|
2630
|
+
console.log(chalk18.gray(`Opening: ${notePath}`));
|
|
2215
2631
|
await openInEditor(notePath);
|
|
2216
2632
|
}
|
|
2217
2633
|
async function generateNoteTemplate(problemId) {
|
|
@@ -2268,7 +2684,7 @@ async function generateNoteTemplate(problemId) {
|
|
|
2268
2684
|
}
|
|
2269
2685
|
|
|
2270
2686
|
// src/commands/today.ts
|
|
2271
|
-
import
|
|
2687
|
+
import chalk19 from "chalk";
|
|
2272
2688
|
import ora12 from "ora";
|
|
2273
2689
|
async function todayCommand() {
|
|
2274
2690
|
const { authorized, username } = await requireAuth();
|
|
@@ -2283,48 +2699,48 @@ async function todayCommand() {
|
|
|
2283
2699
|
]);
|
|
2284
2700
|
spinner.stop();
|
|
2285
2701
|
console.log();
|
|
2286
|
-
console.log(
|
|
2287
|
-
console.log(
|
|
2702
|
+
console.log(chalk19.bold.cyan(`\u{1F4CA} Today's Summary`), chalk19.gray(`- ${username}`));
|
|
2703
|
+
console.log(chalk19.gray("\u2500".repeat(50)));
|
|
2288
2704
|
console.log();
|
|
2289
|
-
console.log(
|
|
2290
|
-
console.log(
|
|
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}`));
|
|
2291
2707
|
console.log();
|
|
2292
2708
|
const total = profile.acSubmissionNum.find((s) => s.difficulty === "All");
|
|
2293
2709
|
const easy = profile.acSubmissionNum.find((s) => s.difficulty === "Easy");
|
|
2294
2710
|
const medium = profile.acSubmissionNum.find((s) => s.difficulty === "Medium");
|
|
2295
2711
|
const hard = profile.acSubmissionNum.find((s) => s.difficulty === "Hard");
|
|
2296
|
-
console.log(
|
|
2297
|
-
console.log(` ${
|
|
2298
|
-
console.log(` ${
|
|
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}`);
|
|
2299
2715
|
console.log();
|
|
2300
|
-
console.log(
|
|
2716
|
+
console.log(chalk19.bold.yellow("\u{1F3AF} Today's Challenge:"));
|
|
2301
2717
|
console.log(` ${daily.question.questionFrontendId}. ${daily.question.title}`);
|
|
2302
2718
|
console.log(` ${colorDifficulty3(daily.question.difficulty)}`);
|
|
2303
2719
|
const status = daily.question.status;
|
|
2304
2720
|
if (status === "ac") {
|
|
2305
|
-
console.log(
|
|
2721
|
+
console.log(chalk19.green(" \u2713 Completed!"));
|
|
2306
2722
|
} else if (status === "notac") {
|
|
2307
|
-
console.log(
|
|
2723
|
+
console.log(chalk19.yellow(" \u25CB Attempted"));
|
|
2308
2724
|
} else {
|
|
2309
|
-
console.log(
|
|
2725
|
+
console.log(chalk19.gray(" - Not started"));
|
|
2310
2726
|
}
|
|
2311
2727
|
console.log();
|
|
2312
|
-
console.log(
|
|
2728
|
+
console.log(chalk19.gray(` leetcode pick ${daily.question.questionFrontendId} # Start working on it`));
|
|
2313
2729
|
} catch (error) {
|
|
2314
2730
|
spinner.fail("Failed to fetch progress");
|
|
2315
2731
|
if (error instanceof Error) {
|
|
2316
|
-
console.log(
|
|
2732
|
+
console.log(chalk19.red(error.message));
|
|
2317
2733
|
}
|
|
2318
2734
|
}
|
|
2319
2735
|
}
|
|
2320
2736
|
function colorDifficulty3(difficulty) {
|
|
2321
2737
|
switch (difficulty.toLowerCase()) {
|
|
2322
2738
|
case "easy":
|
|
2323
|
-
return
|
|
2739
|
+
return chalk19.green(difficulty);
|
|
2324
2740
|
case "medium":
|
|
2325
|
-
return
|
|
2741
|
+
return chalk19.yellow(difficulty);
|
|
2326
2742
|
case "hard":
|
|
2327
|
-
return
|
|
2743
|
+
return chalk19.red(difficulty);
|
|
2328
2744
|
default:
|
|
2329
2745
|
return difficulty;
|
|
2330
2746
|
}
|
|
@@ -2332,8 +2748,8 @@ function colorDifficulty3(difficulty) {
|
|
|
2332
2748
|
|
|
2333
2749
|
// src/commands/sync.ts
|
|
2334
2750
|
import { execSync } from "child_process";
|
|
2335
|
-
import { existsSync as
|
|
2336
|
-
import
|
|
2751
|
+
import { existsSync as existsSync9 } from "fs";
|
|
2752
|
+
import chalk20 from "chalk";
|
|
2337
2753
|
import inquirer3 from "inquirer";
|
|
2338
2754
|
import ora13 from "ora";
|
|
2339
2755
|
function isGitInstalled() {
|
|
@@ -2378,7 +2794,7 @@ async function setupGitRepo(workDir) {
|
|
|
2378
2794
|
}
|
|
2379
2795
|
]);
|
|
2380
2796
|
if (!init) {
|
|
2381
|
-
console.log(
|
|
2797
|
+
console.log(chalk20.yellow("Skipping basic git initialization."));
|
|
2382
2798
|
return false;
|
|
2383
2799
|
}
|
|
2384
2800
|
const spinner = ora13("Initializing git repository...").start();
|
|
@@ -2417,12 +2833,12 @@ async function setupRemote(workDir) {
|
|
|
2417
2833
|
return repoUrl;
|
|
2418
2834
|
} catch (error) {
|
|
2419
2835
|
spinner.fail("Failed to create GitHub repository");
|
|
2420
|
-
console.log(
|
|
2836
|
+
console.log(chalk20.red(error));
|
|
2421
2837
|
}
|
|
2422
2838
|
}
|
|
2423
2839
|
}
|
|
2424
2840
|
if (!repoUrl) {
|
|
2425
|
-
console.log(
|
|
2841
|
+
console.log(chalk20.yellow("\nPlease create a new repository on your Git provider and copy the URL."));
|
|
2426
2842
|
const { url } = await inquirer3.prompt([
|
|
2427
2843
|
{
|
|
2428
2844
|
type: "input",
|
|
@@ -2441,21 +2857,21 @@ async function setupRemote(workDir) {
|
|
|
2441
2857
|
if (!currentRemote && repoUrl) {
|
|
2442
2858
|
try {
|
|
2443
2859
|
execSync(`git remote add origin ${repoUrl}`, { cwd: workDir });
|
|
2444
|
-
console.log(
|
|
2860
|
+
console.log(chalk20.green("\u2713 Added remote origin"));
|
|
2445
2861
|
} catch (e) {
|
|
2446
|
-
console.log(
|
|
2862
|
+
console.log(chalk20.red("Failed to add remote origin"));
|
|
2447
2863
|
}
|
|
2448
2864
|
}
|
|
2449
2865
|
return repoUrl || "";
|
|
2450
2866
|
}
|
|
2451
2867
|
async function syncCommand() {
|
|
2452
2868
|
const workDir = config.getWorkDir();
|
|
2453
|
-
if (!
|
|
2454
|
-
console.log(
|
|
2869
|
+
if (!existsSync9(workDir)) {
|
|
2870
|
+
console.log(chalk20.red(`Work directory does not exist: ${workDir}`));
|
|
2455
2871
|
return;
|
|
2456
2872
|
}
|
|
2457
2873
|
if (!isGitInstalled()) {
|
|
2458
|
-
console.log(
|
|
2874
|
+
console.log(chalk20.red("Git is not installed. Please install Git to use command."));
|
|
2459
2875
|
return;
|
|
2460
2876
|
}
|
|
2461
2877
|
if (!isMapRepo(workDir)) {
|
|
@@ -2489,14 +2905,14 @@ async function syncCommand() {
|
|
|
2489
2905
|
} catch (error) {
|
|
2490
2906
|
spinner.fail("Sync failed");
|
|
2491
2907
|
if (error.message) {
|
|
2492
|
-
console.log(
|
|
2908
|
+
console.log(chalk20.red(error.message));
|
|
2493
2909
|
}
|
|
2494
2910
|
}
|
|
2495
2911
|
}
|
|
2496
2912
|
|
|
2497
2913
|
// src/commands/timer.ts
|
|
2498
2914
|
import ora14 from "ora";
|
|
2499
|
-
import
|
|
2915
|
+
import chalk21 from "chalk";
|
|
2500
2916
|
var DEFAULT_TIMES = {
|
|
2501
2917
|
Easy: 20,
|
|
2502
2918
|
Medium: 40,
|
|
@@ -2525,10 +2941,10 @@ async function timerCommand(idOrSlug, options) {
|
|
|
2525
2941
|
return;
|
|
2526
2942
|
}
|
|
2527
2943
|
if (!idOrSlug) {
|
|
2528
|
-
console.log(
|
|
2529
|
-
console.log(
|
|
2530
|
-
console.log(
|
|
2531
|
-
console.log(
|
|
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"));
|
|
2532
2948
|
return;
|
|
2533
2949
|
}
|
|
2534
2950
|
const { authorized } = await requireAuth();
|
|
@@ -2537,11 +2953,11 @@ async function timerCommand(idOrSlug, options) {
|
|
|
2537
2953
|
if (activeTimer) {
|
|
2538
2954
|
const startedAt = new Date(activeTimer.startedAt);
|
|
2539
2955
|
const elapsed = Math.floor((Date.now() - startedAt.getTime()) / 1e3);
|
|
2540
|
-
console.log(
|
|
2541
|
-
console.log(
|
|
2542
|
-
console.log(
|
|
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)}`));
|
|
2543
2959
|
console.log();
|
|
2544
|
-
console.log(
|
|
2960
|
+
console.log(chalk21.gray("Use `leetcode timer --stop` to stop it first."));
|
|
2545
2961
|
return;
|
|
2546
2962
|
}
|
|
2547
2963
|
const spinner = ora14("Fetching problem...").start();
|
|
@@ -2565,65 +2981,65 @@ async function timerCommand(idOrSlug, options) {
|
|
|
2565
2981
|
durationMinutes
|
|
2566
2982
|
);
|
|
2567
2983
|
console.log();
|
|
2568
|
-
console.log(
|
|
2569
|
-
console.log(
|
|
2984
|
+
console.log(chalk21.bold.cyan("\u23F1\uFE0F Interview Mode Started!"));
|
|
2985
|
+
console.log(chalk21.gray("\u2500".repeat(50)));
|
|
2570
2986
|
console.log();
|
|
2571
|
-
console.log(
|
|
2572
|
-
console.log(
|
|
2573
|
-
console.log(
|
|
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")}`));
|
|
2574
2990
|
console.log();
|
|
2575
|
-
console.log(
|
|
2576
|
-
console.log(
|
|
2577
|
-
console.log(
|
|
2578
|
-
console.log(
|
|
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."));
|
|
2579
2995
|
console.log();
|
|
2580
2996
|
await pickCommand(idOrSlug, { open: true });
|
|
2581
2997
|
} catch (error) {
|
|
2582
2998
|
spinner.fail("Failed to start timer");
|
|
2583
2999
|
if (error instanceof Error) {
|
|
2584
|
-
console.log(
|
|
3000
|
+
console.log(chalk21.red(error.message));
|
|
2585
3001
|
}
|
|
2586
3002
|
}
|
|
2587
3003
|
}
|
|
2588
3004
|
async function stopActiveTimer() {
|
|
2589
3005
|
const result = timerStorage.stopTimer();
|
|
2590
3006
|
if (!result) {
|
|
2591
|
-
console.log(
|
|
3007
|
+
console.log(chalk21.yellow("No active timer to stop."));
|
|
2592
3008
|
return;
|
|
2593
3009
|
}
|
|
2594
|
-
console.log(
|
|
2595
|
-
console.log(
|
|
2596
|
-
console.log(
|
|
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)"));
|
|
2597
3013
|
}
|
|
2598
3014
|
async function showTimerStats(problemId) {
|
|
2599
3015
|
if (problemId && /^\d+$/.test(problemId)) {
|
|
2600
3016
|
const times = timerStorage.getSolveTimes(problemId);
|
|
2601
3017
|
if (times.length === 0) {
|
|
2602
|
-
console.log(
|
|
3018
|
+
console.log(chalk21.yellow(`No solve times recorded for problem ${problemId}`));
|
|
2603
3019
|
return;
|
|
2604
3020
|
}
|
|
2605
3021
|
console.log();
|
|
2606
|
-
console.log(
|
|
2607
|
-
console.log(
|
|
3022
|
+
console.log(chalk21.bold(`\u23F1\uFE0F Solve Times for Problem ${problemId}`));
|
|
3023
|
+
console.log(chalk21.gray("\u2500".repeat(40)));
|
|
2608
3024
|
for (const entry of times) {
|
|
2609
3025
|
const date = new Date(entry.solvedAt).toLocaleDateString();
|
|
2610
3026
|
const duration = formatDuration(entry.durationSeconds);
|
|
2611
3027
|
const limit = entry.timerMinutes;
|
|
2612
3028
|
const withinLimit = entry.durationSeconds <= limit * 60;
|
|
2613
3029
|
console.log(
|
|
2614
|
-
` ${date} ${withinLimit ?
|
|
3030
|
+
` ${date} ${withinLimit ? chalk21.green(duration) : chalk21.red(duration)} (limit: ${limit}m)`
|
|
2615
3031
|
);
|
|
2616
3032
|
}
|
|
2617
3033
|
} else {
|
|
2618
3034
|
const stats = timerStorage.getStats();
|
|
2619
3035
|
const allTimes = timerStorage.getAllSolveTimes();
|
|
2620
3036
|
console.log();
|
|
2621
|
-
console.log(
|
|
2622
|
-
console.log(
|
|
3037
|
+
console.log(chalk21.bold("\u23F1\uFE0F Timer Statistics"));
|
|
3038
|
+
console.log(chalk21.gray("\u2500".repeat(40)));
|
|
2623
3039
|
console.log();
|
|
2624
|
-
console.log(` Problems timed: ${
|
|
2625
|
-
console.log(` Total time: ${
|
|
2626
|
-
console.log(` Average time: ${
|
|
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))}`);
|
|
2627
3043
|
console.log();
|
|
2628
3044
|
const recentSolves = [];
|
|
2629
3045
|
for (const [id, times] of Object.entries(allTimes)) {
|
|
@@ -2638,11 +3054,11 @@ async function showTimerStats(problemId) {
|
|
|
2638
3054
|
}
|
|
2639
3055
|
if (recentSolves.length > 0) {
|
|
2640
3056
|
recentSolves.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
|
|
2641
|
-
console.log(
|
|
3057
|
+
console.log(chalk21.bold(" Recent Solves:"));
|
|
2642
3058
|
for (const solve of recentSolves.slice(0, 5)) {
|
|
2643
3059
|
const date = new Date(solve.date).toLocaleDateString();
|
|
2644
3060
|
console.log(
|
|
2645
|
-
|
|
3061
|
+
chalk21.gray(` ${date} `) + chalk21.white(`${solve.problemId}. ${solve.title.substring(0, 25)}`) + chalk21.gray(" ") + chalk21.cyan(formatDuration(solve.duration))
|
|
2646
3062
|
);
|
|
2647
3063
|
}
|
|
2648
3064
|
}
|
|
@@ -2651,7 +3067,7 @@ async function showTimerStats(problemId) {
|
|
|
2651
3067
|
}
|
|
2652
3068
|
|
|
2653
3069
|
// src/commands/collab.ts
|
|
2654
|
-
import
|
|
3070
|
+
import chalk22 from "chalk";
|
|
2655
3071
|
import ora15 from "ora";
|
|
2656
3072
|
import { readFile as readFile4 } from "fs/promises";
|
|
2657
3073
|
|
|
@@ -2662,29 +3078,35 @@ var SUPABASE_ANON_KEY = "sb_publishable_indrKu8VJmASdyLp7w8Hog_OyqT17cV";
|
|
|
2662
3078
|
var supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
|
|
2663
3079
|
|
|
2664
3080
|
// src/storage/collab.ts
|
|
2665
|
-
import
|
|
2666
|
-
import {
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
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"));
|
|
2673
3090
|
}
|
|
2674
|
-
}
|
|
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
|
+
}
|
|
2675
3101
|
var collabStorage = {
|
|
2676
3102
|
getSession() {
|
|
2677
|
-
return
|
|
3103
|
+
return loadCollab().session;
|
|
2678
3104
|
},
|
|
2679
3105
|
setSession(session) {
|
|
2680
|
-
|
|
2681
|
-
collabStore.set("session", session);
|
|
2682
|
-
} else {
|
|
2683
|
-
collabStore.delete("session");
|
|
2684
|
-
}
|
|
3106
|
+
saveCollab({ session });
|
|
2685
3107
|
},
|
|
2686
3108
|
getPath() {
|
|
2687
|
-
return
|
|
3109
|
+
return getCollabPath();
|
|
2688
3110
|
}
|
|
2689
3111
|
};
|
|
2690
3112
|
|
|
@@ -2812,27 +3234,27 @@ async function collabHostCommand(problemId) {
|
|
|
2812
3234
|
}
|
|
2813
3235
|
spinner.succeed("Room created!");
|
|
2814
3236
|
console.log();
|
|
2815
|
-
console.log(
|
|
2816
|
-
console.log(
|
|
3237
|
+
console.log(chalk22.bold.cyan("\u{1F465} Collaborative Coding Session"));
|
|
3238
|
+
console.log(chalk22.gray("\u2500".repeat(50)));
|
|
2817
3239
|
console.log();
|
|
2818
|
-
console.log(
|
|
2819
|
-
console.log(
|
|
3240
|
+
console.log(chalk22.white(`Room Code: ${chalk22.bold.green(result.roomCode)}`));
|
|
3241
|
+
console.log(chalk22.white(`Problem: ${problemId}`));
|
|
2820
3242
|
console.log();
|
|
2821
|
-
console.log(
|
|
2822
|
-
console.log(
|
|
3243
|
+
console.log(chalk22.gray("Share this code with your partner:"));
|
|
3244
|
+
console.log(chalk22.yellow(` leetcode collab join ${result.roomCode}`));
|
|
2823
3245
|
console.log();
|
|
2824
|
-
console.log(
|
|
2825
|
-
console.log(
|
|
2826
|
-
console.log(
|
|
2827
|
-
console.log(
|
|
2828
|
-
console.log(
|
|
2829
|
-
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"));
|
|
2830
3252
|
console.log();
|
|
2831
3253
|
await pickCommand(problemId, { open: true });
|
|
2832
3254
|
} catch (error) {
|
|
2833
3255
|
spinner.fail("Failed to create room");
|
|
2834
3256
|
if (error instanceof Error) {
|
|
2835
|
-
console.log(
|
|
3257
|
+
console.log(chalk22.red(error.message));
|
|
2836
3258
|
}
|
|
2837
3259
|
}
|
|
2838
3260
|
}
|
|
@@ -2848,32 +3270,32 @@ async function collabJoinCommand(roomCode) {
|
|
|
2848
3270
|
}
|
|
2849
3271
|
spinner.succeed("Joined room!");
|
|
2850
3272
|
console.log();
|
|
2851
|
-
console.log(
|
|
2852
|
-
console.log(
|
|
3273
|
+
console.log(chalk22.bold.cyan("\u{1F465} Collaborative Coding Session"));
|
|
3274
|
+
console.log(chalk22.gray("\u2500".repeat(50)));
|
|
2853
3275
|
console.log();
|
|
2854
|
-
console.log(
|
|
2855
|
-
console.log(
|
|
3276
|
+
console.log(chalk22.white(`Room Code: ${chalk22.bold.green(roomCode.toUpperCase())}`));
|
|
3277
|
+
console.log(chalk22.white(`Problem: ${result.problemId}`));
|
|
2856
3278
|
console.log();
|
|
2857
|
-
console.log(
|
|
2858
|
-
console.log(
|
|
2859
|
-
console.log(
|
|
2860
|
-
console.log(
|
|
2861
|
-
console.log(
|
|
2862
|
-
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"));
|
|
2863
3285
|
console.log();
|
|
2864
3286
|
await pickCommand(result.problemId, { open: true });
|
|
2865
3287
|
} catch (error) {
|
|
2866
3288
|
spinner.fail("Failed to join room");
|
|
2867
3289
|
if (error instanceof Error) {
|
|
2868
|
-
console.log(
|
|
3290
|
+
console.log(chalk22.red(error.message));
|
|
2869
3291
|
}
|
|
2870
3292
|
}
|
|
2871
3293
|
}
|
|
2872
3294
|
async function collabSyncCommand() {
|
|
2873
3295
|
const session = collabService.getSession();
|
|
2874
3296
|
if (!session) {
|
|
2875
|
-
console.log(
|
|
2876
|
-
console.log(
|
|
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."));
|
|
2877
3299
|
return;
|
|
2878
3300
|
}
|
|
2879
3301
|
const spinner = ora15("Syncing your code...").start();
|
|
@@ -2887,7 +3309,7 @@ async function collabSyncCommand() {
|
|
|
2887
3309
|
const result = await collabService.syncCode(code);
|
|
2888
3310
|
if (result.success) {
|
|
2889
3311
|
spinner.succeed("Code synced successfully!");
|
|
2890
|
-
console.log(
|
|
3312
|
+
console.log(chalk22.gray(`Uploaded ${code.split("\n").length} lines from ${filePath}`));
|
|
2891
3313
|
} else {
|
|
2892
3314
|
spinner.fail(result.error || "Sync failed");
|
|
2893
3315
|
}
|
|
@@ -2895,8 +3317,8 @@ async function collabSyncCommand() {
|
|
|
2895
3317
|
async function collabCompareCommand() {
|
|
2896
3318
|
const session = collabService.getSession();
|
|
2897
3319
|
if (!session) {
|
|
2898
|
-
console.log(
|
|
2899
|
-
console.log(
|
|
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."));
|
|
2900
3322
|
return;
|
|
2901
3323
|
}
|
|
2902
3324
|
const spinner = ora15("Fetching solutions...").start();
|
|
@@ -2914,224 +3336,778 @@ async function collabCompareCommand() {
|
|
|
2914
3336
|
}
|
|
2915
3337
|
spinner.stop();
|
|
2916
3338
|
if (!partnerResult.code) {
|
|
2917
|
-
console.log(
|
|
2918
|
-
console.log(
|
|
3339
|
+
console.log(chalk22.yellow("Partner has not synced their code yet."));
|
|
3340
|
+
console.log(chalk22.gray("Ask them to run `leetcode collab sync`."));
|
|
2919
3341
|
return;
|
|
2920
3342
|
}
|
|
2921
3343
|
console.log();
|
|
2922
|
-
console.log(
|
|
2923
|
-
console.log(
|
|
3344
|
+
console.log(chalk22.bold.cyan("\u{1F4CA} Solution Comparison"));
|
|
3345
|
+
console.log(chalk22.gray("\u2500".repeat(60)));
|
|
2924
3346
|
console.log();
|
|
2925
|
-
console.log(
|
|
2926
|
-
console.log(
|
|
3347
|
+
console.log(chalk22.bold.green(`\u25B8 Your Solution (${session.username})`));
|
|
3348
|
+
console.log(chalk22.gray("\u2500".repeat(60)));
|
|
2927
3349
|
const myLines = myCode.split("\n");
|
|
2928
3350
|
for (let i = 0; i < myLines.length; i++) {
|
|
2929
3351
|
const lineNum = String(i + 1).padStart(3, " ");
|
|
2930
|
-
console.log(`${
|
|
3352
|
+
console.log(`${chalk22.gray(lineNum)} ${myLines[i]}`);
|
|
2931
3353
|
}
|
|
2932
3354
|
console.log();
|
|
2933
|
-
console.log(
|
|
2934
|
-
console.log(
|
|
3355
|
+
console.log(chalk22.bold.blue(`\u25B8 ${partnerResult.username}'s Solution`));
|
|
3356
|
+
console.log(chalk22.gray("\u2500".repeat(60)));
|
|
2935
3357
|
const partnerLines = partnerResult.code.split("\n");
|
|
2936
3358
|
for (let i = 0; i < partnerLines.length; i++) {
|
|
2937
3359
|
const lineNum = String(i + 1).padStart(3, " ");
|
|
2938
|
-
console.log(`${
|
|
3360
|
+
console.log(`${chalk22.gray(lineNum)} ${partnerLines[i]}`);
|
|
2939
3361
|
}
|
|
2940
3362
|
console.log();
|
|
2941
|
-
console.log(
|
|
2942
|
-
console.log(
|
|
3363
|
+
console.log(chalk22.gray("\u2500".repeat(60)));
|
|
3364
|
+
console.log(chalk22.gray(`Your code: ${myLines.length} lines | Partner: ${partnerLines.length} lines`));
|
|
2943
3365
|
console.log();
|
|
2944
3366
|
}
|
|
2945
3367
|
async function collabLeaveCommand() {
|
|
2946
3368
|
const session = collabService.getSession();
|
|
2947
3369
|
if (!session) {
|
|
2948
|
-
console.log(
|
|
3370
|
+
console.log(chalk22.yellow("No active collaboration session."));
|
|
2949
3371
|
return;
|
|
2950
3372
|
}
|
|
2951
3373
|
await collabService.leaveRoom();
|
|
2952
|
-
console.log(
|
|
3374
|
+
console.log(chalk22.green("\u2713 Left the collaboration session."));
|
|
2953
3375
|
}
|
|
2954
3376
|
async function collabStatusCommand() {
|
|
2955
3377
|
const session = collabService.getSession();
|
|
2956
3378
|
if (!session) {
|
|
2957
|
-
console.log(
|
|
2958
|
-
console.log(
|
|
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."));
|
|
2959
3381
|
return;
|
|
2960
3382
|
}
|
|
2961
3383
|
const status = await collabService.getRoomStatus();
|
|
2962
3384
|
if ("error" in status) {
|
|
2963
|
-
console.log(
|
|
3385
|
+
console.log(chalk22.red(status.error));
|
|
2964
3386
|
return;
|
|
2965
3387
|
}
|
|
2966
3388
|
console.log();
|
|
2967
|
-
console.log(
|
|
2968
|
-
console.log(
|
|
2969
|
-
console.log(` Room: ${
|
|
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)}`);
|
|
2970
3392
|
console.log(` Problem: ${session.problemId}`);
|
|
2971
3393
|
console.log(` Role: ${session.isHost ? "Host" : "Guest"}`);
|
|
2972
3394
|
console.log();
|
|
2973
|
-
console.log(
|
|
2974
|
-
console.log(` Host: ${status.host} ${status.hasHostCode ?
|
|
2975
|
-
console.log(` Guest: ${status.guest ||
|
|
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)}`);
|
|
2976
3863
|
console.log();
|
|
2977
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
|
+
}
|
|
2978
3941
|
|
|
2979
3942
|
// src/index.ts
|
|
2980
3943
|
var program = new Command();
|
|
2981
3944
|
program.configureHelp({
|
|
2982
3945
|
sortSubcommands: true,
|
|
2983
|
-
subcommandTerm: (cmd) =>
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
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)
|
|
2987
3955
|
});
|
|
2988
|
-
program.name("leetcode").usage("[command] [options]").description(
|
|
2989
|
-
${
|
|
2990
|
-
${
|
|
2991
|
-
${
|
|
2992
|
-
${
|
|
2993
|
-
${
|
|
2994
|
-
${
|
|
2995
|
-
${
|
|
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
|
|
2996
3964
|
`);
|
|
2997
3965
|
program.command("login").description("Login to LeetCode with browser cookies").addHelpText("after", `
|
|
2998
|
-
${
|
|
2999
|
-
1. Open ${
|
|
3966
|
+
${chalk26.yellow("How to login:")}
|
|
3967
|
+
1. Open ${chalk26.cyan("https://leetcode.com")} in your browser
|
|
3000
3968
|
2. Login to your account
|
|
3001
3969
|
3. Open Developer Tools (F12) \u2192 Application \u2192 Cookies
|
|
3002
|
-
4. Copy values of ${
|
|
3970
|
+
4. Copy values of ${chalk26.green("LEETCODE_SESSION")} and ${chalk26.green("csrftoken")}
|
|
3003
3971
|
5. Paste when prompted by this command
|
|
3004
3972
|
`).action(loginCommand);
|
|
3005
3973
|
program.command("logout").description("Clear stored credentials").action(logoutCommand);
|
|
3006
3974
|
program.command("whoami").description("Check current login status").action(whoamiCommand);
|
|
3007
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", `
|
|
3008
|
-
${
|
|
3009
|
-
${
|
|
3010
|
-
${
|
|
3011
|
-
${
|
|
3012
|
-
${
|
|
3013
|
-
${
|
|
3014
|
-
${
|
|
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
|
|
3015
3983
|
`).action(listCommand);
|
|
3016
3984
|
program.command("show <id>").alias("s").description("Show problem description").addHelpText("after", `
|
|
3017
|
-
${
|
|
3018
|
-
${
|
|
3019
|
-
${
|
|
3020
|
-
${
|
|
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
|
|
3021
3989
|
`).action(showCommand);
|
|
3022
3990
|
program.command("daily").alias("d").description("Show today's daily challenge").addHelpText("after", `
|
|
3023
|
-
${
|
|
3024
|
-
${
|
|
3025
|
-
${
|
|
3991
|
+
${chalk26.yellow("Examples:")}
|
|
3992
|
+
${chalk26.cyan("$ leetcode daily")} Show today's challenge
|
|
3993
|
+
${chalk26.cyan("$ leetcode d")} Short alias
|
|
3026
3994
|
`).action(dailyCommand);
|
|
3027
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", `
|
|
3028
|
-
${
|
|
3029
|
-
${
|
|
3030
|
-
${
|
|
3031
|
-
${
|
|
3032
|
-
${
|
|
3033
|
-
${
|
|
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
|
|
3034
4002
|
`).action(randomCommand);
|
|
3035
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", `
|
|
3036
|
-
${
|
|
3037
|
-
${
|
|
3038
|
-
${
|
|
3039
|
-
${
|
|
3040
|
-
${
|
|
3041
|
-
${
|
|
3042
|
-
|
|
3043
|
-
${
|
|
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/")}
|
|
3044
4012
|
`).action(async (id, options) => {
|
|
3045
4013
|
await pickCommand(id, options);
|
|
3046
4014
|
});
|
|
3047
4015
|
program.command("pick-batch <ids...>").description("Generate solution files for multiple problems").option("-l, --lang <language>", "Programming language for the solutions").addHelpText("after", `
|
|
3048
|
-
${
|
|
3049
|
-
${
|
|
3050
|
-
${
|
|
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
|
|
3051
4019
|
`).action(batchPickCommand);
|
|
3052
|
-
program.command("test <file>").alias("t").description("Test solution against sample test cases").option("-c, --testcase <testcase>", "Custom test case").addHelpText("after", `
|
|
3053
|
-
${
|
|
3054
|
-
${
|
|
3055
|
-
${
|
|
3056
|
-
${
|
|
3057
|
-
${
|
|
3058
|
-
${
|
|
3059
|
-
|
|
3060
|
-
|
|
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.")}
|
|
3061
4030
|
`).action(testCommand);
|
|
3062
4031
|
program.command("submit <file>").alias("x").description("Submit solution to LeetCode").addHelpText("after", `
|
|
3063
|
-
${
|
|
3064
|
-
${
|
|
3065
|
-
${
|
|
3066
|
-
${
|
|
3067
|
-
${
|
|
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
|
|
3068
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);
|
|
3069
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", `
|
|
3070
|
-
${
|
|
3071
|
-
${
|
|
3072
|
-
${
|
|
3073
|
-
${
|
|
3074
|
-
${
|
|
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
|
|
3075
4051
|
`).action(submissionsCommand);
|
|
3076
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", `
|
|
3077
|
-
${
|
|
3078
|
-
${
|
|
4053
|
+
${chalk26.yellow("Options Explained:")}
|
|
4054
|
+
${chalk26.cyan("-c, --calendar")} Shows a table of your weekly submissions and active days
|
|
3079
4055
|
for the past 12 weeks. Useful for tracking consistency.
|
|
3080
4056
|
|
|
3081
|
-
${
|
|
4057
|
+
${chalk26.cyan("-s, --skills")} Shows how many problems you solved per topic tag,
|
|
3082
4058
|
grouped by difficulty (Fundamental/Intermediate/Advanced).
|
|
3083
4059
|
Helps identify your strong and weak areas.
|
|
3084
4060
|
|
|
3085
|
-
${
|
|
4061
|
+
${chalk26.cyan("-t, --trend")} Shows a bar chart of daily submissions for the past week.
|
|
3086
4062
|
Visualizes your recent coding activity day by day.
|
|
3087
4063
|
|
|
3088
|
-
${
|
|
3089
|
-
${
|
|
3090
|
-
${
|
|
3091
|
-
${
|
|
3092
|
-
${
|
|
3093
|
-
${
|
|
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
|
|
3094
4070
|
`).action((username, options) => statCommand(username, options));
|
|
3095
4071
|
program.command("today").description("Show today's progress summary").addHelpText("after", `
|
|
3096
|
-
${
|
|
3097
|
-
${
|
|
4072
|
+
${chalk26.yellow("Examples:")}
|
|
4073
|
+
${chalk26.cyan("$ leetcode today")} Show streak, solved, and daily challenge
|
|
3098
4074
|
`).action(todayCommand);
|
|
3099
4075
|
program.command("bookmark <action> [id]").description("Manage problem bookmarks").addHelpText("after", `
|
|
3100
|
-
${
|
|
3101
|
-
${
|
|
3102
|
-
${
|
|
3103
|
-
${
|
|
3104
|
-
${
|
|
3105
|
-
|
|
3106
|
-
${
|
|
3107
|
-
${
|
|
3108
|
-
${
|
|
3109
|
-
${
|
|
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
|
|
3110
4086
|
`).action(bookmarkCommand);
|
|
3111
4087
|
program.command("note <id> [action]").description("View or edit notes for a problem").addHelpText("after", `
|
|
3112
|
-
${
|
|
3113
|
-
${
|
|
3114
|
-
${
|
|
3115
|
-
|
|
3116
|
-
${
|
|
3117
|
-
${
|
|
3118
|
-
${
|
|
3119
|
-
${
|
|
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
|
|
3120
4096
|
`).action(notesCommand);
|
|
3121
4097
|
program.command("sync").description("Sync solutions to Git repository").addHelpText("after", `
|
|
3122
|
-
${
|
|
3123
|
-
${
|
|
4098
|
+
${chalk26.yellow("Examples:")}
|
|
4099
|
+
${chalk26.cyan("$ leetcode sync")} Sync all solutions to remote
|
|
3124
4100
|
`).action(syncCommand);
|
|
3125
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", `
|
|
3126
|
-
${
|
|
3127
|
-
${
|
|
3128
|
-
${
|
|
3129
|
-
${
|
|
3130
|
-
${
|
|
3131
|
-
${
|
|
3132
|
-
${
|
|
3133
|
-
|
|
3134
|
-
${
|
|
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")}
|
|
3135
4111
|
`).action(async (options) => {
|
|
3136
4112
|
if (options.interactive) {
|
|
3137
4113
|
await configInteractiveCommand();
|
|
@@ -3140,31 +4116,37 @@ ${chalk22.gray("Supported languages: typescript, javascript, python3, java, cpp,
|
|
|
3140
4116
|
}
|
|
3141
4117
|
});
|
|
3142
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", `
|
|
3143
|
-
${
|
|
4119
|
+
${chalk26.yellow("How it works:")}
|
|
3144
4120
|
Start a problem with a countdown timer to simulate interview conditions.
|
|
3145
4121
|
Default time limits: Easy (20 min), Medium (40 min), Hard (60 min).
|
|
3146
4122
|
Your solve times are recorded when you submit successfully.
|
|
3147
4123
|
|
|
3148
|
-
${
|
|
3149
|
-
${
|
|
3150
|
-
${
|
|
3151
|
-
${
|
|
3152
|
-
${
|
|
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
|
|
3153
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);
|
|
3154
4136
|
var collabCmd = program.command("collab").description("Collaborative coding with a partner").addHelpText("after", `
|
|
3155
|
-
${
|
|
3156
|
-
${
|
|
3157
|
-
${
|
|
3158
|
-
${
|
|
3159
|
-
${
|
|
3160
|
-
${
|
|
3161
|
-
${
|
|
3162
|
-
|
|
3163
|
-
${
|
|
3164
|
-
${
|
|
3165
|
-
${
|
|
3166
|
-
${
|
|
3167
|
-
${
|
|
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
|
|
3168
4150
|
`);
|
|
3169
4151
|
collabCmd.command("host <problemId>").description("Host a collaboration session").action(collabHostCommand);
|
|
3170
4152
|
collabCmd.command("join <roomCode>").description("Join a collaboration session").action(collabJoinCommand);
|
|
@@ -3172,12 +4154,31 @@ collabCmd.command("sync").description("Sync your code with partner").action(coll
|
|
|
3172
4154
|
collabCmd.command("compare").description("Compare your solution with partner").action(collabCompareCommand);
|
|
3173
4155
|
collabCmd.command("leave").description("Leave the collaboration session").action(collabLeaveCommand);
|
|
3174
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);
|
|
3175
4176
|
program.showHelpAfterError("(add --help for additional information)");
|
|
3176
4177
|
program.parse();
|
|
3177
4178
|
if (!process.argv.slice(2).length) {
|
|
3178
4179
|
console.log();
|
|
3179
|
-
console.log(
|
|
3180
|
-
console.log(
|
|
4180
|
+
console.log(chalk26.bold.cyan(" \u{1F525} LeetCode CLI"));
|
|
4181
|
+
console.log(chalk26.gray(" A modern command-line interface for LeetCode"));
|
|
3181
4182
|
console.log();
|
|
3182
4183
|
program.outputHelp();
|
|
3183
4184
|
}
|