@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.
- package/README.md +12 -0
- package/dist/index.js +229 -3988
- 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
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
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(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/"/g, '"').replace(/'/g, "'").replace(/≤/g, "\u2264").replace(/≥/g, "\u2265").replace(/&#(\d+);/g, (_, code) => String.fromCharCode(parseInt(code, 10)));
|
|
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(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/"/g, '"').replace(/'/g, "'").replace(/≤/g, "<=").replace(/≥/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
|
-
|
|
1735
|
-
|
|
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(/ /g," ").replace(/</g,"<").replace(/>/g,">").replace(/&/g,"&").replace(/"/g,'"').replace(/'/g,"'").replace(/≤/g,"\u2264").replace(/≥/g,"\u2265").replace(/&#(\d+);/g,(n,s)=>String.fromCharCode(parseInt(s,10))),t=t.replace(/\n{3,}/g,`
|
|
1741
170
|
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
let
|
|
1747
|
-
if (
|
|
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
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
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
|
-
|
|
1814
|
-
|
|
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(/ /g," ").replace(/</g,"<").replace(/>/g,">").replace(/&/g,"&").replace(/"/g,'"').replace(/'/g,"'").replace(/≤/g,"<=").replace(/≥/g,">=").replace(/&#(\d+);/g,(t,n)=>String.fromCharCode(parseInt(n,10))),o=o.replace(/\n{3,}/g,`
|
|
1899
185
|
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
if (!authorized) return;
|
|
1904
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
async function
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
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 ${
|
|
254
|
+
4. Copy values of ${l.green("LEETCODE_SESSION")} and ${l.green("csrftoken")}
|
|
3971
255
|
5. Paste when prompted by this command
|
|
3972
|
-
`).action(
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
${
|
|
3977
|
-
${
|
|
3978
|
-
${
|
|
3979
|
-
${
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
${
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
${
|
|
3988
|
-
${
|
|
3989
|
-
`).action(
|
|
3990
|
-
|
|
3991
|
-
${
|
|
3992
|
-
${
|
|
3993
|
-
${
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
${
|
|
3999
|
-
${
|
|
4000
|
-
${
|
|
4001
|
-
${
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
${
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
${
|
|
4008
|
-
${
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
${
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
}
|
|
4015
|
-
|
|
4016
|
-
${
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
`).action(
|
|
4020
|
-
|
|
4021
|
-
${
|
|
4022
|
-
${
|
|
4023
|
-
${
|
|
4024
|
-
${
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
${
|
|
4028
|
-
|
|
4029
|
-
${
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
${
|
|
4033
|
-
${
|
|
4034
|
-
${
|
|
4035
|
-
${
|
|
4036
|
-
${
|
|
4037
|
-
`).action(
|
|
4038
|
-
|
|
4039
|
-
${
|
|
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
|
-
${
|
|
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
|
-
${
|
|
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
|
-
${
|
|
4065
|
-
${
|
|
4066
|
-
${
|
|
4067
|
-
${
|
|
4068
|
-
${
|
|
4069
|
-
${
|
|
4070
|
-
`).action((
|
|
4071
|
-
|
|
4072
|
-
${
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
${
|
|
4077
|
-
${
|
|
4078
|
-
${
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
${
|
|
4083
|
-
${
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
${
|
|
4091
|
-
|
|
4092
|
-
${
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
${
|
|
4096
|
-
`).action(
|
|
4097
|
-
|
|
4098
|
-
${
|
|
4099
|
-
${
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
${
|
|
4103
|
-
${
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
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
|
-
${
|
|
4125
|
-
${
|
|
4126
|
-
${
|
|
4127
|
-
${
|
|
4128
|
-
${
|
|
4129
|
-
`).action((
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
${
|
|
4140
|
-
${
|
|
4141
|
-
${
|
|
4142
|
-
${
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
${
|
|
4146
|
-
${
|
|
4147
|
-
${
|
|
4148
|
-
${
|
|
4149
|
-
${
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
${
|
|
4159
|
-
${
|
|
4160
|
-
${
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
${
|
|
4164
|
-
|
|
4165
|
-
${
|
|
4166
|
-
${
|
|
4167
|
-
${
|
|
4168
|
-
|
|
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());
|