@rhseung/ps-cli 1.3.0 → 1.3.2

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,7 +4,8 @@ import {
4
4
  } from "./chunk-CIG2LEJC.js";
5
5
 
6
6
  // src/utils/problem-id.ts
7
- import { join } from "path";
7
+ import { join, dirname } from "path";
8
+ import { existsSync } from "fs";
8
9
  function detectProblemIdFromPath(cwd = process.cwd()) {
9
10
  const problemDir = getProblemDir();
10
11
  const normalizedPath = cwd.replace(/\\/g, "/");
@@ -50,12 +51,30 @@ function getProblemId(args, cwd = process.cwd()) {
50
51
  }
51
52
  return detectProblemIdFromPath(cwd);
52
53
  }
54
+ function findProjectRoot(startDir = process.cwd()) {
55
+ let currentDir = startDir;
56
+ const rootPath = process.platform === "win32" ? currentDir.split("\\")[0] + "\\" : "/";
57
+ while (currentDir !== rootPath) {
58
+ const projectConfigPath = join(currentDir, ".ps-cli.json");
59
+ if (existsSync(projectConfigPath)) {
60
+ return currentDir;
61
+ }
62
+ const parentDir = dirname(currentDir);
63
+ if (parentDir === currentDir) {
64
+ break;
65
+ }
66
+ currentDir = parentDir;
67
+ }
68
+ return null;
69
+ }
53
70
  function getProblemDirPath(problemId, cwd = process.cwd()) {
54
71
  const problemDir = getProblemDir();
72
+ const projectRoot = findProjectRoot(cwd);
73
+ const baseDir = projectRoot || cwd;
55
74
  if (problemDir === "." || problemDir === "") {
56
- return join(cwd, problemId.toString());
75
+ return join(baseDir, problemId.toString());
57
76
  }
58
- return join(cwd, problemDir, problemId.toString());
77
+ return join(baseDir, problemDir, problemId.toString());
59
78
  }
60
79
 
61
80
  export {
@@ -6,15 +6,15 @@ import {
6
6
  getTierName,
7
7
  source_default
8
8
  } from "../chunk-2E4VSP6O.js";
9
- import {
10
- getProblemDirPath,
11
- getProblemId
12
- } from "../chunk-BM3ZMA4K.js";
13
9
  import {
14
10
  getLanguageConfig,
15
11
  getSupportedLanguages,
16
12
  getSupportedLanguagesString
17
13
  } from "../chunk-TQXMB7XV.js";
14
+ import {
15
+ getProblemDirPath,
16
+ getProblemId
17
+ } from "../chunk-MOPDUECU.js";
18
18
  import {
19
19
  getAutoOpenEditor,
20
20
  getEditor
@@ -481,7 +481,7 @@ function FetchCommand({
481
481
  language
482
482
  );
483
483
  setStatus("success");
484
- setMessage(`\u2713 \uBB38\uC81C \uD30C\uC77C\uC774 \uC0DD\uC131\uB418\uC5C8\uC2B5\uB2C8\uB2E4: ${problemDir}`);
484
+ setMessage(`\uBB38\uC81C \uD30C\uC77C\uC774 \uC0DD\uC131\uB418\uC5C8\uC2B5\uB2C8\uB2E4: ${problemDir}`);
485
485
  if (getAutoOpenEditor()) {
486
486
  try {
487
487
  const editor = getEditor();
@@ -491,8 +491,8 @@ function FetchCommand({
491
491
  stdio: "ignore"
492
492
  });
493
493
  setMessage(
494
- `\u2713 \uBB38\uC81C \uD30C\uC77C\uC774 \uC0DD\uC131\uB418\uC5C8\uC2B5\uB2C8\uB2E4: ${problemDir}
495
- \u2713 ${editor}\uB85C \uC5F4\uC5C8\uC2B5\uB2C8\uB2E4.`
494
+ `\uBB38\uC81C \uD30C\uC77C\uC774 \uC0DD\uC131\uB418\uC5C8\uC2B5\uB2C8\uB2E4: ${problemDir}
495
+ ${editor}\uB85C \uC5F4\uC5C8\uC2B5\uB2C8\uB2E4.`
496
496
  );
497
497
  } catch (err) {
498
498
  console.warn(
@@ -23,6 +23,7 @@ import {
23
23
  Alert,
24
24
  ConfirmInput
25
25
  } from "@inkjs/ui";
26
+ import { execaCommand } from "execa";
26
27
  import { jsx, jsxs } from "react/jsx-runtime";
27
28
  function InitCommand({ onComplete }) {
28
29
  const [currentStep, setCurrentStep] = useState("problem-dir");
@@ -188,6 +189,41 @@ ${gitignorePattern}
188
189
  }
189
190
  }
190
191
  }
192
+ try {
193
+ const gitDir = join(cwd, ".git");
194
+ let isGitRepo = false;
195
+ try {
196
+ await access(gitDir);
197
+ isGitRepo = true;
198
+ } catch {
199
+ }
200
+ if (!isGitRepo) {
201
+ await execaCommand("git init", { cwd });
202
+ setCreated((prev) => [...prev, "Git \uC800\uC7A5\uC18C \uCD08\uAE30\uD654"]);
203
+ }
204
+ const filesToAdd = [".ps-cli.json"];
205
+ const gitignorePath = join(cwd, ".gitignore");
206
+ try {
207
+ await access(gitignorePath);
208
+ filesToAdd.push(".gitignore");
209
+ } catch {
210
+ }
211
+ if (filesToAdd.length > 0) {
212
+ await execaCommand(`git add ${filesToAdd.join(" ")}`, { cwd });
213
+ try {
214
+ await execaCommand("git rev-parse --verify HEAD", { cwd });
215
+ } catch {
216
+ await execaCommand(
217
+ 'git commit -m "chore: ps-cli \uD504\uB85C\uC81D\uD2B8 \uCD08\uAE30\uD654"',
218
+ { cwd }
219
+ );
220
+ setCreated((prev) => [...prev, "\uCD08\uAE30 \uCEE4\uBC0B \uC0DD\uC131"]);
221
+ }
222
+ }
223
+ } catch (err) {
224
+ const error = err;
225
+ console.warn("Git \uC5F0\uB3D9 \uC2E4\uD328:", error.message);
226
+ }
191
227
  setTimeout(() => {
192
228
  onComplete();
193
229
  }, 3e3);
@@ -0,0 +1,149 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ getProblemId
4
+ } from "../chunk-MOPDUECU.js";
5
+ import "../chunk-CIG2LEJC.js";
6
+ import "../chunk-FYS2JH42.js";
7
+
8
+ // src/commands/open.tsx
9
+ import { useState, useEffect } from "react";
10
+ import { render, Text, Box } from "ink";
11
+ import { StatusMessage, Alert } from "@inkjs/ui";
12
+ import { Spinner } from "@inkjs/ui";
13
+ import { execaCommand } from "execa";
14
+ import { jsx, jsxs } from "react/jsx-runtime";
15
+ var BOJ_BASE_URL = "https://www.acmicpc.net";
16
+ function OpenCommand({ problemId, onComplete }) {
17
+ const [status, setStatus] = useState(
18
+ "loading"
19
+ );
20
+ const [error, setError] = useState(null);
21
+ const [url, setUrl] = useState("");
22
+ useEffect(() => {
23
+ async function openBrowser() {
24
+ try {
25
+ const problemUrl = `${BOJ_BASE_URL}/problem/${problemId}`;
26
+ setUrl(problemUrl);
27
+ let command;
28
+ if (process.platform === "win32") {
29
+ command = `start "" "${problemUrl}"`;
30
+ } else if (process.platform === "darwin") {
31
+ command = `open "${problemUrl}"`;
32
+ } else {
33
+ command = `xdg-open "${problemUrl}"`;
34
+ }
35
+ await execaCommand(command, {
36
+ shell: true,
37
+ detached: true,
38
+ stdio: "ignore"
39
+ });
40
+ setStatus("success");
41
+ setTimeout(() => {
42
+ onComplete?.();
43
+ }, 1500);
44
+ } catch (err) {
45
+ setStatus("error");
46
+ setError(err instanceof Error ? err.message : String(err));
47
+ setTimeout(() => {
48
+ onComplete?.();
49
+ }, 2e3);
50
+ }
51
+ }
52
+ openBrowser();
53
+ }, [problemId, onComplete]);
54
+ if (status === "loading") {
55
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
56
+ /* @__PURE__ */ jsx(Spinner, { label: "\uBE0C\uB77C\uC6B0\uC800\uB97C \uC5EC\uB294 \uC911..." }),
57
+ /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
58
+ "\uBB38\uC81C #",
59
+ problemId
60
+ ] }) })
61
+ ] });
62
+ }
63
+ if (status === "error") {
64
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
65
+ /* @__PURE__ */ jsxs(Alert, { variant: "error", children: [
66
+ "\uBE0C\uB77C\uC6B0\uC800\uB97C \uC5F4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ",
67
+ error
68
+ ] }),
69
+ /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
70
+ "URL: ",
71
+ url
72
+ ] }) })
73
+ ] });
74
+ }
75
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", width: "100%", children: [
76
+ /* @__PURE__ */ jsx(StatusMessage, { variant: "success", children: "\uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uBB38\uC81C \uD398\uC774\uC9C0\uB97C \uC5F4\uC5C8\uC2B5\uB2C8\uB2E4!" }),
77
+ /* @__PURE__ */ jsxs(Box, { marginTop: 1, flexDirection: "column", children: [
78
+ /* @__PURE__ */ jsxs(Text, { children: [
79
+ /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\uBB38\uC81C \uBC88\uD638:" }),
80
+ " ",
81
+ problemId
82
+ ] }),
83
+ /* @__PURE__ */ jsxs(Text, { children: [
84
+ /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "URL:" }),
85
+ " ",
86
+ /* @__PURE__ */ jsx(Text, { color: "blue", underline: true, children: url })
87
+ ] })
88
+ ] })
89
+ ] });
90
+ }
91
+ async function openCommand(problemId) {
92
+ return new Promise((resolve) => {
93
+ const { unmount } = render(
94
+ /* @__PURE__ */ jsx(
95
+ OpenCommand,
96
+ {
97
+ problemId,
98
+ onComplete: () => {
99
+ unmount();
100
+ resolve();
101
+ }
102
+ }
103
+ )
104
+ );
105
+ });
106
+ }
107
+ var openHelp = `
108
+ \uC0AC\uC6A9\uBC95:
109
+ $ ps open [\uBB38\uC81C\uBC88\uD638]
110
+
111
+ \uC124\uBA85:
112
+ \uBC31\uC900 \uBB38\uC81C \uD398\uC774\uC9C0\uB97C \uBE0C\uB77C\uC6B0\uC800\uB85C \uC5FD\uB2C8\uB2E4.
113
+ - \uBB38\uC81C \uBC88\uD638\uB97C \uC778\uC790\uB85C \uC804\uB2EC\uD558\uAC70\uB098
114
+ - \uBB38\uC81C \uB514\uB809\uD1A0\uB9AC\uC5D0\uC11C \uC2E4\uD589\uD558\uBA74 \uC790\uB3D9\uC73C\uB85C \uBB38\uC81C \uBC88\uD638\uB97C \uCD94\uB860\uD569\uB2C8\uB2E4.
115
+
116
+ \uC608\uC81C:
117
+ $ ps open 1000 # 1000\uBC88 \uBB38\uC81C \uC5F4\uAE30
118
+ $ ps open # \uBB38\uC81C \uB514\uB809\uD1A0\uB9AC\uC5D0\uC11C \uC2E4\uD589 \uC2DC \uC790\uB3D9 \uCD94\uB860
119
+ $ ps open --help # \uB3C4\uC6C0\uB9D0 \uD45C\uC2DC
120
+ `;
121
+ async function openExecute(args, flags) {
122
+ if (flags.help) {
123
+ console.log(openHelp.trim());
124
+ process.exit(0);
125
+ return;
126
+ }
127
+ const problemId = getProblemId(args);
128
+ if (problemId === null) {
129
+ console.error("\uC624\uB958: \uBB38\uC81C \uBC88\uD638\uB97C \uC785\uB825\uD574\uC8FC\uC138\uC694.");
130
+ console.error(`\uC0AC\uC6A9\uBC95: ps open <\uBB38\uC81C\uBC88\uD638>`);
131
+ console.error(`\uB3C4\uC6C0\uB9D0: ps open --help`);
132
+ console.error(
133
+ `\uD78C\uD2B8: problems/{\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.`
134
+ );
135
+ process.exit(1);
136
+ }
137
+ await openCommand(problemId);
138
+ }
139
+ var openCommandDef = {
140
+ name: "open",
141
+ help: openHelp,
142
+ execute: openExecute
143
+ };
144
+ var open_default = openCommandDef;
145
+ export {
146
+ open_default as default,
147
+ openExecute,
148
+ openHelp
149
+ };
@@ -2,16 +2,16 @@
2
2
  import {
3
3
  runSolution
4
4
  } from "../chunk-EIFFWFLS.js";
5
- import {
6
- detectProblemIdFromPath,
7
- getProblemDirPath,
8
- getProblemId
9
- } from "../chunk-BM3ZMA4K.js";
10
5
  import {
11
6
  detectLanguageFromFile,
12
7
  getSupportedLanguages,
13
8
  getSupportedLanguagesString
14
9
  } from "../chunk-TQXMB7XV.js";
10
+ import {
11
+ detectProblemIdFromPath,
12
+ getProblemDirPath,
13
+ getProblemId
14
+ } from "../chunk-MOPDUECU.js";
15
15
  import "../chunk-CIG2LEJC.js";
16
16
  import "../chunk-FYS2JH42.js";
17
17
 
@@ -1,15 +1,15 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- detectProblemIdFromPath,
4
- getProblemDirPath,
5
- getProblemId
6
- } from "../chunk-BM3ZMA4K.js";
7
2
  import {
8
3
  detectLanguageFromFile,
9
4
  getLanguageConfig,
10
5
  getSupportedLanguages,
11
6
  getSupportedLanguagesString
12
7
  } from "../chunk-TQXMB7XV.js";
8
+ import {
9
+ detectProblemIdFromPath,
10
+ getProblemDirPath,
11
+ getProblemId
12
+ } from "../chunk-MOPDUECU.js";
13
13
  import {
14
14
  getBojSessionCookie,
15
15
  getCodeOpen
@@ -2,16 +2,16 @@
2
2
  import {
3
3
  runSolution
4
4
  } from "../chunk-EIFFWFLS.js";
5
- import {
6
- detectProblemIdFromPath,
7
- getProblemDirPath,
8
- getProblemId
9
- } from "../chunk-BM3ZMA4K.js";
10
5
  import {
11
6
  detectLanguageFromFile,
12
7
  getSupportedLanguages,
13
8
  getSupportedLanguagesString
14
9
  } from "../chunk-TQXMB7XV.js";
10
+ import {
11
+ detectProblemIdFromPath,
12
+ getProblemDirPath,
13
+ getProblemId
14
+ } from "../chunk-MOPDUECU.js";
15
15
  import "../chunk-CIG2LEJC.js";
16
16
  import "../chunk-FYS2JH42.js";
17
17
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rhseung/ps-cli",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "백준(BOJ) 문제 해결을 위한 통합 CLI 도구",
5
5
  "type": "module",
6
6
  "bin": {