@rhseung/ps-cli 1.7.5 → 1.9.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.
@@ -1,84 +1,184 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- source_default
4
- } from "../chunk-ASMT3CRD.js";
5
- import {
6
- getUserStats
7
- } from "../chunk-A6STXEAE.js";
3
+ getUserProblemStats,
4
+ getUserStats,
5
+ getUserTagRatings,
6
+ getUserTop100,
7
+ scrapeUserStats
8
+ } from "../chunk-YH6VDCNG.js";
8
9
  import {
9
10
  Command,
10
11
  CommandBuilder,
11
12
  CommandDef,
13
+ TIER_COLORS,
14
+ __decorateClass,
12
15
  calculateTierProgress,
16
+ defineFlags,
17
+ findProjectRoot,
18
+ getArchiveDir,
13
19
  getNextTierMinRating,
14
20
  getSolvedAcHandle,
21
+ getSolvingDir,
15
22
  getTierColor,
16
- getTierName
17
- } from "../chunk-F4LZ6ENP.js";
18
- import {
19
- __decorateClass
20
- } from "../chunk-7MQMPJ3X.js";
23
+ getTierName,
24
+ getTierShortName,
25
+ icons,
26
+ logger,
27
+ source_default
28
+ } from "../chunk-JZK2464U.js";
21
29
 
22
30
  // src/commands/stats.tsx
23
- import { Alert } from "@inkjs/ui";
24
- import { Spinner } from "@inkjs/ui";
25
- import { Box, Text, Transform } from "ink";
31
+ import { Alert, Spinner } from "@inkjs/ui";
32
+ import { BarChart, StackedBarChart } from "@pppp606/ink-chart";
33
+ import { Box, Text } from "ink";
26
34
 
27
35
  // src/hooks/use-user-stats.ts
36
+ import { existsSync } from "fs";
37
+ import { readdir, stat } from "fs/promises";
38
+ import { join } from "path";
28
39
  import { useEffect, useState } from "react";
40
+ async function countProblems(dir) {
41
+ let count = 0;
42
+ try {
43
+ if (!existsSync(dir)) return 0;
44
+ const entries = await readdir(dir);
45
+ for (const entry of entries) {
46
+ if (entry.startsWith(".")) continue;
47
+ const fullPath = join(dir, entry);
48
+ const s = await stat(fullPath);
49
+ if (s.isDirectory()) {
50
+ if (existsSync(join(fullPath, "meta.json"))) {
51
+ count++;
52
+ } else {
53
+ count += await countProblems(fullPath);
54
+ }
55
+ }
56
+ }
57
+ } catch {
58
+ }
59
+ return count;
60
+ }
29
61
  function useUserStats({
30
62
  handle,
31
- onComplete
63
+ onComplete,
64
+ fetchLocalCount = false
32
65
  }) {
33
66
  const [status, setStatus] = useState(
34
67
  "loading"
35
68
  );
36
69
  const [user, setUser] = useState(null);
70
+ const [top100, setTop100] = useState(null);
71
+ const [problemStats, setProblemStats] = useState(null);
72
+ const [tagRatings, setTagRatings] = useState(
73
+ null
74
+ );
75
+ const [bojStats, setBojStats] = useState(null);
76
+ const [localSolvedCount, setLocalSolvedCount] = useState(null);
37
77
  const [error, setError] = useState(null);
38
78
  useEffect(() => {
39
- void getUserStats(handle).then((userData) => {
40
- setUser(userData);
41
- setStatus("success");
42
- setTimeout(() => {
43
- onComplete();
44
- }, 5e3);
45
- }).catch((err) => {
46
- setError(err instanceof Error ? err.message : String(err));
47
- setStatus("error");
48
- setTimeout(() => {
49
- onComplete();
50
- }, 3e3);
51
- });
52
- }, [handle, onComplete]);
79
+ async function fetchData() {
80
+ try {
81
+ const userData = await getUserStats(handle).catch((err) => {
82
+ if (err instanceof Error && err.message.includes("404")) {
83
+ throw new Error(`\uC0AC\uC6A9\uC790 '${handle}'\uC744(\uB97C) \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`);
84
+ }
85
+ throw err;
86
+ });
87
+ if (!userData) {
88
+ throw new Error(`\uC0AC\uC6A9\uC790 '${handle}'\uC744(\uB97C) \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`);
89
+ }
90
+ setUser(userData);
91
+ const [top100Data, problemStatsData, tagRatingsData, bojStatsData] = await Promise.all([
92
+ getUserTop100(handle).catch((err) => {
93
+ console.error("Error fetching top 100:", err);
94
+ return null;
95
+ }),
96
+ getUserProblemStats(handle).catch((err) => {
97
+ console.error("Error fetching problem stats:", err);
98
+ return null;
99
+ }),
100
+ getUserTagRatings(handle).catch((err) => {
101
+ console.error("Error fetching tag ratings:", err);
102
+ return null;
103
+ }),
104
+ scrapeUserStats(handle).catch((err) => {
105
+ console.error("Error scraping BOJ stats:", err);
106
+ return null;
107
+ })
108
+ ]);
109
+ setTop100(top100Data);
110
+ setProblemStats(problemStatsData);
111
+ setTagRatings(tagRatingsData);
112
+ setBojStats(bojStatsData);
113
+ if (fetchLocalCount) {
114
+ const projectRoot = findProjectRoot();
115
+ if (projectRoot) {
116
+ const archiveDir = getArchiveDir();
117
+ const solvingDir = getSolvingDir();
118
+ const archivePath = join(projectRoot, archiveDir);
119
+ const solvingPath = join(projectRoot, solvingDir);
120
+ const [archiveCount, solvingCount] = await Promise.all([
121
+ countProblems(archivePath),
122
+ countProblems(solvingPath)
123
+ ]);
124
+ setLocalSolvedCount(archiveCount + solvingCount);
125
+ }
126
+ }
127
+ setStatus("success");
128
+ setTimeout(() => {
129
+ onComplete();
130
+ }, 5e3);
131
+ } catch (err) {
132
+ setError(err instanceof Error ? err.message : String(err));
133
+ setStatus("error");
134
+ setTimeout(() => {
135
+ onComplete();
136
+ }, 3e3);
137
+ }
138
+ }
139
+ void fetchData();
140
+ }, [fetchLocalCount, handle, onComplete]);
53
141
  return {
54
142
  status,
55
143
  user,
144
+ top100,
145
+ problemStats,
146
+ tagRatings,
147
+ bojStats,
148
+ localSolvedCount,
56
149
  error
57
150
  };
58
151
  }
59
152
 
60
153
  // src/commands/stats.tsx
61
154
  import { jsx, jsxs } from "react/jsx-runtime";
62
- function ProgressBarWithColor({ value, colorFn }) {
63
- const width = process.stdout.columns || 40;
64
- const barWidth = Math.max(10, Math.min(30, width - 20));
65
- const filled = Math.round(value / 100 * barWidth);
66
- const empty = barWidth - filled;
67
- const filledBar = "\u2588".repeat(filled);
68
- const emptyBar = "\u2591".repeat(empty);
69
- const barText = filledBar + emptyBar;
70
- return /* @__PURE__ */ jsx(Transform, { transform: (output) => colorFn(output), children: /* @__PURE__ */ jsx(Text, { children: barText }) });
71
- }
72
- function StatsView({ handle, onComplete }) {
73
- const { status, user, error } = useUserStats({
155
+ var statsFlagsSchema = {
156
+ handle: {
157
+ type: "string",
158
+ shortFlag: "h",
159
+ description: "Solved.ac \uD578\uB4E4 (\uC124\uC815\uC5D0 \uC800\uC7A5\uB41C \uAC12 \uC0AC\uC6A9 \uAC00\uB2A5)"
160
+ }
161
+ };
162
+ function StatsView({ handle, onComplete, showLocalStats }) {
163
+ const {
164
+ status,
165
+ user,
166
+ top100,
167
+ problemStats,
168
+ tagRatings,
169
+ bojStats,
170
+ localSolvedCount,
171
+ error
172
+ } = useUserStats({
74
173
  handle,
75
- onComplete
174
+ onComplete,
175
+ fetchLocalCount: showLocalStats
76
176
  });
77
177
  if (status === "loading") {
78
- return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: /* @__PURE__ */ jsx(Spinner, { label: "\uD1B5\uACC4\uB97C \uBD88\uB7EC\uC624\uB294 \uC911..." }) });
178
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", paddingY: 1, children: /* @__PURE__ */ jsx(Spinner, { label: "\uD1B5\uACC4\uB97C \uBD88\uB7EC\uC624\uB294 \uC911..." }) });
79
179
  }
80
180
  if (status === "error") {
81
- return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: /* @__PURE__ */ jsxs(Alert, { variant: "error", children: [
181
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", paddingY: 1, children: /* @__PURE__ */ jsxs(Alert, { variant: "error", children: [
82
182
  "\uD1B5\uACC4\uB97C \uBD88\uB7EC\uC62C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ",
83
183
  error
84
184
  ] }) });
@@ -89,50 +189,209 @@ function StatsView({ handle, onComplete }) {
89
189
  const tierColorFn = typeof tierColor === "string" ? source_default.hex(tierColor) : tierColor.multiline;
90
190
  const nextTierMin = getNextTierMinRating(user.tier);
91
191
  const progress = user.tier === 31 ? 100 : calculateTierProgress(user.rating, user.tier);
92
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
93
- /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsxs(Text, { color: "cyan", bold: true, children: [
94
- "\u2728 ",
95
- user.handle
96
- ] }) }),
97
- /* @__PURE__ */ jsx(Box, { marginBottom: 1, flexDirection: "row", gap: 1, children: /* @__PURE__ */ jsxs(Text, { children: [
98
- tierColorFn(tierName),
99
- " ",
100
- /* @__PURE__ */ jsx(Text, { bold: true, children: tierColorFn(user.rating.toLocaleString()) }),
101
- nextTierMin !== null && /* @__PURE__ */ jsx(Text, { bold: true, children: " / " + nextTierMin.toLocaleString() })
102
- ] }) }),
103
- /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsx(ProgressBarWithColor, { value: progress, colorFn: tierColorFn }) }),
192
+ const tierDistData = problemStats ? [
193
+ {
194
+ label: "Bronze",
195
+ value: problemStats.filter((s) => s.level >= 1 && s.level <= 5).reduce((a, b) => a + b.solved, 0),
196
+ color: TIER_COLORS[3]
197
+ // Bronze III
198
+ },
199
+ {
200
+ label: "Silver",
201
+ value: problemStats.filter((s) => s.level >= 6 && s.level <= 10).reduce((a, b) => a + b.solved, 0),
202
+ color: TIER_COLORS[8]
203
+ // Silver III
204
+ },
205
+ {
206
+ label: "Gold",
207
+ value: problemStats.filter((s) => s.level >= 11 && s.level <= 15).reduce((a, b) => a + b.solved, 0),
208
+ color: TIER_COLORS[13]
209
+ // Gold III
210
+ },
211
+ {
212
+ label: "Platinum",
213
+ value: problemStats.filter((s) => s.level >= 16 && s.level <= 20).reduce((a, b) => a + b.solved, 0),
214
+ color: TIER_COLORS[18]
215
+ // Platinum III
216
+ },
217
+ {
218
+ label: "Diamond",
219
+ value: problemStats.filter((s) => s.level >= 21 && s.level <= 25).reduce((a, b) => a + b.solved, 0),
220
+ color: TIER_COLORS[23]
221
+ // Diamond III
222
+ },
223
+ {
224
+ label: "Ruby",
225
+ value: problemStats.filter((s) => s.level >= 26 && s.level <= 30).reduce((a, b) => a + b.solved, 0),
226
+ color: TIER_COLORS[28]
227
+ // Ruby III
228
+ },
229
+ {
230
+ label: "Master",
231
+ value: problemStats.filter((s) => s.level === 31).reduce((a, b) => a + b.solved, 0),
232
+ color: TIER_COLORS[31]
233
+ // Master
234
+ }
235
+ ].filter((d) => d.value > 0) : [];
236
+ const tagChartData = tagRatings ? tagRatings.sort((a, b) => b.rating - a.rating).slice(0, 8).map((tr) => ({
237
+ label: tr.tag.displayNames.find((dn) => dn.language === "ko")?.name || tr.tag.key,
238
+ value: tr.rating
239
+ })) : [];
240
+ const bojSummaryData = bojStats ? [
241
+ { label: "\uC815\uB2F5", value: bojStats.accepted, color: "green" },
242
+ { label: "\uC624\uB2F5", value: bojStats.wrong, color: "red" },
243
+ {
244
+ label: "TLE/MLE",
245
+ value: bojStats.timeout + bojStats.memory,
246
+ color: "yellow"
247
+ },
248
+ {
249
+ label: "\uAE30\uD0C0",
250
+ value: bojStats.runtimeError + bojStats.compileError,
251
+ color: "gray"
252
+ }
253
+ ].filter((d) => d.value > 0) : [];
254
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", gap: 1, children: [
104
255
  /* @__PURE__ */ jsx(
256
+ Box,
257
+ {
258
+ flexDirection: "row",
259
+ justifyContent: "space-between",
260
+ alignItems: "flex-end",
261
+ children: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
262
+ /* @__PURE__ */ jsxs(Text, { bold: true, children: [
263
+ icons.user,
264
+ " ",
265
+ source_default.cyan(user.handle)
266
+ ] }),
267
+ /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
268
+ "https://solved.ac/profile/",
269
+ user.handle
270
+ ] })
271
+ ] })
272
+ }
273
+ ),
274
+ /* @__PURE__ */ jsxs(
105
275
  Box,
106
276
  {
107
277
  flexDirection: "column",
108
278
  borderStyle: "round",
109
279
  borderColor: "gray",
110
- alignSelf: "flex-start",
111
- children: /* @__PURE__ */ jsxs(Box, { paddingX: 1, paddingY: 0, flexDirection: "column", children: [
280
+ paddingX: 1,
281
+ children: [
282
+ /* @__PURE__ */ jsxs(Box, { justifyContent: "space-between", marginBottom: 1, children: [
283
+ /* @__PURE__ */ jsxs(Box, { gap: 1, children: [
284
+ /* @__PURE__ */ jsx(Text, { bold: true, children: tierColorFn(tierName) }),
285
+ /* @__PURE__ */ jsx(Text, { bold: true, children: tierColorFn(user.rating.toLocaleString()) }),
286
+ nextTierMin !== null && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
287
+ " / ",
288
+ nextTierMin.toLocaleString()
289
+ ] })
290
+ ] }),
291
+ /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
292
+ icons.trophy,
293
+ " Rank #",
294
+ user.rank.toLocaleString()
295
+ ] })
296
+ ] }),
297
+ /* @__PURE__ */ jsx(
298
+ StackedBarChart,
299
+ {
300
+ data: [
301
+ {
302
+ label: "Progress",
303
+ value: progress,
304
+ color: typeof tierColor === "string" ? tierColor : "#ff7ca8"
305
+ },
306
+ ...progress < 100 ? [{ label: "Remaining", value: 100 - progress, color: "#333" }] : []
307
+ ],
308
+ showLabels: false,
309
+ showValues: false,
310
+ width: "full"
311
+ }
312
+ )
313
+ ]
314
+ }
315
+ ),
316
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 2, children: [
317
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [
318
+ /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "[ \uC0C1\uC138 \uD1B5\uACC4 ]" }) }),
319
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", gap: 0, children: [
112
320
  /* @__PURE__ */ jsxs(Text, { children: [
113
321
  "\uD574\uACB0\uD55C \uBB38\uC81C:",
114
322
  " ",
115
323
  /* @__PURE__ */ jsx(Text, { bold: true, color: "green", children: user.solvedCount.toLocaleString() }),
324
+ " ",
116
325
  "\uAC1C"
117
326
  ] }),
327
+ localSolvedCount !== null && /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
328
+ " ",
329
+ "\u2514 \uB85C\uCEEC \uAD00\uB9AC: ",
330
+ /* @__PURE__ */ jsx(Text, { bold: true, children: localSolvedCount }),
331
+ " \uAC1C"
332
+ ] }),
118
333
  /* @__PURE__ */ jsxs(Text, { children: [
119
- "\uD074\uB798\uC2A4: ",
120
- /* @__PURE__ */ jsx(Text, { bold: true, children: user.class })
334
+ "\uD074\uB798\uC2A4:",
335
+ " ",
336
+ /* @__PURE__ */ jsxs(Text, { bold: true, color: "magenta", children: [
337
+ user.class,
338
+ user.classDecoration === "gold" ? "++" : user.classDecoration === "silver" ? "+" : ""
339
+ ] })
121
340
  ] }),
122
- user.maxStreak > 0 && /* @__PURE__ */ jsxs(Text, { children: [
123
- "\uCD5C\uB300 \uC5F0\uC18D \uD574\uACB0:",
341
+ /* @__PURE__ */ jsxs(Text, { children: [
342
+ "\uCD5C\uB300 \uC2A4\uD2B8\uB9AD:",
343
+ " ",
344
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "orange", children: user.maxStreak }),
124
345
  " ",
125
- /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: user.maxStreak }),
126
346
  "\uC77C"
127
347
  ] }),
128
348
  /* @__PURE__ */ jsxs(Text, { children: [
129
- "\uC21C\uC704: ",
130
- /* @__PURE__ */ jsx(Text, { bold: true, children: user.rank.toLocaleString() }),
131
- "\uC704"
349
+ "\uAE30\uC5EC \uD69F\uC218:",
350
+ " ",
351
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "blue", children: user.voteCount }),
352
+ " ",
353
+ "\uD68C"
132
354
  ] })
355
+ ] }),
356
+ bojSummaryData.length > 0 && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
357
+ /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "[ \uBC31\uC900 \uC81C\uCD9C \uC694\uC57D ]" }) }),
358
+ /* @__PURE__ */ jsx(
359
+ StackedBarChart,
360
+ {
361
+ data: bojSummaryData,
362
+ mode: "absolute",
363
+ width: 30
364
+ }
365
+ )
133
366
  ] })
134
- }
135
- )
367
+ ] }),
368
+ tagChartData.length > 0 && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", width: 40, children: [
369
+ /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "[ \uC54C\uACE0\uB9AC\uC998 \uAC15\uC810 (Tag Rating) ]" }) }),
370
+ /* @__PURE__ */ jsx(
371
+ BarChart,
372
+ {
373
+ data: tagChartData,
374
+ showValue: "right",
375
+ barChar: "\u2588",
376
+ color: "cyan"
377
+ }
378
+ )
379
+ ] })
380
+ ] }),
381
+ tierDistData.length > 0 && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
382
+ /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "[ \uD2F0\uC5B4\uBCC4 \uD574\uACB0 \uBB38\uC81C \uBD84\uD3EC ]" }) }),
383
+ /* @__PURE__ */ jsx(StackedBarChart, { data: tierDistData, width: "full" })
384
+ ] }),
385
+ top100 && top100.length > 0 && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
386
+ /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "[ \uC0C1\uC704 100\uBB38\uC81C \uBD84\uD3EC ]" }) }),
387
+ /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: Array.from({ length: Math.ceil(top100.length / 20) }).map(
388
+ (_, rowIndex) => /* @__PURE__ */ jsx(Box, { flexDirection: "row", children: top100.slice(rowIndex * 20, (rowIndex + 1) * 20).map((p, colIndex) => {
389
+ const tierColor2 = getTierColor(p.level);
390
+ const tierColorFn2 = typeof tierColor2 === "string" ? source_default.hex(tierColor2) : tierColor2.multiline;
391
+ return /* @__PURE__ */ jsx(Box, { width: 3, children: /* @__PURE__ */ jsx(Text, { children: tierColorFn2(getTierShortName(p.level)) }) }, colIndex);
392
+ }) }, rowIndex)
393
+ ) })
394
+ ] })
136
395
  ] });
137
396
  }
138
397
  return null;
@@ -144,17 +403,15 @@ var StatsCommand = class extends Command {
144
403
  handle = getSolvedAcHandle();
145
404
  }
146
405
  if (!handle) {
147
- console.error("\uC624\uB958: Solved.ac \uD578\uB4E4\uC744 \uC785\uB825\uD574\uC8FC\uC138\uC694.");
148
- console.error(`\uC0AC\uC6A9\uBC95: ps stats <\uD578\uB4E4>`);
149
- console.error(`\uB3C4\uC6C0\uB9D0: ps stats --help`);
150
- console.error(
151
- `\uD78C\uD2B8: \uC124\uC815\uC5D0 \uD578\uB4E4\uC744 \uC800\uC7A5\uD558\uBA74 \uB9E4\uBC88 \uC785\uB825\uD560 \uD544\uC694\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.`
152
- );
406
+ logger.error("Solved.ac \uD578\uB4E4\uC744 \uC785\uB825\uD574\uC8FC\uC138\uC694.");
407
+ console.log(`\uB3C4\uC6C0\uB9D0: ps stats --help`);
153
408
  process.exit(1);
154
409
  return;
155
410
  }
411
+ const showLocalStats = !args[0] && !flags.handle;
156
412
  await this.renderView(StatsView, {
157
- handle
413
+ handle,
414
+ showLocalStats
158
415
  });
159
416
  }
160
417
  };
@@ -163,16 +420,8 @@ StatsCommand = __decorateClass([
163
420
  name: "stats",
164
421
  description: `Solved.ac\uC5D0\uC11C \uC0AC\uC6A9\uC790 \uD1B5\uACC4\uB97C \uC870\uD68C\uD569\uB2C8\uB2E4.
165
422
  - \uD2F0\uC5B4, \uB808\uC774\uD305, \uD574\uACB0\uD55C \uBB38\uC81C \uC218 \uB4F1 \uD45C\uC2DC
166
- - \uADF8\uB77C\uB370\uC774\uC158\uC73C\uB85C \uC2DC\uAC01\uC801\uC73C\uB85C \uD45C\uC2DC`,
167
- flags: [
168
- {
169
- name: "handle",
170
- options: {
171
- shortFlag: "h",
172
- description: "Solved.ac \uD578\uB4E4 (\uC124\uC815\uC5D0 \uC800\uC7A5\uB41C \uAC12 \uC0AC\uC6A9 \uAC00\uB2A5)"
173
- }
174
- }
175
- ],
423
+ - \uAE30\uBCF8 Solved.ac \uD578\uB4E4\uC740 ps config\uC5D0\uC11C \uC124\uC815 \uAC00\uB2A5\uD569\uB2C8\uB2E4.`,
424
+ flags: defineFlags(statsFlagsSchema),
176
425
  autoDetectProblemId: false,
177
426
  examples: ["stats myhandle", "stats --handle myhandle"]
178
427
  })