@rhseung/ps-cli 1.7.3 → 1.7.5

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.
@@ -4,26 +4,26 @@ import {
4
4
  CommandBuilder,
5
5
  CommandDef,
6
6
  findProjectRoot,
7
- getProblemDirPath,
8
- getProblemId,
7
+ getArchiveDirPath,
9
8
  getSolvingDir,
10
- getSolvingDirPath
11
- } from "../chunk-TNGUME4H.js";
9
+ getSolvingDirPath,
10
+ resolveProblemContext
11
+ } from "../chunk-F4LZ6ENP.js";
12
12
  import {
13
13
  __decorateClass
14
14
  } from "../chunk-7MQMPJ3X.js";
15
15
 
16
- // src/commands/solve.tsx
16
+ // src/commands/archive.tsx
17
17
  import { StatusMessage, Alert } from "@inkjs/ui";
18
18
  import { Spinner } from "@inkjs/ui";
19
19
  import { Box } from "ink";
20
20
 
21
- // src/hooks/use-solve.ts
21
+ // src/hooks/use-archive.ts
22
22
  import { access, readFile, rename, mkdir, readdir, rmdir } from "fs/promises";
23
23
  import { join, dirname } from "path";
24
24
  import { execa } from "execa";
25
25
  import { useEffect, useState } from "react";
26
- function useSolve({
26
+ function useArchive({
27
27
  problemId,
28
28
  onComplete
29
29
  }) {
@@ -33,7 +33,7 @@ function useSolve({
33
33
  const [message, setMessage] = useState("\uBB38\uC81C\uB97C \uC544\uCE74\uC774\uBE0C\uD558\uB294 \uC911...");
34
34
  const [error, setError] = useState(null);
35
35
  useEffect(() => {
36
- async function solve() {
36
+ async function archive() {
37
37
  try {
38
38
  const projectRoot = findProjectRoot();
39
39
  if (!projectRoot) {
@@ -71,22 +71,22 @@ function useSolve({
71
71
  }
72
72
  } catch {
73
73
  }
74
- const problemDir = getProblemDirPath(problemId, projectRoot, problem);
74
+ const archiveDir = getArchiveDirPath(problemId, projectRoot, problem);
75
75
  try {
76
- await access(problemDir);
76
+ await access(archiveDir);
77
77
  throw new Error(
78
- `problem \uB514\uB809\uD1A0\uB9AC\uC5D0 \uC774\uBBF8 \uBB38\uC81C ${problemId}\uAC00 \uC874\uC7AC\uD569\uB2C8\uB2E4.`
78
+ `archive \uB514\uB809\uD1A0\uB9AC\uC5D0 \uC774\uBBF8 \uBB38\uC81C ${problemId}\uAC00 \uC874\uC7AC\uD569\uB2C8\uB2E4.`
79
79
  );
80
80
  } catch (err) {
81
81
  if (err instanceof Error && err.message.includes("\uC774\uBBF8")) {
82
82
  throw err;
83
83
  }
84
84
  }
85
- const problemDirParent = dirname(problemDir);
85
+ const archiveDirParent = dirname(archiveDir);
86
86
  setMessage("\uC544\uCE74\uC774\uBE0C \uB514\uB809\uD1A0\uB9AC\uB97C \uC900\uBE44\uD558\uB294 \uC911...");
87
- await mkdir(problemDirParent, { recursive: true });
88
- setMessage("\uBB38\uC81C\uB97C problem \uB514\uB809\uD1A0\uB9AC\uB85C \uC774\uB3D9\uD558\uB294 \uC911...");
89
- await rename(solvingDir, problemDir);
87
+ await mkdir(archiveDirParent, { recursive: true });
88
+ setMessage("\uBB38\uC81C\uB97C archive \uB514\uB809\uD1A0\uB9AC\uB85C \uC774\uB3D9\uD558\uB294 \uC911...");
89
+ await rename(solvingDir, archiveDir);
90
90
  setMessage("\uBE48 \uB514\uB809\uD1A0\uB9AC \uC815\uB9AC \uC911...");
91
91
  try {
92
92
  const solvingDirConfig = getSolvingDir();
@@ -111,7 +111,7 @@ function useSolve({
111
111
  }
112
112
  setMessage("Git \uCEE4\uBC0B\uC744 \uC2E4\uD589\uD558\uB294 \uC911...");
113
113
  try {
114
- await execa("git", ["add", problemDir], { cwd: projectRoot });
114
+ await execa("git", ["add", archiveDir], { cwd: projectRoot });
115
115
  const commitMessage = `solve: ${problemId} - ${problemTitle}`;
116
116
  await execa("git", ["commit", "-m", commitMessage], {
117
117
  cwd: projectRoot
@@ -123,7 +123,7 @@ function useSolve({
123
123
  );
124
124
  }
125
125
  setStatus("success");
126
- setMessage(`\uBB38\uC81C ${problemId}\uB97C \uC544\uCE74\uC774\uBE0C\uD588\uC2B5\uB2C8\uB2E4: ${problemDir}`);
126
+ setMessage(`\uBB38\uC81C ${problemId}\uB97C \uC544\uCE74\uC774\uBE0C\uD588\uC2B5\uB2C8\uB2E4: ${archiveDir}`);
127
127
  setTimeout(() => {
128
128
  onComplete?.();
129
129
  }, 2e3);
@@ -135,7 +135,7 @@ function useSolve({
135
135
  }, 2e3);
136
136
  }
137
137
  }
138
- void solve();
138
+ void archive();
139
139
  }, [problemId, onComplete]);
140
140
  return {
141
141
  status,
@@ -144,10 +144,10 @@ function useSolve({
144
144
  };
145
145
  }
146
146
 
147
- // src/commands/solve.tsx
147
+ // src/commands/archive.tsx
148
148
  import { jsx, jsxs } from "react/jsx-runtime";
149
- function SolveView({ problemId, onComplete }) {
150
- const { status, message, error } = useSolve({
149
+ function ArchiveView({ problemId, onComplete }) {
150
+ const { status, message, error } = useArchive({
151
151
  problemId,
152
152
  onComplete
153
153
  });
@@ -162,40 +162,40 @@ function SolveView({ problemId, onComplete }) {
162
162
  }
163
163
  return /* @__PURE__ */ jsx(Box, { flexDirection: "column", width: "100%", children: /* @__PURE__ */ jsx(StatusMessage, { variant: "success", children: message }) });
164
164
  }
165
- var SolveCommand = class extends Command {
165
+ var ArchiveCommand = class extends Command {
166
166
  async execute(args, _) {
167
- const problemId = getProblemId(args);
168
- if (problemId === null) {
167
+ const context = await resolveProblemContext(args, { requireId: false });
168
+ if (context.problemId === null) {
169
169
  console.error("\uC624\uB958: \uBB38\uC81C \uBC88\uD638\uB97C \uC785\uB825\uD574\uC8FC\uC138\uC694.");
170
- console.error(`\uC0AC\uC6A9\uBC95: ps solve <\uBB38\uC81C\uBC88\uD638>`);
171
- console.error(`\uB3C4\uC6C0\uB9D0: ps solve --help`);
170
+ console.error(`\uC0AC\uC6A9\uBC95: ps archive <\uBB38\uC81C\uBC88\uD638>`);
171
+ console.error(`\uB3C4\uC6C0\uB9D0: ps archive --help`);
172
172
  console.error(
173
173
  `\uD78C\uD2B8: solving/{\uBB38\uC81C\uBC88\uD638} \uB514\uB809\uD1A0\uB9AC\uC5D0\uC11C \uC2E4\uD589\uD558\uBA74 \uC790\uB3D9\uC73C\uB85C \uBB38\uC81C \uBC88\uD638\uB97C \uCD94\uB860\uD569\uB2C8\uB2E4.`
174
174
  );
175
175
  process.exit(1);
176
176
  return;
177
177
  }
178
- await this.renderView(SolveView, {
179
- problemId
178
+ await this.renderView(ArchiveView, {
179
+ problemId: context.problemId
180
180
  });
181
181
  }
182
182
  };
183
- SolveCommand = __decorateClass([
183
+ ArchiveCommand = __decorateClass([
184
184
  CommandDef({
185
- name: "solve",
185
+ name: "archive",
186
186
  description: `\uBB38\uC81C\uB97C \uC544\uCE74\uC774\uBE0C\uD558\uACE0 Git \uCEE4\uBC0B\uC744 \uC0DD\uC131\uD569\uB2C8\uB2E4.
187
- - solving \uB514\uB809\uD1A0\uB9AC\uC5D0\uC11C \uBB38\uC81C\uB97C \uCC3E\uC544 problem \uB514\uB809\uD1A0\uB9AC\uB85C \uC774\uB3D9
187
+ - solving \uB514\uB809\uD1A0\uB9AC\uC5D0\uC11C \uBB38\uC81C\uB97C \uCC3E\uC544 archive \uB514\uB809\uD1A0\uB9AC\uB85C \uC774\uB3D9
188
188
  - Git add \uBC0F commit \uC2E4\uD589 (\uCEE4\uBC0B \uBA54\uC2DC\uC9C0: "solve: {\uBB38\uC81C\uBC88\uD638} - {\uBB38\uC81C\uC774\uB984}")`,
189
189
  autoDetectProblemId: true,
190
190
  requireProblemId: false,
191
191
  examples: [
192
- "solve 1000",
193
- "solve # \uD604\uC7AC \uB514\uB809\uD1A0\uB9AC\uC5D0\uC11C \uBB38\uC81C \uBC88\uD638 \uC790\uB3D9 \uAC10\uC9C0"
192
+ "archive 1000",
193
+ "archive # \uD604\uC7AC \uB514\uB809\uD1A0\uB9AC\uC5D0\uC11C \uBB38\uC81C \uBC88\uD638 \uC790\uB3D9 \uAC10\uC9C0"
194
194
  ]
195
195
  })
196
- ], SolveCommand);
197
- var solve_default = CommandBuilder.fromClass(SolveCommand);
196
+ ], ArchiveCommand);
197
+ var archive_default = CommandBuilder.fromClass(ArchiveCommand);
198
198
  export {
199
- SolveCommand,
200
- solve_default as default
199
+ ArchiveCommand,
200
+ archive_default as default
201
201
  };
@@ -3,13 +3,13 @@ import {
3
3
  Command,
4
4
  CommandBuilder,
5
5
  CommandDef,
6
+ getArchiveDir,
6
7
  getArchiveStrategy,
7
8
  getAutoOpenEditor,
8
9
  getDefaultLanguage,
9
10
  getEditor,
10
- getProblemDir,
11
11
  getSolvedAcHandle
12
- } from "../chunk-TNGUME4H.js";
12
+ } from "../chunk-F4LZ6ENP.js";
13
13
  import {
14
14
  __decorateClass,
15
15
  getSupportedLanguages,
@@ -102,8 +102,8 @@ function useConfig({
102
102
  case "solved-ac-handle":
103
103
  updatedConfig.solvedAcHandle = value;
104
104
  break;
105
- case "problem-dir":
106
- updatedConfig.problemDir = value;
105
+ case "archive-dir":
106
+ updatedConfig.archiveDir = value;
107
107
  break;
108
108
  case "archive-strategy": {
109
109
  const validStrategies = ["flat", "by-range", "by-tier", "by-tag"];
@@ -159,7 +159,7 @@ function getConfigHelp() {
159
159
  editor \uC5D0\uB514\uD130 \uBA85\uB839\uC5B4 (\uC608: code, vim, nano)
160
160
  auto-open-editor fetch \uD6C4 \uC790\uB3D9\uC73C\uB85C \uC5D0\uB514\uD130 \uC5F4\uAE30 (true/false)
161
161
  solved-ac-handle Solved.ac \uD578\uB4E4 (stats \uBA85\uB839\uC5B4\uC6A9)
162
- problem-dir \uBB38\uC81C \uB514\uB809\uD1A0\uB9AC \uACBD\uB85C (\uAE30\uBCF8\uAC12: problems, "." \uB610\uB294 ""\uB294 \uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8)
162
+ archive-dir \uC544\uCE74\uC774\uBE0C \uB514\uB809\uD1A0\uB9AC \uACBD\uB85C (\uAE30\uBCF8\uAC12: problems, "." \uB610\uB294 ""\uB294 \uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8)
163
163
  archive-strategy \uC544\uCE74\uC774\uBE59 \uC804\uB7B5 (flat, by-range, by-tier, by-tag)
164
164
 
165
165
  \uC635\uC158:
@@ -180,7 +180,7 @@ var CONFIG_KEYS = [
180
180
  { label: "editor", value: "editor" },
181
181
  { label: "auto-open-editor", value: "auto-open-editor" },
182
182
  { label: "solved-ac-handle", value: "solved-ac-handle" },
183
- { label: "problem-dir", value: "problem-dir" },
183
+ { label: "archive-dir", value: "archive-dir" },
184
184
  { label: "archive-strategy", value: "archive-strategy" }
185
185
  ];
186
186
  function ConfigView({
@@ -213,7 +213,7 @@ function ConfigView({
213
213
  const editor = config?.editor ?? getEditor();
214
214
  const autoOpen = config?.autoOpenEditor ?? getAutoOpenEditor();
215
215
  const handle = config?.solvedAcHandle ?? getSolvedAcHandle();
216
- const problemDir = config?.problemDir ?? getProblemDir();
216
+ const archiveDir = config?.archiveDir ?? getArchiveDir();
217
217
  const archiveStrategy = config?.archiveStrategy ?? getArchiveStrategy();
218
218
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
219
219
  /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\u2699\uFE0F \uD604\uC7AC \uC124\uC815 (.ps-cli.json)" }) }),
@@ -239,9 +239,9 @@ function ConfigView({
239
239
  /* @__PURE__ */ jsx(Text, { bold: true, color: handle ? "cyan" : "gray", children: handle || "\uC124\uC815 \uC548 \uB428" })
240
240
  ] }),
241
241
  /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
242
- /* @__PURE__ */ jsx(Text, { color: "gray", children: "problem-dir:" }),
242
+ /* @__PURE__ */ jsx(Text, { color: "gray", children: "archive-dir:" }),
243
243
  /* @__PURE__ */ jsx(Text, { children: " " }),
244
- /* @__PURE__ */ jsx(Text, { bold: true, children: problemDir })
244
+ /* @__PURE__ */ jsx(Text, { bold: true, children: archiveDir })
245
245
  ] }),
246
246
  /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
247
247
  /* @__PURE__ */ jsx(Text, { color: "gray", children: "archive-strategy:" }),
@@ -266,8 +266,8 @@ function ConfigView({
266
266
  case "solved-ac-handle":
267
267
  configValue = config?.solvedAcHandle ?? getSolvedAcHandle();
268
268
  break;
269
- case "problem-dir":
270
- configValue = config?.problemDir ?? getProblemDir();
269
+ case "archive-dir":
270
+ configValue = config?.archiveDir ?? getArchiveDir();
271
271
  break;
272
272
  case "archive-strategy":
273
273
  configValue = config?.archiveStrategy ?? getArchiveStrategy();
@@ -440,8 +440,8 @@ var ConfigCommand = class extends Command {
440
440
  return "true \uB610\uB294 false \uC785\uB825";
441
441
  case "solved-ac-handle":
442
442
  return "Solved.ac \uD578\uB4E4 \uC785\uB825";
443
- case "problem-dir":
444
- return "\uBB38\uC81C \uB514\uB809\uD1A0\uB9AC \uACBD\uB85C \uC785\uB825";
443
+ case "archive-dir":
444
+ return "\uC544\uCE74\uC774\uBE0C \uB514\uB809\uD1A0\uB9AC \uACBD\uB85C \uC785\uB825";
445
445
  case "archive-strategy":
446
446
  return "\uC544\uCE74\uC774\uBE59 \uC804\uB7B5 \uC785\uB825 (flat, by-range, by-tier, by-tag)";
447
447
  default:
@@ -458,8 +458,8 @@ var ConfigCommand = class extends Command {
458
458
  return "fetch \uD6C4 \uC790\uB3D9\uC73C\uB85C \uC5D0\uB514\uD130\uB97C \uC5F4\uC9C0 \uC5EC\uBD80";
459
459
  case "solved-ac-handle":
460
460
  return "Solved.ac \uC0AC\uC6A9\uC790 \uD578\uB4E4";
461
- case "problem-dir":
462
- return '\uBB38\uC81C \uB514\uB809\uD1A0\uB9AC \uACBD\uB85C (\uAE30\uBCF8\uAC12: "problems", \uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8: ".")';
461
+ case "archive-dir":
462
+ return '\uC544\uCE74\uC774\uBE0C \uB514\uB809\uD1A0\uB9AC \uACBD\uB85C (\uAE30\uBCF8\uAC12: "problems", \uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8: ".")';
463
463
  case "archive-strategy":
464
464
  return "\uC544\uCE74\uC774\uBE59 \uC804\uB7B5: flat (\uD3C9\uBA74), by-range (1000\uBC88\uB300 \uBB36\uAE30), by-tier (\uD2F0\uC5B4\uBCC4), by-tag (\uD0DC\uADF8\uBCC4)";
465
465
  default:
@@ -474,7 +474,7 @@ var ConfigCommand = class extends Command {
474
474
  return ["code", "cursor", "vim", "nano"];
475
475
  case "auto-open-editor":
476
476
  return ["true", "false"];
477
- case "problem-dir":
477
+ case "archive-dir":
478
478
  return ["problems", ".", ""];
479
479
  case "archive-strategy":
480
480
  return ["flat", "by-range", "by-tier", "by-tag"];
@@ -11,12 +11,12 @@ import {
11
11
  CommandDef,
12
12
  getAutoOpenEditor,
13
13
  getEditor,
14
- getProblemId,
15
14
  getSolvingDirPath,
16
15
  getTierColor,
17
16
  getTierImageUrl,
18
- getTierName
19
- } from "../chunk-TNGUME4H.js";
17
+ getTierName,
18
+ resolveProblemContext
19
+ } from "../chunk-F4LZ6ENP.js";
20
20
  import {
21
21
  __decorateClass,
22
22
  getLanguageConfig,
@@ -75,6 +75,27 @@ function parseTimeLimitToMs(timeLimit) {
75
75
  if (Number.isNaN(seconds)) return void 0;
76
76
  return Math.round(seconds * 1e3);
77
77
  }
78
+ function ensureTrailingNewline(content) {
79
+ if (!content || content.trim().length === 0) {
80
+ return content;
81
+ }
82
+ const trimmed = content.trimEnd();
83
+ if (trimmed.length === 0) {
84
+ return content;
85
+ }
86
+ const lines = trimmed.split("\n");
87
+ const lastLine = lines[lines.length - 1];
88
+ const isListItem = /^[\s]*[-*]\s/.test(lastLine) || /^[\s]*\d+[.)]\s/.test(lastLine);
89
+ const isTableRow = /^\s*\|.+\|\s*$/.test(lastLine);
90
+ const isCodeBlock = trimmed.endsWith("```");
91
+ if (isListItem || isTableRow || isCodeBlock) {
92
+ return trimmed + "\n";
93
+ }
94
+ if (!content.endsWith("\n")) {
95
+ return content + "\n";
96
+ }
97
+ return content;
98
+ }
78
99
  function getProjectRoot() {
79
100
  const __filename = fileURLToPath(import.meta.url);
80
101
  const __dirname = dirname(__filename);
@@ -101,10 +122,13 @@ async function generateProblemFiles(problem, language = "python") {
101
122
  "utf-8"
102
123
  );
103
124
  }
125
+ const testcasesDir = join(problemDir, "testcases");
104
126
  for (let i = 0; i < problem.testCases.length; i++) {
105
127
  const testCase = problem.testCases[i];
106
- const inputPath = join(problemDir, `input${i + 1}.txt`);
107
- const outputPath = join(problemDir, `output${i + 1}.txt`);
128
+ const caseDir = join(testcasesDir, String(i + 1));
129
+ await mkdir(caseDir, { recursive: true });
130
+ const inputPath = join(caseDir, "input.txt");
131
+ const outputPath = join(caseDir, "output.txt");
108
132
  await writeFile(inputPath, testCase.input, "utf-8");
109
133
  await writeFile(outputPath, testCase.output, "utf-8");
110
134
  }
@@ -150,16 +174,19 @@ ${separatorRow}
150
174
  ${valueRow}
151
175
  `;
152
176
  }
177
+ const description = ensureTrailingNewline(problem.description || "\uC124\uBA85 \uC5C6\uC74C");
178
+ const inputFormat = ensureTrailingNewline(
179
+ problem.inputFormat || "\uC785\uB825 \uD615\uC2DD \uC5C6\uC74C"
180
+ );
181
+ const outputFormat = ensureTrailingNewline(
182
+ problem.outputFormat || "\uCD9C\uB825 \uD615\uC2DD \uC5C6\uC74C"
183
+ );
153
184
  const readmeContent = `# [${problem.id}: ${problem.title}](https://www.acmicpc.net/problem/${problem.id})
154
185
 
155
186
  ${infoTable}## \uBB38\uC81C \uC124\uBA85
156
- ${problem.description || "\uC124\uBA85 \uC5C6\uC74C"}
157
-
158
- ## \uC785\uB825
159
- ${problem.inputFormat || "\uC785\uB825 \uD615\uC2DD \uC5C6\uC74C"}
160
-
161
- ## \uCD9C\uB825
162
- ${problem.outputFormat || "\uCD9C\uB825 \uD615\uC2DD \uC5C6\uC74C"}
187
+ ${description}## \uC785\uB825
188
+ ${inputFormat}## \uCD9C\uB825
189
+ ${outputFormat}
163
190
 
164
191
  ## \uC608\uC81C
165
192
  ${problem.testCases.map(
@@ -317,8 +344,8 @@ function FetchView({
317
344
  }
318
345
  var FetchCommand = class extends Command {
319
346
  async execute(args, flags) {
320
- const problemId = getProblemId(args);
321
- if (problemId === null) {
347
+ const context = await resolveProblemContext(args, { requireId: true });
348
+ if (context.problemId === null) {
322
349
  console.error("\uC624\uB958: \uBB38\uC81C \uBC88\uD638\uB97C \uC785\uB825\uD574\uC8FC\uC138\uC694.");
323
350
  console.error(`\uC0AC\uC6A9\uBC95: ps fetch <\uBB38\uC81C\uBC88\uD638> [\uC635\uC158]`);
324
351
  console.error(`\uB3C4\uC6C0\uB9D0: ps fetch --help`);
@@ -338,7 +365,7 @@ var FetchCommand = class extends Command {
338
365
  return;
339
366
  }
340
367
  await this.renderView(FetchView, {
341
- problemId,
368
+ problemId: context.problemId,
342
369
  language: language || "python"
343
370
  });
344
371
  }
@@ -3,14 +3,14 @@ import {
3
3
  Command,
4
4
  CommandBuilder,
5
5
  CommandDef,
6
+ getArchiveDir,
6
7
  getArchiveStrategy,
7
8
  getAutoOpenEditor,
8
9
  getDefaultLanguage,
9
10
  getEditor,
10
- getProblemDir,
11
11
  getSolvedAcHandle,
12
12
  getSolvingDir
13
- } from "../chunk-TNGUME4H.js";
13
+ } from "../chunk-F4LZ6ENP.js";
14
14
  import {
15
15
  __decorateClass,
16
16
  getSupportedLanguages
@@ -32,11 +32,11 @@ import { join } from "path";
32
32
  import { execaCommand, execa } from "execa";
33
33
  import { useEffect, useState, useCallback } from "react";
34
34
  function useInit({ onComplete }) {
35
- const [currentStep, setCurrentStep] = useState("problem-dir");
35
+ const [currentStep, setCurrentStep] = useState("archive-dir");
36
36
  const [completedSteps, setCompletedSteps] = useState([]);
37
37
  const [confirmExit, setConfirmExit] = useState(false);
38
38
  const [initialized, setInitialized] = useState(false);
39
- const [problemDir, setProblemDirValue] = useState(getProblemDir());
39
+ const [archiveDir, setArchiveDirValue] = useState(getArchiveDir());
40
40
  const [solvingDir, setSolvingDirValue] = useState(getSolvingDir());
41
41
  const [archiveStrategy, setArchiveStrategy] = useState(getArchiveStrategy());
42
42
  const [language, setLanguage] = useState(getDefaultLanguage());
@@ -71,8 +71,8 @@ function useInit({ onComplete }) {
71
71
  await access(projectConfigPath);
72
72
  const configContent = await readFile(projectConfigPath, "utf-8");
73
73
  const projectConfig = JSON.parse(configContent);
74
- if (projectConfig.problemDir)
75
- setProblemDirValue(projectConfig.problemDir);
74
+ if (projectConfig.archiveDir)
75
+ setArchiveDirValue(projectConfig.archiveDir);
76
76
  if (projectConfig.solvingDir)
77
77
  setSolvingDirValue(projectConfig.solvingDir);
78
78
  if (projectConfig.archiveStrategy)
@@ -93,8 +93,8 @@ function useInit({ onComplete }) {
93
93
  }, []);
94
94
  const getStepLabel = useCallback((step) => {
95
95
  switch (step) {
96
- case "problem-dir":
97
- return "\uBB38\uC81C \uB514\uB809\uD1A0\uB9AC \uC124\uC815 (\uC544\uCE74\uC774\uBE0C\uB41C \uBB38\uC81C)";
96
+ case "archive-dir":
97
+ return "\uC544\uCE74\uC774\uBE0C \uB514\uB809\uD1A0\uB9AC \uC124\uC815 (\uC544\uCE74\uC774\uBE0C\uB41C \uBB38\uC81C)";
98
98
  case "solving-dir":
99
99
  return "Solving \uB514\uB809\uD1A0\uB9AC \uC124\uC815 (\uD478\uB294 \uC911\uC778 \uBB38\uC81C)";
100
100
  case "archive-strategy":
@@ -114,8 +114,8 @@ function useInit({ onComplete }) {
114
114
  const getStepValue = useCallback(
115
115
  (step) => {
116
116
  switch (step) {
117
- case "problem-dir":
118
- return problemDir === "." ? "\uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8" : problemDir;
117
+ case "archive-dir":
118
+ return archiveDir === "." ? "\uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8" : archiveDir;
119
119
  case "solving-dir":
120
120
  return solvingDir === "." ? "\uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8" : solvingDir;
121
121
  case "archive-strategy": {
@@ -140,13 +140,13 @@ function useInit({ onComplete }) {
140
140
  }
141
141
  },
142
142
  [
143
- problemDir,
143
+ archiveDir,
144
144
  solvingDir,
145
- archiveStrategy,
146
145
  language,
147
146
  editor,
148
147
  autoOpen,
149
- handle
148
+ handle,
149
+ archiveStrategy
150
150
  ]
151
151
  );
152
152
  const executeInit = useCallback(
@@ -155,7 +155,7 @@ function useInit({ onComplete }) {
155
155
  const cwd = process.cwd();
156
156
  const projectConfigPath = join(cwd, ".ps-cli.json");
157
157
  const projectConfig = {
158
- problemDir,
158
+ archiveDir,
159
159
  solvingDir,
160
160
  archiveStrategy,
161
161
  defaultLanguage: language,
@@ -172,11 +172,11 @@ function useInit({ onComplete }) {
172
172
  "utf-8"
173
173
  );
174
174
  setCreated((prev) => [...prev, ".ps-cli.json"]);
175
- if (problemDir !== "." && problemDir !== "") {
176
- const problemDirPath = join(cwd, problemDir);
175
+ if (archiveDir !== "." && archiveDir !== "") {
176
+ const archiveDirPath = join(cwd, archiveDir);
177
177
  try {
178
- await mkdir(problemDirPath, { recursive: true });
179
- setCreated((prev) => [...prev, `${problemDir}/`]);
178
+ await mkdir(archiveDirPath, { recursive: true });
179
+ setCreated((prev) => [...prev, `${archiveDir}/`]);
180
180
  } catch (err) {
181
181
  const error = err;
182
182
  if (error.code !== "EEXIST") {
@@ -280,7 +280,7 @@ ${gitignorePatterns.join("\n")}
280
280
  }
281
281
  },
282
282
  [
283
- problemDir,
283
+ archiveDir,
284
284
  solvingDir,
285
285
  archiveStrategy,
286
286
  language,
@@ -300,7 +300,7 @@ ${gitignorePatterns.join("\n")}
300
300
  setHandle(handleValue);
301
301
  }
302
302
  const stepOrder = [
303
- "problem-dir",
303
+ "archive-dir",
304
304
  "solving-dir",
305
305
  "archive-strategy",
306
306
  "language",
@@ -322,24 +322,14 @@ ${gitignorePatterns.join("\n")}
322
322
  }
323
323
  }
324
324
  },
325
- [
326
- currentStep,
327
- executeInit,
328
- problemDir,
329
- solvingDir,
330
- archiveStrategy,
331
- language,
332
- editor,
333
- autoOpen,
334
- onComplete
335
- ]
325
+ [currentStep, executeInit]
336
326
  );
337
327
  return {
338
328
  currentStep,
339
329
  completedSteps,
340
330
  confirmExit,
341
331
  initialized,
342
- problemDir,
332
+ archiveDir,
343
333
  solvingDir,
344
334
  archiveStrategy,
345
335
  language,
@@ -349,7 +339,7 @@ ${gitignorePatterns.join("\n")}
349
339
  handleInputMode,
350
340
  created,
351
341
  cancelled,
352
- setProblemDirValue,
342
+ setArchiveDirValue,
353
343
  setSolvingDirValue,
354
344
  setArchiveStrategy,
355
345
  setLanguage,
@@ -366,6 +356,22 @@ ${gitignorePatterns.join("\n")}
366
356
  };
367
357
  }
368
358
 
359
+ // src/utils/version.ts
360
+ import { readFileSync } from "fs";
361
+ import { join as join2, dirname } from "path";
362
+ import { fileURLToPath } from "url";
363
+ function getVersion() {
364
+ try {
365
+ const __filename = fileURLToPath(import.meta.url);
366
+ const __dirname = dirname(__filename);
367
+ const packageJsonPath = join2(__dirname, "../../package.json");
368
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
369
+ return packageJson.version;
370
+ } catch {
371
+ return "";
372
+ }
373
+ }
374
+
369
375
  // src/commands/init.tsx
370
376
  import { jsx, jsxs } from "react/jsx-runtime";
371
377
  function InitView({ onComplete }) {
@@ -377,7 +383,7 @@ function InitView({ onComplete }) {
377
383
  handleInputMode,
378
384
  created,
379
385
  cancelled,
380
- setProblemDirValue,
386
+ setArchiveDirValue,
381
387
  setSolvingDirValue,
382
388
  setArchiveStrategy,
383
389
  setLanguage,
@@ -424,7 +430,7 @@ function InitView({ onComplete }) {
424
430
  ] });
425
431
  }
426
432
  switch (currentStep) {
427
- case "problem-dir": {
433
+ case "archive-dir": {
428
434
  const options = [
429
435
  { label: "problems", value: "problems" },
430
436
  { label: ". (\uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8)", value: "." }
@@ -436,7 +442,7 @@ function InitView({ onComplete }) {
436
442
  {
437
443
  options,
438
444
  onChange: (value) => {
439
- setProblemDirValue(value);
445
+ setArchiveDirValue(value);
440
446
  const displayValue = value === "." ? "\uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8" : value;
441
447
  moveToNextStep(displayValue, getStepLabel(currentStep));
442
448
  }
@@ -626,8 +632,16 @@ ${created.map((item) => `\u2022 ${item}`).join("\n")}` : "";
626
632
  if (!initialized) {
627
633
  return /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { color: "gray", children: "\uB85C\uB529 \uC911..." }) });
628
634
  }
635
+ const version = getVersion();
629
636
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
630
- /* @__PURE__ */ jsx(Box, { marginBottom: completedSteps.length > 0 ? 1 : 0, children: /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\u{1F680} ps-cli \uD504\uB85C\uC81D\uD2B8 \uCD08\uAE30\uD654" }) }),
637
+ /* @__PURE__ */ jsxs(Box, { marginBottom: completedSteps.length > 0 ? 1 : 0, children: [
638
+ /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\u{1F680} ps-cli \uD504\uB85C\uC81D\uD2B8 \uCD08\uAE30\uD654" }),
639
+ version && /* @__PURE__ */ jsxs(Text, { color: "gray", dimColor: true, children: [
640
+ " ",
641
+ "v",
642
+ version
643
+ ] })
644
+ ] }),
631
645
  completedSteps.length > 0 && /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: completedSteps.map((step, idx) => /* @__PURE__ */ jsxs(StatusMessage, { variant: "success", children: [
632
646
  step.label,
633
647
  ": ",
@@ -7,9 +7,8 @@ import {
7
7
  Command,
8
8
  CommandBuilder,
9
9
  CommandDef,
10
- getProblemId,
11
10
  resolveProblemContext
12
- } from "../chunk-TNGUME4H.js";
11
+ } from "../chunk-F4LZ6ENP.js";
13
12
  import {
14
13
  __decorateClass
15
14
  } from "../chunk-7MQMPJ3X.js";
@@ -63,11 +62,7 @@ function OpenView({ problemId, onComplete }) {
63
62
  }
64
63
  var OpenCommand = class extends Command {
65
64
  async execute(args, _flags) {
66
- const problemId = getProblemId(args);
67
- const context = await resolveProblemContext(
68
- problemId !== null ? [problemId.toString()] : [],
69
- { requireId: true }
70
- );
65
+ const context = await resolveProblemContext(args, { requireId: true });
71
66
  if (context.problemId === null) {
72
67
  console.error("\uC624\uB958: \uBB38\uC81C \uBC88\uD638\uB97C \uC785\uB825\uD574\uC8FC\uC138\uC694.");
73
68
  console.error(`\uC0AC\uC6A9\uBC95: ps open <\uBB38\uC81C\uBC88\uD638>`);