@night-slayer18/leetcode-cli 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +12 -0
  2. package/dist/index.js +229 -3988
  3. package/package.json +11 -3
package/dist/index.js CHANGED
@@ -1,127 +1,5 @@
1
1
  #!/usr/bin/env node
2
-
3
- // src/index.ts
4
- import { Command } from "commander";
5
- import chalk26 from "chalk";
6
-
7
- // src/commands/login.ts
8
- import inquirer from "inquirer";
9
- import ora from "ora";
10
- import chalk from "chalk";
11
-
12
- // src/api/client.ts
13
- import got from "got";
14
- import { z as z2 } from "zod";
15
-
16
- // src/schemas/api.ts
17
- import { z } from "zod";
18
- var TopicTagSchema = z.object({
19
- name: z.string(),
20
- slug: z.string()
21
- });
22
- var CompanyTagSchema = z.object({
23
- name: z.string(),
24
- slug: z.string()
25
- });
26
- var CodeSnippetSchema = z.object({
27
- lang: z.string(),
28
- langSlug: z.string(),
29
- code: z.string()
30
- });
31
- var ProblemSchema = z.object({
32
- questionId: z.string(),
33
- questionFrontendId: z.string(),
34
- title: z.string(),
35
- titleSlug: z.string(),
36
- difficulty: z.enum(["Easy", "Medium", "Hard"]),
37
- isPaidOnly: z.boolean(),
38
- acRate: z.number().optional().default(0),
39
- topicTags: z.array(TopicTagSchema),
40
- status: z.enum(["ac", "notac"]).nullable()
41
- });
42
- var ProblemDetailSchema = ProblemSchema.extend({
43
- content: z.string().nullable(),
44
- codeSnippets: z.array(CodeSnippetSchema).nullable(),
45
- sampleTestCase: z.string(),
46
- exampleTestcases: z.string(),
47
- hints: z.array(z.string()),
48
- companyTags: z.array(CompanyTagSchema).nullable(),
49
- stats: z.string()
50
- });
51
- var DailyChallengeSchema = z.object({
52
- date: z.string(),
53
- link: z.string(),
54
- question: ProblemSchema
55
- });
56
- var SubmissionSchema = z.object({
57
- id: z.string(),
58
- statusDisplay: z.string(),
59
- lang: z.string(),
60
- runtime: z.string(),
61
- timestamp: z.string(),
62
- memory: z.string()
63
- });
64
- var SubmissionDetailsSchema = z.object({
65
- code: z.string(),
66
- lang: z.object({
67
- name: z.string()
68
- })
69
- });
70
- var TestResultSchema = z.object({
71
- status_code: z.number(),
72
- status_msg: z.string(),
73
- state: z.string(),
74
- run_success: z.boolean(),
75
- code_answer: z.array(z.string()).optional(),
76
- expected_code_answer: z.array(z.string()).optional(),
77
- correct_answer: z.boolean().optional(),
78
- std_output_list: z.array(z.string()).optional(),
79
- compile_error: z.string().optional(),
80
- runtime_error: z.string().optional()
81
- });
82
- var SubmissionResultSchema = z.object({
83
- status_code: z.number(),
84
- status_msg: z.string(),
85
- state: z.string(),
86
- run_success: z.boolean(),
87
- total_correct: z.number(),
88
- total_testcases: z.number(),
89
- status_runtime: z.string(),
90
- status_memory: z.string(),
91
- runtime_percentile: z.number(),
92
- memory_percentile: z.number(),
93
- code_output: z.string().optional(),
94
- std_output: z.string().optional(),
95
- expected_output: z.string().optional(),
96
- compile_error: z.string().optional(),
97
- runtime_error: z.string().optional(),
98
- last_testcase: z.string().optional()
99
- });
100
- var UserProfileSchema = z.object({
101
- username: z.string(),
102
- profile: z.object({
103
- realName: z.string(),
104
- ranking: z.number()
105
- }),
106
- submitStatsGlobal: z.object({
107
- acSubmissionNum: z.array(z.object({
108
- difficulty: z.string(),
109
- count: z.number()
110
- }))
111
- }),
112
- userCalendar: z.object({
113
- streak: z.number(),
114
- totalActiveDays: z.number(),
115
- submissionCalendar: z.string().optional()
116
- })
117
- });
118
- var UserStatusSchema = z.object({
119
- isSignedIn: z.boolean(),
120
- username: z.string().nullable()
121
- });
122
-
123
- // src/api/queries.ts
124
- var PROBLEM_LIST_QUERY = `
2
+ import {Command}from'commander';import l from'chalk';import eo from'inquirer';import Ie from'ora';import pn from'got';import {z}from'zod';import fn from'conf';import {homedir}from'os';import {join,dirname,basename,extname,resolve,sep}from'path';import Oe from'cli-table3';import {mkdir,writeFile,readFile,readdir}from'fs/promises';import {existsSync,writeFileSync,readFileSync,unlinkSync,mkdirSync}from'fs';import {execSync,spawn}from'child_process';import An from'open';import {createClient}from'@supabase/supabase-js';import {diffLines}from'diff';import {fileURLToPath}from'url';var mn=z.object({name:z.string(),slug:z.string()}),dn=z.object({name:z.string(),slug:z.string()}),un=z.object({lang:z.string(),langSlug:z.string(),code:z.string()}),Te=z.object({questionId:z.string(),questionFrontendId:z.string(),title:z.string(),titleSlug:z.string(),difficulty:z.enum(["Easy","Medium","Hard"]),isPaidOnly:z.boolean(),acRate:z.number().optional().default(0),topicTags:z.array(mn),status:z.enum(["ac","notac"]).nullable()}),lo=Te.extend({content:z.string().nullable(),codeSnippets:z.array(un).nullable(),sampleTestCase:z.string(),exampleTestcases:z.string(),hints:z.array(z.string()),companyTags:z.array(dn).nullable(),stats:z.string()}),co=z.object({date:z.string(),link:z.string(),question:Te}),go=z.object({id:z.string(),statusDisplay:z.string(),lang:z.string(),runtime:z.string(),timestamp:z.string(),memory:z.string()}),mo=z.object({code:z.string(),lang:z.object({name:z.string()})}),uo=z.object({status_code:z.number(),status_msg:z.string(),state:z.string(),run_success:z.boolean(),code_answer:z.array(z.string()).optional(),expected_code_answer:z.array(z.string()).optional(),correct_answer:z.boolean().optional(),std_output_list:z.array(z.string()).optional(),compile_error:z.string().optional(),runtime_error:z.string().optional()}),po=z.object({status_code:z.number(),status_msg:z.string(),state:z.string(),run_success:z.boolean(),total_correct:z.number(),total_testcases:z.number(),status_runtime:z.string(),status_memory:z.string(),runtime_percentile:z.number(),memory_percentile:z.number(),code_output:z.string().optional(),std_output:z.string().optional(),expected_output:z.string().optional(),compile_error:z.string().optional(),runtime_error:z.string().optional(),last_testcase:z.string().optional()}),fo=z.object({username:z.string(),profile:z.object({realName:z.string(),ranking:z.number()}),submitStatsGlobal:z.object({acSubmissionNum:z.array(z.object({difficulty:z.string(),count:z.number()}))}),userCalendar:z.object({streak:z.number(),totalActiveDays:z.number(),submissionCalendar:z.string().optional()})}),yo=z.object({isSignedIn:z.boolean(),username:z.string().nullable()});var ho=`
125
3
  query problemsetQuestionList($categorySlug: String, $limit: Int, $skip: Int, $filters: QuestionListFilterInput) {
126
4
  problemsetQuestionList: questionList(
127
5
  categorySlug: $categorySlug
@@ -146,8 +24,7 @@ var PROBLEM_LIST_QUERY = `
146
24
  }
147
25
  }
148
26
  }
149
- `;
150
- var PROBLEM_DETAIL_QUERY = `
27
+ `,bo=`
151
28
  query questionData($titleSlug: String!) {
152
29
  question(titleSlug: $titleSlug) {
153
30
  questionId
@@ -177,16 +54,14 @@ var PROBLEM_DETAIL_QUERY = `
177
54
  status
178
55
  }
179
56
  }
180
- `;
181
- var USER_STATUS_QUERY = `
57
+ `,wo=`
182
58
  query globalData {
183
59
  userStatus {
184
60
  isSignedIn
185
61
  username
186
62
  }
187
63
  }
188
- `;
189
- var USER_PROFILE_QUERY = `
64
+ `,So=`
190
65
  query userPublicProfile($username: String!) {
191
66
  matchedUser(username: $username) {
192
67
  username
@@ -207,8 +82,7 @@ var USER_PROFILE_QUERY = `
207
82
  }
208
83
  }
209
84
  }
210
- `;
211
- var SKILL_STATS_QUERY = `
85
+ `,$o=`
212
86
  query skillStats($username: String!) {
213
87
  matchedUser(username: $username) {
214
88
  tagProblemCounts {
@@ -230,8 +104,7 @@ var SKILL_STATS_QUERY = `
230
104
  }
231
105
  }
232
106
  }
233
- `;
234
- var DAILY_CHALLENGE_QUERY = `
107
+ `,ko=`
235
108
  query questionOfToday {
236
109
  activeDailyCodingChallengeQuestion {
237
110
  date
@@ -252,8 +125,7 @@ var DAILY_CHALLENGE_QUERY = `
252
125
  }
253
126
  }
254
127
  }
255
- `;
256
- var SUBMISSION_LIST_QUERY = `
128
+ `,vo=`
257
129
  query submissionList($questionSlug: String!, $limit: Int, $offset: Int) {
258
130
  questionSubmissionList(
259
131
  questionSlug: $questionSlug
@@ -270,15 +142,13 @@ var SUBMISSION_LIST_QUERY = `
270
142
  }
271
143
  }
272
144
  }
273
- `;
274
- var RANDOM_PROBLEM_QUERY = `
145
+ `,Co=`
275
146
  query randomQuestion($categorySlug: String, $filters: QuestionListFilterInput) {
276
147
  randomQuestion(categorySlug: $categorySlug, filters: $filters) {
277
148
  titleSlug
278
149
  }
279
150
  }
280
- `;
281
- var SUBMISSION_DETAILS_QUERY = `
151
+ `,Po=`
282
152
  query submissionDetails($submissionId: Int!) {
283
153
  submissionDetails(submissionId: $submissionId) {
284
154
  code
@@ -287,2375 +157,48 @@ var SUBMISSION_DETAILS_QUERY = `
287
157
  }
288
158
  }
289
159
  }
290
- `;
291
-
292
- // src/api/client.ts
293
- var LEETCODE_BASE_URL = "https://leetcode.com";
294
- var LEETCODE_GRAPHQL_URL = `${LEETCODE_BASE_URL}/graphql`;
295
- var LeetCodeClient = class {
296
- client;
297
- credentials = null;
298
- constructor() {
299
- this.client = got.extend({
300
- prefixUrl: LEETCODE_BASE_URL,
301
- headers: {
302
- "Content-Type": "application/json",
303
- "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
304
- "Origin": LEETCODE_BASE_URL,
305
- "Referer": `${LEETCODE_BASE_URL}/`
306
- },
307
- timeout: { request: 3e4 },
308
- retry: { limit: 2 }
309
- });
310
- }
311
- setCredentials(credentials2) {
312
- this.credentials = credentials2;
313
- this.client = this.client.extend({
314
- headers: {
315
- "Cookie": `LEETCODE_SESSION=${credentials2.session}; csrftoken=${credentials2.csrfToken}`,
316
- "X-CSRFToken": credentials2.csrfToken
317
- }
318
- });
319
- }
320
- getCredentials() {
321
- return this.credentials;
322
- }
323
- async graphql(query, variables = {}) {
324
- const response = await this.client.post("graphql", {
325
- json: { query, variables }
326
- }).json();
327
- if (response.errors?.length) {
328
- throw new Error(`GraphQL Error: ${response.errors[0].message}`);
329
- }
330
- return response.data;
331
- }
332
- async checkAuth() {
333
- const data = await this.graphql(USER_STATUS_QUERY);
334
- const validated = UserStatusSchema.parse(data.userStatus);
335
- return validated;
336
- }
337
- async getProblems(filters = {}) {
338
- const variables = {
339
- categorySlug: "",
340
- limit: filters.limit ?? 50,
341
- skip: filters.skip ?? 0,
342
- filters: {}
343
- };
344
- if (filters.difficulty) {
345
- variables.filters.difficulty = filters.difficulty;
346
- }
347
- if (filters.status) {
348
- variables.filters.status = filters.status;
349
- }
350
- if (filters.tags?.length) {
351
- variables.filters.tags = filters.tags;
352
- }
353
- if (filters.searchKeywords) {
354
- variables.filters.searchKeywords = filters.searchKeywords;
355
- }
356
- const data = await this.graphql(PROBLEM_LIST_QUERY, variables);
357
- const validatedProblems = z2.array(ProblemSchema).parse(data.problemsetQuestionList.questions);
358
- return {
359
- total: data.problemsetQuestionList.total,
360
- problems: validatedProblems
361
- };
362
- }
363
- async getProblem(titleSlug) {
364
- const data = await this.graphql(
365
- PROBLEM_DETAIL_QUERY,
366
- { titleSlug }
367
- );
368
- const validated = ProblemDetailSchema.parse(data.question);
369
- return validated;
370
- }
371
- async getProblemById(id) {
372
- const { problems } = await this.getProblems({ searchKeywords: id, limit: 10 });
373
- const problem = problems.find((p) => p.questionFrontendId === id);
374
- if (!problem) {
375
- throw new Error(`Problem #${id} not found`);
376
- }
377
- return this.getProblem(problem.titleSlug);
378
- }
379
- async getDailyChallenge() {
380
- const data = await this.graphql(DAILY_CHALLENGE_QUERY);
381
- const validated = DailyChallengeSchema.parse(data.activeDailyCodingChallengeQuestion);
382
- return validated;
383
- }
384
- async getRandomProblem(filters = {}) {
385
- const variables = {
386
- categorySlug: "",
387
- filters: {}
388
- };
389
- if (filters.difficulty) {
390
- variables.filters.difficulty = filters.difficulty;
391
- }
392
- if (filters.tags?.length) {
393
- variables.filters.tags = filters.tags;
394
- }
395
- const data = await this.graphql(RANDOM_PROBLEM_QUERY, variables);
396
- const validated = z2.object({ titleSlug: z2.string() }).parse(data.randomQuestion);
397
- return validated.titleSlug;
398
- }
399
- async getUserProfile(username) {
400
- const data = await this.graphql(USER_PROFILE_QUERY, { username });
401
- const user = data.matchedUser;
402
- const validated = UserProfileSchema.parse(user);
403
- return {
404
- username: validated.username,
405
- realName: validated.profile.realName,
406
- ranking: validated.profile.ranking,
407
- acSubmissionNum: validated.submitStatsGlobal.acSubmissionNum,
408
- streak: validated.userCalendar.streak,
409
- totalActiveDays: validated.userCalendar.totalActiveDays,
410
- submissionCalendar: user.userCalendar.submissionCalendar
411
- };
412
- }
413
- async getSkillStats(username) {
414
- const data = await this.graphql(SKILL_STATS_QUERY, { username });
415
- return data.matchedUser.tagProblemCounts;
416
- }
417
- async getSubmissionList(slug, limit = 20, offset = 0) {
418
- const data = await this.graphql(SUBMISSION_LIST_QUERY, { questionSlug: slug, limit, offset });
419
- const validated = z2.array(SubmissionSchema).parse(data.questionSubmissionList.submissions);
420
- return validated;
421
- }
422
- async getSubmissionDetails(submissionId) {
423
- const data = await this.graphql(SUBMISSION_DETAILS_QUERY, { submissionId });
424
- const validated = SubmissionDetailsSchema.parse(data.submissionDetails);
425
- return validated;
426
- }
427
- async testSolution(titleSlug, code, lang, testcases, questionId) {
428
- const response = await this.client.post(`problems/${titleSlug}/interpret_solution/`, {
429
- json: {
430
- data_input: testcases,
431
- lang,
432
- typed_code: code,
433
- question_id: questionId
434
- }
435
- }).json();
436
- return this.pollSubmission(response.interpret_id, "interpret", TestResultSchema);
437
- }
438
- async submitSolution(titleSlug, code, lang, questionId) {
439
- const response = await this.client.post(`problems/${titleSlug}/submit/`, {
440
- json: {
441
- lang,
442
- typed_code: code,
443
- question_id: questionId
444
- }
445
- }).json();
446
- return this.pollSubmission(response.submission_id.toString(), "submission", SubmissionResultSchema);
447
- }
448
- async pollSubmission(id, _type, schema) {
449
- const endpoint = `submissions/detail/${id}/check/`;
450
- const maxAttempts = 30;
451
- const delay = 1e3;
452
- for (let attempt = 0; attempt < maxAttempts; attempt++) {
453
- const result = await this.client.get(endpoint).json();
454
- if (result.state === "SUCCESS" || result.state === "FAILURE") {
455
- return schema.parse(result);
456
- }
457
- await new Promise((resolve) => setTimeout(resolve, delay));
458
- }
459
- throw new Error("Submission timeout: Result not available after 30 seconds");
460
- }
461
- };
462
- var leetcodeClient = new LeetCodeClient();
463
-
464
- // src/storage/credentials.ts
465
- import Conf from "conf";
466
- import { homedir } from "os";
467
- import { join } from "path";
468
- var credentialsStore = new Conf({
469
- configName: "credentials",
470
- cwd: join(homedir(), ".leetcode"),
471
- defaults: {}
472
- });
473
- var credentials = {
474
- get() {
475
- const session = credentialsStore.get("session");
476
- const csrfToken = credentialsStore.get("csrfToken");
477
- if (!session || !csrfToken) return null;
478
- return { session, csrfToken };
479
- },
480
- set(creds) {
481
- credentialsStore.set("session", creds.session);
482
- credentialsStore.set("csrfToken", creds.csrfToken);
483
- },
484
- clear() {
485
- credentialsStore.clear();
486
- },
487
- getPath() {
488
- return credentialsStore.path;
489
- }
490
- };
491
-
492
- // src/commands/login.ts
493
- async function loginCommand() {
494
- console.log();
495
- console.log(chalk.cyan("LeetCode CLI Login"));
496
- console.log(chalk.gray("\u2500".repeat(40)));
497
- console.log();
498
- console.log(chalk.yellow("To login, you need to provide your LeetCode session cookies."));
499
- console.log(chalk.gray("1. Open https://leetcode.com in your browser"));
500
- console.log(chalk.gray("2. Login to your account"));
501
- console.log(chalk.gray("3. Open DevTools (F12) \u2192 Application \u2192 Cookies \u2192 leetcode.com"));
502
- console.log(chalk.gray("4. Copy the values of LEETCODE_SESSION and csrftoken"));
503
- console.log();
504
- const answers = await inquirer.prompt([
505
- {
506
- type: "password",
507
- name: "session",
508
- message: "LEETCODE_SESSION:",
509
- mask: "*",
510
- validate: (input) => input.length > 0 || "Session token is required"
511
- },
512
- {
513
- type: "password",
514
- name: "csrfToken",
515
- message: "csrftoken:",
516
- mask: "*",
517
- validate: (input) => input.length > 0 || "CSRF token is required"
518
- }
519
- ]);
520
- const creds = {
521
- session: answers.session.trim(),
522
- csrfToken: answers.csrfToken.trim()
523
- };
524
- const spinner = ora("Verifying credentials...").start();
525
- try {
526
- leetcodeClient.setCredentials(creds);
527
- const { isSignedIn, username } = await leetcodeClient.checkAuth();
528
- if (!isSignedIn || !username) {
529
- spinner.fail("Invalid credentials");
530
- console.log(chalk.red("Please check your session cookies and try again."));
531
- return;
532
- }
533
- credentials.set(creds);
534
- spinner.succeed(`Logged in as ${chalk.green(username)}`);
535
- console.log();
536
- console.log(chalk.gray(`Credentials saved to ${credentials.getPath()}`));
537
- } catch (error) {
538
- spinner.fail("Authentication failed");
539
- if (error instanceof Error) {
540
- console.log(chalk.red(error.message));
541
- }
542
- }
543
- }
544
- async function logoutCommand() {
545
- credentials.clear();
546
- console.log(chalk.green("\u2713 Logged out successfully"));
547
- }
548
- async function whoamiCommand() {
549
- const creds = credentials.get();
550
- if (!creds) {
551
- console.log(chalk.yellow('Not logged in. Run "leetcode login" to authenticate.'));
552
- return;
553
- }
554
- const spinner = ora("Checking session...").start();
555
- try {
556
- leetcodeClient.setCredentials(creds);
557
- const { isSignedIn, username } = await leetcodeClient.checkAuth();
558
- if (!isSignedIn || !username) {
559
- spinner.fail("Session expired");
560
- console.log(chalk.yellow('Please run "leetcode login" to re-authenticate.'));
561
- return;
562
- }
563
- spinner.succeed(`Logged in as ${chalk.green(username)}`);
564
- } catch (error) {
565
- spinner.fail("Failed to check session");
566
- if (error instanceof Error) {
567
- console.log(chalk.red(error.message));
568
- }
569
- }
570
- }
571
-
572
- // src/commands/list.ts
573
- import ora2 from "ora";
574
- import chalk5 from "chalk";
575
-
576
- // src/utils/auth.ts
577
- import chalk2 from "chalk";
578
- async function requireAuth() {
579
- const creds = credentials.get();
580
- if (!creds) {
581
- console.log(chalk2.yellow("\u26A0\uFE0F Please login first: leetcode login"));
582
- return { authorized: false };
583
- }
584
- try {
585
- leetcodeClient.setCredentials(creds);
586
- const { isSignedIn, username } = await leetcodeClient.checkAuth();
587
- if (!isSignedIn) {
588
- console.log(chalk2.yellow("\u26A0\uFE0F Session expired. Please run: leetcode login"));
589
- return { authorized: false };
590
- }
591
- return { authorized: true, username: username ?? void 0 };
592
- } catch {
593
- console.log(chalk2.yellow("\u26A0\uFE0F Session validation failed. Please run: leetcode login"));
594
- return { authorized: false };
595
- }
596
- }
597
-
598
- // src/utils/display.ts
599
- import chalk4 from "chalk";
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
812
- function displayProblemList(problems, total) {
813
- const table = new Table({
814
- head: [
815
- chalk4.cyan("ID"),
816
- chalk4.cyan("Title"),
817
- chalk4.cyan("Difficulty"),
818
- chalk4.cyan("Rate"),
819
- chalk4.cyan("Status")
820
- ],
821
- colWidths: [8, 45, 12, 10, 10],
822
- style: { head: [], border: [] }
823
- });
824
- for (const problem of problems) {
825
- let title = problem.title;
826
- if (problem.isPaidOnly) {
827
- title = `\u{1F512} ${title}`;
828
- }
829
- table.push([
830
- problem.questionFrontendId,
831
- title.length > 42 ? title.slice(0, 39) + "..." : title,
832
- colorDifficulty(problem.difficulty),
833
- `${problem.acRate.toFixed(1)}%`,
834
- formatStatus(problem.status)
835
- ]);
836
- }
837
- console.log(table.toString());
838
- console.log(chalk4.gray(`
839
- Showing ${problems.length} of ${total} problems`));
840
- }
841
- function displayProblemDetail(problem) {
842
- console.log();
843
- const titlePrefix = problem.isPaidOnly ? "\u{1F512} " : "";
844
- console.log(chalk4.bold.cyan(` ${problem.questionFrontendId}. ${titlePrefix}${problem.title}`));
845
- console.log(` ${colorDifficulty(problem.difficulty)}`);
846
- console.log(chalk4.gray(` https://leetcode.com/problems/${problem.titleSlug}/`));
847
- console.log();
848
- if (problem.isPaidOnly) {
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.`));
852
- console.log();
853
- }
854
- if (problem.topicTags.length) {
855
- const tags = problem.topicTags.map((t) => chalk4.bgBlue.white(` ${t.name} `)).join(" ");
856
- console.log(` ${tags}`);
857
- console.log();
858
- }
859
- console.log(chalk4.gray("\u2500".repeat(60)));
860
- console.log();
861
- let content = problem.content;
862
- if (!content) {
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."));
866
- console.log();
867
- return;
868
- }
869
- content = content.replace(/<sup>(.*?)<\/sup>/gi, "^$1");
870
- content = content.replace(/<strong class="example">Example (\d+):<\/strong>/gi, "\xA7EXAMPLE\xA7$1\xA7");
871
- content = content.replace(/Input:/gi, "\xA7INPUT\xA7");
872
- content = content.replace(/Output:/gi, "\xA7OUTPUT\xA7");
873
- content = content.replace(/Explanation:/gi, "\xA7EXPLAIN\xA7");
874
- content = content.replace(/<strong>Constraints:<\/strong>/gi, "\xA7CONSTRAINTS\xA7");
875
- content = content.replace(/Constraints:/gi, "\xA7CONSTRAINTS\xA7");
876
- content = content.replace(/<strong>Follow-up:/gi, "\xA7FOLLOWUP\xA7");
877
- content = content.replace(/Follow-up:/gi, "\xA7FOLLOWUP\xA7");
878
- content = content.replace(/<li>/gi, " \u2022 ");
879
- content = content.replace(/<\/li>/gi, "\n");
880
- content = content.replace(/<\/p>/gi, "\n\n");
881
- content = content.replace(/<br\s*\/?>/gi, "\n");
882
- content = content.replace(/<[^>]+>/g, "");
883
- content = content.replace(/&nbsp;/g, " ").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&le;/g, "\u2264").replace(/&ge;/g, "\u2265").replace(/&#(\d+);/g, (_, code) => String.fromCharCode(parseInt(code, 10)));
884
- content = content.replace(/\n{3,}/g, "\n\n").trim();
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:"));
891
- console.log(content);
892
- console.log();
893
- }
894
- function displayTestResult(result, topicTags) {
895
- console.log();
896
- if (result.compile_error) {
897
- console.log(chalk4.red.bold("\u274C Compile Error"));
898
- console.log(chalk4.red(result.compile_error));
899
- return;
900
- }
901
- if (result.runtime_error) {
902
- console.log(chalk4.red.bold("\u274C Runtime Error"));
903
- console.log(chalk4.red(result.runtime_error));
904
- return;
905
- }
906
- if (result.correct_answer) {
907
- console.log(chalk4.green.bold("\u2713 All test cases passed!"));
908
- } else {
909
- console.log(chalk4.yellow.bold("\u2717 Some test cases failed"));
910
- }
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
- }
947
- }
948
- const stdoutEntries = (result.std_output_list ?? []).filter((s) => s);
949
- if (stdoutEntries.length > 0) {
950
- console.log();
951
- console.log(chalk4.gray("Stdout:"));
952
- for (const output of stdoutEntries) {
953
- console.log(chalk4.gray(` ${output}`));
954
- }
955
- }
956
- }
957
- function displaySubmissionResult(result) {
958
- console.log();
959
- if (result.compile_error) {
960
- console.log(chalk4.red.bold("\u274C Compile Error"));
961
- console.log(chalk4.red(result.compile_error));
962
- return;
963
- }
964
- if (result.runtime_error) {
965
- console.log(chalk4.red.bold("\u274C Runtime Error"));
966
- console.log(chalk4.red(result.runtime_error));
967
- if (result.last_testcase) {
968
- console.log(chalk4.gray("Last testcase:"), result.last_testcase);
969
- }
970
- return;
971
- }
972
- if (result.status_msg === "Accepted") {
973
- console.log(chalk4.green.bold("\u2713 Accepted!"));
974
- console.log();
975
- console.log(
976
- chalk4.gray("Runtime:"),
977
- chalk4.white(result.status_runtime),
978
- chalk4.gray(`(beats ${result.runtime_percentile?.toFixed(1) ?? "N/A"}%)`)
979
- );
980
- console.log(
981
- chalk4.gray("Memory:"),
982
- chalk4.white(result.status_memory),
983
- chalk4.gray(`(beats ${result.memory_percentile?.toFixed(1) ?? "N/A"}%)`)
984
- );
985
- } else {
986
- console.log(chalk4.red.bold(`\u274C ${result.status_msg}`));
987
- console.log();
988
- console.log(chalk4.gray(`Passed ${result.total_correct}/${result.total_testcases} testcases`));
989
- if (result.code_output) {
990
- console.log(chalk4.gray("Your Output:"), result.code_output);
991
- }
992
- if (result.expected_output) {
993
- console.log(chalk4.gray("Expected:"), result.expected_output);
994
- }
995
- if (result.last_testcase) {
996
- console.log(chalk4.gray("Failed testcase:"), result.last_testcase);
997
- }
998
- }
999
- }
1000
- function displayUserStats(username, realName, ranking, acStats, streak, totalActiveDays) {
1001
- console.log();
1002
- console.log(chalk4.bold.white(`\u{1F464} ${username}`) + (realName ? chalk4.gray(` (${realName})`) : ""));
1003
- console.log(chalk4.gray(`Ranking: #${ranking.toLocaleString()}`));
1004
- console.log();
1005
- const table = new Table({
1006
- head: [chalk4.cyan("Difficulty"), chalk4.cyan("Solved")],
1007
- style: { head: [], border: [] }
1008
- });
1009
- for (const stat of acStats) {
1010
- if (stat.difficulty !== "All") {
1011
- table.push([
1012
- colorDifficulty(stat.difficulty),
1013
- stat.count.toString()
1014
- ]);
1015
- }
1016
- }
1017
- const total = acStats.find((s) => s.difficulty === "All")?.count ?? 0;
1018
- table.push([chalk4.white.bold("Total"), chalk4.white.bold(total.toString())]);
1019
- console.log(table.toString());
1020
- 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()));
1023
- }
1024
- function displayDailyChallenge(date, problem) {
1025
- console.log();
1026
- console.log(chalk4.bold.yellow("\u{1F3AF} Daily Challenge"), chalk4.gray(`(${date})`));
1027
- console.log();
1028
- console.log(chalk4.white(`${problem.questionFrontendId}. ${problem.title}`));
1029
- console.log(colorDifficulty(problem.difficulty));
1030
- console.log(chalk4.gray(`https://leetcode.com/problems/${problem.titleSlug}/`));
1031
- if (problem.topicTags.length) {
1032
- console.log();
1033
- const tags = problem.topicTags.map((t) => chalk4.blue(t.name)).join(" ");
1034
- console.log(chalk4.gray("Tags:"), tags);
1035
- }
1036
- }
1037
- function colorDifficulty(difficulty) {
1038
- switch (difficulty.toLowerCase()) {
1039
- case "easy":
1040
- return chalk4.green(difficulty);
1041
- case "medium":
1042
- return chalk4.yellow(difficulty);
1043
- case "hard":
1044
- return chalk4.red(difficulty);
1045
- default:
1046
- return difficulty;
1047
- }
1048
- }
1049
- function formatStatus(status) {
1050
- switch (status) {
1051
- case "ac":
1052
- return chalk4.green("\u2713");
1053
- case "notac":
1054
- return chalk4.yellow("\u25CB");
1055
- default:
1056
- return chalk4.gray("-");
1057
- }
1058
- }
1059
- function displaySubmissionsList(submissions) {
1060
- const table = new Table({
1061
- head: [
1062
- chalk4.cyan("ID"),
1063
- chalk4.cyan("Status"),
1064
- chalk4.cyan("Lang"),
1065
- chalk4.cyan("Runtime"),
1066
- chalk4.cyan("Memory"),
1067
- chalk4.cyan("Date")
1068
- ],
1069
- colWidths: [12, 18, 15, 12, 12, 25],
1070
- style: { head: [], border: [] }
1071
- });
1072
- for (const s of submissions) {
1073
- const isAC = s.statusDisplay === "Accepted";
1074
- const cleanTime = new Date(parseInt(s.timestamp) * 1e3).toLocaleString();
1075
- table.push([
1076
- s.id,
1077
- isAC ? chalk4.green(s.statusDisplay) : chalk4.red(s.statusDisplay),
1078
- s.lang,
1079
- s.runtime,
1080
- s.memory,
1081
- cleanTime
1082
- ]);
1083
- }
1084
- console.log(table.toString());
1085
- }
1086
-
1087
- // src/commands/list.ts
1088
- async function listCommand(options) {
1089
- const { authorized } = await requireAuth();
1090
- if (!authorized) return;
1091
- const spinner = ora2("Fetching problems...").start();
1092
- try {
1093
- const filters = {};
1094
- const limit = parseInt(options.limit ?? "20", 10);
1095
- const page = parseInt(options.page ?? "1", 10);
1096
- filters.limit = limit;
1097
- filters.skip = (page - 1) * limit;
1098
- if (options.difficulty) {
1099
- const diffMap = {
1100
- easy: "EASY",
1101
- e: "EASY",
1102
- medium: "MEDIUM",
1103
- m: "MEDIUM",
1104
- hard: "HARD",
1105
- h: "HARD"
1106
- };
1107
- filters.difficulty = diffMap[options.difficulty.toLowerCase()];
1108
- }
1109
- if (options.status) {
1110
- const statusMap = {
1111
- todo: "NOT_STARTED",
1112
- solved: "AC",
1113
- ac: "AC",
1114
- attempted: "TRIED",
1115
- tried: "TRIED"
1116
- };
1117
- filters.status = statusMap[options.status.toLowerCase()];
1118
- }
1119
- if (options.tag?.length) {
1120
- filters.tags = options.tag;
1121
- }
1122
- if (options.search) {
1123
- filters.searchKeywords = options.search;
1124
- }
1125
- const { total, problems } = await leetcodeClient.getProblems(filters);
1126
- spinner.stop();
1127
- if (problems.length === 0) {
1128
- console.log(chalk5.yellow("No problems found matching your criteria."));
1129
- return;
1130
- }
1131
- displayProblemList(problems, total);
1132
- if (page * limit < total) {
1133
- console.log(chalk5.gray(`
1134
- Page ${page} of ${Math.ceil(total / limit)}. Use --page to navigate.`));
1135
- }
1136
- } catch (error) {
1137
- spinner.fail("Failed to fetch problems");
1138
- if (error instanceof Error) {
1139
- console.log(chalk5.red(error.message));
1140
- }
1141
- }
1142
- }
1143
-
1144
- // src/commands/show.ts
1145
- import ora3 from "ora";
1146
- import chalk6 from "chalk";
1147
- async function showCommand(idOrSlug) {
1148
- const { authorized } = await requireAuth();
1149
- if (!authorized) return;
1150
- const spinner = ora3("Fetching problem...").start();
1151
- try {
1152
- let problem;
1153
- if (/^\d+$/.test(idOrSlug)) {
1154
- problem = await leetcodeClient.getProblemById(idOrSlug);
1155
- } else {
1156
- problem = await leetcodeClient.getProblem(idOrSlug);
1157
- }
1158
- if (!problem) {
1159
- spinner.fail(`Problem "${idOrSlug}" not found`);
1160
- return;
1161
- }
1162
- spinner.stop();
1163
- displayProblemDetail(problem);
1164
- } catch (error) {
1165
- spinner.fail("Failed to fetch problem");
1166
- if (error instanceof Error) {
1167
- console.log(chalk6.red(error.message));
1168
- }
1169
- }
1170
- }
1171
-
1172
- // src/commands/pick.ts
1173
- import { writeFile, mkdir } from "fs/promises";
1174
- import { existsSync as existsSync2 } from "fs";
1175
- import { join as join4 } from "path";
1176
- import ora4 from "ora";
1177
- import chalk8 from "chalk";
1178
-
1179
- // src/storage/config.ts
1180
- import { join as join3 } from "path";
1181
-
1182
- // src/storage/workspaces.ts
1183
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
1184
- import { join as join2 } from "path";
1185
- import { homedir as homedir2 } from "os";
1186
- var LEETCODE_DIR = join2(homedir2(), ".leetcode");
1187
- var WORKSPACES_FILE = join2(LEETCODE_DIR, "workspaces.json");
1188
- var WORKSPACES_DIR = join2(LEETCODE_DIR, "workspaces");
1189
- function ensureDir(dir) {
1190
- if (!existsSync(dir)) {
1191
- mkdirSync(dir, { recursive: true });
1192
- }
1193
- }
1194
- function loadRegistry() {
1195
- if (existsSync(WORKSPACES_FILE)) {
1196
- return JSON.parse(readFileSync(WORKSPACES_FILE, "utf-8"));
1197
- }
1198
- return { active: "default", workspaces: [] };
1199
- }
1200
- function saveRegistry(registry) {
1201
- ensureDir(LEETCODE_DIR);
1202
- writeFileSync(WORKSPACES_FILE, JSON.stringify(registry, null, 2));
1203
- }
1204
- var workspaceStorage = {
1205
- /**
1206
- * Initialize workspaces on first run - migrate existing config to "default" workspace
1207
- */
1208
- ensureInitialized() {
1209
- const registry = loadRegistry();
1210
- if (registry.workspaces.length === 0) {
1211
- this.create("default", {
1212
- workDir: join2(homedir2(), "leetcode"),
1213
- lang: "typescript"
1214
- });
1215
- registry.workspaces = ["default"];
1216
- registry.active = "default";
1217
- saveRegistry(registry);
1218
- }
1219
- },
1220
- /**
1221
- * Get the currently active workspace name
1222
- */
1223
- getActive() {
1224
- this.ensureInitialized();
1225
- return loadRegistry().active;
1226
- },
1227
- /**
1228
- * Set the active workspace
1229
- */
1230
- setActive(name) {
1231
- const registry = loadRegistry();
1232
- if (!registry.workspaces.includes(name)) {
1233
- return false;
1234
- }
1235
- registry.active = name;
1236
- saveRegistry(registry);
1237
- return true;
1238
- },
1239
- /**
1240
- * List all workspace names
1241
- */
1242
- list() {
1243
- this.ensureInitialized();
1244
- return loadRegistry().workspaces;
1245
- },
1246
- /**
1247
- * Check if a workspace exists
1248
- */
1249
- exists(name) {
1250
- this.ensureInitialized();
1251
- return loadRegistry().workspaces.includes(name);
1252
- },
1253
- /**
1254
- * Create a new workspace
1255
- */
1256
- create(name, config2) {
1257
- const registry = loadRegistry();
1258
- if (registry.workspaces.includes(name)) {
1259
- return false;
1260
- }
1261
- const wsDir = join2(WORKSPACES_DIR, name);
1262
- ensureDir(wsDir);
1263
- ensureDir(join2(wsDir, "snapshots"));
1264
- const configPath = join2(wsDir, "config.json");
1265
- writeFileSync(configPath, JSON.stringify(config2, null, 2));
1266
- writeFileSync(join2(wsDir, "timer.json"), JSON.stringify({ solveTimes: {}, activeTimer: null }, null, 2));
1267
- writeFileSync(join2(wsDir, "collab.json"), JSON.stringify({ session: null }, null, 2));
1268
- registry.workspaces.push(name);
1269
- saveRegistry(registry);
1270
- return true;
1271
- },
1272
- /**
1273
- * Delete a workspace
1274
- */
1275
- delete(name) {
1276
- if (name === "default") {
1277
- return false;
1278
- }
1279
- const registry = loadRegistry();
1280
- if (!registry.workspaces.includes(name)) {
1281
- return false;
1282
- }
1283
- registry.workspaces = registry.workspaces.filter((w) => w !== name);
1284
- if (registry.active === name) {
1285
- registry.active = "default";
1286
- }
1287
- saveRegistry(registry);
1288
- return true;
1289
- },
1290
- /**
1291
- * Get the directory path for a workspace
1292
- */
1293
- getWorkspaceDir(name) {
1294
- const wsName = name ?? this.getActive();
1295
- return join2(WORKSPACES_DIR, wsName);
1296
- },
1297
- /**
1298
- * Get config for a workspace
1299
- */
1300
- getConfig(name) {
1301
- const wsName = name ?? this.getActive();
1302
- const configPath = join2(WORKSPACES_DIR, wsName, "config.json");
1303
- if (existsSync(configPath)) {
1304
- return JSON.parse(readFileSync(configPath, "utf-8"));
1305
- }
1306
- return {
1307
- workDir: join2(homedir2(), "leetcode"),
1308
- lang: "typescript"
1309
- };
1310
- },
1311
- /**
1312
- * Update config for a workspace
1313
- */
1314
- setConfig(config2, name) {
1315
- const wsName = name ?? this.getActive();
1316
- const wsDir = join2(WORKSPACES_DIR, wsName);
1317
- ensureDir(wsDir);
1318
- const currentConfig = this.getConfig(wsName);
1319
- const newConfig = { ...currentConfig, ...config2 };
1320
- writeFileSync(join2(wsDir, "config.json"), JSON.stringify(newConfig, null, 2));
1321
- },
1322
- /**
1323
- * Get snapshots directory for active workspace
1324
- */
1325
- getSnapshotsDir() {
1326
- return join2(this.getWorkspaceDir(), "snapshots");
1327
- },
1328
- /**
1329
- * Get timer file path for active workspace
1330
- */
1331
- getTimerPath() {
1332
- return join2(this.getWorkspaceDir(), "timer.json");
1333
- },
1334
- /**
1335
- * Get collab file path for active workspace
1336
- */
1337
- getCollabPath() {
1338
- return join2(this.getWorkspaceDir(), "collab.json");
1339
- }
1340
- };
1341
-
1342
- // src/storage/config.ts
1343
- var config = {
1344
- getConfig() {
1345
- const wsConfig = workspaceStorage.getConfig();
1346
- return {
1347
- language: wsConfig.lang,
1348
- editor: wsConfig.editor,
1349
- workDir: wsConfig.workDir,
1350
- repo: wsConfig.syncRepo
1351
- };
1352
- },
1353
- setLanguage(language) {
1354
- workspaceStorage.setConfig({ lang: language });
1355
- },
1356
- setEditor(editor) {
1357
- workspaceStorage.setConfig({ editor });
1358
- },
1359
- setWorkDir(workDir) {
1360
- workspaceStorage.setConfig({ workDir });
1361
- },
1362
- setRepo(repo) {
1363
- workspaceStorage.setConfig({ syncRepo: repo });
1364
- },
1365
- deleteRepo() {
1366
- const wsConfig = workspaceStorage.getConfig();
1367
- delete wsConfig.syncRepo;
1368
- workspaceStorage.setConfig(wsConfig);
1369
- },
1370
- getLanguage() {
1371
- return workspaceStorage.getConfig().lang;
1372
- },
1373
- getEditor() {
1374
- return workspaceStorage.getConfig().editor;
1375
- },
1376
- getWorkDir() {
1377
- return workspaceStorage.getConfig().workDir;
1378
- },
1379
- getRepo() {
1380
- return workspaceStorage.getConfig().syncRepo;
1381
- },
1382
- getPath() {
1383
- return join3(workspaceStorage.getWorkspaceDir(), "config.json");
1384
- },
1385
- // New workspace-aware methods
1386
- getActiveWorkspace() {
1387
- return workspaceStorage.getActive();
1388
- }
1389
- };
1390
-
1391
- // src/utils/templates.ts
1392
- var LANGUAGE_EXTENSIONS = {
1393
- typescript: "ts",
1394
- javascript: "js",
1395
- python3: "py",
1396
- java: "java",
1397
- cpp: "cpp",
1398
- c: "c",
1399
- csharp: "cs",
1400
- go: "go",
1401
- rust: "rs",
1402
- kotlin: "kt",
1403
- swift: "swift"
1404
- };
1405
- var LANG_SLUG_MAP = {
1406
- typescript: "typescript",
1407
- javascript: "javascript",
1408
- python3: "python3",
1409
- python: "python3",
1410
- java: "java",
1411
- "c++": "cpp",
1412
- cpp: "cpp",
1413
- c: "c",
1414
- "c#": "csharp",
1415
- csharp: "csharp",
1416
- go: "go",
1417
- golang: "go",
1418
- rust: "rust",
1419
- kotlin: "kotlin",
1420
- swift: "swift"
1421
- };
1422
- function getCodeTemplate(snippets, preferredLanguage) {
1423
- const preferred = snippets.find(
1424
- (s) => LANG_SLUG_MAP[s.langSlug.toLowerCase()] === preferredLanguage
1425
- );
1426
- if (preferred) return preferred;
1427
- return snippets[0] ?? null;
1428
- }
1429
- function generateSolutionFile(problemId, titleSlug, title, difficulty, codeSnippet, language, problemContent) {
1430
- const commentStyle = getCommentStyle(language);
1431
- const header = generateHeader(commentStyle, problemId, title, difficulty, titleSlug, problemContent);
1432
- return `${header}
1433
-
1434
- ${codeSnippet}
1435
- `;
1436
- }
1437
- function getCommentStyle(language) {
1438
- switch (language) {
1439
- case "python3":
1440
- return { single: "#", blockStart: '"""', blockEnd: '"""', linePrefix: "" };
1441
- case "c":
1442
- case "cpp":
1443
- case "java":
1444
- case "typescript":
1445
- case "javascript":
1446
- case "go":
1447
- case "rust":
1448
- case "kotlin":
1449
- case "swift":
1450
- case "csharp":
1451
- default:
1452
- return { single: "//", blockStart: "/*", blockEnd: "*/", linePrefix: " * " };
1453
- }
1454
- }
1455
- function formatProblemContent(html) {
1456
- let content = html;
1457
- content = content.replace(/<sup>(.*?)<\/sup>/gi, "^$1");
1458
- content = content.replace(/<strong class="example">Example (\d+):<\/strong>/gi, "\nExample $1:");
1459
- content = content.replace(/<li>/gi, "\u2022 ");
1460
- content = content.replace(/<\/li>/gi, "\n");
1461
- content = content.replace(/<\/p>/gi, "\n\n");
1462
- content = content.replace(/<br\s*\/?>/gi, "\n");
1463
- content = content.replace(/<[^>]+>/g, "");
1464
- content = content.replace(/&nbsp;/g, " ").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&le;/g, "<=").replace(/&ge;/g, ">=").replace(/&#(\d+);/g, (_, code) => String.fromCharCode(parseInt(code, 10)));
1465
- content = content.replace(/\n{3,}/g, "\n\n").trim();
1466
- return content;
1467
- }
1468
- function generateHeader(style, problemId, title, difficulty, titleSlug, problemContent) {
1469
- const prefix = style.linePrefix;
1470
- const lines = [
1471
- style.blockStart,
1472
- `${prefix}${problemId}. ${title}`,
1473
- `${prefix}Difficulty: ${difficulty}`,
1474
- `${prefix}https://leetcode.com/problems/${titleSlug}/`
1475
- ];
1476
- if (problemContent) {
1477
- lines.push(`${prefix}`);
1478
- lines.push(`${prefix}${"\u2500".repeat(50)}`);
1479
- lines.push(`${prefix}`);
1480
- const formattedContent = formatProblemContent(problemContent);
1481
- const contentLines = formattedContent.split("\n");
1482
- for (const line of contentLines) {
1483
- if (line.length > 70) {
1484
- const words = line.split(" ");
1485
- let currentLine = "";
1486
- for (const word of words) {
1487
- if ((currentLine + " " + word).length > 70) {
1488
- lines.push(`${prefix}${currentLine.trim()}`);
1489
- currentLine = word;
1490
- } else {
1491
- currentLine += " " + word;
1492
- }
1493
- }
1494
- if (currentLine.trim()) {
1495
- lines.push(`${prefix}${currentLine.trim()}`);
1496
- }
1497
- } else {
1498
- lines.push(`${prefix}${line}`);
1499
- }
1500
- }
1501
- }
1502
- lines.push(style.blockEnd);
1503
- return lines.join("\n");
1504
- }
1505
- function getSolutionFileName(problemId, titleSlug, language) {
1506
- const ext = LANGUAGE_EXTENSIONS[language];
1507
- return `${problemId}.${titleSlug}.${ext}`;
1508
- }
1509
-
1510
- // src/utils/editor.ts
1511
- import { spawn } from "child_process";
1512
- import open from "open";
1513
- import chalk7 from "chalk";
1514
- var TERMINAL_EDITORS = ["vim", "nvim", "vi", "nano", "emacs", "micro", "helix"];
1515
- var VSCODE_EDITORS = ["code", "code-insiders", "cursor", "codium", "vscodium"];
1516
- async function openInEditor(filePath, workDir) {
1517
- const editor = config.getEditor() ?? process.env.EDITOR ?? "code";
1518
- const workspace = workDir ?? config.getWorkDir();
1519
- if (TERMINAL_EDITORS.includes(editor)) {
1520
- console.log();
1521
- console.log(chalk7.gray(`Open with: ${editor} ${filePath}`));
1522
- return;
1523
- }
1524
- try {
1525
- if (VSCODE_EDITORS.includes(editor)) {
1526
- const child = spawn(editor, ["-r", workspace, "-g", filePath], {
1527
- detached: true,
1528
- stdio: "ignore"
1529
- });
1530
- child.unref();
1531
- return;
1532
- }
1533
- await open(filePath, { app: { name: editor } });
1534
- } catch {
1535
- }
1536
- }
1537
-
1538
- // src/commands/pick.ts
1539
- async function pickCommand(idOrSlug, options) {
1540
- const { authorized } = await requireAuth();
1541
- if (!authorized) return false;
1542
- const spinner = ora4({ text: "Fetching problem details...", spinner: "dots" }).start();
1543
- try {
1544
- let problem;
1545
- if (/^\d+$/.test(idOrSlug)) {
1546
- problem = await leetcodeClient.getProblemById(idOrSlug);
1547
- } else {
1548
- problem = await leetcodeClient.getProblem(idOrSlug);
1549
- }
1550
- if (!problem) {
1551
- spinner.fail(`Problem "${idOrSlug}" not found`);
1552
- return false;
1553
- }
1554
- spinner.text = "Generating solution file...";
1555
- const langInput = options.lang?.toLowerCase() ?? config.getLanguage();
1556
- const language = LANG_SLUG_MAP[langInput] ?? langInput;
1557
- const snippets = problem.codeSnippets ?? [];
1558
- const template = getCodeTemplate(snippets, language);
1559
- let code;
1560
- if (snippets.length === 0) {
1561
- spinner.warn(chalk8.yellow("Premium Problem (No code snippets available)"));
1562
- console.log(chalk8.gray("Generating placeholder file with problem info..."));
1563
- code = `// \u{1F512} Premium Problem - ${problem.title}
1564
- // Solution stub not available - visit LeetCode to view`;
1565
- } else if (!template) {
1566
- spinner.fail(`No code template available for ${language}`);
1567
- console.log(chalk8.gray(`Available languages: ${snippets.map((s) => s.langSlug).join(", ")}`));
1568
- return false;
1569
- } else {
1570
- code = template.code;
1571
- }
1572
- const content = generateSolutionFile(
1573
- problem.questionFrontendId,
1574
- problem.titleSlug,
1575
- problem.title,
1576
- problem.difficulty,
1577
- code,
1578
- language,
1579
- problem.content ?? void 0
1580
- );
1581
- const workDir = config.getWorkDir();
1582
- const difficulty = problem.difficulty;
1583
- const category = problem.topicTags.length > 0 ? problem.topicTags[0].name.replace(/[^\w\s-]/g, "").trim() : "Uncategorized";
1584
- const targetDir = join4(workDir, difficulty, category);
1585
- if (!existsSync2(targetDir)) {
1586
- await mkdir(targetDir, { recursive: true });
1587
- }
1588
- const fileName = getSolutionFileName(problem.questionFrontendId, problem.titleSlug, language);
1589
- const filePath = join4(targetDir, fileName);
1590
- if (existsSync2(filePath)) {
1591
- spinner.warn(`File already exists: ${fileName}`);
1592
- console.log(chalk8.gray(`Path: ${filePath}`));
1593
- if (options.open !== false) {
1594
- await openInEditor(filePath);
1595
- }
1596
- return true;
1597
- }
1598
- await writeFile(filePath, content, "utf-8");
1599
- spinner.succeed(`Created ${chalk8.green(fileName)}`);
1600
- console.log(chalk8.gray(`Path: ${filePath}`));
1601
- console.log();
1602
- console.log(chalk8.cyan(`${problem.questionFrontendId}. ${problem.title}`));
1603
- console.log(chalk8.gray(`Difficulty: ${problem.difficulty} | Category: ${category}`));
1604
- if (options.open !== false) {
1605
- await openInEditor(filePath);
1606
- }
1607
- return true;
1608
- } catch (error) {
1609
- spinner.fail("Failed to fetch problem");
1610
- if (error instanceof Error) {
1611
- if (error.message.includes("expected object, received null")) {
1612
- console.log(chalk8.red(`Problem "${idOrSlug}" not found`));
1613
- } else {
1614
- try {
1615
- const zodError = JSON.parse(error.message);
1616
- if (Array.isArray(zodError)) {
1617
- console.log(chalk8.red("API Response Validation Failed"));
1618
- } else {
1619
- console.log(chalk8.red(error.message));
1620
- }
1621
- } catch {
1622
- console.log(chalk8.red(error.message));
1623
- }
1624
- }
1625
- }
1626
- return false;
1627
- }
1628
- }
1629
- async function batchPickCommand(ids, options) {
1630
- if (ids.length === 0) {
1631
- console.log(chalk8.yellow("Please provide at least one problem ID"));
1632
- return;
1633
- }
1634
- const { authorized } = await requireAuth();
1635
- if (!authorized) return;
1636
- console.log(chalk8.cyan(`\u{1F4E6} Picking ${ids.length} problem${ids.length !== 1 ? "s" : ""}...`));
1637
- console.log();
1638
- console.log();
1639
- let succeeded = 0;
1640
- let failed = 0;
1641
- for (const id of ids) {
1642
- const success = await pickCommand(id, { ...options, open: false });
1643
- if (success) {
1644
- succeeded++;
1645
- } else {
1646
- failed++;
1647
- }
1648
- console.log();
1649
- }
1650
- console.log(chalk8.gray("\u2500".repeat(50)));
1651
- console.log(
1652
- chalk8.bold(
1653
- `Done! ${chalk8.green(`${succeeded} succeeded`)}${failed > 0 ? `, ${chalk8.red(`${failed} failed`)}` : ""}`
1654
- )
1655
- );
1656
- }
1657
-
1658
- // src/commands/test.ts
1659
- import { readFile } from "fs/promises";
1660
- import { existsSync as existsSync4 } from "fs";
1661
- import { basename } from "path";
1662
- import ora5 from "ora";
1663
- import chalk9 from "chalk";
1664
-
1665
- // src/utils/fileUtils.ts
1666
- import { readdir } from "fs/promises";
1667
- import { existsSync as existsSync3 } from "fs";
1668
- import { join as join5 } from "path";
1669
- var MAX_SEARCH_DEPTH = 5;
1670
- async function findSolutionFile(dir, problemId, currentDepth = 0) {
1671
- if (!existsSync3(dir)) return null;
1672
- if (currentDepth >= MAX_SEARCH_DEPTH) return null;
1673
- const entries = await readdir(dir, { withFileTypes: true });
1674
- for (const entry of entries) {
1675
- if (entry.name.startsWith(".")) continue;
1676
- const fullPath = join5(dir, entry.name);
1677
- if (entry.isDirectory()) {
1678
- const found = await findSolutionFile(fullPath, problemId, currentDepth + 1);
1679
- if (found) return found;
1680
- } else if (entry.name.startsWith(`${problemId}.`)) {
1681
- const ext = entry.name.split(".").pop()?.toLowerCase();
1682
- if (ext && ext in EXT_TO_LANG_MAP) {
1683
- return fullPath;
1684
- }
1685
- }
1686
- }
1687
- return null;
1688
- }
1689
- async function findFileByName(dir, fileName, currentDepth = 0) {
1690
- if (!existsSync3(dir)) return null;
1691
- if (currentDepth >= MAX_SEARCH_DEPTH) return null;
1692
- const entries = await readdir(dir, { withFileTypes: true });
1693
- for (const entry of entries) {
1694
- const fullPath = join5(dir, entry.name);
1695
- if (entry.isDirectory()) {
1696
- const found = await findFileByName(fullPath, fileName, currentDepth + 1);
1697
- if (found) return found;
1698
- } else if (entry.name === fileName) {
1699
- return fullPath;
1700
- }
1701
- }
1702
- return null;
1703
- }
1704
- var EXT_TO_LANG_MAP = {
1705
- ts: "typescript",
1706
- js: "javascript",
1707
- py: "python3",
1708
- java: "java",
1709
- cpp: "cpp",
1710
- c: "c",
1711
- cs: "csharp",
1712
- go: "go",
1713
- rs: "rust",
1714
- kt: "kotlin",
1715
- swift: "swift"
1716
- };
1717
- function getLangSlugFromExtension(ext) {
1718
- const langMap = {
1719
- ts: "typescript",
1720
- js: "javascript",
1721
- py: "python3",
1722
- java: "java",
1723
- cpp: "cpp",
1724
- c: "c",
1725
- cs: "csharp",
1726
- go: "golang",
1727
- rs: "rust",
1728
- kt: "kotlin",
1729
- swift: "swift"
1730
- };
1731
- return langMap[ext.toLowerCase()] ?? null;
1732
- }
160
+ `;var xe="https://leetcode.com",Me=class{client;credentials=null;constructor(){this.client=pn.extend({prefixUrl:xe,headers:{"Content-Type":"application/json","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",Origin:xe,Referer:`${xe}/`},timeout:{request:3e4},retry:{limit:2}});}setCredentials(o){this.credentials=o,this.client=this.client.extend({headers:{Cookie:`LEETCODE_SESSION=${o.session}; csrftoken=${o.csrfToken}`,"X-CSRFToken":o.csrfToken}});}getCredentials(){return this.credentials}async graphql(o,t={}){let n=await this.client.post("graphql",{json:{query:o,variables:t}}).json();if(n.errors?.length)throw new Error(`GraphQL Error: ${n.errors[0].message}`);return n.data}async checkAuth(){let o=await this.graphql(wo);return yo.parse(o.userStatus)}async getProblems(o={}){let t={categorySlug:"",limit:o.limit??50,skip:o.skip??0,filters:{}};o.difficulty&&(t.filters.difficulty=o.difficulty),o.status&&(t.filters.status=o.status),o.tags?.length&&(t.filters.tags=o.tags),o.searchKeywords&&(t.filters.searchKeywords=o.searchKeywords);let n=await this.graphql(ho,t),s=z.array(Te).parse(n.problemsetQuestionList.questions);return {total:n.problemsetQuestionList.total,problems:s}}async getProblem(o){let t=await this.graphql(bo,{titleSlug:o});return lo.parse(t.question)}async getProblemById(o){let{problems:t}=await this.getProblems({searchKeywords:o,limit:10}),n=t.find(s=>s.questionFrontendId===o);if(!n)throw new Error(`Problem #${o} not found`);return this.getProblem(n.titleSlug)}async getDailyChallenge(){let o=await this.graphql(ko);return co.parse(o.activeDailyCodingChallengeQuestion)}async getRandomProblem(o={}){let t={categorySlug:"",filters:{}};o.difficulty&&(t.filters.difficulty=o.difficulty),o.tags?.length&&(t.filters.tags=o.tags);let n=await this.graphql(Co,t);return z.object({titleSlug:z.string()}).parse(n.randomQuestion).titleSlug}async getUserProfile(o){let n=(await this.graphql(So,{username:o})).matchedUser,s=fo.parse(n);return {username:s.username,realName:s.profile.realName,ranking:s.profile.ranking,acSubmissionNum:s.submitStatsGlobal.acSubmissionNum,streak:s.userCalendar.streak,totalActiveDays:s.userCalendar.totalActiveDays,submissionCalendar:n.userCalendar.submissionCalendar}}async getSkillStats(o){return (await this.graphql($o,{username:o})).matchedUser.tagProblemCounts}async getSubmissionList(o,t=20,n=0){let s=await this.graphql(vo,{questionSlug:o,limit:t,offset:n});return z.array(go).parse(s.questionSubmissionList.submissions)}async getSubmissionDetails(o){let t=await this.graphql(Po,{submissionId:o});return mo.parse(t.submissionDetails)}async testSolution(o,t,n,s,r){let i=await this.client.post(`problems/${o}/interpret_solution/`,{json:{data_input:s,lang:n,typed_code:t,question_id:r}}).json();return this.pollSubmission(i.interpret_id,"interpret",uo)}async submitSolution(o,t,n,s){let r=await this.client.post(`problems/${o}/submit/`,{json:{lang:n,typed_code:t,question_id:s}}).json();return this.pollSubmission(r.submission_id.toString(),"submission",po)}async pollSubmission(o,t,n){let s=`submissions/detail/${o}/check/`,r=30,i=1e3;for(let a=0;a<r;a++){let c=await this.client.get(s).json();if(c.state==="SUCCESS"||c.state==="FAILURE")return n.parse(c);await new Promise(g=>setTimeout(g,i));}throw new Error("Submission timeout: Result not available after 30 seconds")}},h=new Me;var ce=new fn({configName:"credentials",cwd:join(homedir(),".leetcode"),defaults:{}}),K={get(){let e=ce.get("session"),o=ce.get("csrfToken");return !e||!o?null:{session:e,csrfToken:o}},set(e){ce.set("session",e.session),ce.set("csrfToken",e.csrfToken);},clear(){ce.clear();},getPath(){return ce.path}};async function Do(){console.log(),console.log(l.cyan("LeetCode CLI Login")),console.log(l.gray("\u2500".repeat(40))),console.log(),console.log(l.yellow("To login, you need to provide your LeetCode session cookies.")),console.log(l.gray("1. Open https://leetcode.com in your browser")),console.log(l.gray("2. Login to your account")),console.log(l.gray("3. Open DevTools (F12) \u2192 Application \u2192 Cookies \u2192 leetcode.com")),console.log(l.gray("4. Copy the values of LEETCODE_SESSION and csrftoken")),console.log();let e=await eo.prompt([{type:"password",name:"session",message:"LEETCODE_SESSION:",mask:"*",validate:n=>n.length>0||"Session token is required"},{type:"password",name:"csrfToken",message:"csrftoken:",mask:"*",validate:n=>n.length>0||"CSRF token is required"}]),o={session:e.session.trim(),csrfToken:e.csrfToken.trim()},t=Ie("Verifying credentials...").start();try{h.setCredentials(o);let{isSignedIn:n,username:s}=await h.checkAuth();if(!n||!s){t.fail("Invalid credentials"),console.log(l.red("Please check your session cookies and try again."));return}K.set(o),t.succeed(`Logged in as ${l.green(s)}`),console.log(),console.log(l.gray(`Credentials saved to ${K.getPath()}`));}catch(n){t.fail("Authentication failed"),n instanceof Error&&console.log(l.red(n.message));}}async function xo(){K.clear(),console.log(l.green("\u2713 Logged out successfully"));}async function Eo(){let e=K.get();if(!e){console.log(l.yellow('Not logged in. Run "leetcode login" to authenticate.'));return}let o=Ie("Checking session...").start();try{h.setCredentials(e);let{isSignedIn:t,username:n}=await h.checkAuth();if(!t||!n){o.fail("Session expired"),console.log(l.yellow('Please run "leetcode login" to re-authenticate.'));return}o.succeed(`Logged in as ${l.green(n)}`);}catch(t){o.fail("Failed to check session"),t instanceof Error&&console.log(l.red(t.message));}}async function v(){let e=K.get();if(!e)return console.log(l.yellow("\u26A0\uFE0F Please login first: leetcode login")),{authorized:false};try{h.setCredentials(e);let{isSignedIn:o,username:t}=await h.checkAuth();return o?{authorized:!0,username:t??void 0}:(console.log(l.yellow("\u26A0\uFE0F Session expired. Please run: leetcode login")),{authorized:!1})}catch{return console.log(l.yellow("\u26A0\uFE0F Session validation failed. Please run: leetcode login")),{authorized:false}}}var wn={"Linked List":"linkedlist","Doubly-Linked List":"linkedlist",Tree:"tree","Binary Tree":"tree","Binary Search Tree":"tree",Trie:"tree","Segment Tree":"tree","Binary Indexed Tree":"tree",Graph:"graph",Matrix:"matrix",Array:"array","Hash Table":"array",Stack:"array",Queue:"array","Monotonic Stack":"array","Monotonic Queue":"array","Heap (Priority Queue)":"array",String:"string"};function Sn(e){for(let o of e){let t=wn[o.name];if(t)return t}return null}function Lo(e){try{return JSON.parse(e)}catch{return e}}function ee(e){return Array.isArray(e)&&e.length>0&&Array.isArray(e[0])}function Ao(e,o){if(!Array.isArray(e)||e.length===0)return String(e);let t=Math.max(...e.map(i=>String(i).length),1),n=Math.max(t,3),s=e.map((i,a)=>`[${a}]`.padStart(n).padEnd(n)).join(" "),r=e.map((i,a)=>{let c=String(i).padStart(n).padEnd(n);return o&&Array.isArray(o)&&o[a]!==i?l.red.bold(c):c}).join(" ");return `${l.gray(s)}
161
+ ${r}`}function Ro(e,o){return Array.isArray(e)?e.length===0?l.gray("(empty)"):e.map((n,s)=>{let r=String(n);return o&&Array.isArray(o)&&(s>=o.length||o[s]!==n)?l.red.bold(r):r}).join(l.gray(" \u2192 ")):String(e)}function _o(e){if(!Array.isArray(e)||e.length===0)return l.gray("(empty tree)");let o=[],t=Math.floor(Math.log2(e.length))+1,n=Math.pow(2,t)*3;function s(r,i,a,c){if(i>e.length-1)return;let g=[],u=[],p=Math.floor(n/Math.pow(2,r+1));for(let b=i;b<=a&&b<e.length;b++){let C=e[b],I=C===null?" ":String(C);g.push(I.padStart(p).padEnd(p));let R=2*b+1,q=2*b+2,Z=R<e.length&&e[R]!==null,re=q<e.length&&e[q]!==null;if(Z||re){let H="";Z?H+="/":H+=" ",H+=" ",re?H+="\\":H+=" ",u.push(H.padStart(p).padEnd(p));}}g.length>0&&(o.push(g.join("")),u.length>0&&r<t-1&&o.push(u.join("")));}for(let r=0;r<t;r++){let i=Math.pow(2,r)-1,a=Math.pow(2,r+1)-2;s(r,i,a);}return o.join(`
162
+ `)}function Ee(e,o){if(!ee(e)||e.length===0)return String(e);let t=e.length,n=e[0].length,s=3,r=[],i=" "+e[0].map((a,c)=>String(c).padStart(s).padEnd(s)).join(" ");r.push(l.gray(i)),r.push(" \u250C"+Array(n).fill("\u2500\u2500\u2500").join("\u252C")+"\u2510");for(let a=0;a<t;a++){let c=e[a].map((g,u)=>{let p=String(g).padStart(2);return o&&ee(o)&&o[a]&&o[a][u]!==g?l.red.bold(p):p}).join(" \u2502 ");r.push(l.gray(` ${a} `)+`\u2502 ${c} \u2502`),a<t-1?r.push(" \u251C"+Array(n).fill("\u2500\u2500\u2500").join("\u253C")+"\u2524"):r.push(" \u2514"+Array(n).fill("\u2500\u2500\u2500").join("\u2534")+"\u2518");}return r.join(`
163
+ `)}function Fo(e){return ee(e)?e.map((t,n)=>{let s=Array.isArray(t)?t.join(", "):String(t);return ` ${l.cyan(String(n))} \u2192 [${s}]`}).join(`
164
+ `):String(e)}function No(e,o,t){let n=Lo(e),s=Lo(o),r=e===o,i=Sn(t),a,c;if(ee(n)){let g=ee(s)?s:void 0;return a=Ee(n,g),c=ee(s)?Ee(s):String(o),{outputVis:a,expectedVis:c,matches:r}}if(i===null)return {outputVis:String(e),expectedVis:String(o),matches:r,unsupported:true};switch(i){case "linkedlist":a=Ro(n,s),c=Ro(s);break;case "tree":a=_o(n),c=_o(s);break;case "graph":a=Fo(n),c=Fo(s);break;case "matrix":let g=ee(s)?s:void 0;a=Ee(n,g),c=ee(s)?Ee(s):String(o);break;case "array":case "string":Array.isArray(n)?(a=Ao(n,s),c=Array.isArray(s)?Ao(s):String(o)):(a=r?String(e):l.red.bold(String(e)),c=String(o));break}return {outputVis:a,expectedVis:c,matches:r}}function Io(e,o){let t=new Oe({head:[l.cyan("ID"),l.cyan("Title"),l.cyan("Difficulty"),l.cyan("Rate"),l.cyan("Status")],colWidths:[8,45,12,10,10],style:{head:[],border:[]}});for(let n of e){let s=n.title;n.isPaidOnly&&(s=`\u{1F512} ${s}`),t.push([n.questionFrontendId,s.length>42?s.slice(0,39)+"...":s,Le(n.difficulty),`${n.acRate.toFixed(1)}%`,$n(n.status)]);}console.log(t.toString()),console.log(l.gray(`
165
+ Showing ${e.length} of ${o} problems`));}function Uo(e){console.log();let o=e.isPaidOnly?"\u{1F512} ":"";if(console.log(l.bold.cyan(` ${e.questionFrontendId}. ${o}${e.title}`)),console.log(` ${Le(e.difficulty)}`),console.log(l.gray(` https://leetcode.com/problems/${e.titleSlug}/`)),console.log(),e.isPaidOnly&&(console.log(l.yellow(" \u26A0\uFE0F Premium Problem")),console.log(l.gray(" This problem requires a LeetCode Premium subscription.")),console.log(l.gray(" Visit the URL above to view on LeetCode.")),console.log()),e.topicTags.length){let n=e.topicTags.map(s=>l.bgBlue.white(` ${s.name} `)).join(" ");console.log(` ${n}`),console.log();}console.log(l.gray("\u2500".repeat(60))),console.log();let t=e.content;if(!t){console.log(l.yellow(" \u{1F512} Premium Content")),console.log(l.gray(" Problem description is not available directly.")),console.log(l.gray(" Please visit the URL above to view on LeetCode.")),console.log();return}t=t.replace(/<sup>(.*?)<\/sup>/gi,"^$1"),t=t.replace(/<strong class="example">Example (\d+):<\/strong>/gi,"\xA7EXAMPLE\xA7$1\xA7"),t=t.replace(/Input:/gi,"\xA7INPUT\xA7"),t=t.replace(/Output:/gi,"\xA7OUTPUT\xA7"),t=t.replace(/Explanation:/gi,"\xA7EXPLAIN\xA7"),t=t.replace(/<strong>Constraints:<\/strong>/gi,"\xA7CONSTRAINTS\xA7"),t=t.replace(/Constraints:/gi,"\xA7CONSTRAINTS\xA7"),t=t.replace(/<strong>Follow-up:/gi,"\xA7FOLLOWUP\xA7"),t=t.replace(/Follow-up:/gi,"\xA7FOLLOWUP\xA7"),t=t.replace(/<li>/gi," \u2022 "),t=t.replace(/<\/li>/gi,`
166
+ `),t=t.replace(/<\/p>/gi,`
1733
167
 
1734
- // src/utils/validation.ts
1735
- function isProblemId(input) {
1736
- return /^\d+$/.test(input);
1737
- }
1738
- function isFileName(input) {
1739
- return !input.includes("/") && !input.includes("\\") && input.includes(".");
1740
- }
168
+ `),t=t.replace(/<br\s*\/?>/gi,`
169
+ `),t=t.replace(/<[^>]+>/g,""),t=t.replace(/&nbsp;/g," ").replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&amp;/g,"&").replace(/&quot;/g,'"').replace(/&#39;/g,"'").replace(/&le;/g,"\u2264").replace(/&ge;/g,"\u2265").replace(/&#(\d+);/g,(n,s)=>String.fromCharCode(parseInt(s,10))),t=t.replace(/\n{3,}/g,`
1741
170
 
1742
- // src/commands/test.ts
1743
- async function testCommand(fileOrId, options) {
1744
- const { authorized } = await requireAuth();
1745
- if (!authorized) return;
1746
- let filePath = fileOrId;
1747
- if (isProblemId(fileOrId)) {
1748
- const workDir = config.getWorkDir();
1749
- const found = await findSolutionFile(workDir, fileOrId);
1750
- if (!found) {
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.`));
1754
- return;
1755
- }
1756
- filePath = found;
1757
- console.log(chalk9.gray(`Found: ${filePath}`));
1758
- } else if (isFileName(fileOrId)) {
1759
- const workDir = config.getWorkDir();
1760
- const found = await findFileByName(workDir, fileOrId);
1761
- if (!found) {
1762
- console.log(chalk9.red(`File not found: ${fileOrId}`));
1763
- console.log(chalk9.gray(`Looking in: ${workDir}`));
1764
- return;
1765
- }
1766
- filePath = found;
1767
- console.log(chalk9.gray(`Found: ${filePath}`));
1768
- }
1769
- if (!existsSync4(filePath)) {
1770
- console.log(chalk9.red(`File not found: ${filePath}`));
1771
- return;
1772
- }
1773
- const spinner = ora5({ text: "Reading solution file...", spinner: "dots" }).start();
1774
- try {
1775
- const fileName = basename(filePath);
1776
- const match = fileName.match(/^(\d+)\.([^.]+)\./);
1777
- if (!match) {
1778
- spinner.fail("Invalid filename format");
1779
- console.log(chalk9.gray("Expected format: {id}.{title-slug}.{ext}"));
1780
- console.log(chalk9.gray("Example: 1.two-sum.ts"));
1781
- return;
1782
- }
1783
- const [, problemId, titleSlug] = match;
1784
- const ext = fileName.split(".").pop();
1785
- const lang = getLangSlugFromExtension(ext);
1786
- if (!lang) {
1787
- spinner.fail(`Unsupported file extension: .${ext}`);
1788
- return;
1789
- }
1790
- const code = await readFile(filePath, "utf-8");
1791
- spinner.text = "Fetching problem details...";
1792
- const problem = await leetcodeClient.getProblem(titleSlug);
1793
- const testcases = options.testcase ?? problem.exampleTestcases ?? problem.sampleTestCase;
1794
- spinner.text = "Running tests...";
1795
- const result = await leetcodeClient.testSolution(titleSlug, code, lang, testcases, problem.questionId);
1796
- spinner.stop();
1797
- displayTestResult(result, options.visualize ? problem.topicTags : void 0);
1798
- } catch (error) {
1799
- spinner.fail("Test failed");
1800
- if (error instanceof Error) {
1801
- console.log(chalk9.red(error.message));
1802
- }
1803
- }
1804
- }
171
+ `).trim(),t=t.replace(/§EXAMPLE§(\d+)§/g,(n,s)=>l.green.bold(`\u{1F4CC} Example ${s}:`)),t=t.replace(/§INPUT§/g,l.yellow("Input:")),t=t.replace(/§OUTPUT§/g,l.yellow("Output:")),t=t.replace(/§EXPLAIN§/g,l.gray("Explanation:")),t=t.replace(/§CONSTRAINTS§/g,l.cyan.bold(`
172
+ \u{1F4CB} Constraints:`)),t=t.replace(/§FOLLOWUP§/g,l.magenta.bold(`
173
+ \u{1F4A1} Follow-up:`)),console.log(t),console.log();}function jo(e,o){if(console.log(),e.compile_error){console.log(l.red.bold("\u274C Compile Error")),console.log(l.red(e.compile_error));return}if(e.runtime_error){console.log(l.red.bold("\u274C Runtime Error")),console.log(l.red(e.runtime_error));return}e.correct_answer?console.log(l.green.bold("\u2713 All test cases passed!")):console.log(l.yellow.bold("\u2717 Some test cases failed"));let t=e.code_answer??[],n=e.expected_code_answer??[];if(o&&t.length>0){console.log(),console.log(l.gray.bold("\u2500".repeat(50)));let r=t.map((i,a)=>({out:i,exp:n[a]??""})).filter(({out:i,exp:a})=>i!==""||a!=="");for(let i=0;i<r.length;i++){let{out:a,exp:c}=r[i],{outputVis:g,expectedVis:u,matches:p,unsupported:b}=No(a,c,o);console.log(),console.log(l.gray(`Test Case ${i+1}:`)),b&&(console.log(l.yellow(" \u26A0 No visualization available for this problem type")),console.log(l.gray(` Tags: ${o.map(C=>C.name).join(", ")}`))),console.log(),console.log(l.cyan(" Your Output:")),g.split(`
174
+ `).forEach(C=>console.log(` ${C}`)),console.log(),console.log(l.cyan(" Expected:")),u.split(`
175
+ `).forEach(C=>console.log(` ${C}`)),console.log(),console.log(p?l.green(" \u2713 Match"):l.red(" \u2717 Mismatch"));}console.log(l.gray.bold("\u2500".repeat(50)));}else {console.log(),console.log(l.gray("Your Output:"));for(let r of t)console.log(l.white(` ${r}`));console.log(),console.log(l.gray("Expected Output:"));for(let r of n)console.log(l.white(` ${r}`));}let s=(e.std_output_list??[]).filter(r=>r);if(s.length>0){console.log(),console.log(l.gray("Stdout:"));for(let r of s)console.log(l.gray(` ${r}`));}}function Mo(e){if(console.log(),e.compile_error){console.log(l.red.bold("\u274C Compile Error")),console.log(l.red(e.compile_error));return}if(e.runtime_error){console.log(l.red.bold("\u274C Runtime Error")),console.log(l.red(e.runtime_error)),e.last_testcase&&console.log(l.gray("Last testcase:"),e.last_testcase);return}e.status_msg==="Accepted"?(console.log(l.green.bold("\u2713 Accepted!")),console.log(),console.log(l.gray("Runtime:"),l.white(e.status_runtime),l.gray(`(beats ${e.runtime_percentile?.toFixed(1)??"N/A"}%)`)),console.log(l.gray("Memory:"),l.white(e.status_memory),l.gray(`(beats ${e.memory_percentile?.toFixed(1)??"N/A"}%)`))):(console.log(l.red.bold(`\u274C ${e.status_msg}`)),console.log(),console.log(l.gray(`Passed ${e.total_correct}/${e.total_testcases} testcases`)),e.code_output&&console.log(l.gray("Your Output:"),e.code_output),e.expected_output&&console.log(l.gray("Expected:"),e.expected_output),e.last_testcase&&console.log(l.gray("Failed testcase:"),e.last_testcase));}function qo(e,o,t,n,s,r){console.log(),console.log(l.bold.white(`\u{1F464} ${e}`)+(o?l.gray(` (${o})`):"")),console.log(l.gray(`Ranking: #${t.toLocaleString()}`)),console.log();let i=new Oe({head:[l.cyan("Difficulty"),l.cyan("Solved")],style:{head:[],border:[]}});for(let c of n)c.difficulty!=="All"&&i.push([Le(c.difficulty),c.count.toString()]);let a=n.find(c=>c.difficulty==="All")?.count??0;i.push([l.white.bold("Total"),l.white.bold(a.toString())]),console.log(i.toString()),console.log(),console.log(l.gray("\u{1F525} Current streak:"),l.hex("#FFA500")(s.toString()),l.gray("days")),console.log(l.gray("\u{1F4C5} Total active days:"),l.white(r.toString()));}function Oo(e,o){if(console.log(),console.log(l.bold.yellow("\u{1F3AF} Daily Challenge"),l.gray(`(${e})`)),console.log(),console.log(l.white(`${o.questionFrontendId}. ${o.title}`)),console.log(Le(o.difficulty)),console.log(l.gray(`https://leetcode.com/problems/${o.titleSlug}/`)),o.topicTags.length){console.log();let t=o.topicTags.map(n=>l.blue(n.name)).join(" ");console.log(l.gray("Tags:"),t);}}function Le(e){switch(e.toLowerCase()){case "easy":return l.green(e);case "medium":return l.yellow(e);case "hard":return l.red(e);default:return e}}function $n(e){switch(e){case "ac":return l.green("\u2713");case "notac":return l.yellow("\u25CB");default:return l.gray("-")}}function We(e){let o=new Oe({head:[l.cyan("ID"),l.cyan("Status"),l.cyan("Lang"),l.cyan("Runtime"),l.cyan("Memory"),l.cyan("Date")],colWidths:[12,18,15,12,12,25],style:{head:[],border:[]}});for(let t of e){let n=t.statusDisplay==="Accepted",s=new Date(parseInt(t.timestamp)*1e3).toLocaleString();o.push([t.id,n?l.green(t.statusDisplay):l.red(t.statusDisplay),t.lang,t.runtime,t.memory,s]);}console.log(o.toString());}async function Wo(e){let{authorized:o}=await v();if(!o)return;let t=Ie("Fetching problems...").start();try{let n={},s=parseInt(e.limit??"20",10),r=parseInt(e.page??"1",10);if(n.limit=s,n.skip=(r-1)*s,e.difficulty){let c={easy:"EASY",e:"EASY",medium:"MEDIUM",m:"MEDIUM",hard:"HARD",h:"HARD"};n.difficulty=c[e.difficulty.toLowerCase()];}if(e.status){let c={todo:"NOT_STARTED",solved:"AC",ac:"AC",attempted:"TRIED",tried:"TRIED"};n.status=c[e.status.toLowerCase()];}e.tag?.length&&(n.tags=e.tag),e.search&&(n.searchKeywords=e.search);let{total:i,problems:a}=await h.getProblems(n);if(t.stop(),a.length===0){console.log(l.yellow("No problems found matching your criteria."));return}Io(a,i),r*s<i&&console.log(l.gray(`
176
+ Page ${r} of ${Math.ceil(i/s)}. Use --page to navigate.`));}catch(n){t.fail("Failed to fetch problems"),n instanceof Error&&console.log(l.red(n.message));}}async function Ae(e){let{authorized:o}=await v();if(!o)return;let t=Ie("Fetching problem...").start();try{let n;if(/^\d+$/.test(e)?n=await h.getProblemById(e):n=await h.getProblem(e),!n){t.fail(`Problem "${e}" not found`);return}t.stop(),Uo(n);}catch(n){t.fail("Failed to fetch problem"),n instanceof Error&&console.log(l.red(n.message));}}var Ge=join(homedir(),".leetcode"),ze=join(Ge,"workspaces.json"),Re=join(Ge,"workspaces");function Fe(e){existsSync(e)||mkdirSync(e,{recursive:true});}function ie(){return existsSync(ze)?JSON.parse(readFileSync(ze,"utf-8")):{active:"default",workspaces:[]}}function _e(e){Fe(Ge),writeFileSync(ze,JSON.stringify(e,null,2));}var $={ensureInitialized(){let e=ie();e.workspaces.length===0&&(this.create("default",{workDir:join(homedir(),"leetcode"),lang:"typescript"}),e.workspaces=["default"],e.active="default",_e(e));},getActive(){return this.ensureInitialized(),ie().active},setActive(e){let o=ie();return o.workspaces.includes(e)?(o.active=e,_e(o),true):false},list(){return this.ensureInitialized(),ie().workspaces},exists(e){return this.ensureInitialized(),ie().workspaces.includes(e)},create(e,o){let t=ie();if(t.workspaces.includes(e))return false;let n=join(Re,e);Fe(n),Fe(join(n,"snapshots"));let s=join(n,"config.json");return writeFileSync(s,JSON.stringify(o,null,2)),writeFileSync(join(n,"timer.json"),JSON.stringify({solveTimes:{},activeTimer:null},null,2)),writeFileSync(join(n,"collab.json"),JSON.stringify({session:null},null,2)),t.workspaces.push(e),_e(t),true},delete(e){if(e==="default")return false;let o=ie();return o.workspaces.includes(e)?(o.workspaces=o.workspaces.filter(t=>t!==e),o.active===e&&(o.active="default"),_e(o),true):false},getWorkspaceDir(e){let o=e??this.getActive();return join(Re,o)},getConfig(e){let o=e??this.getActive(),t=join(Re,o,"config.json");return existsSync(t)?JSON.parse(readFileSync(t,"utf-8")):{workDir:join(homedir(),"leetcode"),lang:"typescript"}},setConfig(e,o){let t=o??this.getActive(),n=join(Re,t);Fe(n);let r={...this.getConfig(t),...e};writeFileSync(join(n,"config.json"),JSON.stringify(r,null,2));},getSnapshotsDir(){return join(this.getWorkspaceDir(),"snapshots")},getTimerPath(){return join(this.getWorkspaceDir(),"timer.json")},getCollabPath(){return join(this.getWorkspaceDir(),"collab.json")}};var w={getConfig(){let e=$.getConfig();return {language:e.lang,editor:e.editor,workDir:e.workDir,repo:e.syncRepo}},setLanguage(e){$.setConfig({lang:e});},setEditor(e){$.setConfig({editor:e});},setWorkDir(e){$.setConfig({workDir:e});},setRepo(e){$.setConfig({syncRepo:e});},deleteRepo(){let e=$.getConfig();delete e.syncRepo,$.setConfig(e);},getLanguage(){return $.getConfig().lang},getEditor(){return $.getConfig().editor},getWorkDir(){return $.getConfig().workDir},getRepo(){return $.getConfig().syncRepo},getPath(){return join($.getWorkspaceDir(),"config.json")},getActiveWorkspace(){return $.getActive()}};var ge={typescript:"ts",javascript:"js",python3:"py",java:"java",cpp:"cpp",c:"c",csharp:"cs",go:"go",rust:"rs",kotlin:"kt",swift:"swift"},we={typescript:"typescript",javascript:"javascript",python3:"python3",python:"python3",java:"java","c++":"cpp",cpp:"cpp",c:"c","c#":"csharp",csharp:"csharp",go:"go",golang:"go",rust:"rust",kotlin:"kotlin",swift:"swift"};function Vo(e,o){let t=e.find(n=>we[n.langSlug.toLowerCase()]===o);return t||(e[0]??null)}function zo(e,o,t,n,s,r,i){let a=Dn(r);return `${En(a,e,t,n,o,i)}
1805
177
 
1806
- // src/commands/submit.ts
1807
- import { readFile as readFile2 } from "fs/promises";
1808
- import { existsSync as existsSync6 } from "fs";
1809
- import { basename as basename2 } from "path";
1810
- import ora6 from "ora";
1811
- import chalk10 from "chalk";
178
+ ${s}
179
+ `}function Dn(e){return e==="python3"?{single:"#",blockStart:'"""',blockEnd:'"""',linePrefix:""}:{single:"//",blockStart:"/*",blockEnd:"*/",linePrefix:" * "}}function xn(e){let o=e;return o=o.replace(/<sup>(.*?)<\/sup>/gi,"^$1"),o=o.replace(/<strong class="example">Example (\d+):<\/strong>/gi,`
180
+ Example $1:`),o=o.replace(/<li>/gi,"\u2022 "),o=o.replace(/<\/li>/gi,`
181
+ `),o=o.replace(/<\/p>/gi,`
1812
182
 
1813
- // src/storage/timer.ts
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"));
1823
- }
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
- }
1834
- var timerStorage = {
1835
- startTimer(problemId, title, difficulty, durationMinutes) {
1836
- const data = loadTimer();
1837
- data.activeTimer = {
1838
- problemId,
1839
- title,
1840
- difficulty,
1841
- startedAt: (/* @__PURE__ */ new Date()).toISOString(),
1842
- durationMinutes
1843
- };
1844
- saveTimer(data);
1845
- },
1846
- getActiveTimer() {
1847
- return loadTimer().activeTimer;
1848
- },
1849
- stopTimer() {
1850
- const data = loadTimer();
1851
- const active = data.activeTimer;
1852
- if (!active) return null;
1853
- const startedAt = new Date(active.startedAt);
1854
- const now = /* @__PURE__ */ new Date();
1855
- const durationSeconds = Math.floor((now.getTime() - startedAt.getTime()) / 1e3);
1856
- data.activeTimer = null;
1857
- saveTimer(data);
1858
- return { durationSeconds };
1859
- },
1860
- recordSolveTime(problemId, title, difficulty, durationSeconds, timerMinutes) {
1861
- const data = loadTimer();
1862
- if (!data.solveTimes[problemId]) {
1863
- data.solveTimes[problemId] = [];
1864
- }
1865
- data.solveTimes[problemId].push({
1866
- problemId,
1867
- title,
1868
- difficulty,
1869
- solvedAt: (/* @__PURE__ */ new Date()).toISOString(),
1870
- durationSeconds,
1871
- timerMinutes
1872
- });
1873
- saveTimer(data);
1874
- },
1875
- getSolveTimes(problemId) {
1876
- const data = loadTimer();
1877
- return data.solveTimes[problemId] ?? [];
1878
- },
1879
- getAllSolveTimes() {
1880
- return loadTimer().solveTimes ?? {};
1881
- },
1882
- getStats() {
1883
- const solveTimes = loadTimer().solveTimes ?? {};
1884
- let totalProblems = 0;
1885
- let totalTime = 0;
1886
- for (const times of Object.values(solveTimes)) {
1887
- totalProblems += times.length;
1888
- for (const t of times) {
1889
- totalTime += t.durationSeconds;
1890
- }
1891
- }
1892
- return {
1893
- totalProblems,
1894
- totalTime,
1895
- avgTime: totalProblems > 0 ? Math.floor(totalTime / totalProblems) : 0
1896
- };
1897
- }
1898
- };
183
+ `),o=o.replace(/<br\s*\/?>/gi,`
184
+ `),o=o.replace(/<[^>]+>/g,""),o=o.replace(/&nbsp;/g," ").replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&amp;/g,"&").replace(/&quot;/g,'"').replace(/&#39;/g,"'").replace(/&le;/g,"<=").replace(/&ge;/g,">=").replace(/&#(\d+);/g,(t,n)=>String.fromCharCode(parseInt(n,10))),o=o.replace(/\n{3,}/g,`
1899
185
 
1900
- // src/commands/submit.ts
1901
- async function submitCommand(fileOrId) {
1902
- const { authorized } = await requireAuth();
1903
- if (!authorized) return;
1904
- let filePath = fileOrId;
1905
- if (isProblemId(fileOrId)) {
1906
- const workDir = config.getWorkDir();
1907
- const found = await findSolutionFile(workDir, fileOrId);
1908
- if (!found) {
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.`));
1912
- return;
1913
- }
1914
- filePath = found;
1915
- console.log(chalk10.gray(`Found: ${filePath}`));
1916
- } else if (isFileName(fileOrId)) {
1917
- const workDir = config.getWorkDir();
1918
- const found = await findFileByName(workDir, fileOrId);
1919
- if (!found) {
1920
- console.log(chalk10.red(`File not found: ${fileOrId}`));
1921
- console.log(chalk10.gray(`Looking in: ${workDir}`));
1922
- return;
1923
- }
1924
- filePath = found;
1925
- console.log(chalk10.gray(`Found: ${filePath}`));
1926
- }
1927
- if (!existsSync6(filePath)) {
1928
- console.log(chalk10.red(`File not found: ${filePath}`));
1929
- return;
1930
- }
1931
- const spinner = ora6({ text: "Reading solution file...", spinner: "dots" }).start();
1932
- try {
1933
- const fileName = basename2(filePath);
1934
- const match = fileName.match(/^(\d+)\.([^.]+)\./);
1935
- if (!match) {
1936
- spinner.fail("Invalid filename format");
1937
- console.log(chalk10.gray("Expected format: {id}.{title-slug}.{ext}"));
1938
- console.log(chalk10.gray("Example: 1.two-sum.ts"));
1939
- return;
1940
- }
1941
- const [, problemId, titleSlug] = match;
1942
- const ext = fileName.split(".").pop();
1943
- const lang = getLangSlugFromExtension(ext);
1944
- if (!lang) {
1945
- spinner.fail(`Unsupported file extension: .${ext}`);
1946
- return;
1947
- }
1948
- const code = await readFile2(filePath, "utf-8");
1949
- spinner.text = "Fetching problem details...";
1950
- const problem = await leetcodeClient.getProblem(titleSlug);
1951
- spinner.text = "Submitting solution...";
1952
- const result = await leetcodeClient.submitSolution(
1953
- titleSlug,
1954
- code,
1955
- lang,
1956
- problem.questionId
1957
- );
1958
- spinner.stop();
1959
- displaySubmissionResult(result);
1960
- if (result.status_msg === "Accepted") {
1961
- const activeTimer = timerStorage.getActiveTimer();
1962
- if (activeTimer && activeTimer.problemId === problemId) {
1963
- const timerResult = timerStorage.stopTimer();
1964
- if (timerResult) {
1965
- timerStorage.recordSolveTime(
1966
- problemId,
1967
- problem.title,
1968
- problem.difficulty,
1969
- timerResult.durationSeconds,
1970
- activeTimer.durationMinutes
1971
- );
1972
- const mins = Math.floor(timerResult.durationSeconds / 60);
1973
- const secs = timerResult.durationSeconds % 60;
1974
- const timeStr = `${mins}m ${secs}s`;
1975
- const withinLimit = timerResult.durationSeconds <= activeTimer.durationMinutes * 60;
1976
- console.log();
1977
- console.log(chalk10.bold("\u23F1\uFE0F Timer Result:"));
1978
- console.log(
1979
- ` Solved in ${withinLimit ? chalk10.green(timeStr) : chalk10.yellow(timeStr)} (limit: ${activeTimer.durationMinutes}m)`
1980
- );
1981
- if (withinLimit) {
1982
- console.log(chalk10.green(" \u2713 Within time limit!"));
1983
- } else {
1984
- console.log(chalk10.yellow(" \u26A0 Exceeded time limit"));
1985
- }
1986
- }
1987
- }
1988
- }
1989
- } catch (error) {
1990
- spinner.fail("Submission failed");
1991
- if (error instanceof Error) {
1992
- console.log(chalk10.red(error.message));
1993
- }
1994
- }
1995
- }
186
+ `).trim(),o}function En(e,o,t,n,s,r){let i=e.linePrefix,a=[e.blockStart,`${i}${o}. ${t}`,`${i}Difficulty: ${n}`,`${i}https://leetcode.com/problems/${s}/`];if(r){a.push(`${i}`),a.push(`${i}${"\u2500".repeat(50)}`),a.push(`${i}`);let g=xn(r).split(`
187
+ `);for(let u of g)if(u.length>70){let p=u.split(" "),b="";for(let C of p)(b+" "+C).length>70?(a.push(`${i}${b.trim()}`),b=C):b+=" "+C;b.trim()&&a.push(`${i}${b.trim()}`);}else a.push(`${i}${u}`);}return a.push(e.blockEnd),a.join(`
188
+ `)}function Bo(e,o,t){let n=ge[t];return `${e}.${o}.${n}`}var _n=["vim","nvim","vi","nano","emacs","micro","helix"],Fn=["code","code-insiders","cursor","codium","vscodium"];async function Se(e,o){let t=w.getEditor()??process.env.EDITOR??"code",n=w.getWorkDir();if(_n.includes(t)){console.log(),console.log(l.gray(`Open with: ${t} ${e}`));return}try{if(Fn.includes(t)){spawn(t,["-r",n,"-g",e],{detached:!0,stdio:"ignore"}).unref();return}await An(e,{app:{name:t}});}catch{}}async function Y(e,o){let{authorized:t}=await v();if(!t)return false;let n=Ie({text:"Fetching problem details...",spinner:"dots"}).start();try{let s;if(/^\d+$/.test(e)?s=await h.getProblemById(e):s=await h.getProblem(e),!s)return n.fail(`Problem "${e}" not found`),!1;n.text="Generating solution file...";let r=o.lang?.toLowerCase()??w.getLanguage(),i=we[r]??r,a=s.codeSnippets??[],c=Vo(a,i),g;if(a.length===0)n.warn(l.yellow("Premium Problem (No code snippets available)")),console.log(l.gray("Generating placeholder file with problem info...")),g=`// \u{1F512} Premium Problem - ${s.title}
189
+ // Solution stub not available - visit LeetCode to view`;else if(c)g=c.code;else return n.fail(`No code template available for ${i}`),console.log(l.gray(`Available languages: ${a.map(Z=>Z.langSlug).join(", ")}`)),!1;let u=zo(s.questionFrontendId,s.titleSlug,s.title,s.difficulty,g,i,s.content??void 0),p=w.getWorkDir(),b=s.difficulty,C=s.topicTags.length>0?s.topicTags[0].name.replace(/[^\w\s-]/g,"").trim():"Uncategorized",I=join(p,b,C);existsSync(I)||await mkdir(I,{recursive:!0});let R=Bo(s.questionFrontendId,s.titleSlug,i),q=join(I,R);return existsSync(q)?(n.warn(`File already exists: ${R}`),console.log(l.gray(`Path: ${q}`)),o.open!==!1&&await Se(q),!0):(await writeFile(q,u,"utf-8"),n.succeed(`Created ${l.green(R)}`),console.log(l.gray(`Path: ${q}`)),console.log(),console.log(l.cyan(`${s.questionFrontendId}. ${s.title}`)),console.log(l.gray(`Difficulty: ${s.difficulty} | Category: ${C}`)),o.open!==!1&&await Se(q),!0)}catch(s){if(n.fail("Failed to fetch problem"),s instanceof Error)if(s.message.includes("expected object, received null"))console.log(l.red(`Problem "${e}" not found`));else try{let r=JSON.parse(s.message);Array.isArray(r)?console.log(l.red("API Response Validation Failed")):console.log(l.red(s.message));}catch{console.log(l.red(s.message));}return false}}async function Jo(e,o){if(e.length===0){console.log(l.yellow("Please provide at least one problem ID"));return}let{authorized:t}=await v();if(!t)return;console.log(l.cyan(`\u{1F4E6} Picking ${e.length} problem${e.length!==1?"s":""}...`)),console.log(),console.log();let n=0,s=0;for(let r of e)await Y(r,{...o,open:false})?n++:s++,console.log();console.log(l.gray("\u2500".repeat(50))),console.log(l.bold(`Done! ${l.green(`${n} succeeded`)}${s>0?`, ${l.red(`${s} failed`)}`:""}`));}var Zo=5;async function O(e,o,t=0){if(!existsSync(e)||t>=Zo)return null;let n=await readdir(e,{withFileTypes:true});for(let s of n){if(s.name.startsWith("."))continue;let r=join(e,s.name);if(s.isDirectory()){let i=await O(r,o,t+1);if(i)return i}else if(s.name.startsWith(`${o}.`)){let i=s.name.split(".").pop()?.toLowerCase();if(i&&i in jn)return r}}return null}async function $e(e,o,t=0){if(!existsSync(e)||t>=Zo)return null;let n=await readdir(e,{withFileTypes:true});for(let s of n){let r=join(e,s.name);if(s.isDirectory()){let i=await $e(r,o,t+1);if(i)return i}else if(s.name===o)return r}return null}var jn={ts:"typescript",js:"javascript",py:"python3",java:"java",cpp:"cpp",c:"c",cs:"csharp",go:"go",rs:"rust",kt:"kotlin",swift:"swift"};function ae(e){return {ts:"typescript",js:"javascript",py:"python3",java:"java",cpp:"cpp",c:"c",cs:"csharp",go:"golang",rs:"rust",kt:"kotlin",swift:"swift"}[e.toLowerCase()]??null}function oe(e){return /^\d+$/.test(e)}function Ne(e){return !e.includes("/")&&!e.includes("\\")&&e.includes(".")}function me(e,o){let t=resolve(e),n=resolve(o),s=n.endsWith(sep)?n:n+sep;return t===n||t.startsWith(s)}async function tt(e,o){let{authorized:t}=await v();if(!t)return;let n=e,s=w.getWorkDir();if(oe(e)){let i=await O(s,e);if(!i){console.log(l.red(`No solution file found for problem ${e}`)),console.log(l.gray(`Looking in: ${s}`)),console.log(l.gray(`Run "leetcode pick ${e}" first to create a solution file.`));return}n=i,console.log(l.gray(`Found: ${n}`));}else if(Ne(e)){let i=await $e(s,e);if(!i){console.log(l.red(`File not found: ${e}`)),console.log(l.gray(`Looking in: ${s}`));return}n=i,console.log(l.gray(`Found: ${n}`));}if(!existsSync(n)){console.log(l.red(`File not found: ${n}`));return}if(!me(n,s)){console.log(l.red("\u26A0\uFE0F Security Error: File path is outside the configured workspace")),console.log(l.gray(`File: ${n}`)),console.log(l.gray(`Workspace: ${s}`)),console.log(l.yellow(`
190
+ For security reasons, you can only test files from within your workspace.`)),console.log(l.gray('Use "leetcode config workdir <path>" to change your workspace.'));return}let r=Ie({text:"Reading solution file...",spinner:"dots"}).start();try{let i=basename(n),a=i.match(/^(\d+)\.([^.]+)\./);if(!a){r.fail("Invalid filename format"),console.log(l.gray("Expected format: {id}.{title-slug}.{ext}")),console.log(l.gray("Example: 1.two-sum.ts"));return}let[,c,g]=a,u=i.split(".").pop(),p=ae(u);if(!p){r.fail(`Unsupported file extension: .${u}`);return}let b=await readFile(n,"utf-8");r.text="Fetching problem details...";let C=await h.getProblem(g),I=o.testcase??C.exampleTestcases??C.sampleTestCase;r.text="Running tests...";let R=await h.testSolution(g,b,p,I,C.questionId);r.stop(),jo(R,o.visualize?C.topicTags:void 0);}catch(i){r.fail("Test failed"),i instanceof Error&&console.log(l.red(i.message));}}function st(){return $.getTimerPath()}function le(){let e=st();return existsSync(e)?JSON.parse(readFileSync(e,"utf-8")):{solveTimes:{},activeTimer:null}}function Ye(e){let o=st(),t=dirname(o);existsSync(t)||mkdirSync(t,{recursive:true}),writeFileSync(o,JSON.stringify(e,null,2));}var B={startTimer(e,o,t,n){let s=le();s.activeTimer={problemId:e,title:o,difficulty:t,startedAt:new Date().toISOString(),durationMinutes:n},Ye(s);},getActiveTimer(){return le().activeTimer},stopTimer(){let e=le(),o=e.activeTimer;if(!o)return null;let t=new Date(o.startedAt),s=Math.floor((new Date().getTime()-t.getTime())/1e3);return e.activeTimer=null,Ye(e),{durationSeconds:s}},recordSolveTime(e,o,t,n,s){let r=le();r.solveTimes[e]||(r.solveTimes[e]=[]),r.solveTimes[e].push({problemId:e,title:o,difficulty:t,solvedAt:new Date().toISOString(),durationSeconds:n,timerMinutes:s}),Ye(r);},getSolveTimes(e){return le().solveTimes[e]??[]},getAllSolveTimes(){return le().solveTimes??{}},getStats(){let e=le().solveTimes??{},o=0,t=0;for(let n of Object.values(e)){o+=n.length;for(let s of n)t+=s.durationSeconds;}return {totalProblems:o,totalTime:t,avgTime:o>0?Math.floor(t/o):0}}};async function rt(e){let{authorized:o}=await v();if(!o)return;let t=e,n=w.getWorkDir();if(oe(e)){let r=await O(n,e);if(!r){console.log(l.red(`No solution file found for problem ${e}`)),console.log(l.gray(`Looking in: ${n}`)),console.log(l.gray(`Run "leetcode pick ${e}" first to create a solution file.`));return}t=r,console.log(l.gray(`Found: ${t}`));}else if(Ne(e)){let r=await $e(n,e);if(!r){console.log(l.red(`File not found: ${e}`)),console.log(l.gray(`Looking in: ${n}`));return}t=r,console.log(l.gray(`Found: ${t}`));}if(!existsSync(t)){console.log(l.red(`File not found: ${t}`));return}if(!me(t,n)){console.log(l.red("\u26A0\uFE0F Security Error: File path is outside the configured workspace")),console.log(l.gray(`File: ${t}`)),console.log(l.gray(`Workspace: ${n}`)),console.log(l.yellow(`
191
+ For security reasons, you can only submit files from within your workspace.`)),console.log(l.gray('Use "leetcode config workdir <path>" to change your workspace.'));return}let s=Ie({text:"Reading solution file...",spinner:"dots"}).start();try{let r=basename(t),i=r.match(/^(\d+)\.([^.]+)\./);if(!i){s.fail("Invalid filename format"),console.log(l.gray("Expected format: {id}.{title-slug}.{ext}")),console.log(l.gray("Example: 1.two-sum.ts"));return}let[,a,c]=i,g=r.split(".").pop(),u=ae(g);if(!u){s.fail(`Unsupported file extension: .${g}`);return}let p=await readFile(t,"utf-8");s.text="Fetching problem details...";let b=await h.getProblem(c);s.text="Submitting solution...";let C=await h.submitSolution(c,p,u,b.questionId);if(s.stop(),Mo(C),C.status_msg==="Accepted"){let I=B.getActiveTimer();if(I&&I.problemId===a){let R=B.stopTimer();if(R){B.recordSolveTime(a,b.title,b.difficulty,R.durationSeconds,I.durationMinutes);let q=Math.floor(R.durationSeconds/60),Z=R.durationSeconds%60,re=`${q}m ${Z}s`,H=R.durationSeconds<=I.durationMinutes*60;console.log(),console.log(l.bold("\u23F1\uFE0F Timer Result:")),console.log(` Solved in ${H?l.green(re):l.yellow(re)} (limit: ${I.durationMinutes}m)`),console.log(H?l.green(" \u2713 Within time limit!"):l.yellow(" \u26A0 Exceeded time limit"));}}}}catch(r){s.fail("Submission failed"),r instanceof Error&&console.log(l.red(r.message));}}var it=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];function at(e){let o=JSON.parse(e),t=new Date,n=[];for(let i=11;i>=0;i--){let a=new Date(t);a.setDate(a.getDate()-i*7-a.getDay());let c=0,g=0;for(let p=0;p<7;p++){let b=new Date(a);if(b.setDate(b.getDate()+p),b>t)break;let C=new Date(Date.UTC(b.getFullYear(),b.getMonth(),b.getDate())),I=Math.floor(C.getTime()/1e3).toString(),R=o[I]||0;c+=R,R>0&&g++;}let u=new Date(a);u.setDate(u.getDate()+6),n.push({start:`${it[a.getMonth()]} ${a.getDate()}`,end:`${it[u.getMonth()]} ${u.getDate()}`,count:c,days:g});}let s=n.reduce((i,a)=>i+a.count,0),r=n.reduce((i,a)=>i+a.days,0);console.log(),console.log(l.bold("\u{1F4C5} Activity (Last 12 Weeks)")),console.log(l.gray("How many problems you submitted and days you practiced.")),console.log(l.gray("\u2500".repeat(50))),console.log();for(let i of n){let a=`${i.start} - ${i.end}`.padEnd(18),c=i.count>0?l.green("\u2588".repeat(Math.min(i.count,10))).padEnd(10):l.gray("\xB7").padEnd(10),g=i.count>0?`${i.count} subs`.padEnd(10):"".padEnd(10),u=i.days>0?`${i.days}d active`:"";console.log(` ${l.white(a)} ${c} ${l.cyan(g)} ${l.yellow(u)}`);}console.log(l.gray("\u2500".repeat(50))),console.log(` ${l.bold.white("Total:")} ${l.cyan.bold(s+" submissions")}, ${l.yellow.bold(r+" days active")}`),console.log();}function lt(e,o,t){console.log(),console.log(l.bold("\u{1F3AF} Skill Breakdown")),console.log(l.gray("\u2500".repeat(45)));let n=(s,r,i)=>{if(r.length===0)return;console.log(),console.log(i.bold(` ${s}`));let a=[...r].sort((c,g)=>g.problemsSolved-c.problemsSolved);for(let c of a.slice(0,8)){let g=c.tagName.padEnd(22),u=i("\u2588".repeat(Math.min(c.problemsSolved,15)));console.log(` ${l.white(g)} ${u} ${l.white(c.problemsSolved)}`);}};n("Fundamental",e,l.green),n("Intermediate",o,l.yellow),n("Advanced",t,l.red),console.log();}function ct(e){let o=JSON.parse(e),t=new Date,n=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],s=[];for(let c=6;c>=0;c--){let g=new Date(t);g.setDate(g.getDate()-c);let u=new Date(Date.UTC(g.getFullYear(),g.getMonth(),g.getDate())),p=Math.floor(u.getTime()/1e3).toString();s.push({label:n[g.getDay()],count:o[p]||0});}let r=Math.max(...s.map(c=>c.count),1),i=6;console.log(),console.log(l.bold("\u{1F4C8} Submission Trend (Last 7 Days)")),console.log(l.gray("\u2500".repeat(35))),console.log();for(let c=i;c>=1;c--){let g=` ${c===i?r.toString().padStart(2):" "} \u2502`;for(let u of s)Math.round(u.count/r*i)>=c?g+=l.green(" \u2588\u2588 "):g+=" ";console.log(g);}console.log(` 0 \u2514${"\u2500\u2500\u2500\u2500".repeat(7)}`),console.log(` ${s.map(c=>c.label.padEnd(4)).join("")}`),console.log(l.gray(` ${s.map(c=>c.count.toString().padEnd(4)).join("")}`));let a=s.reduce((c,g)=>c+g.count,0);console.log(),console.log(l.white(` Total: ${a} submissions this week`)),console.log();}async function gt(e,o={}){let{authorized:t,username:n}=await v();if(!t)return;let s=Ie("Fetching statistics...").start();try{let r=e||n;if(!r){s.fail("No username found");return}let i=await h.getUserProfile(r);if(s.stop(),!o.calendar&&!o.skills&&!o.trend){qo(i.username,i.realName,i.ranking,i.acSubmissionNum,i.streak,i.totalActiveDays);return}if(o.calendar&&(i.submissionCalendar?at(i.submissionCalendar):console.log(l.yellow("Calendar data not available."))),o.trend&&(i.submissionCalendar?ct(i.submissionCalendar):console.log(l.yellow("Calendar data not available."))),o.skills){s.start("Fetching skill stats...");let a=await h.getSkillStats(r);s.stop(),lt(a.fundamental,a.intermediate,a.advanced);}}catch(r){s.fail("Failed to fetch statistics"),r instanceof Error&&console.log(l.red(r.message));}}async function mt(){let{authorized:e}=await v();if(!e)return;let o=Ie("Fetching daily challenge...").start();try{let t=await h.getDailyChallenge();o.stop(),Oo(t.date,t.question),console.log(),console.log(l.gray("Run the following to start working on this problem:")),console.log(l.cyan(` leetcode pick ${t.question.titleSlug}`));}catch(t){o.fail("Failed to fetch daily challenge"),t instanceof Error&&console.log(l.red(t.message));}}async function dt(e){let{authorized:o}=await v();if(!o)return;let t=Ie("Fetching random problem...").start();try{let n={};if(e.difficulty){let i={easy:"EASY",e:"EASY",medium:"MEDIUM",m:"MEDIUM",hard:"HARD",h:"HARD"}[e.difficulty.toLowerCase()];if(i)n.difficulty=i;else {t.fail(`Invalid difficulty: ${e.difficulty}`);return}}e.tag&&(n.tags=[e.tag]);let s=await h.getRandomProblem(n);t.succeed("Found random problem!"),console.log(),e.pick?await Y(s,{open:e.open??!0}):(await Ae(s),console.log(l.gray("Run following to start solving:")),console.log(l.cyan(` leetcode pick ${s}`)));}catch(n){t.fail("Failed to fetch random problem"),n instanceof Error&&console.log(l.red(n.message));}}async function ft(e,o){let{authorized:t}=await v();if(!t)return;let n=Ie("Fetching problem info...").start();try{let s;if(/^\d+$/.test(e)?s=await h.getProblemById(e):s=await h.getProblem(e),!s){n.fail(`Problem "${e}" not found`);return}let r=s.titleSlug;n.text="Fetching submissions...";let i=o.limit?parseInt(o.limit,10):20,a=await h.getSubmissionList(r,i);if(n.stop(),a.length===0){console.log(l.yellow("No submissions found."));return}if(o.last){let c=a.find(g=>g.statusDisplay==="Accepted");c?(console.log(l.bold("Last Accepted Submission:")),We([c])):console.log(l.yellow("No accepted submissions found in recent history."));}else We(a);if(o.download){let c=Ie("Downloading submission...").start(),g=a.find(gn=>gn.statusDisplay==="Accepted");if(!g){c.fail("No accepted submission found to download.");return}let u=parseInt(g.id,10);if(isNaN(u)){c.fail("Invalid submission ID format");return}let p=await h.getSubmissionDetails(u),b=w.getWorkDir(),C=s.difficulty,I=s.topicTags.length>0?s.topicTags[0].name.replace(/[^\w\s-]/g,"").trim():"Uncategorized",R=join(b,C,I);existsSync(R)||await mkdir(R,{recursive:!0});let q=p.lang.name,Z=we[q]??"txt",re=ge[Z]??q,H=`${s.questionFrontendId}.${s.titleSlug}.submission-${g.id}.${re}`,ao=join(R,H);await writeFile(ao,p.code,"utf-8"),c.succeed(`Downloaded to ${l.green(H)}`),console.log(l.gray(`Path: ${ao}`));}}catch(s){n.fail("Failed to fetch submissions"),s instanceof Error&&console.log(l.red(s.message));}}var Xe=["typescript","javascript","python3","java","cpp","c","csharp","go","rust","kotlin","swift"];async function yt(e){if(!e.lang&&!e.editor&&!e.workdir){bt();return}if(e.lang){let o=e.lang.toLowerCase();if(!Xe.includes(o)){console.log(l.red(`Unsupported language: ${e.lang}`)),console.log(l.gray(`Supported: ${Xe.join(", ")}`));return}let t=o;w.setLanguage(t),console.log(l.green(`\u2713 Default language set to ${t}`));}e.editor&&(w.setEditor(e.editor),console.log(l.green(`\u2713 Editor set to ${e.editor}`))),e.workdir&&(w.setWorkDir(e.workdir),console.log(l.green(`\u2713 Work directory set to ${e.workdir}`))),e.repo!==void 0&&(e.repo.trim()===""?(w.deleteRepo(),console.log(l.green("\u2713 Repository URL cleared"))):(w.setRepo(e.repo),console.log(l.green(`\u2713 Repository URL set to ${e.repo}`))));}async function ht(){let e=w.getConfig(),o=w.getActiveWorkspace();console.log(),console.log(l.bold.cyan(`\u{1F4C1} Configuring workspace: ${o}`)),console.log(l.gray("\u2500".repeat(40)));let t=await eo.prompt([{type:"list",name:"language",message:"Default programming language:",choices:Xe,default:e.language},{type:"input",name:"editor",message:"Editor command (e.g., code, vim, nvim):",default:e.editor??"code"},{type:"input",name:"workDir",message:"Working directory for solution files:",default:e.workDir},{type:"input",name:"repo",message:"Git repository URL (optional):",default:e.repo}]);w.setLanguage(t.language),w.setEditor(t.editor),w.setWorkDir(t.workDir),t.repo?w.setRepo(t.repo):w.deleteRepo(),console.log(),console.log(l.green("\u2713 Configuration saved")),bt();}function bt(){let e=w.getConfig(),o=K.get(),t=w.getActiveWorkspace();console.log(),console.log(l.bold.cyan(`\u{1F4C1} Workspace: ${t}`)),console.log(l.gray("\u2500".repeat(40))),console.log(),console.log(l.gray("Config file:"),w.getPath()),console.log(),console.log(l.gray("Language: "),l.white(e.language)),console.log(l.gray("Editor: "),l.white(e.editor??"(not set)")),console.log(l.gray("Work Dir: "),l.white(e.workDir)),console.log(l.gray("Repo URL: "),l.white(e.repo??"(not set)")),console.log(l.gray("Logged in: "),o?l.green("Yes"):l.yellow("No"));}var te=new fn({projectName:"leetcode-cli-bookmarks",defaults:{bookmarks:[]}}),ue={add(e){let o=te.get("bookmarks");return o.includes(e)?false:(te.set("bookmarks",[...o,e]),true)},remove(e){let o=te.get("bookmarks");return o.includes(e)?(te.set("bookmarks",o.filter(t=>t!==e)),true):false},list(){return te.get("bookmarks")},has(e){return te.get("bookmarks").includes(e)},count(){return te.get("bookmarks").length},clear(){te.set("bookmarks",[]);}};async function wt(e,o){if(!["add","remove","list","clear"].includes(e)){console.log(l.red(`Invalid action: ${e}`)),console.log(l.gray("Valid actions: add, remove, list, clear"));return}switch(e){case "add":if(!o){console.log(l.red("Please provide a problem ID to bookmark"));return}if(!oe(o)){console.log(l.red(`Invalid problem ID: ${o}`)),console.log(l.gray("Problem ID must be a positive integer"));return}ue.add(o)?console.log(l.green(`\u2713 Bookmarked problem ${o}`)):console.log(l.yellow(`Problem ${o} is already bookmarked`));break;case "remove":if(!o){console.log(l.red("Please provide a problem ID to remove"));return}ue.remove(o)?console.log(l.green(`\u2713 Removed bookmark for problem ${o}`)):console.log(l.yellow(`Problem ${o} is not bookmarked`));break;case "list":await as();break;case "clear":let n=ue.count();n===0?console.log(l.yellow("No bookmarks to clear")):(ue.clear(),console.log(l.green(`\u2713 Cleared ${n} bookmark${n!==1?"s":""}`)));break}}async function as(){let e=ue.list();if(e.length===0){console.log(l.yellow("\u{1F4CC} No bookmarked problems")),console.log(l.gray('Use "leetcode bookmark add <id>" to bookmark a problem'));return}console.log(),console.log(l.bold.cyan(`\u{1F4CC} Bookmarked Problems (${e.length})`)),console.log();let{authorized:o}=await v();if(o){let t=Ie({text:"Fetching problem details...",spinner:"dots"}).start();try{let n=new Oe({head:[l.cyan("ID"),l.cyan("Title"),l.cyan("Difficulty"),l.cyan("Status")],colWidths:[8,45,12,10],style:{head:[],border:[]}});for(let s of e)try{let r=await h.getProblemById(s);r?n.push([r.questionFrontendId,r.title.length>42?r.title.slice(0,39)+"...":r.title,ls(r.difficulty),r.status==="ac"?l.green("\u2713"):l.gray("-")]):n.push([s,l.gray("(not found)"),"-","-"]);}catch{n.push([s,l.gray("(error fetching)"),"-","-"]);}t.stop(),console.log(n.toString());}catch{t.stop(),console.log(l.gray("IDs: ")+e.join(", "));}}else console.log(l.gray("IDs: ")+e.join(", ")),console.log(),console.log(l.gray("Login to see problem details"));}function ls(e){switch(e.toLowerCase()){case "easy":return l.green(e);case "medium":return l.yellow(e);case "hard":return l.red(e);default:return e}}async function $t(e,o){if(!oe(e)){console.log(l.red(`Invalid problem ID: ${e}`)),console.log(l.gray("Problem ID must be a positive integer"));return}let t=o==="view"?"view":"edit",n=join(w.getWorkDir(),".notes"),s=join(n,`${e}.md`);existsSync(n)||await mkdir(n,{recursive:true}),t==="view"?await ds(s,e):await us(s,e);}async function ds(e,o){if(!existsSync(e)){console.log(l.yellow(`No notes found for problem ${o}`)),console.log(l.gray(`Use "leetcode note ${o} edit" to create notes`));return}try{let t=await readFile(e,"utf-8");console.log(),console.log(l.bold.cyan(`\u{1F4DD} Notes for Problem ${o}`)),console.log(l.gray("\u2500".repeat(50))),console.log(),console.log(t);}catch(t){console.log(l.red("Failed to read notes")),t instanceof Error&&console.log(l.gray(t.message));}}async function us(e,o){if(!existsSync(e)){let t=await ps(o);await writeFile(e,t,"utf-8"),console.log(l.green(`\u2713 Created notes file for problem ${o}`));}console.log(l.gray(`Opening: ${e}`)),await Se(e);}async function ps(e){let o=`# Problem ${e} Notes
1996
192
 
1997
- // src/commands/stat.ts
1998
- import ora7 from "ora";
1999
- import chalk12 from "chalk";
193
+ `,{authorized:t}=await v();if(t)try{let n=await h.getProblemById(e);n&&(o=`# ${e}. ${n.title}
2000
194
 
2001
- // src/utils/stats-display.ts
2002
- import chalk11 from "chalk";
2003
- var MONTH_NAMES = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
2004
- function renderHeatmap(calendarJson) {
2005
- const data = JSON.parse(calendarJson);
2006
- const now = /* @__PURE__ */ new Date();
2007
- const weeks = [];
2008
- for (let w = 11; w >= 0; w--) {
2009
- const weekStart = new Date(now);
2010
- weekStart.setDate(weekStart.getDate() - w * 7 - weekStart.getDay());
2011
- let weekCount = 0;
2012
- let activeDays = 0;
2013
- for (let d = 0; d < 7; d++) {
2014
- const day = new Date(weekStart);
2015
- day.setDate(day.getDate() + d);
2016
- if (day > now) break;
2017
- const midnight = new Date(Date.UTC(day.getFullYear(), day.getMonth(), day.getDate()));
2018
- const timestamp = Math.floor(midnight.getTime() / 1e3).toString();
2019
- const count = data[timestamp] || 0;
2020
- weekCount += count;
2021
- if (count > 0) activeDays++;
2022
- }
2023
- const weekEnd = new Date(weekStart);
2024
- weekEnd.setDate(weekEnd.getDate() + 6);
2025
- weeks.push({
2026
- start: `${MONTH_NAMES[weekStart.getMonth()]} ${weekStart.getDate()}`,
2027
- end: `${MONTH_NAMES[weekEnd.getMonth()]} ${weekEnd.getDate()}`,
2028
- count: weekCount,
2029
- days: activeDays
2030
- });
2031
- }
2032
- const totalSubmissions = weeks.reduce((sum, w) => sum + w.count, 0);
2033
- const totalActiveDays = weeks.reduce((sum, w) => sum + w.days, 0);
2034
- 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)));
2038
- console.log();
2039
- for (const week of weeks) {
2040
- const weekLabel = `${week.start} - ${week.end}`.padEnd(18);
2041
- const bar = week.count > 0 ? chalk11.green("\u2588".repeat(Math.min(week.count, 10))).padEnd(10) : chalk11.gray("\xB7").padEnd(10);
2042
- const countStr = week.count > 0 ? `${week.count} subs`.padEnd(10) : "".padEnd(10);
2043
- const daysStr = week.days > 0 ? `${week.days}d active` : "";
2044
- console.log(` ${chalk11.white(weekLabel)} ${bar} ${chalk11.cyan(countStr)} ${chalk11.yellow(daysStr)}`);
2045
- }
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")}`);
2048
- console.log();
2049
- }
2050
- function renderSkillStats(fundamental, intermediate, advanced) {
2051
- console.log();
2052
- console.log(chalk11.bold("\u{1F3AF} Skill Breakdown"));
2053
- console.log(chalk11.gray("\u2500".repeat(45)));
2054
- const renderSection = (title, stats, color) => {
2055
- if (stats.length === 0) return;
2056
- console.log();
2057
- console.log(color.bold(` ${title}`));
2058
- const sorted = [...stats].sort((a, b) => b.problemsSolved - a.problemsSolved);
2059
- for (const stat of sorted.slice(0, 8)) {
2060
- const name = stat.tagName.padEnd(22);
2061
- const bar = color("\u2588".repeat(Math.min(stat.problemsSolved, 15)));
2062
- console.log(` ${chalk11.white(name)} ${bar} ${chalk11.white(stat.problemsSolved)}`);
2063
- }
2064
- };
2065
- renderSection("Fundamental", fundamental, chalk11.green);
2066
- renderSection("Intermediate", intermediate, chalk11.yellow);
2067
- renderSection("Advanced", advanced, chalk11.red);
2068
- console.log();
2069
- }
2070
- function renderTrendChart(calendarJson) {
2071
- const data = JSON.parse(calendarJson);
2072
- const now = /* @__PURE__ */ new Date();
2073
- const dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
2074
- const days = [];
2075
- for (let d = 6; d >= 0; d--) {
2076
- const day = new Date(now);
2077
- day.setDate(day.getDate() - d);
2078
- const midnight = new Date(Date.UTC(day.getFullYear(), day.getMonth(), day.getDate()));
2079
- const timestamp = Math.floor(midnight.getTime() / 1e3).toString();
2080
- days.push({
2081
- label: dayNames[day.getDay()],
2082
- count: data[timestamp] || 0
2083
- });
2084
- }
2085
- const maxVal = Math.max(...days.map((d) => d.count), 1);
2086
- const chartHeight = 6;
2087
- console.log();
2088
- console.log(chalk11.bold("\u{1F4C8} Submission Trend (Last 7 Days)"));
2089
- console.log(chalk11.gray("\u2500".repeat(35)));
2090
- console.log();
2091
- for (let row = chartHeight; row >= 1; row--) {
2092
- let line = ` ${row === chartHeight ? maxVal.toString().padStart(2) : " "} \u2502`;
2093
- for (const day of days) {
2094
- const barHeight = Math.round(day.count / maxVal * chartHeight);
2095
- if (barHeight >= row) {
2096
- line += chalk11.green(" \u2588\u2588 ");
2097
- } else {
2098
- line += " ";
2099
- }
2100
- }
2101
- console.log(line);
2102
- }
2103
- console.log(` 0 \u2514${"\u2500\u2500\u2500\u2500".repeat(7)}`);
2104
- console.log(` ${days.map((d) => d.label.padEnd(4)).join("")}`);
2105
- console.log(chalk11.gray(` ${days.map((d) => d.count.toString().padEnd(4)).join("")}`));
2106
- const total = days.reduce((a, b) => a + b.count, 0);
2107
- console.log();
2108
- console.log(chalk11.white(` Total: ${total} submissions this week`));
2109
- console.log();
2110
- }
195
+ `,o+=`**Difficulty:** ${n.difficulty}
196
+ `,o+=`**URL:** https://leetcode.com/problems/${n.titleSlug}/
197
+ `,n.topicTags.length>0&&(o+=`**Topics:** ${n.topicTags.map(s=>s.name).join(", ")}
198
+ `),o+=`
199
+ ---
2111
200
 
2112
- // src/commands/stat.ts
2113
- async function statCommand(username, options = {}) {
2114
- const { authorized, username: currentUser } = await requireAuth();
2115
- if (!authorized) return;
2116
- const spinner = ora7("Fetching statistics...").start();
2117
- try {
2118
- const targetUsername = username || currentUser;
2119
- if (!targetUsername) {
2120
- spinner.fail("No username found");
2121
- return;
2122
- }
2123
- const profile = await leetcodeClient.getUserProfile(targetUsername);
2124
- spinner.stop();
2125
- if (!options.calendar && !options.skills && !options.trend) {
2126
- displayUserStats(
2127
- profile.username,
2128
- profile.realName,
2129
- profile.ranking,
2130
- profile.acSubmissionNum,
2131
- profile.streak,
2132
- profile.totalActiveDays
2133
- );
2134
- return;
2135
- }
2136
- if (options.calendar) {
2137
- if (profile.submissionCalendar) {
2138
- renderHeatmap(profile.submissionCalendar);
2139
- } else {
2140
- console.log(chalk12.yellow("Calendar data not available."));
2141
- }
2142
- }
2143
- if (options.trend) {
2144
- if (profile.submissionCalendar) {
2145
- renderTrendChart(profile.submissionCalendar);
2146
- } else {
2147
- console.log(chalk12.yellow("Calendar data not available."));
2148
- }
2149
- }
2150
- if (options.skills) {
2151
- spinner.start("Fetching skill stats...");
2152
- const skills = await leetcodeClient.getSkillStats(targetUsername);
2153
- spinner.stop();
2154
- renderSkillStats(skills.fundamental, skills.intermediate, skills.advanced);
2155
- }
2156
- } catch (error) {
2157
- spinner.fail("Failed to fetch statistics");
2158
- if (error instanceof Error) {
2159
- console.log(chalk12.red(error.message));
2160
- }
2161
- }
2162
- }
2163
-
2164
- // src/commands/daily.ts
2165
- import ora8 from "ora";
2166
- import chalk13 from "chalk";
2167
- async function dailyCommand() {
2168
- const { authorized } = await requireAuth();
2169
- if (!authorized) return;
2170
- const spinner = ora8("Fetching daily challenge...").start();
2171
- try {
2172
- const daily = await leetcodeClient.getDailyChallenge();
2173
- spinner.stop();
2174
- displayDailyChallenge(daily.date, daily.question);
2175
- 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}`));
2178
- } catch (error) {
2179
- spinner.fail("Failed to fetch daily challenge");
2180
- if (error instanceof Error) {
2181
- console.log(chalk13.red(error.message));
2182
- }
2183
- }
2184
- }
2185
-
2186
- // src/commands/random.ts
2187
- import ora9 from "ora";
2188
- import chalk14 from "chalk";
2189
- async function randomCommand(options) {
2190
- const { authorized } = await requireAuth();
2191
- if (!authorized) return;
2192
- const spinner = ora9("Fetching random problem...").start();
2193
- try {
2194
- const filters = {};
2195
- if (options.difficulty) {
2196
- const diffMap = {
2197
- easy: "EASY",
2198
- e: "EASY",
2199
- medium: "MEDIUM",
2200
- m: "MEDIUM",
2201
- hard: "HARD",
2202
- h: "HARD"
2203
- };
2204
- const diff = diffMap[options.difficulty.toLowerCase()];
2205
- if (diff) {
2206
- filters.difficulty = diff;
2207
- } else {
2208
- spinner.fail(`Invalid difficulty: ${options.difficulty}`);
2209
- return;
2210
- }
2211
- }
2212
- if (options.tag) {
2213
- filters.tags = [options.tag];
2214
- }
2215
- const titleSlug = await leetcodeClient.getRandomProblem(filters);
2216
- spinner.succeed("Found random problem!");
2217
- console.log();
2218
- if (options.pick) {
2219
- await pickCommand(titleSlug, { open: options.open ?? true });
2220
- } else {
2221
- await showCommand(titleSlug);
2222
- console.log(chalk14.gray("Run following to start solving:"));
2223
- console.log(chalk14.cyan(` leetcode pick ${titleSlug}`));
2224
- }
2225
- } catch (error) {
2226
- spinner.fail("Failed to fetch random problem");
2227
- if (error instanceof Error) {
2228
- console.log(chalk14.red(error.message));
2229
- }
2230
- }
2231
- }
2232
-
2233
- // src/commands/submissions.ts
2234
- import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
2235
- import { existsSync as existsSync7 } from "fs";
2236
- import { join as join6 } from "path";
2237
- import ora10 from "ora";
2238
- import chalk15 from "chalk";
2239
- async function submissionsCommand(idOrSlug, options) {
2240
- const { authorized } = await requireAuth();
2241
- if (!authorized) return;
2242
- const spinner = ora10("Fetching problem info...").start();
2243
- try {
2244
- let problem;
2245
- if (/^\d+$/.test(idOrSlug)) {
2246
- problem = await leetcodeClient.getProblemById(idOrSlug);
2247
- } else {
2248
- problem = await leetcodeClient.getProblem(idOrSlug);
2249
- }
2250
- if (!problem) {
2251
- spinner.fail(`Problem "${idOrSlug}" not found`);
2252
- return;
2253
- }
2254
- const slug = problem.titleSlug;
2255
- spinner.text = "Fetching submissions...";
2256
- const limit = options.limit ? parseInt(options.limit, 10) : 20;
2257
- const submissions = await leetcodeClient.getSubmissionList(slug, limit);
2258
- spinner.stop();
2259
- if (submissions.length === 0) {
2260
- console.log(chalk15.yellow("No submissions found."));
2261
- return;
2262
- }
2263
- if (options.last) {
2264
- const lastAC = submissions.find((s) => s.statusDisplay === "Accepted");
2265
- if (lastAC) {
2266
- console.log(chalk15.bold("Last Accepted Submission:"));
2267
- displaySubmissionsList([lastAC]);
2268
- } else {
2269
- console.log(chalk15.yellow("No accepted submissions found in recent history."));
2270
- }
2271
- } else {
2272
- displaySubmissionsList(submissions);
2273
- }
2274
- if (options.download) {
2275
- const downloadSpinner = ora10("Downloading submission...").start();
2276
- const lastAC = submissions.find((s) => s.statusDisplay === "Accepted");
2277
- if (!lastAC) {
2278
- downloadSpinner.fail("No accepted submission found to download.");
2279
- return;
2280
- }
2281
- const submissionId = parseInt(lastAC.id, 10);
2282
- if (isNaN(submissionId)) {
2283
- downloadSpinner.fail("Invalid submission ID format");
2284
- return;
2285
- }
2286
- const details = await leetcodeClient.getSubmissionDetails(submissionId);
2287
- const workDir = config.getWorkDir();
2288
- const difficulty = problem.difficulty;
2289
- const category = problem.topicTags.length > 0 ? problem.topicTags[0].name.replace(/[^\w\s-]/g, "").trim() : "Uncategorized";
2290
- const targetDir = join6(workDir, difficulty, category);
2291
- if (!existsSync7(targetDir)) {
2292
- await mkdir2(targetDir, { recursive: true });
2293
- }
2294
- const langSlug = details.lang.name;
2295
- const supportedLang = LANG_SLUG_MAP[langSlug] ?? "txt";
2296
- const ext = LANGUAGE_EXTENSIONS[supportedLang] ?? langSlug;
2297
- const fileName = `${problem.questionFrontendId}.${problem.titleSlug}.submission-${lastAC.id}.${ext}`;
2298
- const filePath = join6(targetDir, fileName);
2299
- await writeFile2(filePath, details.code, "utf-8");
2300
- downloadSpinner.succeed(`Downloaded to ${chalk15.green(fileName)}`);
2301
- console.log(chalk15.gray(`Path: ${filePath}`));
2302
- }
2303
- } catch (error) {
2304
- spinner.fail("Failed to fetch submissions");
2305
- if (error instanceof Error) {
2306
- console.log(chalk15.red(error.message));
2307
- }
2308
- }
2309
- }
2310
-
2311
- // src/commands/config.ts
2312
- import inquirer2 from "inquirer";
2313
- import chalk16 from "chalk";
2314
- var SUPPORTED_LANGUAGES = [
2315
- "typescript",
2316
- "javascript",
2317
- "python3",
2318
- "java",
2319
- "cpp",
2320
- "c",
2321
- "csharp",
2322
- "go",
2323
- "rust",
2324
- "kotlin",
2325
- "swift"
2326
- ];
2327
- async function configCommand(options) {
2328
- if (!options.lang && !options.editor && !options.workdir) {
2329
- showCurrentConfig();
2330
- return;
2331
- }
2332
- if (options.lang) {
2333
- const langInput = options.lang.toLowerCase();
2334
- if (!SUPPORTED_LANGUAGES.includes(langInput)) {
2335
- console.log(chalk16.red(`Unsupported language: ${options.lang}`));
2336
- console.log(chalk16.gray(`Supported: ${SUPPORTED_LANGUAGES.join(", ")}`));
2337
- return;
2338
- }
2339
- const lang = langInput;
2340
- config.setLanguage(lang);
2341
- console.log(chalk16.green(`\u2713 Default language set to ${lang}`));
2342
- }
2343
- if (options.editor) {
2344
- config.setEditor(options.editor);
2345
- console.log(chalk16.green(`\u2713 Editor set to ${options.editor}`));
2346
- }
2347
- if (options.workdir) {
2348
- config.setWorkDir(options.workdir);
2349
- console.log(chalk16.green(`\u2713 Work directory set to ${options.workdir}`));
2350
- }
2351
- if (options.repo !== void 0) {
2352
- if (options.repo.trim() === "") {
2353
- config.deleteRepo();
2354
- console.log(chalk16.green("\u2713 Repository URL cleared"));
2355
- } else {
2356
- config.setRepo(options.repo);
2357
- console.log(chalk16.green(`\u2713 Repository URL set to ${options.repo}`));
2358
- }
2359
- }
2360
- }
2361
- async function configInteractiveCommand() {
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)));
2367
- const answers = await inquirer2.prompt([
2368
- {
2369
- type: "list",
2370
- name: "language",
2371
- message: "Default programming language:",
2372
- choices: SUPPORTED_LANGUAGES,
2373
- default: currentConfig.language
2374
- },
2375
- {
2376
- type: "input",
2377
- name: "editor",
2378
- message: "Editor command (e.g., code, vim, nvim):",
2379
- default: currentConfig.editor ?? "code"
2380
- },
2381
- {
2382
- type: "input",
2383
- name: "workDir",
2384
- message: "Working directory for solution files:",
2385
- default: currentConfig.workDir
2386
- },
2387
- {
2388
- type: "input",
2389
- name: "repo",
2390
- message: "Git repository URL (optional):",
2391
- default: currentConfig.repo
2392
- }
2393
- ]);
2394
- config.setLanguage(answers.language);
2395
- config.setEditor(answers.editor);
2396
- config.setWorkDir(answers.workDir);
2397
- if (answers.repo) {
2398
- config.setRepo(answers.repo);
2399
- } else {
2400
- config.deleteRepo();
2401
- }
2402
- console.log();
2403
- console.log(chalk16.green("\u2713 Configuration saved"));
2404
- showCurrentConfig();
2405
- }
2406
- function showCurrentConfig() {
2407
- const currentConfig = config.getConfig();
2408
- const creds = credentials.get();
2409
- const workspace = config.getActiveWorkspace();
2410
- console.log();
2411
- console.log(chalk16.bold.cyan(`\u{1F4C1} Workspace: ${workspace}`));
2412
- console.log(chalk16.gray("\u2500".repeat(40)));
2413
- console.log();
2414
- console.log(chalk16.gray("Config file:"), config.getPath());
2415
- 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"));
2421
- }
2422
-
2423
- // src/commands/bookmark.ts
2424
- import chalk17 from "chalk";
2425
- import Table2 from "cli-table3";
2426
- import ora11 from "ora";
2427
-
2428
- // src/storage/bookmarks.ts
2429
- import Conf2 from "conf";
2430
- var bookmarksStore = new Conf2({
2431
- projectName: "leetcode-cli-bookmarks",
2432
- defaults: { bookmarks: [] }
2433
- });
2434
- var bookmarks = {
2435
- add(problemId) {
2436
- const current = bookmarksStore.get("bookmarks");
2437
- if (current.includes(problemId)) {
2438
- return false;
2439
- }
2440
- bookmarksStore.set("bookmarks", [...current, problemId]);
2441
- return true;
2442
- },
2443
- remove(problemId) {
2444
- const current = bookmarksStore.get("bookmarks");
2445
- if (!current.includes(problemId)) {
2446
- return false;
2447
- }
2448
- bookmarksStore.set("bookmarks", current.filter((id) => id !== problemId));
2449
- return true;
2450
- },
2451
- list() {
2452
- return bookmarksStore.get("bookmarks");
2453
- },
2454
- has(problemId) {
2455
- return bookmarksStore.get("bookmarks").includes(problemId);
2456
- },
2457
- count() {
2458
- return bookmarksStore.get("bookmarks").length;
2459
- },
2460
- clear() {
2461
- bookmarksStore.set("bookmarks", []);
2462
- }
2463
- };
2464
-
2465
- // src/commands/bookmark.ts
2466
- async function bookmarkCommand(action, id) {
2467
- const validActions = ["add", "remove", "list", "clear"];
2468
- if (!validActions.includes(action)) {
2469
- console.log(chalk17.red(`Invalid action: ${action}`));
2470
- console.log(chalk17.gray("Valid actions: add, remove, list, clear"));
2471
- return;
2472
- }
2473
- switch (action) {
2474
- case "add":
2475
- if (!id) {
2476
- console.log(chalk17.red("Please provide a problem ID to bookmark"));
2477
- return;
2478
- }
2479
- if (!isProblemId(id)) {
2480
- console.log(chalk17.red(`Invalid problem ID: ${id}`));
2481
- console.log(chalk17.gray("Problem ID must be a positive integer"));
2482
- return;
2483
- }
2484
- if (bookmarks.add(id)) {
2485
- console.log(chalk17.green(`\u2713 Bookmarked problem ${id}`));
2486
- } else {
2487
- console.log(chalk17.yellow(`Problem ${id} is already bookmarked`));
2488
- }
2489
- break;
2490
- case "remove":
2491
- if (!id) {
2492
- console.log(chalk17.red("Please provide a problem ID to remove"));
2493
- return;
2494
- }
2495
- if (bookmarks.remove(id)) {
2496
- console.log(chalk17.green(`\u2713 Removed bookmark for problem ${id}`));
2497
- } else {
2498
- console.log(chalk17.yellow(`Problem ${id} is not bookmarked`));
2499
- }
2500
- break;
2501
- case "list":
2502
- await listBookmarks();
2503
- break;
2504
- case "clear":
2505
- const count = bookmarks.count();
2506
- if (count === 0) {
2507
- console.log(chalk17.yellow("No bookmarks to clear"));
2508
- } else {
2509
- bookmarks.clear();
2510
- console.log(chalk17.green(`\u2713 Cleared ${count} bookmark${count !== 1 ? "s" : ""}`));
2511
- }
2512
- break;
2513
- }
2514
- }
2515
- async function listBookmarks() {
2516
- const bookmarkList = bookmarks.list();
2517
- if (bookmarkList.length === 0) {
2518
- console.log(chalk17.yellow("\u{1F4CC} No bookmarked problems"));
2519
- console.log(chalk17.gray('Use "leetcode bookmark add <id>" to bookmark a problem'));
2520
- return;
2521
- }
2522
- console.log();
2523
- console.log(chalk17.bold.cyan(`\u{1F4CC} Bookmarked Problems (${bookmarkList.length})`));
2524
- console.log();
2525
- const { authorized } = await requireAuth();
2526
- if (authorized) {
2527
- const spinner = ora11({ text: "Fetching problem details...", spinner: "dots" }).start();
2528
- try {
2529
- const table = new Table2({
2530
- head: [
2531
- chalk17.cyan("ID"),
2532
- chalk17.cyan("Title"),
2533
- chalk17.cyan("Difficulty"),
2534
- chalk17.cyan("Status")
2535
- ],
2536
- colWidths: [8, 45, 12, 10],
2537
- style: { head: [], border: [] }
2538
- });
2539
- for (const id of bookmarkList) {
2540
- try {
2541
- const problem = await leetcodeClient.getProblemById(id);
2542
- if (problem) {
2543
- table.push([
2544
- problem.questionFrontendId,
2545
- problem.title.length > 42 ? problem.title.slice(0, 39) + "..." : problem.title,
2546
- colorDifficulty2(problem.difficulty),
2547
- problem.status === "ac" ? chalk17.green("\u2713") : chalk17.gray("-")
2548
- ]);
2549
- } else {
2550
- table.push([id, chalk17.gray("(not found)"), "-", "-"]);
2551
- }
2552
- } catch {
2553
- table.push([id, chalk17.gray("(error fetching)"), "-", "-"]);
2554
- }
2555
- }
2556
- spinner.stop();
2557
- console.log(table.toString());
2558
- } catch {
2559
- spinner.stop();
2560
- console.log(chalk17.gray("IDs: ") + bookmarkList.join(", "));
2561
- }
2562
- } else {
2563
- console.log(chalk17.gray("IDs: ") + bookmarkList.join(", "));
2564
- console.log();
2565
- console.log(chalk17.gray("Login to see problem details"));
2566
- }
2567
- }
2568
- function colorDifficulty2(difficulty) {
2569
- switch (difficulty.toLowerCase()) {
2570
- case "easy":
2571
- return chalk17.green(difficulty);
2572
- case "medium":
2573
- return chalk17.yellow(difficulty);
2574
- case "hard":
2575
- return chalk17.red(difficulty);
2576
- default:
2577
- return difficulty;
2578
- }
2579
- }
2580
-
2581
- // src/commands/notes.ts
2582
- import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
2583
- import { join as join7 } from "path";
2584
- import { existsSync as existsSync8 } from "fs";
2585
- import chalk18 from "chalk";
2586
- async function notesCommand(problemId, action) {
2587
- if (!isProblemId(problemId)) {
2588
- console.log(chalk18.red(`Invalid problem ID: ${problemId}`));
2589
- console.log(chalk18.gray("Problem ID must be a positive integer"));
2590
- return;
2591
- }
2592
- const noteAction = action === "view" ? "view" : "edit";
2593
- const notesDir = join7(config.getWorkDir(), ".notes");
2594
- const notePath = join7(notesDir, `${problemId}.md`);
2595
- if (!existsSync8(notesDir)) {
2596
- await mkdir3(notesDir, { recursive: true });
2597
- }
2598
- if (noteAction === "view") {
2599
- await viewNote(notePath, problemId);
2600
- } else {
2601
- await editNote(notePath, problemId);
2602
- }
2603
- }
2604
- async function viewNote(notePath, problemId) {
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`));
2608
- return;
2609
- }
2610
- try {
2611
- const content = await readFile3(notePath, "utf-8");
2612
- console.log();
2613
- console.log(chalk18.bold.cyan(`\u{1F4DD} Notes for Problem ${problemId}`));
2614
- console.log(chalk18.gray("\u2500".repeat(50)));
2615
- console.log();
2616
- console.log(content);
2617
- } catch (error) {
2618
- console.log(chalk18.red("Failed to read notes"));
2619
- if (error instanceof Error) {
2620
- console.log(chalk18.gray(error.message));
2621
- }
2622
- }
2623
- }
2624
- async function editNote(notePath, problemId) {
2625
- if (!existsSync8(notePath)) {
2626
- const template = await generateNoteTemplate(problemId);
2627
- await writeFile3(notePath, template, "utf-8");
2628
- console.log(chalk18.green(`\u2713 Created notes file for problem ${problemId}`));
2629
- }
2630
- console.log(chalk18.gray(`Opening: ${notePath}`));
2631
- await openInEditor(notePath);
2632
- }
2633
- async function generateNoteTemplate(problemId) {
2634
- let header = `# Problem ${problemId} Notes
2635
-
2636
- `;
2637
- const { authorized } = await requireAuth();
2638
- if (authorized) {
2639
- try {
2640
- const problem = await leetcodeClient.getProblemById(problemId);
2641
- if (problem) {
2642
- header = `# ${problemId}. ${problem.title}
2643
-
2644
- `;
2645
- header += `**Difficulty:** ${problem.difficulty}
2646
- `;
2647
- header += `**URL:** https://leetcode.com/problems/${problem.titleSlug}/
2648
- `;
2649
- if (problem.topicTags.length > 0) {
2650
- header += `**Topics:** ${problem.topicTags.map((t) => t.name).join(", ")}
2651
- `;
2652
- }
2653
- header += "\n---\n\n";
2654
- }
2655
- } catch {
2656
- }
2657
- }
2658
- return `${header}## Approach
201
+ `);}catch{}return `${o}## Approach
2659
202
 
2660
203
  <!-- Describe your approach to solving this problem -->
2661
204
 
@@ -2680,1505 +223,203 @@ async function generateNoteTemplate(problemId) {
2680
223
 
2681
224
  <!-- What did you learn from this problem? -->
2682
225
 
2683
- `;
2684
- }
2685
-
2686
- // src/commands/today.ts
2687
- import chalk19 from "chalk";
2688
- import ora12 from "ora";
2689
- async function todayCommand() {
2690
- const { authorized, username } = await requireAuth();
2691
- if (!authorized || !username) {
2692
- return;
2693
- }
2694
- const spinner = ora12({ text: "Fetching your progress...", spinner: "dots" }).start();
2695
- try {
2696
- const [profile, daily] = await Promise.all([
2697
- leetcodeClient.getUserProfile(username),
2698
- leetcodeClient.getDailyChallenge()
2699
- ]);
2700
- spinner.stop();
2701
- console.log();
2702
- console.log(chalk19.bold.cyan(`\u{1F4CA} Today's Summary`), chalk19.gray(`- ${username}`));
2703
- console.log(chalk19.gray("\u2500".repeat(50)));
2704
- 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}`));
2707
- console.log();
2708
- const total = profile.acSubmissionNum.find((s) => s.difficulty === "All");
2709
- const easy = profile.acSubmissionNum.find((s) => s.difficulty === "Easy");
2710
- const medium = profile.acSubmissionNum.find((s) => s.difficulty === "Medium");
2711
- const hard = profile.acSubmissionNum.find((s) => s.difficulty === "Hard");
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}`);
2715
- console.log();
2716
- console.log(chalk19.bold.yellow("\u{1F3AF} Today's Challenge:"));
2717
- console.log(` ${daily.question.questionFrontendId}. ${daily.question.title}`);
2718
- console.log(` ${colorDifficulty3(daily.question.difficulty)}`);
2719
- const status = daily.question.status;
2720
- if (status === "ac") {
2721
- console.log(chalk19.green(" \u2713 Completed!"));
2722
- } else if (status === "notac") {
2723
- console.log(chalk19.yellow(" \u25CB Attempted"));
2724
- } else {
2725
- console.log(chalk19.gray(" - Not started"));
2726
- }
2727
- console.log();
2728
- console.log(chalk19.gray(` leetcode pick ${daily.question.questionFrontendId} # Start working on it`));
2729
- } catch (error) {
2730
- spinner.fail("Failed to fetch progress");
2731
- if (error instanceof Error) {
2732
- console.log(chalk19.red(error.message));
2733
- }
2734
- }
2735
- }
2736
- function colorDifficulty3(difficulty) {
2737
- switch (difficulty.toLowerCase()) {
2738
- case "easy":
2739
- return chalk19.green(difficulty);
2740
- case "medium":
2741
- return chalk19.yellow(difficulty);
2742
- case "hard":
2743
- return chalk19.red(difficulty);
2744
- default:
2745
- return difficulty;
2746
- }
2747
- }
2748
-
2749
- // src/commands/sync.ts
2750
- import { execSync } from "child_process";
2751
- import { existsSync as existsSync9 } from "fs";
2752
- import chalk20 from "chalk";
2753
- import inquirer3 from "inquirer";
2754
- import ora13 from "ora";
2755
- function isGitInstalled() {
2756
- try {
2757
- execSync("git --version", { stdio: "ignore" });
2758
- return true;
2759
- } catch (error) {
2760
- return false;
2761
- }
2762
- }
2763
- function isMapRepo(workDir) {
2764
- try {
2765
- execSync("git rev-parse --is-inside-work-tree", { cwd: workDir, stdio: "ignore" });
2766
- return true;
2767
- } catch (error) {
2768
- return false;
2769
- }
2770
- }
2771
- function isGhInstalled() {
2772
- try {
2773
- execSync("gh --version", { stdio: "ignore" });
2774
- return true;
2775
- } catch (error) {
2776
- return false;
2777
- }
2778
- }
2779
- function getRemoteUrl(workDir) {
2780
- try {
2781
- const url = execSync("git config --get remote.origin.url", { cwd: workDir, encoding: "utf-8" });
2782
- return url.trim();
2783
- } catch (error) {
2784
- return null;
2785
- }
2786
- }
2787
- async function setupGitRepo(workDir) {
2788
- const { init } = await inquirer3.prompt([
2789
- {
2790
- type: "confirm",
2791
- name: "init",
2792
- message: "Work directory is not a git repository. Initialize?",
2793
- default: true
2794
- }
2795
- ]);
2796
- if (!init) {
2797
- console.log(chalk20.yellow("Skipping basic git initialization."));
2798
- return false;
2799
- }
2800
- const spinner = ora13("Initializing git repository...").start();
2801
- try {
2802
- execSync("git init", { cwd: workDir });
2803
- spinner.succeed("Initialized git repository");
2804
- return true;
2805
- } catch (error) {
2806
- spinner.fail("Failed to initialize git repository");
2807
- throw error;
2808
- }
2809
- }
2810
- async function setupRemote(workDir) {
2811
- const spinner = ora13();
2812
- let repoUrl = config.getRepo();
2813
- if (!repoUrl) {
2814
- if (isGhInstalled()) {
2815
- const { createGh } = await inquirer3.prompt([
2816
- {
2817
- type: "confirm",
2818
- name: "createGh",
2819
- message: "Create a new private GitHub repository?",
2820
- default: true
2821
- }
2822
- ]);
2823
- if (createGh) {
2824
- spinner.start("Creating GitHub repository...");
2825
- try {
2826
- const repoName = workDir.split("/").pop() || "leetcode-solutions";
2827
- execSync(`gh repo create ${repoName} --private --source=. --remote=origin`, { cwd: workDir });
2828
- spinner.succeed("Created and linked GitHub repository");
2829
- repoUrl = getRemoteUrl(workDir) || "";
2830
- if (repoUrl) {
2831
- config.setRepo(repoUrl);
2832
- }
2833
- return repoUrl;
2834
- } catch (error) {
2835
- spinner.fail("Failed to create GitHub repository");
2836
- console.log(chalk20.red(error));
2837
- }
2838
- }
2839
- }
2840
- if (!repoUrl) {
2841
- console.log(chalk20.yellow("\nPlease create a new repository on your Git provider and copy the URL."));
2842
- const { url } = await inquirer3.prompt([
2843
- {
2844
- type: "input",
2845
- name: "url",
2846
- message: "Enter remote repository URL:",
2847
- validate: (input) => input.length > 0 ? true : "URL cannot be empty"
2848
- }
2849
- ]);
2850
- repoUrl = url;
2851
- }
2852
- }
2853
- if (repoUrl) {
2854
- config.setRepo(repoUrl);
2855
- }
2856
- const currentRemote = getRemoteUrl(workDir);
2857
- if (!currentRemote && repoUrl) {
2858
- try {
2859
- execSync(`git remote add origin ${repoUrl}`, { cwd: workDir });
2860
- console.log(chalk20.green("\u2713 Added remote origin"));
2861
- } catch (e) {
2862
- console.log(chalk20.red("Failed to add remote origin"));
2863
- }
2864
- }
2865
- return repoUrl || "";
2866
- }
2867
- async function syncCommand() {
2868
- const workDir = config.getWorkDir();
2869
- if (!existsSync9(workDir)) {
2870
- console.log(chalk20.red(`Work directory does not exist: ${workDir}`));
2871
- return;
2872
- }
2873
- if (!isGitInstalled()) {
2874
- console.log(chalk20.red("Git is not installed. Please install Git to use command."));
2875
- return;
2876
- }
2877
- if (!isMapRepo(workDir)) {
2878
- const initialized = await setupGitRepo(workDir);
2879
- if (!initialized) return;
2880
- }
2881
- await setupRemote(workDir);
2882
- const spinner = ora13("Syncing solutions...").start();
2883
- try {
2884
- const status = execSync("git status --porcelain", { cwd: workDir, encoding: "utf-8" });
2885
- if (!status) {
2886
- spinner.info("No changes to sync");
2887
- return;
2888
- }
2889
- execSync("git add .", { cwd: workDir });
2890
- const lines = status.trim().split("\n");
2891
- const count = lines.length;
2892
- const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").substring(0, 19);
2893
- const message = `Sync: ${count} solutions - ${timestamp}`;
2894
- execSync(`git commit -m "${message}"`, { cwd: workDir });
2895
- try {
2896
- execSync("git push -u origin main", { cwd: workDir, stdio: "ignore" });
2897
- } catch {
2898
- try {
2899
- execSync("git push -u origin master", { cwd: workDir, stdio: "ignore" });
2900
- } catch (e) {
2901
- throw new Error("Failed to push to remote. Please check your git credentials and branch status.");
2902
- }
2903
- }
2904
- spinner.succeed("Successfully synced solutions to remote");
2905
- } catch (error) {
2906
- spinner.fail("Sync failed");
2907
- if (error.message) {
2908
- console.log(chalk20.red(error.message));
2909
- }
2910
- }
2911
- }
2912
-
2913
- // src/commands/timer.ts
2914
- import ora14 from "ora";
2915
- import chalk21 from "chalk";
2916
- var DEFAULT_TIMES = {
2917
- Easy: 20,
2918
- Medium: 40,
2919
- Hard: 60
2920
- };
2921
- function formatDuration(seconds) {
2922
- if (seconds < 60) {
2923
- return `${seconds}s`;
2924
- } else if (seconds < 3600) {
2925
- const mins = Math.floor(seconds / 60);
2926
- const secs = seconds % 60;
2927
- return secs > 0 ? `${mins}m ${secs}s` : `${mins}m`;
2928
- } else {
2929
- const hours = Math.floor(seconds / 3600);
2930
- const mins = Math.floor(seconds % 3600 / 60);
2931
- return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`;
2932
- }
2933
- }
2934
- async function timerCommand(idOrSlug, options) {
2935
- if (options.stats) {
2936
- await showTimerStats(idOrSlug);
2937
- return;
2938
- }
2939
- if (options.stop) {
2940
- await stopActiveTimer();
2941
- return;
2942
- }
2943
- if (!idOrSlug) {
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"));
2948
- return;
2949
- }
2950
- const { authorized } = await requireAuth();
2951
- if (!authorized) return;
2952
- const activeTimer = timerStorage.getActiveTimer();
2953
- if (activeTimer) {
2954
- const startedAt = new Date(activeTimer.startedAt);
2955
- const elapsed = Math.floor((Date.now() - startedAt.getTime()) / 1e3);
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)}`));
2959
- console.log();
2960
- console.log(chalk21.gray("Use `leetcode timer --stop` to stop it first."));
2961
- return;
2962
- }
2963
- const spinner = ora14("Fetching problem...").start();
2964
- try {
2965
- let problem;
2966
- if (/^\d+$/.test(idOrSlug)) {
2967
- problem = await leetcodeClient.getProblemById(idOrSlug);
2968
- } else {
2969
- problem = await leetcodeClient.getProblem(idOrSlug);
2970
- }
2971
- if (!problem) {
2972
- spinner.fail(`Problem "${idOrSlug}" not found`);
2973
- return;
2974
- }
2975
- spinner.stop();
2976
- const durationMinutes = options.minutes ?? DEFAULT_TIMES[problem.difficulty] ?? 30;
2977
- timerStorage.startTimer(
2978
- problem.questionFrontendId,
2979
- problem.title,
2980
- problem.difficulty,
2981
- durationMinutes
2982
- );
2983
- console.log();
2984
- console.log(chalk21.bold.cyan("\u23F1\uFE0F Interview Mode Started!"));
2985
- console.log(chalk21.gray("\u2500".repeat(50)));
2986
- 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")}`));
2990
- 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."));
2995
- console.log();
2996
- await pickCommand(idOrSlug, { open: true });
2997
- } catch (error) {
2998
- spinner.fail("Failed to start timer");
2999
- if (error instanceof Error) {
3000
- console.log(chalk21.red(error.message));
3001
- }
3002
- }
3003
- }
3004
- async function stopActiveTimer() {
3005
- const result = timerStorage.stopTimer();
3006
- if (!result) {
3007
- console.log(chalk21.yellow("No active timer to stop."));
3008
- return;
3009
- }
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)"));
3013
- }
3014
- async function showTimerStats(problemId) {
3015
- if (problemId && /^\d+$/.test(problemId)) {
3016
- const times = timerStorage.getSolveTimes(problemId);
3017
- if (times.length === 0) {
3018
- console.log(chalk21.yellow(`No solve times recorded for problem ${problemId}`));
3019
- return;
3020
- }
3021
- console.log();
3022
- console.log(chalk21.bold(`\u23F1\uFE0F Solve Times for Problem ${problemId}`));
3023
- console.log(chalk21.gray("\u2500".repeat(40)));
3024
- for (const entry of times) {
3025
- const date = new Date(entry.solvedAt).toLocaleDateString();
3026
- const duration = formatDuration(entry.durationSeconds);
3027
- const limit = entry.timerMinutes;
3028
- const withinLimit = entry.durationSeconds <= limit * 60;
3029
- console.log(
3030
- ` ${date} ${withinLimit ? chalk21.green(duration) : chalk21.red(duration)} (limit: ${limit}m)`
3031
- );
3032
- }
3033
- } else {
3034
- const stats = timerStorage.getStats();
3035
- const allTimes = timerStorage.getAllSolveTimes();
3036
- console.log();
3037
- console.log(chalk21.bold("\u23F1\uFE0F Timer Statistics"));
3038
- console.log(chalk21.gray("\u2500".repeat(40)));
3039
- console.log();
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))}`);
3043
- console.log();
3044
- const recentSolves = [];
3045
- for (const [id, times] of Object.entries(allTimes)) {
3046
- for (const t of times) {
3047
- recentSolves.push({
3048
- problemId: id,
3049
- title: t.title,
3050
- duration: t.durationSeconds,
3051
- date: t.solvedAt
3052
- });
3053
- }
3054
- }
3055
- if (recentSolves.length > 0) {
3056
- recentSolves.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
3057
- console.log(chalk21.bold(" Recent Solves:"));
3058
- for (const solve of recentSolves.slice(0, 5)) {
3059
- const date = new Date(solve.date).toLocaleDateString();
3060
- console.log(
3061
- chalk21.gray(` ${date} `) + chalk21.white(`${solve.problemId}. ${solve.title.substring(0, 25)}`) + chalk21.gray(" ") + chalk21.cyan(formatDuration(solve.duration))
3062
- );
3063
- }
3064
- }
3065
- console.log();
3066
- }
3067
- }
3068
-
3069
- // src/commands/collab.ts
3070
- import chalk22 from "chalk";
3071
- import ora15 from "ora";
3072
- import { readFile as readFile4 } from "fs/promises";
3073
-
3074
- // src/services/supabase.ts
3075
- import { createClient } from "@supabase/supabase-js";
3076
- var SUPABASE_URL = "https://abagrmwdpvnfyuqizyym.supabase.co";
3077
- var SUPABASE_ANON_KEY = "sb_publishable_indrKu8VJmASdyLp7w8Hog_OyqT17cV";
3078
- var supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
3079
-
3080
- // src/storage/collab.ts
3081
- import { existsSync as existsSync10, readFileSync as readFileSync3, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3 } from "fs";
3082
- import { dirname as dirname2 } from "path";
3083
- function getCollabPath() {
3084
- return workspaceStorage.getCollabPath();
3085
- }
3086
- function loadCollab() {
3087
- const path = getCollabPath();
3088
- if (existsSync10(path)) {
3089
- return JSON.parse(readFileSync3(path, "utf-8"));
3090
- }
3091
- return { session: null };
3092
- }
3093
- function saveCollab(data) {
3094
- const collabPath = getCollabPath();
3095
- const dir = dirname2(collabPath);
3096
- if (!existsSync10(dir)) {
3097
- mkdirSync3(dir, { recursive: true });
3098
- }
3099
- writeFileSync4(collabPath, JSON.stringify(data, null, 2));
3100
- }
3101
- var collabStorage = {
3102
- getSession() {
3103
- return loadCollab().session;
3104
- },
3105
- setSession(session) {
3106
- saveCollab({ session });
3107
- },
3108
- getPath() {
3109
- return getCollabPath();
3110
- }
3111
- };
3112
-
3113
- // src/services/collab.ts
3114
- function generateRoomCode() {
3115
- const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
3116
- let code = "";
3117
- for (let i = 0; i < 6; i++) {
3118
- code += chars.charAt(Math.floor(Math.random() * chars.length));
3119
- }
3120
- return code;
3121
- }
3122
- var collabService = {
3123
- getSession() {
3124
- return collabStorage.getSession();
3125
- },
3126
- async createRoom(problemId, username) {
3127
- const roomCode = generateRoomCode();
3128
- const { error } = await supabase.from("collab_rooms").insert({
3129
- room_code: roomCode,
3130
- problem_id: problemId,
3131
- host_username: username,
3132
- host_code: "",
3133
- guest_username: null,
3134
- guest_code: null
3135
- });
3136
- if (error) {
3137
- return { error: error.message };
3138
- }
3139
- collabStorage.setSession({
3140
- roomCode,
3141
- problemId,
3142
- isHost: true,
3143
- username
3144
- });
3145
- return { roomCode };
3146
- },
3147
- async joinRoom(roomCode, username) {
3148
- const { data: room, error: fetchError } = await supabase.from("collab_rooms").select("*").eq("room_code", roomCode.toUpperCase()).single();
3149
- if (fetchError || !room) {
3150
- return { error: "Room not found" };
3151
- }
3152
- const { error: updateError } = await supabase.from("collab_rooms").update({ guest_username: username }).eq("room_code", roomCode.toUpperCase());
3153
- if (updateError) {
3154
- return { error: updateError.message };
3155
- }
3156
- collabStorage.setSession({
3157
- roomCode: roomCode.toUpperCase(),
3158
- problemId: room.problem_id,
3159
- isHost: false,
3160
- username
3161
- });
3162
- return { problemId: room.problem_id };
3163
- },
3164
- async syncCode(code) {
3165
- const session = collabStorage.getSession();
3166
- if (!session) {
3167
- return { success: false, error: "No active session" };
3168
- }
3169
- const column = session.isHost ? "host_code" : "guest_code";
3170
- const { error } = await supabase.from("collab_rooms").update({ [column]: code }).eq("room_code", session.roomCode);
3171
- if (error) {
3172
- return { success: false, error: error.message };
3173
- }
3174
- return { success: true };
3175
- },
3176
- async getPartnerCode() {
3177
- const session = collabStorage.getSession();
3178
- if (!session) {
3179
- return { error: "No active session" };
3180
- }
3181
- const { data: room, error } = await supabase.from("collab_rooms").select("*").eq("room_code", session.roomCode).single();
3182
- if (error || !room) {
3183
- return { error: "Room not found" };
3184
- }
3185
- if (session.isHost) {
3186
- return {
3187
- code: room.guest_code || "",
3188
- username: room.guest_username || "Partner"
3189
- };
3190
- } else {
3191
- return {
3192
- code: room.host_code || "",
3193
- username: room.host_username || "Host"
3194
- };
3195
- }
3196
- },
3197
- async getRoomStatus() {
3198
- const session = collabStorage.getSession();
3199
- if (!session) {
3200
- return { error: "No active session" };
3201
- }
3202
- const { data: room, error } = await supabase.from("collab_rooms").select("*").eq("room_code", session.roomCode).single();
3203
- if (error || !room) {
3204
- return { error: "Room not found" };
3205
- }
3206
- return {
3207
- host: room.host_username,
3208
- guest: room.guest_username,
3209
- hasHostCode: !!room.host_code,
3210
- hasGuestCode: !!room.guest_code
3211
- };
3212
- },
3213
- async leaveRoom() {
3214
- const session = collabStorage.getSession();
3215
- if (session) {
3216
- if (session.isHost) {
3217
- await supabase.from("collab_rooms").delete().eq("room_code", session.roomCode);
3218
- }
3219
- }
3220
- collabStorage.setSession(null);
3221
- }
3222
- };
3223
-
3224
- // src/commands/collab.ts
3225
- async function collabHostCommand(problemId) {
3226
- const { authorized, username } = await requireAuth();
3227
- if (!authorized || !username) return;
3228
- const spinner = ora15("Creating collaboration room...").start();
3229
- try {
3230
- const result = await collabService.createRoom(problemId, username);
3231
- if ("error" in result) {
3232
- spinner.fail(result.error);
3233
- return;
3234
- }
3235
- spinner.succeed("Room created!");
3236
- console.log();
3237
- console.log(chalk22.bold.cyan("\u{1F465} Collaborative Coding Session"));
3238
- console.log(chalk22.gray("\u2500".repeat(50)));
3239
- console.log();
3240
- console.log(chalk22.white(`Room Code: ${chalk22.bold.green(result.roomCode)}`));
3241
- console.log(chalk22.white(`Problem: ${problemId}`));
3242
- console.log();
3243
- console.log(chalk22.gray("Share this code with your partner:"));
3244
- console.log(chalk22.yellow(` leetcode collab join ${result.roomCode}`));
3245
- console.log();
3246
- console.log(chalk22.gray("\u2500".repeat(50)));
3247
- console.log(chalk22.gray("After solving, sync and compare:"));
3248
- console.log(chalk22.gray(" leetcode collab sync - Upload your solution"));
3249
- console.log(chalk22.gray(" leetcode collab compare - See both solutions"));
3250
- console.log(chalk22.gray(" leetcode collab status - Check room status"));
3251
- console.log(chalk22.gray(" leetcode collab leave - End session"));
3252
- console.log();
3253
- await pickCommand(problemId, { open: true });
3254
- } catch (error) {
3255
- spinner.fail("Failed to create room");
3256
- if (error instanceof Error) {
3257
- console.log(chalk22.red(error.message));
3258
- }
3259
- }
3260
- }
3261
- async function collabJoinCommand(roomCode) {
3262
- const { authorized, username } = await requireAuth();
3263
- if (!authorized || !username) return;
3264
- const spinner = ora15(`Joining room ${roomCode}...`).start();
3265
- try {
3266
- const result = await collabService.joinRoom(roomCode.toUpperCase(), username);
3267
- if ("error" in result) {
3268
- spinner.fail(result.error);
3269
- return;
3270
- }
3271
- spinner.succeed("Joined room!");
3272
- console.log();
3273
- console.log(chalk22.bold.cyan("\u{1F465} Collaborative Coding Session"));
3274
- console.log(chalk22.gray("\u2500".repeat(50)));
3275
- console.log();
3276
- console.log(chalk22.white(`Room Code: ${chalk22.bold.green(roomCode.toUpperCase())}`));
3277
- console.log(chalk22.white(`Problem: ${result.problemId}`));
3278
- console.log();
3279
- console.log(chalk22.gray("\u2500".repeat(50)));
3280
- console.log(chalk22.gray("After solving, sync and compare:"));
3281
- console.log(chalk22.gray(" leetcode collab sync - Upload your solution"));
3282
- console.log(chalk22.gray(" leetcode collab compare - See both solutions"));
3283
- console.log(chalk22.gray(" leetcode collab status - Check room status"));
3284
- console.log(chalk22.gray(" leetcode collab leave - End session"));
3285
- console.log();
3286
- await pickCommand(result.problemId, { open: true });
3287
- } catch (error) {
3288
- spinner.fail("Failed to join room");
3289
- if (error instanceof Error) {
3290
- console.log(chalk22.red(error.message));
3291
- }
3292
- }
3293
- }
3294
- async function collabSyncCommand() {
3295
- const session = collabService.getSession();
3296
- if (!session) {
3297
- console.log(chalk22.yellow("No active collaboration session."));
3298
- console.log(chalk22.gray("Use `leetcode collab host <id>` or `leetcode collab join <code>` first."));
3299
- return;
3300
- }
3301
- const spinner = ora15("Syncing your code...").start();
3302
- const workDir = config.getWorkDir();
3303
- const filePath = await findSolutionFile(workDir, session.problemId);
3304
- if (!filePath) {
3305
- spinner.fail(`No solution file found for problem ${session.problemId}`);
3306
- return;
3307
- }
3308
- const code = await readFile4(filePath, "utf-8");
3309
- const result = await collabService.syncCode(code);
3310
- if (result.success) {
3311
- spinner.succeed("Code synced successfully!");
3312
- console.log(chalk22.gray(`Uploaded ${code.split("\n").length} lines from ${filePath}`));
3313
- } else {
3314
- spinner.fail(result.error || "Sync failed");
3315
- }
3316
- }
3317
- async function collabCompareCommand() {
3318
- const session = collabService.getSession();
3319
- if (!session) {
3320
- console.log(chalk22.yellow("No active collaboration session."));
3321
- console.log(chalk22.gray("Use `leetcode collab host <id>` or `leetcode collab join <code>` first."));
3322
- return;
3323
- }
3324
- const spinner = ora15("Fetching solutions...").start();
3325
- const workDir = config.getWorkDir();
3326
- const filePath = await findSolutionFile(workDir, session.problemId);
3327
- if (!filePath) {
3328
- spinner.fail(`No solution file found for problem ${session.problemId}`);
3329
- return;
3330
- }
3331
- const myCode = await readFile4(filePath, "utf-8");
3332
- const partnerResult = await collabService.getPartnerCode();
3333
- if ("error" in partnerResult) {
3334
- spinner.fail(partnerResult.error);
3335
- return;
3336
- }
3337
- spinner.stop();
3338
- if (!partnerResult.code) {
3339
- console.log(chalk22.yellow("Partner has not synced their code yet."));
3340
- console.log(chalk22.gray("Ask them to run `leetcode collab sync`."));
3341
- return;
3342
- }
3343
- console.log();
3344
- console.log(chalk22.bold.cyan("\u{1F4CA} Solution Comparison"));
3345
- console.log(chalk22.gray("\u2500".repeat(60)));
3346
- console.log();
3347
- console.log(chalk22.bold.green(`\u25B8 Your Solution (${session.username})`));
3348
- console.log(chalk22.gray("\u2500".repeat(60)));
3349
- const myLines = myCode.split("\n");
3350
- for (let i = 0; i < myLines.length; i++) {
3351
- const lineNum = String(i + 1).padStart(3, " ");
3352
- console.log(`${chalk22.gray(lineNum)} ${myLines[i]}`);
3353
- }
3354
- console.log();
3355
- console.log(chalk22.bold.blue(`\u25B8 ${partnerResult.username}'s Solution`));
3356
- console.log(chalk22.gray("\u2500".repeat(60)));
3357
- const partnerLines = partnerResult.code.split("\n");
3358
- for (let i = 0; i < partnerLines.length; i++) {
3359
- const lineNum = String(i + 1).padStart(3, " ");
3360
- console.log(`${chalk22.gray(lineNum)} ${partnerLines[i]}`);
3361
- }
3362
- console.log();
3363
- console.log(chalk22.gray("\u2500".repeat(60)));
3364
- console.log(chalk22.gray(`Your code: ${myLines.length} lines | Partner: ${partnerLines.length} lines`));
3365
- console.log();
3366
- }
3367
- async function collabLeaveCommand() {
3368
- const session = collabService.getSession();
3369
- if (!session) {
3370
- console.log(chalk22.yellow("No active collaboration session."));
3371
- return;
3372
- }
3373
- await collabService.leaveRoom();
3374
- console.log(chalk22.green("\u2713 Left the collaboration session."));
3375
- }
3376
- async function collabStatusCommand() {
3377
- const session = collabService.getSession();
3378
- if (!session) {
3379
- console.log(chalk22.yellow("No active collaboration session."));
3380
- console.log(chalk22.gray("Use `leetcode collab host <id>` or `leetcode collab join <code>` to start."));
3381
- return;
3382
- }
3383
- const status = await collabService.getRoomStatus();
3384
- if ("error" in status) {
3385
- console.log(chalk22.red(status.error));
3386
- return;
3387
- }
3388
- console.log();
3389
- console.log(chalk22.bold.cyan("\u{1F465} Collaboration Status"));
3390
- console.log(chalk22.gray("\u2500".repeat(40)));
3391
- console.log(` Room: ${chalk22.green(session.roomCode)}`);
3392
- console.log(` Problem: ${session.problemId}`);
3393
- console.log(` Role: ${session.isHost ? "Host" : "Guest"}`);
3394
- console.log();
3395
- console.log(chalk22.bold(" Participants:"));
3396
- console.log(` Host: ${status.host} ${status.hasHostCode ? chalk22.green("\u2713 synced") : chalk22.gray("pending")}`);
3397
- console.log(` Guest: ${status.guest || chalk22.gray("(waiting...)")} ${status.hasGuestCode ? chalk22.green("\u2713 synced") : chalk22.gray("pending")}`);
3398
- console.log();
3399
- }
3400
-
3401
- // src/commands/snapshot.ts
3402
- import chalk23 from "chalk";
3403
-
3404
- // src/storage/snapshots.ts
3405
- import { existsSync as existsSync11, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync5, unlinkSync } from "fs";
3406
- import { join as join8 } from "path";
3407
- function getSnapshotsDir() {
3408
- return workspaceStorage.getSnapshotsDir();
3409
- }
3410
- function getSnapshotDir(problemId) {
3411
- return join8(getSnapshotsDir(), problemId);
3412
- }
3413
- function getMetaPath(problemId) {
3414
- return join8(getSnapshotDir(problemId), "meta.json");
3415
- }
3416
- function getFilesDir(problemId) {
3417
- return join8(getSnapshotDir(problemId), "files");
3418
- }
3419
- function ensureSnapshotDir(problemId) {
3420
- const dir = getFilesDir(problemId);
3421
- if (!existsSync11(dir)) {
3422
- mkdirSync4(dir, { recursive: true });
3423
- }
3424
- }
3425
- function loadMeta(problemId) {
3426
- const metaPath = getMetaPath(problemId);
3427
- if (existsSync11(metaPath)) {
3428
- return JSON.parse(readFileSync4(metaPath, "utf-8"));
3429
- }
3430
- return {
3431
- problemId,
3432
- problemTitle: "",
3433
- snapshots: []
3434
- };
3435
- }
3436
- function saveMeta(problemId, meta) {
3437
- ensureSnapshotDir(problemId);
3438
- writeFileSync5(getMetaPath(problemId), JSON.stringify(meta, null, 2));
3439
- }
3440
- var snapshotStorage = {
3441
- /**
3442
- * Save a snapshot of the solution
3443
- */
3444
- save(problemId, problemTitle, code, language, name) {
3445
- ensureSnapshotDir(problemId);
3446
- const meta = loadMeta(problemId);
3447
- if (problemTitle) {
3448
- meta.problemTitle = problemTitle;
3449
- }
3450
- const nextId = meta.snapshots.length > 0 ? Math.max(...meta.snapshots.map((s) => s.id)) + 1 : 1;
3451
- const snapshotName = name || `snapshot-${nextId}`;
3452
- const existing = meta.snapshots.find((s) => s.name === snapshotName);
3453
- if (existing) {
3454
- return { error: `Snapshot with name "${snapshotName}" already exists (ID: ${existing.id})` };
3455
- }
3456
- const ext = LANGUAGE_EXTENSIONS[language] || language;
3457
- const fileName = `${nextId}_${snapshotName}.${ext}`;
3458
- const filePath = join8(getFilesDir(problemId), fileName);
3459
- writeFileSync5(filePath, code, "utf-8");
3460
- const snapshot = {
3461
- id: nextId,
3462
- name: snapshotName,
3463
- fileName,
3464
- language,
3465
- lines: code.split("\n").length,
3466
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
3467
- };
3468
- meta.snapshots.push(snapshot);
3469
- saveMeta(problemId, meta);
3470
- return snapshot;
3471
- },
3472
- /**
3473
- * Get all snapshots for a problem
3474
- */
3475
- list(problemId) {
3476
- const meta = loadMeta(problemId);
3477
- return meta.snapshots;
3478
- },
3479
- /**
3480
- * Get snapshot metadata
3481
- */
3482
- getMeta(problemId) {
3483
- return loadMeta(problemId);
3484
- },
3485
- /**
3486
- * Get a specific snapshot by ID or name
3487
- */
3488
- get(problemId, idOrName) {
3489
- const meta = loadMeta(problemId);
3490
- const byId = meta.snapshots.find((s) => s.id === parseInt(idOrName, 10));
3491
- if (byId) return byId;
3492
- const byName = meta.snapshots.find((s) => s.name === idOrName);
3493
- return byName || null;
3494
- },
3495
- /**
3496
- * Get snapshot code content
3497
- */
3498
- getCode(problemId, snapshot) {
3499
- const filePath = join8(getFilesDir(problemId), snapshot.fileName);
3500
- if (!existsSync11(filePath)) {
3501
- throw new Error(`Snapshot file not found: ${snapshot.fileName}`);
3502
- }
3503
- return readFileSync4(filePath, "utf-8");
3504
- },
3505
- /**
3506
- * Delete a snapshot
3507
- */
3508
- delete(problemId, idOrName) {
3509
- const meta = loadMeta(problemId);
3510
- const snapshot = this.get(problemId, idOrName);
3511
- if (!snapshot) {
3512
- return false;
3513
- }
3514
- const filePath = join8(getFilesDir(problemId), snapshot.fileName);
3515
- if (existsSync11(filePath)) {
3516
- unlinkSync(filePath);
3517
- }
3518
- meta.snapshots = meta.snapshots.filter((s) => s.id !== snapshot.id);
3519
- saveMeta(problemId, meta);
3520
- return true;
3521
- },
3522
- /**
3523
- * Check if snapshots exist for a problem
3524
- */
3525
- hasSnapshots(problemId) {
3526
- return this.list(problemId).length > 0;
3527
- }
3528
- };
3529
-
3530
- // src/commands/snapshot.ts
3531
- import { readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
3532
- import { extname, basename as basename3 } from "path";
3533
- import { diffLines } from "diff";
3534
- function formatTimeAgo(dateStr) {
3535
- const date = new Date(dateStr);
3536
- const now = /* @__PURE__ */ new Date();
3537
- const diffMs = now.getTime() - date.getTime();
3538
- const diffMins = Math.floor(diffMs / 6e4);
3539
- const diffHours = Math.floor(diffMins / 60);
3540
- const diffDays = Math.floor(diffHours / 24);
3541
- if (diffMins < 1) return "just now";
3542
- if (diffMins < 60) return `${diffMins}m ago`;
3543
- if (diffHours < 24) return `${diffHours}h ago`;
3544
- return `${diffDays}d ago`;
3545
- }
3546
- async function snapshotSaveCommand(problemId, name) {
3547
- const workDir = config.getWorkDir();
3548
- try {
3549
- const filePath = await findSolutionFile(workDir, problemId);
3550
- if (!filePath) {
3551
- console.log(chalk23.red(`No solution file found for problem ${problemId}`));
3552
- console.log(chalk23.gray("Run `leetcode pick " + problemId + "` first to create a solution file."));
3553
- return;
3554
- }
3555
- const code = await readFile5(filePath, "utf-8");
3556
- const ext = extname(filePath).slice(1);
3557
- const lang = getLangSlugFromExtension(ext) || ext;
3558
- const fileName = basename3(filePath);
3559
- const titleMatch = fileName.match(/^\d+\.(.+)\.\w+$/);
3560
- const title = titleMatch ? titleMatch[1] : "";
3561
- const result = snapshotStorage.save(problemId, title, code, lang, name);
3562
- if ("error" in result) {
3563
- console.log(chalk23.red("\u2717 " + result.error));
3564
- return;
3565
- }
3566
- const snapshot = result;
3567
- console.log(chalk23.green("\u2713 Snapshot saved!"));
3568
- console.log();
3569
- console.log(` ID: ${chalk23.cyan(snapshot.id)}`);
3570
- console.log(` Name: ${chalk23.white(snapshot.name)}`);
3571
- console.log(` Lines: ${chalk23.gray(snapshot.lines)}`);
3572
- console.log(` File: ${chalk23.gray(filePath)}`);
3573
- } catch (error) {
3574
- console.log(chalk23.red("Failed to save snapshot"));
3575
- if (error instanceof Error) {
3576
- console.log(chalk23.gray(error.message));
3577
- }
3578
- }
3579
- }
3580
- async function snapshotListCommand(problemId) {
3581
- const snapshots = snapshotStorage.list(problemId);
3582
- if (snapshots.length === 0) {
3583
- console.log(chalk23.yellow(`No snapshots found for problem ${problemId}`));
3584
- console.log(chalk23.gray("Use `leetcode snapshot save " + problemId + "` to create one."));
3585
- return;
3586
- }
3587
- const meta = snapshotStorage.getMeta(problemId);
3588
- console.log();
3589
- console.log(chalk23.bold(`\u{1F4F8} Snapshots for Problem ${problemId}`));
3590
- if (meta.problemTitle) {
3591
- console.log(chalk23.gray(` ${meta.problemTitle}`));
3592
- }
3593
- console.log(chalk23.gray("\u2500".repeat(50)));
3594
- console.log();
3595
- for (const snap of snapshots) {
3596
- const timeAgo = formatTimeAgo(snap.createdAt);
3597
- console.log(
3598
- ` ${chalk23.cyan(snap.id.toString().padStart(2))}. ${chalk23.white(snap.name.padEnd(25))} ${chalk23.gray(snap.lines + " lines")} ${chalk23.gray("\xB7")} ${chalk23.gray(timeAgo)}`
3599
- );
3600
- }
3601
- console.log();
3602
- console.log(chalk23.gray("Commands:"));
3603
- console.log(chalk23.gray(` restore: leetcode snapshot restore ${problemId} <id|name>`));
3604
- console.log(chalk23.gray(` diff: leetcode snapshot diff ${problemId} <id1> <id2>`));
3605
- console.log(chalk23.gray(` delete: leetcode snapshot delete ${problemId} <id|name>`));
3606
- }
3607
- async function snapshotRestoreCommand(problemId, idOrName) {
3608
- const workDir = config.getWorkDir();
3609
- try {
3610
- const snapshot = snapshotStorage.get(problemId, idOrName);
3611
- if (!snapshot) {
3612
- console.log(chalk23.red(`Snapshot "${idOrName}" not found for problem ${problemId}`));
3613
- console.log(chalk23.gray("Run `leetcode snapshot list " + problemId + "` to see available snapshots."));
3614
- return;
3615
- }
3616
- const filePath = await findSolutionFile(workDir, problemId);
3617
- if (!filePath) {
3618
- console.log(chalk23.red(`No solution file found for problem ${problemId}`));
3619
- return;
3620
- }
3621
- const currentCode = await readFile5(filePath, "utf-8");
3622
- const backupName = `backup-before-restore-${Date.now()}`;
3623
- const ext = extname(filePath).slice(1);
3624
- const lang = getLangSlugFromExtension(ext) || ext;
3625
- snapshotStorage.save(problemId, "", currentCode, lang, backupName);
3626
- const snapshotCode = snapshotStorage.getCode(problemId, snapshot);
3627
- await writeFile4(filePath, snapshotCode, "utf-8");
3628
- console.log(chalk23.green("\u2713 Snapshot restored!"));
3629
- console.log();
3630
- console.log(` Restored: ${chalk23.cyan(snapshot.name)} (${snapshot.lines} lines)`);
3631
- console.log(` File: ${chalk23.gray(filePath)}`);
3632
- console.log(` Backup: ${chalk23.gray(backupName)}`);
3633
- } catch (error) {
3634
- console.log(chalk23.red("Failed to restore snapshot"));
3635
- if (error instanceof Error) {
3636
- console.log(chalk23.gray(error.message));
3637
- }
3638
- }
3639
- }
3640
- async function snapshotDiffCommand(problemId, idOrName1, idOrName2) {
3641
- try {
3642
- const snap1 = snapshotStorage.get(problemId, idOrName1);
3643
- const snap2 = snapshotStorage.get(problemId, idOrName2);
3644
- if (!snap1) {
3645
- console.log(chalk23.red(`Snapshot "${idOrName1}" not found`));
3646
- return;
3647
- }
3648
- if (!snap2) {
3649
- console.log(chalk23.red(`Snapshot "${idOrName2}" not found`));
3650
- return;
3651
- }
3652
- const code1 = snapshotStorage.getCode(problemId, snap1);
3653
- const code2 = snapshotStorage.getCode(problemId, snap2);
3654
- console.log();
3655
- console.log(chalk23.bold(`\u{1F4CA} Diff: ${snap1.name} \u2192 ${snap2.name}`));
3656
- console.log(chalk23.gray("\u2500".repeat(50)));
3657
- console.log();
3658
- const diff = diffLines(code1, code2);
3659
- let added = 0;
3660
- let removed = 0;
3661
- for (const part of diff) {
3662
- const lines = part.value.split("\n").filter((l) => l !== "");
3663
- if (part.added) {
3664
- added += lines.length;
3665
- for (const line of lines) {
3666
- console.log(chalk23.green("+ " + line));
3667
- }
3668
- } else if (part.removed) {
3669
- removed += lines.length;
3670
- for (const line of lines) {
3671
- console.log(chalk23.red("- " + line));
3672
- }
3673
- } else {
3674
- if (lines.length <= 4) {
3675
- for (const line of lines) {
3676
- console.log(chalk23.gray(" " + line));
3677
- }
3678
- } else {
3679
- console.log(chalk23.gray(" " + lines[0]));
3680
- console.log(chalk23.gray(" " + lines[1]));
3681
- console.log(chalk23.gray(` ... (${lines.length - 4} more lines)`));
3682
- console.log(chalk23.gray(" " + lines[lines.length - 2]));
3683
- console.log(chalk23.gray(" " + lines[lines.length - 1]));
3684
- }
3685
- }
3686
- }
3687
- console.log();
3688
- console.log(chalk23.gray("\u2500".repeat(50)));
3689
- console.log(
3690
- `${chalk23.green("+" + added + " added")} ${chalk23.gray("\xB7")} ${chalk23.red("-" + removed + " removed")} ${chalk23.gray("\xB7")} ${chalk23.gray(snap1.lines + " \u2192 " + snap2.lines + " lines")}`
3691
- );
3692
- } catch (error) {
3693
- console.log(chalk23.red("Failed to diff snapshots"));
3694
- if (error instanceof Error) {
3695
- console.log(chalk23.gray(error.message));
3696
- }
3697
- }
3698
- }
3699
- async function snapshotDeleteCommand(problemId, idOrName) {
3700
- const snapshot = snapshotStorage.get(problemId, idOrName);
3701
- if (!snapshot) {
3702
- console.log(chalk23.red(`Snapshot "${idOrName}" not found for problem ${problemId}`));
3703
- return;
3704
- }
3705
- const deleted = snapshotStorage.delete(problemId, idOrName);
3706
- if (deleted) {
3707
- console.log(chalk23.green(`\u2713 Deleted snapshot: ${snapshot.name}`));
3708
- } else {
3709
- console.log(chalk23.red("Failed to delete snapshot"));
3710
- }
3711
- }
3712
-
3713
- // src/commands/diff.ts
3714
- import ora16 from "ora";
3715
- import chalk24 from "chalk";
3716
- import { readFile as readFile6 } from "fs/promises";
3717
- import { existsSync as existsSync12 } from "fs";
3718
- import { diffLines as diffLines2 } from "diff";
3719
- function showCodeBlock(code, label) {
3720
- const lines = code.split("\n");
3721
- const lineCount = lines.length;
3722
- console.log();
3723
- console.log(chalk24.bold.cyan(`=== ${label} (${lineCount} lines) ===`));
3724
- console.log(chalk24.gray("\u2500".repeat(60)));
3725
- lines.forEach((line, i) => {
3726
- const lineNum = (i + 1).toString().padStart(3);
3727
- console.log(chalk24.gray(lineNum + " \u2502 ") + line);
3728
- });
3729
- }
3730
- function showUnifiedDiff(sourceCode, targetCode, sourceLabel, targetLabel) {
3731
- const sourceLines = sourceCode.split("\n").length;
3732
- const targetLines = targetCode.split("\n").length;
3733
- console.log();
3734
- console.log(chalk24.bold(`\u{1F4CA} Unified Diff: ${sourceLabel} \u2192 ${targetLabel}`));
3735
- console.log(chalk24.gray("\u2500".repeat(60)));
3736
- console.log();
3737
- const diff = diffLines2(sourceCode, targetCode);
3738
- let added = 0;
3739
- let removed = 0;
3740
- for (const part of diff) {
3741
- const lines = part.value.split("\n").filter((l) => l !== "");
3742
- if (part.added) {
3743
- added += lines.length;
3744
- for (const line of lines) {
3745
- console.log(chalk24.green("+ " + line));
3746
- }
3747
- } else if (part.removed) {
3748
- removed += lines.length;
3749
- for (const line of lines) {
3750
- console.log(chalk24.red("- " + line));
3751
- }
3752
- } else {
3753
- if (lines.length <= 6) {
3754
- for (const line of lines) {
3755
- console.log(chalk24.gray(" " + line));
3756
- }
3757
- } else {
3758
- console.log(chalk24.gray(" " + lines[0]));
3759
- console.log(chalk24.gray(" " + lines[1]));
3760
- console.log(chalk24.dim(` ... (${lines.length - 4} unchanged lines)`));
3761
- console.log(chalk24.gray(" " + lines[lines.length - 2]));
3762
- console.log(chalk24.gray(" " + lines[lines.length - 1]));
3763
- }
3764
- }
3765
- }
3766
- console.log();
3767
- console.log(chalk24.gray("\u2500".repeat(60)));
3768
- console.log(
3769
- `${chalk24.green("+" + added + " added")} ${chalk24.gray("\xB7")} ${chalk24.red("-" + removed + " removed")} ${chalk24.gray("\xB7")} ${chalk24.gray(sourceLines + " \u2192 " + targetLines + " lines")}`
3770
- );
3771
- }
3772
- function showComparison(sourceCode, targetCode, sourceLabel, targetLabel, unified) {
3773
- if (unified) {
3774
- showUnifiedDiff(sourceCode, targetCode, sourceLabel, targetLabel);
3775
- } else {
3776
- showCodeBlock(sourceCode, sourceLabel);
3777
- showCodeBlock(targetCode, targetLabel);
3778
- const diff = diffLines2(sourceCode, targetCode);
3779
- let added = 0;
3780
- let removed = 0;
3781
- for (const part of diff) {
3782
- const lines = part.value.split("\n").filter((l) => l !== "");
3783
- if (part.added) added += lines.length;
3784
- else if (part.removed) removed += lines.length;
3785
- }
3786
- console.log();
3787
- console.log(chalk24.gray("\u2500".repeat(60)));
3788
- console.log(
3789
- `${chalk24.bold("Summary:")} ${chalk24.green("+" + added + " added")} ${chalk24.gray("\xB7")} ${chalk24.red("-" + removed + " removed")}`
3790
- );
3791
- console.log(chalk24.gray("Tip: Use --unified for line-by-line diff"));
3792
- }
3793
- }
3794
- async function diffCommand(problemId, options) {
3795
- const { authorized } = await requireAuth();
3796
- if (!authorized) return;
3797
- const workDir = config.getWorkDir();
3798
- const spinner = ora16("Finding solution file...").start();
3799
- try {
3800
- const filePath = await findSolutionFile(workDir, problemId);
3801
- if (!filePath) {
3802
- spinner.fail(`No solution file found for problem ${problemId}`);
3803
- console.log(chalk24.gray("Run `leetcode pick " + problemId + "` first to create a solution file."));
3804
- return;
3805
- }
3806
- const currentCode = await readFile6(filePath, "utf-8");
3807
- spinner.text = "Fetching comparison target...";
3808
- if (options.file) {
3809
- spinner.stop();
3810
- if (!existsSync12(options.file)) {
3811
- console.log(chalk24.red(`File not found: ${options.file}`));
3812
- return;
3813
- }
3814
- const otherCode = await readFile6(options.file, "utf-8");
3815
- showComparison(currentCode, otherCode, "Your Solution", options.file, options.unified ?? false);
3816
- return;
3817
- }
3818
- const problem = await leetcodeClient.getProblemById(problemId);
3819
- if (!problem) {
3820
- spinner.fail(`Problem ${problemId} not found`);
3821
- return;
3822
- }
3823
- if (options.submission) {
3824
- const submissionId = parseInt(options.submission, 10);
3825
- const submission = await leetcodeClient.getSubmissionDetails(submissionId);
3826
- spinner.stop();
3827
- showComparison(currentCode, submission.code, "Your Solution", `Submission #${submissionId}`, options.unified ?? false);
3828
- return;
3829
- }
3830
- const submissions = await leetcodeClient.getSubmissionList(problem.titleSlug, 50);
3831
- const accepted = submissions.find((s) => s.statusDisplay === "Accepted");
3832
- if (!accepted) {
3833
- spinner.fail("No accepted submissions found for this problem");
3834
- console.log(chalk24.gray("Tip: Use --file to compare with a local file instead"));
3835
- return;
3836
- }
3837
- const acceptedDetails = await leetcodeClient.getSubmissionDetails(parseInt(accepted.id, 10));
3838
- spinner.stop();
3839
- showComparison(currentCode, acceptedDetails.code, "Your Solution", "Last Accepted", options.unified ?? false);
3840
- } catch (error) {
3841
- spinner.fail("Failed to diff");
3842
- if (error instanceof Error) {
3843
- console.log(chalk24.red(error.message));
3844
- }
3845
- }
3846
- }
3847
-
3848
- // src/commands/workspace.ts
3849
- import chalk25 from "chalk";
3850
- import inquirer4 from "inquirer";
3851
- import { homedir as homedir3 } from "os";
3852
- import { join as join9 } from "path";
3853
- async function workspaceCurrentCommand() {
3854
- const active = workspaceStorage.getActive();
3855
- const config2 = workspaceStorage.getConfig(active);
3856
- console.log();
3857
- console.log(chalk25.bold.cyan(`\u{1F4C1} Active Workspace: ${active}`));
3858
- console.log(chalk25.gray("\u2500".repeat(40)));
3859
- console.log(` workDir: ${chalk25.white(config2.workDir)}`);
3860
- console.log(` lang: ${chalk25.white(config2.lang)}`);
3861
- if (config2.editor) console.log(` editor: ${chalk25.white(config2.editor)}`);
3862
- if (config2.syncRepo) console.log(` syncRepo: ${chalk25.white(config2.syncRepo)}`);
3863
- console.log();
3864
- }
3865
- async function workspaceListCommand() {
3866
- const workspaces = workspaceStorage.list();
3867
- const active = workspaceStorage.getActive();
3868
- console.log();
3869
- console.log(chalk25.bold("Workspaces:"));
3870
- console.log();
3871
- for (const ws of workspaces) {
3872
- const config2 = workspaceStorage.getConfig(ws);
3873
- const marker = ws === active ? chalk25.green("\u25B8 ") : " ";
3874
- const name = ws === active ? chalk25.green.bold(ws) : ws;
3875
- console.log(`${marker}${name}`);
3876
- console.log(` ${chalk25.gray(config2.workDir)}`);
3877
- }
3878
- console.log();
3879
- }
3880
- async function workspaceCreateCommand(name, options) {
3881
- if (workspaceStorage.exists(name)) {
3882
- console.log(chalk25.red(`Workspace "${name}" already exists`));
3883
- return;
3884
- }
3885
- const workDir = options.workdir ?? join9(homedir3(), "leetcode", name);
3886
- const config2 = {
3887
- workDir,
3888
- lang: "typescript"
3889
- };
3890
- const success = workspaceStorage.create(name, config2);
3891
- if (success) {
3892
- console.log(chalk25.green(`\u2713 Created workspace "${name}"`));
3893
- console.log(` workDir: ${chalk25.gray(workDir)}`);
3894
- console.log();
3895
- console.log(chalk25.gray(`Switch to it: leetcode workspace use ${name}`));
3896
- } else {
3897
- console.log(chalk25.red("Failed to create workspace"));
3898
- }
3899
- }
3900
- async function workspaceUseCommand(name) {
3901
- if (!workspaceStorage.exists(name)) {
3902
- console.log(chalk25.red(`Workspace "${name}" not found`));
3903
- console.log(chalk25.gray("Use `leetcode workspace list` to see available workspaces"));
3904
- return;
3905
- }
3906
- const success = workspaceStorage.setActive(name);
3907
- if (success) {
3908
- const config2 = workspaceStorage.getConfig(name);
3909
- console.log(chalk25.green(`\u2713 Switched to workspace "${name}"`));
3910
- console.log(` workDir: ${chalk25.gray(config2.workDir)}`);
3911
- } else {
3912
- console.log(chalk25.red("Failed to switch workspace"));
3913
- }
3914
- }
3915
- async function workspaceDeleteCommand(name) {
3916
- if (name === "default") {
3917
- console.log(chalk25.red("Cannot delete the default workspace"));
3918
- return;
3919
- }
3920
- if (!workspaceStorage.exists(name)) {
3921
- console.log(chalk25.red(`Workspace "${name}" not found`));
3922
- return;
3923
- }
3924
- const { confirmed } = await inquirer4.prompt([{
3925
- type: "confirm",
3926
- name: "confirmed",
3927
- message: `Delete workspace "${name}"? (files in workDir will NOT be deleted)`,
3928
- default: false
3929
- }]);
3930
- if (!confirmed) {
3931
- console.log(chalk25.gray("Cancelled"));
3932
- return;
3933
- }
3934
- const success = workspaceStorage.delete(name);
3935
- if (success) {
3936
- console.log(chalk25.green(`\u2713 Deleted workspace "${name}"`));
3937
- } else {
3938
- console.log(chalk25.red("Failed to delete workspace"));
3939
- }
3940
- }
3941
-
3942
- // src/index.ts
3943
- var program = new Command();
3944
- program.configureHelp({
3945
- sortSubcommands: true,
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)
3955
- });
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
3964
- `);
3965
- program.command("login").description("Login to LeetCode with browser cookies").addHelpText("after", `
3966
- ${chalk26.yellow("How to login:")}
3967
- 1. Open ${chalk26.cyan("https://leetcode.com")} in your browser
226
+ `}async function kt(){let{authorized:e,username:o}=await v();if(!e||!o)return;let t=Ie({text:"Fetching your progress...",spinner:"dots"}).start();try{let[n,s]=await Promise.all([h.getUserProfile(o),h.getDailyChallenge()]);t.stop(),console.log(),console.log(l.bold.cyan("\u{1F4CA} Today's Summary"),l.gray(`- ${o}`)),console.log(l.gray("\u2500".repeat(50))),console.log(),console.log(l.yellow(`\u{1F525} Current Streak: ${n.streak} day${n.streak!==1?"s":""}`)),console.log(l.gray(` Total Active Days: ${n.totalActiveDays}`)),console.log();let r=n.acSubmissionNum.find(u=>u.difficulty==="All"),i=n.acSubmissionNum.find(u=>u.difficulty==="Easy"),a=n.acSubmissionNum.find(u=>u.difficulty==="Medium"),c=n.acSubmissionNum.find(u=>u.difficulty==="Hard");console.log(l.white("\u{1F4C8} Problems Solved:")),console.log(` ${l.green("Easy")}: ${i?.count??0} | ${l.yellow("Medium")}: ${a?.count??0} | ${l.red("Hard")}: ${c?.count??0}`),console.log(` ${l.bold("Total")}: ${r?.count??0}`),console.log(),console.log(l.bold.yellow("\u{1F3AF} Today's Challenge:")),console.log(` ${s.question.questionFrontendId}. ${s.question.title}`),console.log(` ${ys(s.question.difficulty)}`);let g=s.question.status;console.log(g==="ac"?l.green(" \u2713 Completed!"):g==="notac"?l.yellow(" \u25CB Attempted"):l.gray(" - Not started")),console.log(),console.log(l.gray(` leetcode pick ${s.question.questionFrontendId} # Start working on it`));}catch(n){t.fail("Failed to fetch progress"),n instanceof Error&&console.log(l.red(n.message));}}function ys(e){switch(e.toLowerCase()){case "easy":return l.green(e);case "medium":return l.yellow(e);case "hard":return l.red(e);default:return e}}function bs(e){return e.replace(/[^a-zA-Z0-9_-]/g,"-").replace(/--+/g,"-")}function ws(e){let o=/^https:\/\/[\w.-]+\/[\w./-]+$/,t=/^git@[\w.-]+:[\w./-]+$/;return o.test(e)||t.test(e)}function Ss(e){return `'${e.replace(/'/g,"'\\''")}'`}function $s(){try{return execSync("git --version",{stdio:"ignore"}),!0}catch{return false}}function ks(e){try{return execSync("git rev-parse --is-inside-work-tree",{cwd:e,stdio:"ignore"}),!0}catch{return false}}function vs(){try{return execSync("gh --version",{stdio:"ignore"}),!0}catch{return false}}function vt(e){try{return execSync("git config --get remote.origin.url",{cwd:e,encoding:"utf-8"}).trim()}catch{return null}}async function Cs(e){let{init:o}=await eo.prompt([{type:"confirm",name:"init",message:"Work directory is not a git repository. Initialize?",default:true}]);if(!o)return console.log(l.yellow("Skipping basic git initialization.")),false;let t=Ie("Initializing git repository...").start();try{return execSync("git init",{cwd:e}),t.succeed("Initialized git repository"),!0}catch(n){throw t.fail("Failed to initialize git repository"),n}}async function Ps(e){let o=Ie(),t=w.getRepo();if(!t){if(vs()){let{createGh:s}=await eo.prompt([{type:"confirm",name:"createGh",message:"Create a new private GitHub repository?",default:true}]);if(s){o.start("Creating GitHub repository...");try{let r=e.split("/").pop()||"leetcode-solutions",i=bs(r);return execSync(`gh repo create ${i} --private --source=. --remote=origin`,{cwd:e}),o.succeed("Created and linked GitHub repository"),t=vt(e)||"",t&&w.setRepo(t),t}catch(r){o.fail("Failed to create GitHub repository"),console.log(l.red(r));}}}if(!t){console.log(l.yellow(`
227
+ Please create a new repository on your Git provider and copy the URL.`));let{url:s}=await eo.prompt([{type:"input",name:"url",message:"Enter remote repository URL:",validate:r=>r.length>0?true:"URL cannot be empty"}]);t=s;}}if(t&&!ws(t))return console.log(l.red("Invalid repository URL format.")),console.log(l.gray("Expected: https://github.com/user/repo or git@github.com:user/repo")),"";if(t&&w.setRepo(t),!vt(e)&&t)try{execSync(`git remote add origin ${t}`,{cwd:e}),console.log(l.green("\u2713 Added remote origin"));}catch{console.log(l.red("Failed to add remote origin"));}return t||""}async function Ct(){let e=w.getWorkDir();if(!existsSync(e)){console.log(l.red(`Work directory does not exist: ${e}`));return}if(!$s()){console.log(l.red("Git is not installed. Please install Git to use command."));return}if(!ks(e)&&!await Cs(e))return;await Ps(e);let o=Ie("Syncing solutions...").start();try{let t=execSync("git status --porcelain",{cwd:e,encoding:"utf-8"});if(!t){o.info("No changes to sync");return}execSync("git add .",{cwd:e});let s=t.trim().split(`
228
+ `).length,r=new Date().toISOString().replace("T"," ").substring(0,19),i=`Sync: ${s} solutions - ${r}`;execSync(`git commit -m ${Ss(i)}`,{cwd:e});try{execSync("git push -u origin main",{cwd:e,stdio:"ignore"});}catch{try{execSync("git push -u origin master",{cwd:e,stdio:"ignore"});}catch{throw new Error("Failed to push to remote. Please check your git credentials and branch status.")}}o.succeed("Successfully synced solutions to remote");}catch(t){o.fail("Sync failed"),t.message&&console.log(l.red(t.message));}}var Ds={Easy:20,Medium:40,Hard:60};function pe(e){if(e<60)return `${e}s`;if(e<3600){let o=Math.floor(e/60),t=e%60;return t>0?`${o}m ${t}s`:`${o}m`}else {let o=Math.floor(e/3600),t=Math.floor(e%3600/60);return t>0?`${o}h ${t}m`:`${o}h`}}async function Pt(e,o){if(o.stats){await Es(e);return}if(o.stop){await xs();return}if(!e){console.log(l.yellow("Please provide a problem ID to start the timer.")),console.log(l.gray("Usage: leetcode timer <id>")),console.log(l.gray(" leetcode timer --stats")),console.log(l.gray(" leetcode timer --stop"));return}let{authorized:t}=await v();if(!t)return;let n=B.getActiveTimer();if(n){let r=new Date(n.startedAt),i=Math.floor((Date.now()-r.getTime())/1e3);console.log(l.yellow("\u26A0\uFE0F You have an active timer running:")),console.log(l.white(` Problem: ${n.title}`)),console.log(l.white(` Elapsed: ${pe(i)}`)),console.log(),console.log(l.gray("Use `leetcode timer --stop` to stop it first."));return}let s=Ie("Fetching problem...").start();try{let r;if(/^\d+$/.test(e)?r=await h.getProblemById(e):r=await h.getProblem(e),!r){s.fail(`Problem "${e}" not found`);return}s.stop();let i=o.minutes??Ds[r.difficulty]??30;B.startTimer(r.questionFrontendId,r.title,r.difficulty,i),console.log(),console.log(l.bold.cyan("\u23F1\uFE0F Interview Mode Started!")),console.log(l.gray("\u2500".repeat(50))),console.log(),console.log(l.white(`Problem: ${r.questionFrontendId}. ${r.title}`)),console.log(l.white(`Difficulty: ${l.bold(r.difficulty)}`)),console.log(l.white(`Time Limit: ${l.bold.yellow(i+" minutes")}`)),console.log(),console.log(l.gray("\u2500".repeat(50))),console.log(l.green("\u2713 Timer is running in background")),console.log(l.gray(" When you submit successfully, your time will be recorded.")),console.log(l.gray(" Use `leetcode timer --stop` to cancel.")),console.log(),await Y(e,{open:!0});}catch(r){s.fail("Failed to start timer"),r instanceof Error&&console.log(l.red(r.message));}}async function xs(){let e=B.stopTimer();if(!e){console.log(l.yellow("No active timer to stop."));return}console.log(l.green("\u23F1\uFE0F Timer stopped.")),console.log(l.gray(`Elapsed time: ${pe(e.durationSeconds)}`)),console.log(l.gray("(Time not recorded since problem was not submitted)"));}async function Es(e){if(e&&/^\d+$/.test(e)){let o=B.getSolveTimes(e);if(o.length===0){console.log(l.yellow(`No solve times recorded for problem ${e}`));return}console.log(),console.log(l.bold(`\u23F1\uFE0F Solve Times for Problem ${e}`)),console.log(l.gray("\u2500".repeat(40)));for(let t of o){let n=new Date(t.solvedAt).toLocaleDateString(),s=pe(t.durationSeconds),r=t.timerMinutes,i=t.durationSeconds<=r*60;console.log(` ${n} ${i?l.green(s):l.red(s)} (limit: ${r}m)`);}}else {let o=B.getStats(),t=B.getAllSolveTimes();console.log(),console.log(l.bold("\u23F1\uFE0F Timer Statistics")),console.log(l.gray("\u2500".repeat(40))),console.log(),console.log(` Problems timed: ${l.cyan(o.totalProblems)}`),console.log(` Total time: ${l.cyan(pe(o.totalTime))}`),console.log(` Average time: ${l.cyan(pe(o.avgTime))}`),console.log();let n=[];for(let[s,r]of Object.entries(t))for(let i of r)n.push({problemId:s,title:i.title,duration:i.durationSeconds,date:i.solvedAt});if(n.length>0){n.sort((s,r)=>new Date(r.date).getTime()-new Date(s.date).getTime()),console.log(l.bold(" Recent Solves:"));for(let s of n.slice(0,5)){let r=new Date(s.date).toLocaleDateString();console.log(l.gray(` ${r} `)+l.white(`${s.problemId}. ${s.title.substring(0,25)}`)+l.gray(" ")+l.cyan(pe(s.duration)));}}console.log();}}var As="https://abagrmwdpvnfyuqizyym.supabase.co",Rs="sb_publishable_indrKu8VJmASdyLp7w8Hog_OyqT17cV",ne=createClient(As,Rs);function to(){return $.getCollabPath()}function Us(){let e=to();return existsSync(e)?JSON.parse(readFileSync(e,"utf-8")):{session:null}}function js(e){let o=to(),t=dirname(o);existsSync(t)||mkdirSync(t,{recursive:true}),writeFileSync(o,JSON.stringify(e,null,2));}var X={getSession(){return Us().session},setSession(e){js({session:e});},getPath(){return to()}};function Ms(){let e="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",o="";for(let t=0;t<6;t++)o+=e.charAt(Math.floor(Math.random()*e.length));return o}var G={getSession(){return X.getSession()},async createRoom(e,o){let t=Ms(),{error:n}=await ne.from("collab_rooms").insert({room_code:t,problem_id:e,host_username:o,host_code:"",guest_username:null,guest_code:null});return n?{error:n.message}:(X.setSession({roomCode:t,problemId:e,isHost:true,username:o}),{roomCode:t})},async joinRoom(e,o){let{data:t,error:n}=await ne.from("collab_rooms").select("*").eq("room_code",e.toUpperCase()).single();if(n||!t)return {error:"Room not found"};let{error:s}=await ne.from("collab_rooms").update({guest_username:o}).eq("room_code",e.toUpperCase());return s?{error:s.message}:(X.setSession({roomCode:e.toUpperCase(),problemId:t.problem_id,isHost:false,username:o}),{problemId:t.problem_id})},async syncCode(e){let o=X.getSession();if(!o)return {success:false,error:"No active session"};let t=o.isHost?"host_code":"guest_code",{error:n}=await ne.from("collab_rooms").update({[t]:e}).eq("room_code",o.roomCode);return n?{success:false,error:n.message}:{success:true}},async getPartnerCode(){let e=X.getSession();if(!e)return {error:"No active session"};let{data:o,error:t}=await ne.from("collab_rooms").select("*").eq("room_code",e.roomCode).single();return t||!o?{error:"Room not found"}:e.isHost?{code:o.guest_code||"",username:o.guest_username||"Partner"}:{code:o.host_code||"",username:o.host_username||"Host"}},async getRoomStatus(){let e=X.getSession();if(!e)return {error:"No active session"};let{data:o,error:t}=await ne.from("collab_rooms").select("*").eq("room_code",e.roomCode).single();return t||!o?{error:"Room not found"}:{host:o.host_username,guest:o.guest_username,hasHostCode:!!o.host_code,hasGuestCode:!!o.guest_code}},async leaveRoom(){let e=X.getSession();e&&e.isHost&&await ne.from("collab_rooms").delete().eq("room_code",e.roomCode),X.setSession(null);}};async function xt(e){let{authorized:o,username:t}=await v();if(!o||!t)return;let n=Ie("Creating collaboration room...").start();try{let s=await G.createRoom(e,t);if("error"in s){n.fail(s.error);return}n.succeed("Room created!"),console.log(),console.log(l.bold.cyan("\u{1F465} Collaborative Coding Session")),console.log(l.gray("\u2500".repeat(50))),console.log(),console.log(l.white(`Room Code: ${l.bold.green(s.roomCode)}`)),console.log(l.white(`Problem: ${e}`)),console.log(),console.log(l.gray("Share this code with your partner:")),console.log(l.yellow(` leetcode collab join ${s.roomCode}`)),console.log(),console.log(l.gray("\u2500".repeat(50))),console.log(l.gray("After solving, sync and compare:")),console.log(l.gray(" leetcode collab sync - Upload your solution")),console.log(l.gray(" leetcode collab compare - See both solutions")),console.log(l.gray(" leetcode collab status - Check room status")),console.log(l.gray(" leetcode collab leave - End session")),console.log(),await Y(e,{open:!0});}catch(s){n.fail("Failed to create room"),s instanceof Error&&console.log(l.red(s.message));}}async function Et(e){let{authorized:o,username:t}=await v();if(!o||!t)return;let n=Ie(`Joining room ${e}...`).start();try{let s=await G.joinRoom(e.toUpperCase(),t);if("error"in s){n.fail(s.error);return}n.succeed("Joined room!"),console.log(),console.log(l.bold.cyan("\u{1F465} Collaborative Coding Session")),console.log(l.gray("\u2500".repeat(50))),console.log(),console.log(l.white(`Room Code: ${l.bold.green(e.toUpperCase())}`)),console.log(l.white(`Problem: ${s.problemId}`)),console.log(),console.log(l.gray("\u2500".repeat(50))),console.log(l.gray("After solving, sync and compare:")),console.log(l.gray(" leetcode collab sync - Upload your solution")),console.log(l.gray(" leetcode collab compare - See both solutions")),console.log(l.gray(" leetcode collab status - Check room status")),console.log(l.gray(" leetcode collab leave - End session")),console.log(),await Y(s.problemId,{open:!0});}catch(s){n.fail("Failed to join room"),s instanceof Error&&console.log(l.red(s.message));}}async function Lt(){let e=G.getSession();if(!e){console.log(l.yellow("No active collaboration session.")),console.log(l.gray("Use `leetcode collab host <id>` or `leetcode collab join <code>` first."));return}let o=Ie("Syncing your code...").start(),t=w.getWorkDir(),n=await O(t,e.problemId);if(!n){o.fail(`No solution file found for problem ${e.problemId}`);return}let s=await readFile(n,"utf-8"),r=await G.syncCode(s);r.success?(o.succeed("Code synced successfully!"),console.log(l.gray(`Uploaded ${s.split(`
229
+ `).length} lines from ${n}`))):o.fail(r.error||"Sync failed");}async function At(){let e=G.getSession();if(!e){console.log(l.yellow("No active collaboration session.")),console.log(l.gray("Use `leetcode collab host <id>` or `leetcode collab join <code>` first."));return}let o=Ie("Fetching solutions...").start(),t=w.getWorkDir(),n=await O(t,e.problemId);if(!n){o.fail(`No solution file found for problem ${e.problemId}`);return}let s=await readFile(n,"utf-8"),r=await G.getPartnerCode();if("error"in r){o.fail(r.error);return}if(o.stop(),!r.code){console.log(l.yellow("Partner has not synced their code yet.")),console.log(l.gray("Ask them to run `leetcode collab sync`."));return}console.log(),console.log(l.bold.cyan("\u{1F4CA} Solution Comparison")),console.log(l.gray("\u2500".repeat(60))),console.log(),console.log(l.bold.green(`\u25B8 Your Solution (${e.username})`)),console.log(l.gray("\u2500".repeat(60)));let i=s.split(`
230
+ `);for(let c=0;c<i.length;c++){let g=String(c+1).padStart(3," ");console.log(`${l.gray(g)} ${i[c]}`);}console.log(),console.log(l.bold.blue(`\u25B8 ${r.username}'s Solution`)),console.log(l.gray("\u2500".repeat(60)));let a=r.code.split(`
231
+ `);for(let c=0;c<a.length;c++){let g=String(c+1).padStart(3," ");console.log(`${l.gray(g)} ${a[c]}`);}console.log(),console.log(l.gray("\u2500".repeat(60))),console.log(l.gray(`Your code: ${i.length} lines | Partner: ${a.length} lines`)),console.log();}async function Rt(){if(!G.getSession()){console.log(l.yellow("No active collaboration session."));return}await G.leaveRoom(),console.log(l.green("\u2713 Left the collaboration session."));}async function _t(){let e=G.getSession();if(!e){console.log(l.yellow("No active collaboration session.")),console.log(l.gray("Use `leetcode collab host <id>` or `leetcode collab join <code>` to start."));return}let o=await G.getRoomStatus();if("error"in o){console.log(l.red(o.error));return}console.log(),console.log(l.bold.cyan("\u{1F465} Collaboration Status")),console.log(l.gray("\u2500".repeat(40))),console.log(` Room: ${l.green(e.roomCode)}`),console.log(` Problem: ${e.problemId}`),console.log(` Role: ${e.isHost?"Host":"Guest"}`),console.log(),console.log(l.bold(" Participants:")),console.log(` Host: ${o.host} ${o.hasHostCode?l.green("\u2713 synced"):l.gray("pending")}`),console.log(` Guest: ${o.guest||l.gray("(waiting...)")} ${o.hasGuestCode?l.green("\u2713 synced"):l.gray("pending")}`),console.log();}function Ws(){return $.getSnapshotsDir()}function Ut(e){return join(Ws(),e)}function jt(e){return join(Ut(e),"meta.json")}function Ue(e){return join(Ut(e),"files")}function Mt(e){let o=Ue(e);existsSync(o)||mkdirSync(o,{recursive:true});}function ke(e){let o=jt(e);return existsSync(o)?JSON.parse(readFileSync(o,"utf-8")):{problemId:e,problemTitle:"",snapshots:[]}}function Ft(e,o){Mt(e),writeFileSync(jt(e),JSON.stringify(o,null,2));}var W={save(e,o,t,n,s){Mt(e);let r=ke(e);o&&(r.problemTitle=o);let i=r.snapshots.length>0?Math.max(...r.snapshots.map(C=>C.id))+1:1,a=s||`snapshot-${i}`,c=r.snapshots.find(C=>C.name===a);if(c)return {error:`Snapshot with name "${a}" already exists (ID: ${c.id})`};let g=ge[n]||n,u=`${i}_${a}.${g}`,p=join(Ue(e),u);writeFileSync(p,t,"utf-8");let b={id:i,name:a,fileName:u,language:n,lines:t.split(`
232
+ `).length,createdAt:new Date().toISOString()};return r.snapshots.push(b),Ft(e,r),b},list(e){return ke(e).snapshots},getMeta(e){return ke(e)},get(e,o){let t=ke(e),n=t.snapshots.find(r=>r.id===parseInt(o,10));return n||t.snapshots.find(r=>r.name===o)||null},getCode(e,o){let t=join(Ue(e),o.fileName);if(!existsSync(t))throw new Error(`Snapshot file not found: ${o.fileName}`);return readFileSync(t,"utf-8")},delete(e,o){let t=ke(e),n=this.get(e,o);if(!n)return false;let s=join(Ue(e),n.fileName);return existsSync(s)&&unlinkSync(s),t.snapshots=t.snapshots.filter(r=>r.id!==n.id),Ft(e,t),true},hasSnapshots(e){return this.list(e).length>0}};function Bs(e){let o=new Date(e),n=new Date().getTime()-o.getTime(),s=Math.floor(n/6e4),r=Math.floor(s/60),i=Math.floor(r/24);return s<1?"just now":s<60?`${s}m ago`:r<24?`${r}h ago`:`${i}d ago`}async function Wt(e,o){let t=w.getWorkDir();try{let n=await O(t,e);if(!n){console.log(l.red(`No solution file found for problem ${e}`)),console.log(l.gray("Run `leetcode pick "+e+"` first to create a solution file."));return}let s=await readFile(n,"utf-8"),r=extname(n).slice(1),i=ae(r)||r,c=basename(n).match(/^\d+\.(.+)\.\w+$/),g=c?c[1]:"",u=W.save(e,g,s,i,o);if("error"in u){console.log(l.red("\u2717 "+u.error));return}let p=u;console.log(l.green("\u2713 Snapshot saved!")),console.log(),console.log(` ID: ${l.cyan(p.id)}`),console.log(` Name: ${l.white(p.name)}`),console.log(` Lines: ${l.gray(p.lines)}`),console.log(` File: ${l.gray(n)}`);}catch(n){console.log(l.red("Failed to save snapshot")),n instanceof Error&&console.log(l.gray(n.message));}}async function Ht(e){let o=W.list(e);if(o.length===0){console.log(l.yellow(`No snapshots found for problem ${e}`)),console.log(l.gray("Use `leetcode snapshot save "+e+"` to create one."));return}let t=W.getMeta(e);console.log(),console.log(l.bold(`\u{1F4F8} Snapshots for Problem ${e}`)),t.problemTitle&&console.log(l.gray(` ${t.problemTitle}`)),console.log(l.gray("\u2500".repeat(50))),console.log();for(let n of o){let s=Bs(n.createdAt);console.log(` ${l.cyan(n.id.toString().padStart(2))}. ${l.white(n.name.padEnd(25))} ${l.gray(n.lines+" lines")} ${l.gray("\xB7")} ${l.gray(s)}`);}console.log(),console.log(l.gray("Commands:")),console.log(l.gray(` restore: leetcode snapshot restore ${e} <id|name>`)),console.log(l.gray(` diff: leetcode snapshot diff ${e} <id1> <id2>`)),console.log(l.gray(` delete: leetcode snapshot delete ${e} <id|name>`));}async function Vt(e,o){let t=w.getWorkDir();try{let n=W.get(e,o);if(!n){console.log(l.red(`Snapshot "${o}" not found for problem ${e}`)),console.log(l.gray("Run `leetcode snapshot list "+e+"` to see available snapshots."));return}let s=await O(t,e);if(!s){console.log(l.red(`No solution file found for problem ${e}`));return}let r=await readFile(s,"utf-8"),i=`backup-before-restore-${Date.now()}`,a=extname(s).slice(1),c=ae(a)||a;W.save(e,"",r,c,i);let g=W.getCode(e,n);await writeFile(s,g,"utf-8"),console.log(l.green("\u2713 Snapshot restored!")),console.log(),console.log(` Restored: ${l.cyan(n.name)} (${n.lines} lines)`),console.log(` File: ${l.gray(s)}`),console.log(` Backup: ${l.gray(i)}`);}catch(n){console.log(l.red("Failed to restore snapshot")),n instanceof Error&&console.log(l.gray(n.message));}}async function zt(e,o,t){try{let n=W.get(e,o),s=W.get(e,t);if(!n){console.log(l.red(`Snapshot "${o}" not found`));return}if(!s){console.log(l.red(`Snapshot "${t}" not found`));return}let r=W.getCode(e,n),i=W.getCode(e,s);console.log(),console.log(l.bold(`\u{1F4CA} Diff: ${n.name} \u2192 ${s.name}`)),console.log(l.gray("\u2500".repeat(50))),console.log();let a=diffLines(r,i),c=0,g=0;for(let u of a){let p=u.value.split(`
233
+ `).filter(b=>b!=="");if(u.added){c+=p.length;for(let b of p)console.log(l.green("+ "+b));}else if(u.removed){g+=p.length;for(let b of p)console.log(l.red("- "+b));}else if(p.length<=4)for(let b of p)console.log(l.gray(" "+b));else console.log(l.gray(" "+p[0])),console.log(l.gray(" "+p[1])),console.log(l.gray(` ... (${p.length-4} more lines)`)),console.log(l.gray(" "+p[p.length-2])),console.log(l.gray(" "+p[p.length-1]));}console.log(),console.log(l.gray("\u2500".repeat(50))),console.log(`${l.green("+"+c+" added")} ${l.gray("\xB7")} ${l.red("-"+g+" removed")} ${l.gray("\xB7")} ${l.gray(n.lines+" \u2192 "+s.lines+" lines")}`);}catch(n){console.log(l.red("Failed to diff snapshots")),n instanceof Error&&console.log(l.gray(n.message));}}async function Bt(e,o){let t=W.get(e,o);if(!t){console.log(l.red(`Snapshot "${o}" not found for problem ${e}`));return}let n=W.delete(e,o);console.log(n?l.green(`\u2713 Deleted snapshot: ${t.name}`):l.red("Failed to delete snapshot"));}function Yt(e,o){let t=e.split(`
234
+ `),n=t.length;console.log(),console.log(l.bold.cyan(`=== ${o} (${n} lines) ===`)),console.log(l.gray("\u2500".repeat(60))),t.forEach((s,r)=>{let i=(r+1).toString().padStart(3);console.log(l.gray(i+" \u2502 ")+s);});}function Js(e,o,t,n){let s=e.split(`
235
+ `).length,r=o.split(`
236
+ `).length;console.log(),console.log(l.bold(`\u{1F4CA} Unified Diff: ${t} \u2192 ${n}`)),console.log(l.gray("\u2500".repeat(60))),console.log();let i=diffLines(e,o),a=0,c=0;for(let g of i){let u=g.value.split(`
237
+ `).filter(p=>p!=="");if(g.added){a+=u.length;for(let p of u)console.log(l.green("+ "+p));}else if(g.removed){c+=u.length;for(let p of u)console.log(l.red("- "+p));}else if(u.length<=6)for(let p of u)console.log(l.gray(" "+p));else console.log(l.gray(" "+u[0])),console.log(l.gray(" "+u[1])),console.log(l.dim(` ... (${u.length-4} unchanged lines)`)),console.log(l.gray(" "+u[u.length-2])),console.log(l.gray(" "+u[u.length-1]));}console.log(),console.log(l.gray("\u2500".repeat(60))),console.log(`${l.green("+"+a+" added")} ${l.gray("\xB7")} ${l.red("-"+c+" removed")} ${l.gray("\xB7")} ${l.gray(s+" \u2192 "+r+" lines")}`);}function no(e,o,t,n,s){if(s)Js(e,o,t,n);else {Yt(e,t),Yt(o,n);let r=diffLines(e,o),i=0,a=0;for(let c of r){let g=c.value.split(`
238
+ `).filter(u=>u!=="");c.added?i+=g.length:c.removed&&(a+=g.length);}console.log(),console.log(l.gray("\u2500".repeat(60))),console.log(`${l.bold("Summary:")} ${l.green("+"+i+" added")} ${l.gray("\xB7")} ${l.red("-"+a+" removed")}`),console.log(l.gray("Tip: Use --unified for line-by-line diff"));}}async function Qt(e,o){let{authorized:t}=await v();if(!t)return;let n=w.getWorkDir(),s=Ie("Finding solution file...").start();try{let r=await O(n,e);if(!r){s.fail(`No solution file found for problem ${e}`),console.log(l.gray("Run `leetcode pick "+e+"` first to create a solution file."));return}let i=await readFile(r,"utf-8");if(s.text="Fetching comparison target...",o.file){if(s.stop(),!existsSync(o.file)){console.log(l.red(`File not found: ${o.file}`));return}if(!me(o.file,n)){console.log(l.red("\u26A0\uFE0F Security Error: File path is outside the configured workspace")),console.log(l.gray(`File: ${o.file}`)),console.log(l.gray(`Workspace: ${n}`)),console.log(l.yellow(`
239
+ For security reasons, you can only diff files from within your workspace.`));return}let p=await readFile(o.file,"utf-8");no(i,p,"Your Solution",o.file,o.unified??!1);return}let a=await h.getProblemById(e);if(!a){s.fail(`Problem ${e} not found`);return}if(o.submission){let p=parseInt(o.submission,10),b=await h.getSubmissionDetails(p);s.stop(),no(i,b.code,"Your Solution",`Submission #${p}`,o.unified??!1);return}let g=(await h.getSubmissionList(a.titleSlug,50)).find(p=>p.statusDisplay==="Accepted");if(!g){s.fail("No accepted submissions found for this problem"),console.log(l.gray("Tip: Use --file to compare with a local file instead"));return}let u=await h.getSubmissionDetails(parseInt(g.id,10));s.stop(),no(i,u.code,"Your Solution","Last Accepted",o.unified??!1);}catch(r){s.fail("Failed to diff"),r instanceof Error&&console.log(l.red(r.message));}}async function Kt(){let e=$.getActive(),o=$.getConfig(e);console.log(),console.log(l.bold.cyan(`\u{1F4C1} Active Workspace: ${e}`)),console.log(l.gray("\u2500".repeat(40))),console.log(` workDir: ${l.white(o.workDir)}`),console.log(` lang: ${l.white(o.lang)}`),o.editor&&console.log(` editor: ${l.white(o.editor)}`),o.syncRepo&&console.log(` syncRepo: ${l.white(o.syncRepo)}`),console.log();}async function Xt(){let e=$.list(),o=$.getActive();console.log(),console.log(l.bold("Workspaces:")),console.log();for(let t of e){let n=$.getConfig(t),s=t===o?l.green("\u25B8 "):" ",r=t===o?l.green.bold(t):t;console.log(`${s}${r}`),console.log(` ${l.gray(n.workDir)}`);}console.log();}async function Zt(e,o){if($.exists(e)){console.log(l.red(`Workspace "${e}" already exists`));return}let t=o.workdir??join(homedir(),"leetcode",e),n={workDir:t,lang:"typescript"};$.create(e,n)?(console.log(l.green(`\u2713 Created workspace "${e}"`)),console.log(` workDir: ${l.gray(t)}`),console.log(),console.log(l.gray(`Switch to it: leetcode workspace use ${e}`))):console.log(l.red("Failed to create workspace"));}async function en(e){if(!$.exists(e)){console.log(l.red(`Workspace "${e}" not found`)),console.log(l.gray("Use `leetcode workspace list` to see available workspaces"));return}if($.setActive(e)){let t=$.getConfig(e);console.log(l.green(`\u2713 Switched to workspace "${e}"`)),console.log(` workDir: ${l.gray(t.workDir)}`);}else console.log(l.red("Failed to switch workspace"));}async function on(e){if(e==="default"){console.log(l.red("Cannot delete the default workspace"));return}if(!$.exists(e)){console.log(l.red(`Workspace "${e}" not found`));return}let{confirmed:o}=await eo.prompt([{type:"confirm",name:"confirmed",message:`Delete workspace "${e}"? (files in workDir will NOT be deleted)`,default:false}]);if(!o){console.log(l.gray("Cancelled"));return}let t=$.delete(e);console.log(t?l.green(`\u2713 Deleted workspace "${e}"`):l.red("Failed to delete workspace"));}var so=join(homedir(),".leetcode"),ve=join(so,"version-cache.json"),sr=1440*60*1e3;function rr(){existsSync(so)||mkdirSync(so,{recursive:true});}function tn(){if(existsSync(ve))try{return JSON.parse(readFileSync(ve,"utf-8"))}catch{return null}return null}function ir(e){rr(),writeFileSync(ve,JSON.stringify(e,null,2));}var se={shouldCheck(){let e=tn();return e?Date.now()-e.lastCheck>sr:true},getCached(){return tn()},updateCache(e,o){ir({lastCheck:Date.now(),latestVersion:e,hasBreakingChanges:o});},clearCache(){existsSync(ve)&&unlinkSync(ve);}};var ur=fileURLToPath(import.meta.url),pr=dirname(ur),fr="https://registry.npmjs.org/@night-slayer18/leetcode-cli/latest",yr="@night-slayer18/leetcode-cli";function ye(){try{let e=join(pr,"../package.json");return JSON.parse(readFileSync(e,"utf-8")).version}catch{return "0.0.0"}}async function rn(){let o=(await pn(fr,{timeout:{request:1e4},retry:{limit:2}}).json()).version,t=ye(),n=parseInt(t.split(".")[0]),r=parseInt(o.split(".")[0])>n;return {version:o,hasBreakingChanges:r}}function io(e,o){let t=e.split(".").map(Number),n=o.split(".").map(Number);for(let s=0;s<3;s++){if(n[s]>t[s])return true;if(n[s]<t[s])return false}return false}function hr(e,o,t){console.log();let n=60,s="\u256D"+"\u2500".repeat(n-2)+"\u256E",r="\u2570"+"\u2500".repeat(n-2)+"\u256F";console.log(l.cyan(s));let i=` \u{1F680} Update available: ${l.gray(e)} \u2192 ${l.green(o)}`;if(console.log(l.cyan("\u2502")+i.padEnd(n+18)+l.cyan("\u2502")),t){console.log(l.cyan("\u2502")+"".padEnd(n-2)+l.cyan("\u2502"));let c=` ${l.yellow("\u26A0\uFE0F This update contains breaking changes!")}`;console.log(l.cyan("\u2502")+c.padEnd(n+20)+l.cyan("\u2502"));let g=` ${l.gray("Run:")} leetcode changelog ${l.gray("to review changes")}`;console.log(l.cyan("\u2502")+g.padEnd(n+16)+l.cyan("\u2502"));}console.log(l.cyan("\u2502")+"".padEnd(n-2)+l.cyan("\u2502"));let a=` ${l.gray("Run:")} npm update -g ${yr}`;console.log(l.cyan("\u2502")+a.padEnd(n+8)+l.cyan("\u2502")),console.log(l.cyan(r)),console.log();}async function an(e){let o=ye();e.force&&se.clearCache();let t=se.getCached(),n,s;if(t&&!se.shouldCheck()&&!e.force)n=t.latestVersion,s=t.hasBreakingChanges;else {let r=Ie("Checking for updates...").start();try{let i=await rn();n=i.version,s=i.hasBreakingChanges,se.updateCache(n,s),r.stop();}catch(i){r.fail("Failed to check for updates"),process.env.DEBUG&&console.log(l.gray(` Debug: ${i instanceof Error?i.message:String(i)}`)),console.log(l.gray(" Could not reach npm registry. Check your internet connection."));return}}io(o,n)?(hr(o,n,s),e.checkOnly||s&&(console.log(l.yellow("\u{1F4A1} Tip: Review the changelog before updating to check for breaking changes.")),console.log(l.gray(` Run: leetcode changelog
240
+ `)))):(console.log(),console.log(l.green("\u2713")+` You're on the latest version (${l.cyan(o)})`),console.log());}async function ln(){if(!se.shouldCheck()){let e=se.getCached();e&&io(ye(),e.latestVersion)&&sn(ye(),e.latestVersion,e.hasBreakingChanges);return}try{let e=await rn();se.updateCache(e.version,e.hasBreakingChanges),io(ye(),e.version)&&sn(ye(),e.version,e.hasBreakingChanges);}catch{}}function sn(e,o,t){console.log(),t?(console.log(l.yellow(`\u26A0\uFE0F Update ${e} \u2192 ${o} available (breaking changes!)`)),console.log(l.gray(" Run: leetcode changelog && leetcode update"))):(console.log(l.cyan(`\u{1F680} Update available: ${e} \u2192 ${o}`)),console.log(l.gray(" Run: leetcode update"))),console.log();}var Cr=fileURLToPath(import.meta.url),Pr=dirname(Cr),Tr="https://raw.githubusercontent.com/night-slayer18/leetcode-cli/main/docs/releases.md";function Dr(){try{let e=join(Pr,"../package.json");return JSON.parse(readFileSync(e,"utf-8")).version}catch{return "0.0.0"}}function xr(e,o){let t=e.replace("v","").split(".").map(Number),n=o.replace("v","").split(".").map(Number);for(let s=0;s<3;s++){if(t[s]>n[s])return true;if(t[s]<n[s])return false}return false}async function Er(){return await pn(Tr,{timeout:{request:1e4},retry:{limit:2}}).text()}function Lr(e){let o=[],t=/^## (v[\d.]+)/gm,n=[...e.matchAll(t)];for(let s=0;s<n.length;s++){let r=n[s],i=r[1],a=r.index+r[0].length,c=s+1<n.length?n[s+1].index:e.length,g=e.slice(a,c).trim(),u=g.includes("\u26A0\uFE0F Breaking Change");o.push({version:i,content:g,hasBreakingChanges:u});}return o}function Ar(e,o,t){let n=t?l.bgRed.white.bold(` ${e} `)+l.red(" \u26A0\uFE0F BREAKING CHANGES"):l.bgCyan.black.bold(` ${e} `);console.log(n),console.log();let s=o.split(`
241
+ `),r=false;for(let i of s)if(!(i.trim()===""&&!r)){if(i.startsWith("> **Release Date**")){let a=i.replace("> **Release Date**: ","").trim();console.log(l.gray(` \u{1F4C5} ${a}`));continue}if(i.startsWith("> **Focus**")){let a=i.replace("> **Focus**: ","").trim();console.log(l.gray(` \u{1F3AF} ${a}`)),console.log();continue}if(i.startsWith("### ")){let a=i.replace("### ","").trim();if(console.log(),/^[\u{1F300}-\u{1F9FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}]/u.test(a))console.log(l.bold.yellow(` ${a}`));else {let g="\u{1F4CC}";a.includes("Breaking")?g="\u26A0\uFE0F":a.includes("Feature")||a.includes("New")?g="\u{1F680}":a.includes("Fix")||a.includes("Bug")?g="\u{1F41B}":a.includes("Security")?g="\u{1F512}":a.includes("Improvement")?g="\u2728":a.includes("Architecture")?g="\u{1F3D7}\uFE0F":a.includes("Testing")?g="\u{1F9EA}":a.includes("Config")&&(g="\u2699\uFE0F"),console.log(l.bold.yellow(` ${g} ${a}`));}r=true;continue}if(i.startsWith("#### ")){let a=i.replace("#### ","").trim();console.log(l.bold.white(` ${a}`));continue}if(i.startsWith("- **")){let a=i.match(/^- \*\*(.+?)\*\*:?\s*(.*)/);console.log(a?l.cyan(` \u2022 ${l.bold(a[1])}`)+(a[2]?l.white(`: ${a[2]}`):""):l.cyan(` \u2022 ${i.replace("- ","")}`));continue}if(i.startsWith("- ")){let a=i.replace("- ","").trim();console.log(l.white(` \u2022 ${a}`));continue}i.startsWith("---")||i.trim()===">"||i.trim()&&console.log(l.gray(` ${i.trim()}`));}}async function cn(e,o={}){let t=Ie("Fetching changelog...").start();try{let n=await Er();t.stop();let s=Lr(n);if(s.length===0){console.log(l.yellow("No release entries found."));return}let r=Dr(),i=s;if(e){let a=e.startsWith("v")?e:`v${e}`;if(i=s.filter(c=>c.version===a),i.length===0){console.log(l.red(`Version ${e} not found in changelog.`)),console.log(l.gray("Available versions: "+s.map(c=>c.version).join(", ")));return}}else if(o.latest)i=s.slice(0,1);else if(o.breaking){if(i=s.filter(a=>a.hasBreakingChanges),i.length===0){console.log(l.green("\u2713 No breaking changes in any release."));return}}else if(o.all)i=s;else {if(i=s.filter(a=>xr(a.version,r)),i.length===0){console.log(l.green(`\u2713 You're on the latest version (${r})`)),console.log(l.gray("Use --all to see the full changelog."));return}console.log(l.gray(`Showing changes since your version (${r})`)),console.log(l.gray("Use --all to see the full changelog."));}console.log(),console.log(l.bold.cyan("\u{1F4CB} LeetCode CLI Release Notes")),console.log(l.gray("\u2500".repeat(50))),console.log();for(let a of i)Ar(a.version,a.content,a.hasBreakingChanges),console.log(l.gray("\u2500".repeat(60))),console.log();if(!e&&!o.latest){let a=s.filter(c=>c.hasBreakingChanges).length;console.log(l.gray(`Showing ${i.length} of ${s.length} releases`)),a>0&&!o.breaking&&console.log(l.yellow(`${a} release(s) contain breaking changes. Use --breaking to filter.`));}}catch{t.fail("Failed to fetch changelog"),console.log(l.gray(" Could not fetch release notes from GitHub.")),console.log(l.gray(" Visit: https://github.com/night-slayer18/leetcode-cli/blob/main/docs/releases.md"));}}var T=new Command;T.configureHelp({sortSubcommands:true,subcommandTerm:e=>{let o=e.name(),t=e.alias(),n=t?`${o}|${t}`:o;return l.cyan(n.padEnd(16))},subcommandDescription:e=>l.white(e.description()),optionTerm:e=>l.yellow(e.flags),optionDescription:e=>l.white(e.description)});T.name("leetcode").usage("[command] [options]").description(l.bold.cyan("\u{1F525} A modern LeetCode CLI built with TypeScript")).version("2.1.0","-v, --version","Output the version number").helpOption("-h, --help","Display help for command").addHelpText("after",`
242
+ ${l.yellow("Examples:")}
243
+ ${l.cyan("$ leetcode login")} Login to LeetCode
244
+ ${l.cyan("$ leetcode list -d easy")} List easy problems
245
+ ${l.cyan("$ leetcode random -d medium")} Get random medium problem
246
+ ${l.cyan("$ leetcode pick 1")} Start solving "Two Sum"
247
+ ${l.cyan("$ leetcode test 1")} Test your solution
248
+ ${l.cyan("$ leetcode submit 1")} Submit your solution
249
+ `);T.command("login").description("Login to LeetCode with browser cookies").addHelpText("after",`
250
+ ${l.yellow("How to login:")}
251
+ 1. Open ${l.cyan("https://leetcode.com")} in your browser
3968
252
  2. Login to your account
3969
253
  3. Open Developer Tools (F12) \u2192 Application \u2192 Cookies
3970
- 4. Copy values of ${chalk26.green("LEETCODE_SESSION")} and ${chalk26.green("csrftoken")}
254
+ 4. Copy values of ${l.green("LEETCODE_SESSION")} and ${l.green("csrftoken")}
3971
255
  5. Paste when prompted by this command
3972
- `).action(loginCommand);
3973
- program.command("logout").description("Clear stored credentials").action(logoutCommand);
3974
- program.command("whoami").description("Check current login status").action(whoamiCommand);
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", `
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
3983
- `).action(listCommand);
3984
- program.command("show <id>").alias("s").description("Show problem description").addHelpText("after", `
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
3989
- `).action(showCommand);
3990
- program.command("daily").alias("d").description("Show today's daily challenge").addHelpText("after", `
3991
- ${chalk26.yellow("Examples:")}
3992
- ${chalk26.cyan("$ leetcode daily")} Show today's challenge
3993
- ${chalk26.cyan("$ leetcode d")} Short alias
3994
- `).action(dailyCommand);
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", `
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
4002
- `).action(randomCommand);
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", `
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/")}
4012
- `).action(async (id, options) => {
4013
- await pickCommand(id, options);
4014
- });
4015
- program.command("pick-batch <ids...>").description("Generate solution files for multiple problems").option("-l, --lang <language>", "Programming language for the solutions").addHelpText("after", `
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
4019
- `).action(batchPickCommand);
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.")}
4030
- `).action(testCommand);
4031
- program.command("submit <file>").alias("x").description("Submit solution to LeetCode").addHelpText("after", `
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
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);
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", `
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
4051
- `).action(submissionsCommand);
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", `
4053
- ${chalk26.yellow("Options Explained:")}
4054
- ${chalk26.cyan("-c, --calendar")} Shows a table of your weekly submissions and active days
256
+ `).action(Do);T.command("logout").description("Clear stored credentials").action(xo);T.command("whoami").description("Check current login status").action(Eo);T.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",`
257
+ ${l.yellow("Examples:")}
258
+ ${l.cyan("$ leetcode list")} List first 20 problems
259
+ ${l.cyan("$ leetcode list -d easy")} List easy problems only
260
+ ${l.cyan("$ leetcode list -s solved")} List your solved problems
261
+ ${l.cyan("$ leetcode list -t array -t string")} Filter by multiple tags
262
+ ${l.cyan('$ leetcode list -q "two sum"')} Search by keywords
263
+ ${l.cyan("$ leetcode list -n 50 -p 2")} Show 50 problems, page 2
264
+ `).action(Wo);T.command("show <id>").alias("s").description("Show problem description").addHelpText("after",`
265
+ ${l.yellow("Examples:")}
266
+ ${l.cyan("$ leetcode show 1")} Show by problem ID
267
+ ${l.cyan("$ leetcode show two-sum")} Show by problem slug
268
+ ${l.cyan("$ leetcode s 412")} Short alias
269
+ `).action(Ae);T.command("daily").alias("d").description("Show today's daily challenge").addHelpText("after",`
270
+ ${l.yellow("Examples:")}
271
+ ${l.cyan("$ leetcode daily")} Show today's challenge
272
+ ${l.cyan("$ leetcode d")} Short alias
273
+ `).action(mt);T.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",`
274
+ ${l.yellow("Examples:")}
275
+ ${l.cyan("$ leetcode random")} Get any random problem
276
+ ${l.cyan("$ leetcode random -d medium")} Random medium problem
277
+ ${l.cyan("$ leetcode random -t array")} Random array problem
278
+ ${l.cyan("$ leetcode random --pick")} Random + create file
279
+ ${l.cyan("$ leetcode r -d easy --pick")} Random easy + file
280
+ `).action(dt);T.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",`
281
+ ${l.yellow("Examples:")}
282
+ ${l.cyan("$ leetcode pick 1")} Pick by problem ID
283
+ ${l.cyan("$ leetcode pick two-sum")} Pick by problem slug
284
+ ${l.cyan("$ leetcode pick 1 -l python3")} Pick with specific language
285
+ ${l.cyan("$ leetcode pick 1 --no-open")} Create file without opening
286
+ ${l.cyan("$ leetcode p 412")} Short alias
287
+
288
+ ${l.gray("Files are organized by: workDir/Difficulty/Category/")}
289
+ `).action(async(e,o)=>{await Y(e,o);});T.command("pick-batch <ids...>").description("Generate solution files for multiple problems").option("-l, --lang <language>","Programming language for the solutions").addHelpText("after",`
290
+ ${l.yellow("Examples:")}
291
+ ${l.cyan("$ leetcode pick-batch 1 2 3")} Pick problems 1, 2, and 3
292
+ ${l.cyan("$ leetcode pick-batch 1 2 3 -l py")} Pick with Python
293
+ `).action(Jo);T.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",`
294
+ ${l.yellow("Examples:")}
295
+ ${l.cyan("$ leetcode test 1")} Test by problem ID
296
+ ${l.cyan("$ leetcode test two-sum")} Test by problem slug
297
+ ${l.cyan("$ leetcode test ./path/to/file.py")} Test by file path
298
+ ${l.cyan('$ leetcode test 1 -c "[1,2]\\n3"')} Test with custom case
299
+ ${l.cyan("$ leetcode test 1 --visualize")} Visual mode for debugging
300
+ ${l.cyan("$ leetcode t 412")} Short alias
301
+
302
+ ${l.gray("Testcases use \\n to separate multiple inputs.")}
303
+ `).action(tt);T.command("submit <file>").alias("x").description("Submit solution to LeetCode").addHelpText("after",`
304
+ ${l.yellow("Examples:")}
305
+ ${l.cyan("$ leetcode submit 1")} Submit by problem ID
306
+ ${l.cyan("$ leetcode submit two-sum")} Submit by problem slug
307
+ ${l.cyan("$ leetcode submit ./path/to/file.py")} Submit by file path
308
+ ${l.cyan("$ leetcode x 412")} Short alias
309
+ `).action(rt);T.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",`
310
+ ${l.yellow("Examples:")}
311
+ ${l.cyan("$ leetcode diff 1")} Compare with last accepted
312
+ ${l.cyan("$ leetcode diff 1 -u")} Show unified diff
313
+ ${l.cyan("$ leetcode diff 1 -s 12345")} Compare with specific submission
314
+ ${l.cyan("$ leetcode diff 1 -f other.py")} Compare with local file
315
+ `).action(Qt);T.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",`
316
+ ${l.yellow("Examples:")}
317
+ ${l.cyan("$ leetcode submissions 1")} View submissions for problem
318
+ ${l.cyan("$ leetcode submissions 1 -n 5")} Show last 5 submissions
319
+ ${l.cyan("$ leetcode submissions 1 --last")} Show last accepted submission
320
+ ${l.cyan("$ leetcode submissions 1 --download")} Download last accepted code
321
+ `).action(ft);T.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",`
322
+ ${l.yellow("Options Explained:")}
323
+ ${l.cyan("-c, --calendar")} Shows a table of your weekly submissions and active days
4055
324
  for the past 12 weeks. Useful for tracking consistency.
4056
325
 
4057
- ${chalk26.cyan("-s, --skills")} Shows how many problems you solved per topic tag,
326
+ ${l.cyan("-s, --skills")} Shows how many problems you solved per topic tag,
4058
327
  grouped by difficulty (Fundamental/Intermediate/Advanced).
4059
328
  Helps identify your strong and weak areas.
4060
329
 
4061
- ${chalk26.cyan("-t, --trend")} Shows a bar chart of daily submissions for the past week.
330
+ ${l.cyan("-t, --trend")} Shows a bar chart of daily submissions for the past week.
4062
331
  Visualizes your recent coding activity day by day.
4063
332
 
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
4070
- `).action((username, options) => statCommand(username, options));
4071
- program.command("today").description("Show today's progress summary").addHelpText("after", `
4072
- ${chalk26.yellow("Examples:")}
4073
- ${chalk26.cyan("$ leetcode today")} Show streak, solved, and daily challenge
4074
- `).action(todayCommand);
4075
- program.command("bookmark <action> [id]").description("Manage problem bookmarks").addHelpText("after", `
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
4086
- `).action(bookmarkCommand);
4087
- program.command("note <id> [action]").description("View or edit notes for a problem").addHelpText("after", `
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
4096
- `).action(notesCommand);
4097
- program.command("sync").description("Sync solutions to Git repository").addHelpText("after", `
4098
- ${chalk26.yellow("Examples:")}
4099
- ${chalk26.cyan("$ leetcode sync")} Sync all solutions to remote
4100
- `).action(syncCommand);
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", `
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")}
4111
- `).action(async (options) => {
4112
- if (options.interactive) {
4113
- await configInteractiveCommand();
4114
- } else {
4115
- await configCommand(options);
4116
- }
4117
- });
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", `
4119
- ${chalk26.yellow("How it works:")}
333
+ ${l.yellow("Examples:")}
334
+ ${l.cyan("$ leetcode stat")} Show basic stats (solved count, rank)
335
+ ${l.cyan("$ leetcode stat lee215")} Show another user's stats
336
+ ${l.cyan("$ leetcode stat -c")} Weekly activity table
337
+ ${l.cyan("$ leetcode stat -s")} Topic-wise breakdown
338
+ ${l.cyan("$ leetcode stat -t")} 7-day trend chart
339
+ `).action((e,o)=>gt(e,o));T.command("today").description("Show today's progress summary").addHelpText("after",`
340
+ ${l.yellow("Examples:")}
341
+ ${l.cyan("$ leetcode today")} Show streak, solved, and daily challenge
342
+ `).action(kt);T.command("bookmark <action> [id]").description("Manage problem bookmarks").addHelpText("after",`
343
+ ${l.yellow("Actions:")}
344
+ ${l.cyan("add <id>")} Bookmark a problem
345
+ ${l.cyan("remove <id>")} Remove a bookmark
346
+ ${l.cyan("list")} List all bookmarks
347
+ ${l.cyan("clear")} Clear all bookmarks
348
+
349
+ ${l.yellow("Examples:")}
350
+ ${l.cyan("$ leetcode bookmark add 1")} Bookmark problem 1
351
+ ${l.cyan("$ leetcode bookmark remove 1")} Remove bookmark
352
+ ${l.cyan("$ leetcode bookmark list")} List all bookmarks
353
+ `).action(wt);T.command("note <id> [action]").description("View or edit notes for a problem").addHelpText("after",`
354
+ ${l.yellow("Actions:")}
355
+ ${l.cyan("edit")} Open notes in editor (default)
356
+ ${l.cyan("view")} Display notes in terminal
357
+
358
+ ${l.yellow("Examples:")}
359
+ ${l.cyan("$ leetcode note 1")} Edit notes for problem 1
360
+ ${l.cyan("$ leetcode note 1 edit")} Edit notes (explicit)
361
+ ${l.cyan("$ leetcode note 1 view")} View notes in terminal
362
+ `).action($t);T.command("sync").description("Sync solutions to Git repository").addHelpText("after",`
363
+ ${l.yellow("Examples:")}
364
+ ${l.cyan("$ leetcode sync")} Sync all solutions to remote
365
+ `).action(Ct);T.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",`
366
+ ${l.yellow("Examples:")}
367
+ ${l.cyan("$ leetcode config")} View current config
368
+ ${l.cyan("$ leetcode config -l python3")} Set language to Python
369
+ ${l.cyan('$ leetcode config -e "code"')} Set editor to VS Code
370
+ ${l.cyan("$ leetcode config -w ~/leetcode")} Set solutions folder
371
+ ${l.cyan("$ leetcode config -r https://...")} Set git repository
372
+ ${l.cyan("$ leetcode config -i")} Interactive setup
373
+
374
+ ${l.gray("Supported languages: typescript, javascript, python3, java, cpp, c, csharp, go, rust, kotlin, swift")}
375
+ `).action(async e=>{e.interactive?await ht():await yt(e);});T.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",`
376
+ ${l.yellow("How it works:")}
4120
377
  Start a problem with a countdown timer to simulate interview conditions.
4121
378
  Default time limits: Easy (20 min), Medium (40 min), Hard (60 min).
4122
379
  Your solve times are recorded when you submit successfully.
4123
380
 
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
4129
- `).action((id, options) => timerCommand(id, options));
4130
- var workspaceCmd = program.command("workspace").description("Manage workspaces for different contexts");
4131
- workspaceCmd.command("current").description("Show current workspace").action(workspaceCurrentCommand);
4132
- workspaceCmd.command("list").description("List all workspaces").action(workspaceListCommand);
4133
- workspaceCmd.command("create <name>").description("Create a new workspace").option("-w, --workdir <path>", "Set working directory for this workspace").action(workspaceCreateCommand);
4134
- workspaceCmd.command("use <name>").description("Switch to a workspace").action(workspaceUseCommand);
4135
- workspaceCmd.command("delete <name>").description("Delete a workspace").action(workspaceDeleteCommand);
4136
- var collabCmd = program.command("collab").description("Collaborative coding with a partner").addHelpText("after", `
4137
- ${chalk26.yellow("Subcommands:")}
4138
- ${chalk26.cyan("host <id>")} Create a room and get a code to share
4139
- ${chalk26.cyan("join <code>")} Join a room with the shared code
4140
- ${chalk26.cyan("sync")} Upload your solution to the room
4141
- ${chalk26.cyan("compare")} View both solutions side by side
4142
- ${chalk26.cyan("status")} Check room and sync status
4143
- ${chalk26.cyan("leave")} End the collaboration session
4144
-
4145
- ${chalk26.yellow("Examples:")}
4146
- ${chalk26.gray("$ leetcode collab host 1")} Start a session for Two Sum
4147
- ${chalk26.gray("$ leetcode collab join ABC123")} Join your partner's session
4148
- ${chalk26.gray("$ leetcode collab sync")} Upload your code after solving
4149
- ${chalk26.gray("$ leetcode collab compare")} Compare solutions
4150
- `);
4151
- collabCmd.command("host <problemId>").description("Host a collaboration session").action(collabHostCommand);
4152
- collabCmd.command("join <roomCode>").description("Join a collaboration session").action(collabJoinCommand);
4153
- collabCmd.command("sync").description("Sync your code with partner").action(collabSyncCommand);
4154
- collabCmd.command("compare").description("Compare your solution with partner").action(collabCompareCommand);
4155
- collabCmd.command("leave").description("Leave the collaboration session").action(collabLeaveCommand);
4156
- collabCmd.command("status").description("Show collaboration status").action(collabStatusCommand);
4157
- var snapshotCmd = program.command("snapshot").description("Save and restore solution versions").addHelpText("after", `
4158
- ${chalk26.yellow("Subcommands:")}
4159
- ${chalk26.cyan("save <id> [name]")} Save current solution as a snapshot
4160
- ${chalk26.cyan("list <id>")} List all snapshots for a problem
4161
- ${chalk26.cyan("restore <id> <snapshot>")} Restore a snapshot
4162
- ${chalk26.cyan("diff <id> <s1> <s2>")} Compare two snapshots
4163
- ${chalk26.cyan("delete <id> <snapshot>")} Delete a snapshot
4164
-
4165
- ${chalk26.yellow("Examples:")}
4166
- ${chalk26.gray('$ leetcode snapshot save 1 "brute-force"')} Save current solution
4167
- ${chalk26.gray("$ leetcode snapshot list 1")} List snapshots
4168
- ${chalk26.gray("$ leetcode snapshot restore 1 2")} Restore snapshot #2
4169
- ${chalk26.gray("$ leetcode snapshot diff 1 1 2")} Compare snapshots
4170
- `);
4171
- snapshotCmd.command("save <id> [name]").description("Save current solution as a snapshot").action(snapshotSaveCommand);
4172
- snapshotCmd.command("list <id>").description("List all snapshots for a problem").action(snapshotListCommand);
4173
- snapshotCmd.command("restore <id> <snapshot>").description("Restore a snapshot").action(snapshotRestoreCommand);
4174
- snapshotCmd.command("diff <id> <snap1> <snap2>").description("Compare two snapshots").action(snapshotDiffCommand);
4175
- snapshotCmd.command("delete <id> <snapshot>").description("Delete a snapshot").action(snapshotDeleteCommand);
4176
- program.showHelpAfterError("(add --help for additional information)");
4177
- program.parse();
4178
- if (!process.argv.slice(2).length) {
4179
- console.log();
4180
- console.log(chalk26.bold.cyan(" \u{1F525} LeetCode CLI"));
4181
- console.log(chalk26.gray(" A modern command-line interface for LeetCode"));
4182
- console.log();
4183
- program.outputHelp();
4184
- }
381
+ ${l.yellow("Examples:")}
382
+ ${l.cyan("$ leetcode timer 1")} Start problem 1 with default time
383
+ ${l.cyan("$ leetcode timer 1 -m 30")} Start with 30 minute limit
384
+ ${l.cyan("$ leetcode timer --stats")} Show your solve time statistics
385
+ ${l.cyan("$ leetcode timer --stop")} Stop active timer
386
+ `).action((e,o)=>Pt(e,o));var Ce=T.command("workspace").description("Manage workspaces for different contexts");Ce.command("current").description("Show current workspace").action(Kt);Ce.command("list").description("List all workspaces").action(Xt);Ce.command("create <name>").description("Create a new workspace").option("-w, --workdir <path>","Set working directory for this workspace").action(Zt);Ce.command("use <name>").description("Switch to a workspace").action(en);Ce.command("delete <name>").description("Delete a workspace").action(on);var he=T.command("collab").description("Collaborative coding with a partner").addHelpText("after",`
387
+ ${l.yellow("Subcommands:")}
388
+ ${l.cyan("host <id>")} Create a room and get a code to share
389
+ ${l.cyan("join <code>")} Join a room with the shared code
390
+ ${l.cyan("sync")} Upload your solution to the room
391
+ ${l.cyan("compare")} View both solutions side by side
392
+ ${l.cyan("status")} Check room and sync status
393
+ ${l.cyan("leave")} End the collaboration session
394
+
395
+ ${l.yellow("Examples:")}
396
+ ${l.gray("$ leetcode collab host 1")} Start a session for Two Sum
397
+ ${l.gray("$ leetcode collab join ABC123")} Join your partner's session
398
+ ${l.gray("$ leetcode collab sync")} Upload your code after solving
399
+ ${l.gray("$ leetcode collab compare")} Compare solutions
400
+ `);he.command("host <problemId>").description("Host a collaboration session").action(xt);he.command("join <roomCode>").description("Join a collaboration session").action(Et);he.command("sync").description("Sync your code with partner").action(Lt);he.command("compare").description("Compare your solution with partner").action(At);he.command("leave").description("Leave the collaboration session").action(Rt);he.command("status").description("Show collaboration status").action(_t);var Pe=T.command("snapshot").description("Save and restore solution versions").addHelpText("after",`
401
+ ${l.yellow("Subcommands:")}
402
+ ${l.cyan("save <id> [name]")} Save current solution as a snapshot
403
+ ${l.cyan("list <id>")} List all snapshots for a problem
404
+ ${l.cyan("restore <id> <snapshot>")} Restore a snapshot
405
+ ${l.cyan("diff <id> <s1> <s2>")} Compare two snapshots
406
+ ${l.cyan("delete <id> <snapshot>")} Delete a snapshot
407
+
408
+ ${l.yellow("Examples:")}
409
+ ${l.gray('$ leetcode snapshot save 1 "brute-force"')} Save current solution
410
+ ${l.gray("$ leetcode snapshot list 1")} List snapshots
411
+ ${l.gray("$ leetcode snapshot restore 1 2")} Restore snapshot #2
412
+ ${l.gray("$ leetcode snapshot diff 1 1 2")} Compare snapshots
413
+ `);Pe.command("save <id> [name]").description("Save current solution as a snapshot").action(Wt);Pe.command("list <id>").description("List all snapshots for a problem").action(Ht);Pe.command("restore <id> <snapshot>").description("Restore a snapshot").action(Vt);Pe.command("diff <id> <snap1> <snap2>").description("Compare two snapshots").action(zt);Pe.command("delete <id> <snapshot>").description("Delete a snapshot").action(Bt);T.command("update").description("Check for CLI updates").option("--check-only","Only check for updates, do not show update instructions").option("-f, --force","Force check even if recently checked").addHelpText("after",`
414
+ ${l.yellow("Examples:")}
415
+ ${l.cyan("$ leetcode update")} Check for updates
416
+ ${l.cyan("$ leetcode update --force")} Force re-check npm registry
417
+ ${l.cyan("$ leetcode update --check-only")} Just check, minimal output
418
+ `).action(an);T.command("changelog [version]").description("View release notes and changelog").option("--latest","Show only the latest version").option("--breaking","Show only versions with breaking changes").option("-a, --all","Show all versions (default: only newer than installed)").addHelpText("after",`
419
+ ${l.yellow("Examples:")}
420
+ ${l.cyan("$ leetcode changelog")} Show what's new since your version
421
+ ${l.cyan("$ leetcode changelog --all")} View full changelog
422
+ ${l.cyan("$ leetcode changelog 2.0.0")} Show specific version
423
+ ${l.cyan("$ leetcode changelog --latest")} Show latest version only
424
+ ${l.cyan("$ leetcode changelog --breaking")} Filter to breaking changes
425
+ `).action((e,o)=>cn(e,o));T.showHelpAfterError("(add --help for additional information)");var _r=process.argv.length>2&&!["update","changelog","--version","-v","--help","-h"].includes(process.argv[2]);_r&&ln().catch(()=>{});T.parse();process.argv.slice(2).length||(console.log(),console.log(l.bold.cyan(" \u{1F525} LeetCode CLI")),console.log(l.gray(" A modern command-line interface for LeetCode")),console.log(),T.outputHelp());