@rhseung/ps-cli 1.0.0 → 1.3.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 CHANGED
@@ -36,6 +36,31 @@ bunx @rhseung/ps-cli fetch 1000
36
36
 
37
37
  ## 명령어
38
38
 
39
+ ### `init` - 프로젝트 초기화
40
+
41
+ 현재 디렉토리를 ps-cli 프로젝트로 초기화합니다.
42
+
43
+ ```bash
44
+ ps init
45
+ ```
46
+
47
+ **기능:**
48
+
49
+ - `problems/` 디렉토리 생성
50
+ - `.gitignore`에 `problems/` 추가 (이미 있으면 스킵)
51
+
52
+ **예제:**
53
+
54
+ ```bash
55
+ # 프로젝트 폴더에서 초기화
56
+ mkdir my-algorithm-problems
57
+ cd my-algorithm-problems
58
+ ps init
59
+
60
+ # 이제 문제를 가져올 수 있습니다
61
+ ps fetch 1000
62
+ ```
63
+
39
64
  ### `fetch` - 문제 가져오기
40
65
 
41
66
  백준 문제를 가져와서 로컬에 파일을 생성합니다.
@@ -206,6 +231,7 @@ ps config --list
206
231
  - `editor`: 에디터 명령어 (예: code, vim, nano)
207
232
  - `auto-open-editor`: fetch 후 자동으로 에디터 열기 (true/false)
208
233
  - `solved-ac-handle`: Solved.ac 핸들 (stats 명령어용)
234
+ - `problem-dir`: 문제 디렉토리 경로 (기본값: `problems`, `"."` 또는 `""`는 프로젝트 루트에 직접 저장)
209
235
 
210
236
  **옵션:**
211
237
 
@@ -247,24 +273,27 @@ ps <명령어> --help
247
273
  ### 전체 워크플로우
248
274
 
249
275
  ```bash
250
- # 1. 문제 가져오기
276
+ # 1. 프로젝트 초기화 (최초 1회)
277
+ ps init
278
+
279
+ # 2. 문제 가져오기
251
280
  ps fetch 1000 --language python
252
281
 
253
- # 2. 문제 디렉토리로 이동
282
+ # 3. 문제 디렉토리로 이동
254
283
  cd problems/1000
255
284
 
256
- # 3. 코드 작성 (solution.py 편집)
285
+ # 4. 코드 작성 (solution.py 편집)
257
286
 
258
- # 4. 로컬 테스트
287
+ # 5. 로컬 테스트
259
288
  ps test
260
289
 
261
- # 5. Watch 모드로 개발 (파일 저장 시 자동 테스트)
290
+ # 6. Watch 모드로 개발 (파일 저장 시 자동 테스트)
262
291
  ps test --watch
263
292
 
264
- # 6. 단일 입력으로 실행 테스트
293
+ # 7. 단일 입력으로 실행 테스트
265
294
  ps run
266
295
 
267
- # 7. BOJ에 제출
296
+ # 8. BOJ에 제출
268
297
  ps submit
269
298
  ```
270
299
 
@@ -283,6 +312,30 @@ ps config solved-ac-handle myhandle
283
312
  # fetch 후 자동으로 VS Code 열기
284
313
  ps config editor code
285
314
  ps config auto-open-editor true
315
+
316
+ # 문제 디렉토리 설정
317
+ ps config problem-dir "problems" # problems 디렉토리 사용 (기본값)
318
+ ps config problem-dir "." # 프로젝트 루트에 직접 저장
319
+ ps config problem-dir "solutions" # solutions 디렉토리 사용
320
+ ```
321
+
322
+ ### 문제 디렉토리 설정
323
+
324
+ `problem-dir` 설정을 통해 문제 파일이 저장되는 위치를 변경할 수 있습니다:
325
+
326
+ - **기본값 (`problems`)**: `problems/{문제번호}/` 형식으로 저장
327
+ - **프로젝트 루트 (`"."` 또는 `""`)**: 프로젝트 루트에 `{문제번호}/` 형식으로 직접 저장
328
+
329
+ **예제:**
330
+
331
+ ```bash
332
+ # 프로젝트 루트에 직접 저장하도록 설정
333
+ ps config problem-dir "."
334
+
335
+ # 문제 가져오기 (프로젝트 루트에 1000/ 디렉토리 생성)
336
+ ps fetch 1000
337
+
338
+ # 결과: ./1000/solution.py, ./1000/input1.txt 등
286
339
  ```
287
340
 
288
341
  ## 라이선스
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ getProblemDir
4
+ } from "./chunk-CIG2LEJC.js";
5
+
6
+ // src/utils/problem-id.ts
7
+ import { join } from "path";
8
+ function detectProblemIdFromPath(cwd = process.cwd()) {
9
+ const problemDir = getProblemDir();
10
+ const normalizedPath = cwd.replace(/\\/g, "/");
11
+ if (problemDir === "." || problemDir === "") {
12
+ const segments = normalizedPath.split("/").filter(Boolean);
13
+ const lastSegment = segments[segments.length - 1];
14
+ if (lastSegment) {
15
+ const problemId2 = parseInt(lastSegment, 10);
16
+ if (!isNaN(problemId2) && problemId2 > 0 && lastSegment === problemId2.toString()) {
17
+ return problemId2;
18
+ }
19
+ }
20
+ return null;
21
+ }
22
+ const dirPattern = `/${problemDir}/`;
23
+ const dirIndex = normalizedPath.indexOf(dirPattern);
24
+ if (dirIndex === -1) {
25
+ return null;
26
+ }
27
+ const afterDir = normalizedPath.substring(dirIndex + dirPattern.length);
28
+ if (!afterDir) {
29
+ return null;
30
+ }
31
+ const firstSegment = afterDir.split("/")[0];
32
+ if (!firstSegment) {
33
+ return null;
34
+ }
35
+ const problemId = parseInt(firstSegment, 10);
36
+ if (isNaN(problemId) || problemId <= 0) {
37
+ return null;
38
+ }
39
+ if (firstSegment !== problemId.toString()) {
40
+ return null;
41
+ }
42
+ return problemId;
43
+ }
44
+ function getProblemId(args, cwd = process.cwd()) {
45
+ if (args.length > 0 && args[0]) {
46
+ const problemId = parseInt(args[0], 10);
47
+ if (!isNaN(problemId) && problemId > 0) {
48
+ return problemId;
49
+ }
50
+ }
51
+ return detectProblemIdFromPath(cwd);
52
+ }
53
+ function getProblemDirPath(problemId, cwd = process.cwd()) {
54
+ const problemDir = getProblemDir();
55
+ if (problemDir === "." || problemDir === "") {
56
+ return join(cwd, problemId.toString());
57
+ }
58
+ return join(cwd, problemDir, problemId.toString());
59
+ }
60
+
61
+ export {
62
+ detectProblemIdFromPath,
63
+ getProblemId,
64
+ getProblemDirPath
65
+ };
@@ -9878,6 +9878,8 @@ var Conf = class {
9878
9878
  };
9879
9879
 
9880
9880
  // src/utils/config.ts
9881
+ import { readFileSync, existsSync } from "fs";
9882
+ import { join } from "path";
9881
9883
  var config = new Conf({
9882
9884
  projectName: "ps-cli",
9883
9885
  defaults: {
@@ -9888,9 +9890,40 @@ var config = new Conf({
9888
9890
  // 기본값: VS Code
9889
9891
  autoOpenEditor: false,
9890
9892
  // 기본값: 자동 열기 비활성화
9891
- solvedAcHandle: void 0
9893
+ solvedAcHandle: void 0,
9894
+ problemDir: "problems"
9895
+ // 기본값: problems 디렉토리
9892
9896
  }
9893
9897
  });
9898
+ var projectConfigCache = null;
9899
+ var projectConfigCachePath = null;
9900
+ function getProjectConfigSync() {
9901
+ try {
9902
+ const cwd = process.cwd();
9903
+ const projectConfigPath = join(cwd, ".ps-cli.json");
9904
+ if (projectConfigCache && projectConfigCachePath === projectConfigPath) {
9905
+ return projectConfigCache;
9906
+ }
9907
+ if (!existsSync(projectConfigPath)) {
9908
+ projectConfigCache = null;
9909
+ projectConfigCachePath = null;
9910
+ return null;
9911
+ }
9912
+ try {
9913
+ const content = readFileSync(projectConfigPath, "utf-8");
9914
+ const projectConfig = JSON.parse(content);
9915
+ projectConfigCache = projectConfig;
9916
+ projectConfigCachePath = projectConfigPath;
9917
+ return projectConfig;
9918
+ } catch {
9919
+ projectConfigCache = null;
9920
+ projectConfigCachePath = null;
9921
+ return null;
9922
+ }
9923
+ } catch {
9924
+ return null;
9925
+ }
9926
+ }
9894
9927
  function getBojSessionCookie() {
9895
9928
  const envCookie = process.env.PS_CLI_BOJ_COOKIE;
9896
9929
  if (envCookie) {
@@ -9898,51 +9931,51 @@ function getBojSessionCookie() {
9898
9931
  }
9899
9932
  return config.get("bojSessionCookie");
9900
9933
  }
9901
- function setBojSessionCookie(cookie) {
9902
- config.set("bojSessionCookie", cookie);
9903
- }
9904
9934
  function getDefaultLanguage() {
9935
+ const projectConfig = getProjectConfigSync();
9936
+ if (projectConfig?.defaultLanguage) {
9937
+ return projectConfig.defaultLanguage;
9938
+ }
9905
9939
  return config.get("defaultLanguage") ?? "python";
9906
9940
  }
9907
- function setDefaultLanguage(language) {
9908
- config.set("defaultLanguage", language);
9909
- }
9910
9941
  function getCodeOpen() {
9911
9942
  return config.get("codeOpen") ?? false;
9912
9943
  }
9913
- function setCodeOpen(open) {
9914
- config.set("codeOpen", open);
9915
- }
9916
9944
  function getEditor() {
9945
+ const projectConfig = getProjectConfigSync();
9946
+ if (projectConfig?.editor) {
9947
+ return projectConfig.editor;
9948
+ }
9917
9949
  return config.get("editor") ?? "code";
9918
9950
  }
9919
- function setEditor(editor) {
9920
- config.set("editor", editor);
9921
- }
9922
9951
  function getAutoOpenEditor() {
9952
+ const projectConfig = getProjectConfigSync();
9953
+ if (projectConfig?.autoOpenEditor !== void 0) {
9954
+ return projectConfig.autoOpenEditor;
9955
+ }
9923
9956
  return config.get("autoOpenEditor") ?? false;
9924
9957
  }
9925
- function setAutoOpenEditor(enabled) {
9926
- config.set("autoOpenEditor", enabled);
9927
- }
9928
9958
  function getSolvedAcHandle() {
9959
+ const projectConfig = getProjectConfigSync();
9960
+ if (projectConfig?.solvedAcHandle) {
9961
+ return projectConfig.solvedAcHandle;
9962
+ }
9929
9963
  return config.get("solvedAcHandle");
9930
9964
  }
9931
- function setSolvedAcHandle(handle) {
9932
- config.set("solvedAcHandle", handle);
9965
+ function getProblemDir() {
9966
+ const projectConfig = getProjectConfigSync();
9967
+ if (projectConfig?.problemDir !== void 0) {
9968
+ return projectConfig.problemDir;
9969
+ }
9970
+ return config.get("problemDir") ?? "problems";
9933
9971
  }
9934
9972
 
9935
9973
  export {
9936
9974
  getBojSessionCookie,
9937
- setBojSessionCookie,
9938
9975
  getDefaultLanguage,
9939
- setDefaultLanguage,
9940
9976
  getCodeOpen,
9941
- setCodeOpen,
9942
9977
  getEditor,
9943
- setEditor,
9944
9978
  getAutoOpenEditor,
9945
- setAutoOpenEditor,
9946
9979
  getSolvedAcHandle,
9947
- setSolvedAcHandle
9980
+ getProblemDir
9948
9981
  };