@rhseung/ps-cli 1.7.2 → 1.7.4
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 +285 -24
- package/dist/{chunk-TNGUME4H.js → chunk-F4LZ6ENP.js} +23 -24
- package/dist/commands/{solve.js → archive.js} +37 -37
- package/dist/commands/config.js +16 -16
- package/dist/commands/fetch.js +37 -13
- package/dist/commands/init.js +51 -37
- package/dist/commands/open.js +2 -7
- package/dist/commands/run.js +5 -9
- package/dist/commands/search.js +4 -4
- package/dist/commands/stats.js +1 -1
- package/dist/commands/submit.js +5 -10
- package/dist/commands/test.js +4 -8
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -24,99 +24,313 @@ ps test
|
|
|
24
24
|
|
|
25
25
|
# 4. 제출
|
|
26
26
|
ps submit
|
|
27
|
+
|
|
28
|
+
# 5. 커밋 및 아카이빙
|
|
29
|
+
ps archive
|
|
27
30
|
```
|
|
28
31
|
|
|
29
32
|
## 명령어
|
|
30
33
|
|
|
31
34
|
### `init` - 프로젝트 초기화
|
|
32
35
|
|
|
33
|
-
프로젝트를 초기화하고 설정을 구성합니다.
|
|
36
|
+
프로젝트를 대화형으로 초기화하고 설정을 구성합니다.
|
|
37
|
+
|
|
38
|
+
**사용법:**
|
|
34
39
|
|
|
35
40
|
```bash
|
|
36
41
|
ps init
|
|
37
42
|
```
|
|
38
43
|
|
|
44
|
+
**설명:**
|
|
45
|
+
|
|
46
|
+
- 단계별로 설정을 물어봅니다
|
|
47
|
+
- 아카이브 디렉토리, solving 디렉토리, 아카이빙 전략, 기본 언어, 에디터 등을 설정할 수 있습니다
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
39
51
|
### `fetch` - 문제 가져오기
|
|
40
52
|
|
|
41
53
|
백준 문제를 가져와서 로컬에 파일을 생성합니다.
|
|
42
54
|
|
|
55
|
+
**사용법:**
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
ps fetch <문제번호> [옵션]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**옵션:**
|
|
62
|
+
|
|
63
|
+
- `--language`, `-l`: 언어 선택 (python, javascript, typescript, cpp)
|
|
64
|
+
- 기본값: python 또는 설정 파일의 `default-language`
|
|
65
|
+
|
|
66
|
+
**예제:**
|
|
67
|
+
|
|
43
68
|
```bash
|
|
44
69
|
ps fetch 1000
|
|
45
70
|
ps fetch 1000 --language python
|
|
71
|
+
ps fetch 1000 -l cpp
|
|
46
72
|
```
|
|
47
73
|
|
|
74
|
+
**설명:**
|
|
75
|
+
|
|
76
|
+
- Solved.ac API와 BOJ 크롤링을 통해 문제 정보 수집
|
|
77
|
+
- 문제 설명, 입출력 형식, 예제 입출력 파일 자동 생성
|
|
78
|
+
- 선택한 언어의 솔루션 템플릿 파일 생성
|
|
79
|
+
- README.md에 문제 정보, 통계, 태그 등 포함
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
48
83
|
### `test` - 로컬 테스트
|
|
49
84
|
|
|
50
85
|
예제 입출력으로 테스트를 실행합니다.
|
|
51
86
|
|
|
87
|
+
**사용법:**
|
|
88
|
+
|
|
52
89
|
```bash
|
|
53
|
-
ps test
|
|
54
|
-
|
|
55
|
-
|
|
90
|
+
ps test [문제번호] [옵션]
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**옵션:**
|
|
94
|
+
|
|
95
|
+
- `--language`, `-l`: 언어 선택 (지정 시 자동 감지 무시)
|
|
96
|
+
- 지원 언어: python, javascript, typescript, cpp
|
|
97
|
+
- `--watch`, `-w`: watch 모드 (파일 변경 시 자동 재테스트)
|
|
98
|
+
- solution._, input_.txt, output\*.txt 파일 변경 감지
|
|
99
|
+
|
|
100
|
+
**예제:**
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
ps test # 현재 디렉토리에서 테스트
|
|
104
|
+
ps test 1000 # 1000번 문제 테스트
|
|
105
|
+
ps test --watch # watch 모드로 테스트
|
|
106
|
+
ps test 1000 --watch # 1000번 문제를 watch 모드로 테스트
|
|
107
|
+
ps test --language python # Python으로 테스트
|
|
56
108
|
```
|
|
57
109
|
|
|
110
|
+
**설명:**
|
|
111
|
+
|
|
112
|
+
- 현재 디렉토리 또는 지정한 문제 번호의 테스트 실행
|
|
113
|
+
- solution.\* 파일을 자동으로 찾아 언어 감지
|
|
114
|
+
- input*.txt와 output*.txt 파일을 기반으로 테스트
|
|
115
|
+
- 문제의 시간 제한을 자동으로 적용
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
58
119
|
### `run` - 코드 실행
|
|
59
120
|
|
|
60
121
|
테스트 없이 코드를 실행합니다.
|
|
61
122
|
|
|
123
|
+
**사용법:**
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
ps run [문제번호] [옵션]
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**옵션:**
|
|
130
|
+
|
|
131
|
+
- `--language`, `-l`: 언어 선택 (지정 시 자동 감지 무시)
|
|
132
|
+
- 지원 언어: python, javascript, typescript, cpp
|
|
133
|
+
- `--input`, `-i`: 입력 파일 지정
|
|
134
|
+
- 기본값: input.txt 또는 input1.txt
|
|
135
|
+
|
|
136
|
+
**예제:**
|
|
137
|
+
|
|
62
138
|
```bash
|
|
63
|
-
ps run
|
|
64
|
-
ps run 1000
|
|
139
|
+
ps run # 현재 디렉토리에서 실행
|
|
140
|
+
ps run 1000 # 1000번 문제 실행
|
|
141
|
+
ps run --language python # Python으로 실행
|
|
142
|
+
ps run --input input2.txt # 특정 입력 파일 사용
|
|
65
143
|
```
|
|
66
144
|
|
|
145
|
+
**설명:**
|
|
146
|
+
|
|
147
|
+
- 현재 디렉토리 또는 지정한 문제 번호의 코드 실행
|
|
148
|
+
- solution.\* 파일을 자동으로 찾아 언어 감지
|
|
149
|
+
- input.txt 또는 input1.txt를 표준 입력으로 사용
|
|
150
|
+
- 테스트 케이스 검증 없이 단순 실행
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
67
154
|
### `submit` - 제출
|
|
68
155
|
|
|
69
156
|
백준 제출 페이지를 열고 소스 코드를 클립보드에 복사합니다.
|
|
70
157
|
|
|
158
|
+
**사용법:**
|
|
159
|
+
|
|
71
160
|
```bash
|
|
72
|
-
ps submit
|
|
73
|
-
|
|
161
|
+
ps submit [문제번호] [옵션]
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**옵션:**
|
|
165
|
+
|
|
166
|
+
- `--language`, `-l`: 언어 선택 (지정 시 자동 감지 무시)
|
|
167
|
+
- 지원 언어: python, javascript, typescript, cpp
|
|
168
|
+
|
|
169
|
+
**예제:**
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
ps submit # 현재 디렉토리에서 제출
|
|
173
|
+
ps submit 1000 # 1000번 문제 제출
|
|
174
|
+
ps submit --language python # Python으로 제출
|
|
74
175
|
```
|
|
75
176
|
|
|
76
|
-
|
|
177
|
+
**설명:**
|
|
178
|
+
|
|
179
|
+
- 문제 번호를 인자로 전달하거나 문제 디렉토리에서 실행하면 자동으로 문제 번호를 추론
|
|
180
|
+
- solution.\* 파일을 자동으로 찾아 언어 감지
|
|
181
|
+
- 소스 코드를 클립보드에 자동 복사
|
|
182
|
+
- 제출 페이지를 브라우저로 자동 열기
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
### `archive` - 아카이빙
|
|
187
|
+
|
|
188
|
+
solving 디렉토리의 문제를 archive 디렉토리로 이동하고 Git 커밋을 생성합니다.
|
|
189
|
+
|
|
190
|
+
**사용법:**
|
|
77
191
|
|
|
78
|
-
|
|
192
|
+
```bash
|
|
193
|
+
ps archive [문제번호]
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**예제:**
|
|
79
197
|
|
|
80
198
|
```bash
|
|
81
|
-
ps
|
|
199
|
+
ps archive 1000 # 1000번 문제 아카이빙
|
|
200
|
+
ps archive # 현재 디렉토리에서 문제 번호 자동 감지
|
|
82
201
|
```
|
|
83
202
|
|
|
203
|
+
**설명:**
|
|
204
|
+
|
|
205
|
+
- solving 디렉토리에서 문제를 찾아 archive 디렉토리로 이동
|
|
206
|
+
- Git add 및 commit 실행
|
|
207
|
+
- 커밋 메시지: "solve: {문제번호} - {문제이름}"
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
84
211
|
### `open` - 문제 페이지 열기
|
|
85
212
|
|
|
86
213
|
백준 문제 페이지를 브라우저로 엽니다.
|
|
87
214
|
|
|
215
|
+
**사용법:**
|
|
216
|
+
|
|
88
217
|
```bash
|
|
89
|
-
ps open
|
|
218
|
+
ps open [문제번호]
|
|
90
219
|
```
|
|
91
220
|
|
|
221
|
+
**예제:**
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
ps open 1000 # 1000번 문제 열기
|
|
225
|
+
ps open # 문제 디렉토리에서 실행 시 자동 추론
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
**설명:**
|
|
229
|
+
|
|
230
|
+
- 문제 번호를 인자로 전달하거나 문제 디렉토리에서 실행하면 자동으로 문제 번호를 추론
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
92
234
|
### `search` - 문제 검색
|
|
93
235
|
|
|
94
|
-
solved.ac에서 문제를
|
|
236
|
+
solved.ac에서 문제를 검색하거나 백준 문제집의 문제 목록을 표시합니다.
|
|
237
|
+
|
|
238
|
+
**사용법:**
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
ps search <쿼리> [옵션]
|
|
242
|
+
ps search --workbook <문제집ID>
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
**옵션:**
|
|
246
|
+
|
|
247
|
+
- `--workbook`: 문제집 ID를 지정하여 해당 문제집의 문제 목록을 표시
|
|
248
|
+
|
|
249
|
+
**예제:**
|
|
95
250
|
|
|
96
251
|
```bash
|
|
97
|
-
ps search "*g1...g5"
|
|
98
|
-
ps search
|
|
252
|
+
ps search "*g1...g5" # Gold 1-5 문제 검색
|
|
253
|
+
ps search "tier:g1...g5" # Gold 1-5 문제 검색 (tier: 문법)
|
|
254
|
+
ps search "#dp" # DP 태그 문제 검색
|
|
255
|
+
ps search "tag:dp" # DP 태그 문제 검색 (tag: 문법)
|
|
256
|
+
ps search "*g1...g5 #dp" # Gold 1-5 티어의 DP 태그 문제 검색
|
|
257
|
+
ps search --workbook 25052 # 문제집 25052의 문제 목록 표시
|
|
99
258
|
```
|
|
100
259
|
|
|
260
|
+
**설명:**
|
|
261
|
+
|
|
262
|
+
- solved.ac 검색어 문법을 지원합니다
|
|
263
|
+
- 문제 목록에서 선택하면 자동으로 브라우저에서 문제 페이지를 엽니다
|
|
264
|
+
- 페이지네이션을 통해 여러 페이지의 결과를 탐색할 수 있습니다
|
|
265
|
+
- `--workbook` 옵션으로 백준 문제집의 문제 목록을 볼 수 있습니다
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
101
269
|
### `stats` - 통계 조회
|
|
102
270
|
|
|
103
|
-
Solved.ac 사용자 통계를 조회합니다.
|
|
271
|
+
Solved.ac에서 사용자 통계를 조회합니다.
|
|
272
|
+
|
|
273
|
+
**사용법:**
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
ps stats [핸들] [옵션]
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
**옵션:**
|
|
280
|
+
|
|
281
|
+
- `--handle`, `-h`: Solved.ac 핸들
|
|
282
|
+
- 설정에 저장된 값 사용 가능
|
|
283
|
+
- 인자로 전달하거나 플래그로 지정 가능
|
|
284
|
+
|
|
285
|
+
**예제:**
|
|
104
286
|
|
|
105
287
|
```bash
|
|
106
|
-
ps stats
|
|
107
|
-
ps stats myhandle
|
|
288
|
+
ps stats myhandle # myhandle의 통계 조회
|
|
289
|
+
ps stats --handle myhandle # 플래그로 핸들 지정
|
|
290
|
+
ps stats # 설정에 저장된 핸들 사용
|
|
108
291
|
```
|
|
109
292
|
|
|
293
|
+
**설명:**
|
|
294
|
+
|
|
295
|
+
- 티어, 레이팅, 해결한 문제 수 등 표시
|
|
296
|
+
- 그라데이션으로 시각적으로 표시
|
|
297
|
+
- 핸들 우선순위: 인자 > 플래그 > 설정 파일
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
110
301
|
### `config` - 설정 관리
|
|
111
302
|
|
|
112
|
-
프로젝트
|
|
303
|
+
프로젝트 설정 파일(.ps-cli.json)을 관리합니다.
|
|
304
|
+
|
|
305
|
+
**사용법:**
|
|
113
306
|
|
|
114
307
|
```bash
|
|
115
|
-
ps config
|
|
116
|
-
ps config set default-language python
|
|
117
|
-
ps config get archive-strategy
|
|
308
|
+
ps config <명령어> [키] [값]
|
|
118
309
|
```
|
|
119
310
|
|
|
311
|
+
**명령어:**
|
|
312
|
+
|
|
313
|
+
- `get [키]`: 설정 값 조회 (키 없으면 대화형 선택)
|
|
314
|
+
- `set [키] [값]`: 설정 값 설정 (키/값 없으면 대화형 선택)
|
|
315
|
+
- `list`: 모든 설정 조회
|
|
316
|
+
- `clear`: .ps-cli.json 파일 삭제
|
|
317
|
+
|
|
318
|
+
**예제:**
|
|
319
|
+
|
|
320
|
+
```bash
|
|
321
|
+
ps config get # 대화형으로 키 선택 후 값 조회
|
|
322
|
+
ps config get default-language # default-language 값 조회
|
|
323
|
+
ps config set # 대화형으로 키 선택 후 값 설정
|
|
324
|
+
ps config set editor cursor # editor를 cursor로 설정
|
|
325
|
+
ps config list # 모든 설정 조회
|
|
326
|
+
ps config clear # .ps-cli.json 파일 삭제
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
**설명:**
|
|
330
|
+
|
|
331
|
+
- 설정은 현재 프로젝트의 .ps-cli.json 파일에 저장됩니다
|
|
332
|
+
- 대화형 모드로 키와 값을 선택할 수 있습니다
|
|
333
|
+
|
|
120
334
|
## 설정
|
|
121
335
|
|
|
122
336
|
프로젝트 루트의 `.ps-cli.json` 파일에 저장됩니다.
|
|
@@ -127,7 +341,7 @@ ps config get archive-strategy
|
|
|
127
341
|
- `editor`: 에디터 명령어 (code, cursor, vim 등)
|
|
128
342
|
- `auto-open-editor`: fetch 후 자동으로 에디터 열기 (true/false)
|
|
129
343
|
- `solved-ac-handle`: Solved.ac 핸들
|
|
130
|
-
- `
|
|
344
|
+
- `archive-dir`: 아카이브된 문제 디렉토리 (기본값: problems)
|
|
131
345
|
- `solving-dir`: 푸는 중인 문제 디렉토리 (기본값: solving)
|
|
132
346
|
- `archive-strategy`: 아카이빙 전략
|
|
133
347
|
|
|
@@ -173,7 +387,54 @@ ps config get archive-strategy
|
|
|
173
387
|
3. **작성**: `solving/1000/`에서 코드 작성
|
|
174
388
|
4. **테스트**: `ps test`로 로컬 테스트
|
|
175
389
|
5. **제출**: `ps submit`으로 제출
|
|
176
|
-
6. **아카이빙**: `ps
|
|
390
|
+
6. **아카이빙**: `ps archive`로 archive 디렉토리로 이동
|
|
391
|
+
|
|
392
|
+
## 개발
|
|
393
|
+
|
|
394
|
+
로컬에서 개발하거나 테스트할 때는 글로벌로 설치된 `ps` 명령어와 충돌을 피하기 위해 다음 방법을 사용할 수 있습니다:
|
|
395
|
+
|
|
396
|
+
### 방법 1: 절대 경로로 직접 실행 (외부 폴더 테스트 가능)
|
|
397
|
+
|
|
398
|
+
```bash
|
|
399
|
+
# 빌드
|
|
400
|
+
bun run build
|
|
401
|
+
|
|
402
|
+
# 프로젝트 디렉토리에서 절대 경로로 실행
|
|
403
|
+
/path/to/ps-cli/dist/index.js init
|
|
404
|
+
/path/to/ps-cli/dist/index.js fetch 1000
|
|
405
|
+
|
|
406
|
+
# 또는 프로젝트 디렉토리로 이동 후
|
|
407
|
+
cd /path/to/ps-cli
|
|
408
|
+
node dist/index.js init
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### 방법 2: npm link 사용 (주의 필요)
|
|
412
|
+
|
|
413
|
+
```bash
|
|
414
|
+
# 프로젝트 디렉토리에서
|
|
415
|
+
bun run build
|
|
416
|
+
npm link
|
|
417
|
+
|
|
418
|
+
# 외부 폴더에서 테스트
|
|
419
|
+
cd /path/to/test-project
|
|
420
|
+
ps init # 로컬 버전이 사용됨
|
|
421
|
+
|
|
422
|
+
# 테스트 후 링크 해제
|
|
423
|
+
npm unlink -g @rhseung/ps-cli
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
**주의:** `npm link`를 사용하면 글로벌 설치된 버전이 링크된 버전으로 대체됩니다.
|
|
427
|
+
|
|
428
|
+
### 방법 3: 프로젝트 내에서만 테스트
|
|
429
|
+
|
|
430
|
+
```bash
|
|
431
|
+
# 빌드
|
|
432
|
+
bun run build
|
|
433
|
+
|
|
434
|
+
# 프로젝트 디렉토리 내에서만
|
|
435
|
+
bun run ps init
|
|
436
|
+
bun run ps fetch 1000
|
|
437
|
+
```
|
|
177
438
|
|
|
178
439
|
## 라이선스
|
|
179
440
|
|
|
@@ -9924,7 +9924,7 @@ var config = new Conf({
|
|
|
9924
9924
|
autoOpenEditor: false,
|
|
9925
9925
|
// 기본값: 자동 열기 비활성화
|
|
9926
9926
|
solvedAcHandle: void 0,
|
|
9927
|
-
|
|
9927
|
+
archiveDir: "problems",
|
|
9928
9928
|
// 기본값: problems 디렉토리
|
|
9929
9929
|
solvingDir: "solving"
|
|
9930
9930
|
// 기본값: solving 디렉토리
|
|
@@ -10009,12 +10009,12 @@ function getSolvedAcHandle() {
|
|
|
10009
10009
|
}
|
|
10010
10010
|
return config.get("solvedAcHandle");
|
|
10011
10011
|
}
|
|
10012
|
-
function
|
|
10012
|
+
function getArchiveDir() {
|
|
10013
10013
|
const projectConfig = getProjectConfigSync();
|
|
10014
|
-
if (projectConfig?.
|
|
10015
|
-
return projectConfig.
|
|
10014
|
+
if (projectConfig?.archiveDir !== void 0) {
|
|
10015
|
+
return projectConfig.archiveDir;
|
|
10016
10016
|
}
|
|
10017
|
-
return config.get("
|
|
10017
|
+
return config.get("archiveDir") ?? "problems";
|
|
10018
10018
|
}
|
|
10019
10019
|
function getSolvingDir() {
|
|
10020
10020
|
const projectConfig = getProjectConfigSync();
|
|
@@ -10264,11 +10264,11 @@ function getArchiveSubPath(problemId, strategy = "flat", problem) {
|
|
|
10264
10264
|
}
|
|
10265
10265
|
}
|
|
10266
10266
|
function detectProblemIdFromPath(cwd = process.cwd()) {
|
|
10267
|
-
const
|
|
10267
|
+
const archiveDir = getArchiveDir();
|
|
10268
10268
|
const solvingDir = getSolvingDir();
|
|
10269
10269
|
const archiveStrategy = getArchiveStrategy();
|
|
10270
10270
|
const normalizedPath = cwd.replace(/\\/g, "/");
|
|
10271
|
-
const dirsToCheck = [
|
|
10271
|
+
const dirsToCheck = [archiveDir, solvingDir].filter(
|
|
10272
10272
|
(dir) => dir && dir !== "." && dir !== ""
|
|
10273
10273
|
);
|
|
10274
10274
|
if (dirsToCheck.length === 0) {
|
|
@@ -10359,22 +10359,22 @@ function getProblemId(args, cwd = process.cwd()) {
|
|
|
10359
10359
|
}
|
|
10360
10360
|
return detectProblemIdFromPath(cwd);
|
|
10361
10361
|
}
|
|
10362
|
-
function
|
|
10363
|
-
const
|
|
10362
|
+
function getArchiveDirPath(problemId, cwd = process.cwd(), problem) {
|
|
10363
|
+
const archiveDir = getArchiveDir();
|
|
10364
10364
|
const archiveStrategy = getArchiveStrategy();
|
|
10365
10365
|
const projectRoot = findProjectRoot(cwd);
|
|
10366
10366
|
const baseDir = projectRoot || cwd;
|
|
10367
10367
|
const subPath = getArchiveSubPath(problemId, archiveStrategy, problem);
|
|
10368
|
-
if (
|
|
10368
|
+
if (archiveDir === "." || archiveDir === "") {
|
|
10369
10369
|
if (subPath) {
|
|
10370
10370
|
return join2(baseDir, subPath, problemId.toString());
|
|
10371
10371
|
}
|
|
10372
10372
|
return join2(baseDir, problemId.toString());
|
|
10373
10373
|
}
|
|
10374
10374
|
if (subPath) {
|
|
10375
|
-
return join2(baseDir,
|
|
10375
|
+
return join2(baseDir, archiveDir, subPath, problemId.toString());
|
|
10376
10376
|
}
|
|
10377
|
-
return join2(baseDir,
|
|
10377
|
+
return join2(baseDir, archiveDir, problemId.toString());
|
|
10378
10378
|
}
|
|
10379
10379
|
function getSolvingDirPath(problemId, cwd = process.cwd(), _) {
|
|
10380
10380
|
const solvingDir = getSolvingDir();
|
|
@@ -10408,27 +10408,27 @@ async function resolveProblemContext(args, options = {}) {
|
|
|
10408
10408
|
);
|
|
10409
10409
|
}
|
|
10410
10410
|
const isCurrentDir = problemId === null || problemId !== null && currentPathProblemId === problemId;
|
|
10411
|
-
let
|
|
10411
|
+
let archiveDir;
|
|
10412
10412
|
if (problemId && !isCurrentDir) {
|
|
10413
10413
|
const solvingDirPath = getSolvingDirPath(problemId);
|
|
10414
|
-
const
|
|
10414
|
+
const archiveDirPath = getArchiveDirPath(problemId);
|
|
10415
10415
|
const solvingDirExists = await directoryExists(solvingDirPath);
|
|
10416
10416
|
if (solvingDirExists) {
|
|
10417
|
-
|
|
10417
|
+
archiveDir = solvingDirPath;
|
|
10418
10418
|
} else {
|
|
10419
|
-
const
|
|
10420
|
-
if (
|
|
10421
|
-
|
|
10419
|
+
const archiveDirExists = await directoryExists(archiveDirPath);
|
|
10420
|
+
if (archiveDirExists) {
|
|
10421
|
+
archiveDir = archiveDirPath;
|
|
10422
10422
|
} else {
|
|
10423
|
-
|
|
10423
|
+
archiveDir = solvingDirPath;
|
|
10424
10424
|
}
|
|
10425
10425
|
}
|
|
10426
10426
|
} else {
|
|
10427
|
-
|
|
10427
|
+
archiveDir = process.cwd();
|
|
10428
10428
|
}
|
|
10429
10429
|
return {
|
|
10430
10430
|
problemId,
|
|
10431
|
-
|
|
10431
|
+
archiveDir,
|
|
10432
10432
|
isCurrentDir
|
|
10433
10433
|
};
|
|
10434
10434
|
}
|
|
@@ -10594,7 +10594,7 @@ export {
|
|
|
10594
10594
|
getEditor,
|
|
10595
10595
|
getAutoOpenEditor,
|
|
10596
10596
|
getSolvedAcHandle,
|
|
10597
|
-
|
|
10597
|
+
getArchiveDir,
|
|
10598
10598
|
getSolvingDir,
|
|
10599
10599
|
getArchiveStrategy,
|
|
10600
10600
|
getNextTierMinRating,
|
|
@@ -10603,8 +10603,7 @@ export {
|
|
|
10603
10603
|
getTierColor,
|
|
10604
10604
|
getTierImageUrl,
|
|
10605
10605
|
detectProblemIdFromPath,
|
|
10606
|
-
|
|
10607
|
-
getProblemDirPath,
|
|
10606
|
+
getArchiveDirPath,
|
|
10608
10607
|
getSolvingDirPath,
|
|
10609
10608
|
resolveProblemContext,
|
|
10610
10609
|
resolveLanguage,
|
|
@@ -4,26 +4,26 @@ import {
|
|
|
4
4
|
CommandBuilder,
|
|
5
5
|
CommandDef,
|
|
6
6
|
findProjectRoot,
|
|
7
|
-
|
|
8
|
-
getProblemId,
|
|
7
|
+
getArchiveDirPath,
|
|
9
8
|
getSolvingDir,
|
|
10
|
-
getSolvingDirPath
|
|
11
|
-
|
|
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/
|
|
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-
|
|
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
|
|
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
|
|
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
|
|
74
|
+
const archiveDir = getArchiveDirPath(problemId, projectRoot, problem);
|
|
75
75
|
try {
|
|
76
|
-
await access(
|
|
76
|
+
await access(archiveDir);
|
|
77
77
|
throw new Error(
|
|
78
|
-
`
|
|
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
|
|
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(
|
|
88
|
-
setMessage("\uBB38\uC81C\uB97C
|
|
89
|
-
await rename(solvingDir,
|
|
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",
|
|
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: ${
|
|
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
|
|
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/
|
|
147
|
+
// src/commands/archive.tsx
|
|
148
148
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
149
|
-
function
|
|
150
|
-
const { status, message, error } =
|
|
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
|
|
165
|
+
var ArchiveCommand = class extends Command {
|
|
166
166
|
async execute(args, _) {
|
|
167
|
-
const
|
|
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
|
|
171
|
-
console.error(`\uB3C4\uC6C0\uB9D0: ps
|
|
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(
|
|
179
|
-
problemId
|
|
178
|
+
await this.renderView(ArchiveView, {
|
|
179
|
+
problemId: context.problemId
|
|
180
180
|
});
|
|
181
181
|
}
|
|
182
182
|
};
|
|
183
|
-
|
|
183
|
+
ArchiveCommand = __decorateClass([
|
|
184
184
|
CommandDef({
|
|
185
|
-
name: "
|
|
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
|
|
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
|
-
"
|
|
193
|
-
"
|
|
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
|
-
],
|
|
197
|
-
var
|
|
196
|
+
], ArchiveCommand);
|
|
197
|
+
var archive_default = CommandBuilder.fromClass(ArchiveCommand);
|
|
198
198
|
export {
|
|
199
|
-
|
|
200
|
-
|
|
199
|
+
ArchiveCommand,
|
|
200
|
+
archive_default as default
|
|
201
201
|
};
|
package/dist/commands/config.js
CHANGED
|
@@ -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-
|
|
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 "
|
|
106
|
-
updatedConfig.
|
|
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
|
-
|
|
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: "
|
|
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
|
|
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: "
|
|
242
|
+
/* @__PURE__ */ jsx(Text, { color: "gray", children: "archive-dir:" }),
|
|
243
243
|
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
244
|
-
/* @__PURE__ */ jsx(Text, { bold: true, children:
|
|
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 "
|
|
270
|
-
configValue = config?.
|
|
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 "
|
|
444
|
-
return "\
|
|
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 "
|
|
462
|
-
return '\
|
|
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 "
|
|
477
|
+
case "archive-dir":
|
|
478
478
|
return ["problems", ".", ""];
|
|
479
479
|
case "archive-strategy":
|
|
480
480
|
return ["flat", "by-range", "by-tier", "by-tag"];
|
package/dist/commands/fetch.js
CHANGED
|
@@ -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
|
-
|
|
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);
|
|
@@ -150,16 +171,19 @@ ${separatorRow}
|
|
|
150
171
|
${valueRow}
|
|
151
172
|
`;
|
|
152
173
|
}
|
|
174
|
+
const description = ensureTrailingNewline(problem.description || "\uC124\uBA85 \uC5C6\uC74C");
|
|
175
|
+
const inputFormat = ensureTrailingNewline(
|
|
176
|
+
problem.inputFormat || "\uC785\uB825 \uD615\uC2DD \uC5C6\uC74C"
|
|
177
|
+
);
|
|
178
|
+
const outputFormat = ensureTrailingNewline(
|
|
179
|
+
problem.outputFormat || "\uCD9C\uB825 \uD615\uC2DD \uC5C6\uC74C"
|
|
180
|
+
);
|
|
153
181
|
const readmeContent = `# [${problem.id}: ${problem.title}](https://www.acmicpc.net/problem/${problem.id})
|
|
154
182
|
|
|
155
183
|
${infoTable}## \uBB38\uC81C \uC124\uBA85
|
|
156
|
-
${
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
${problem.inputFormat || "\uC785\uB825 \uD615\uC2DD \uC5C6\uC74C"}
|
|
160
|
-
|
|
161
|
-
## \uCD9C\uB825
|
|
162
|
-
${problem.outputFormat || "\uCD9C\uB825 \uD615\uC2DD \uC5C6\uC74C"}
|
|
184
|
+
${description}## \uC785\uB825
|
|
185
|
+
${inputFormat}## \uCD9C\uB825
|
|
186
|
+
${outputFormat}
|
|
163
187
|
|
|
164
188
|
## \uC608\uC81C
|
|
165
189
|
${problem.testCases.map(
|
|
@@ -317,8 +341,8 @@ function FetchView({
|
|
|
317
341
|
}
|
|
318
342
|
var FetchCommand = class extends Command {
|
|
319
343
|
async execute(args, flags) {
|
|
320
|
-
const
|
|
321
|
-
if (problemId === null) {
|
|
344
|
+
const context = await resolveProblemContext(args, { requireId: true });
|
|
345
|
+
if (context.problemId === null) {
|
|
322
346
|
console.error("\uC624\uB958: \uBB38\uC81C \uBC88\uD638\uB97C \uC785\uB825\uD574\uC8FC\uC138\uC694.");
|
|
323
347
|
console.error(`\uC0AC\uC6A9\uBC95: ps fetch <\uBB38\uC81C\uBC88\uD638> [\uC635\uC158]`);
|
|
324
348
|
console.error(`\uB3C4\uC6C0\uB9D0: ps fetch --help`);
|
|
@@ -338,7 +362,7 @@ var FetchCommand = class extends Command {
|
|
|
338
362
|
return;
|
|
339
363
|
}
|
|
340
364
|
await this.renderView(FetchView, {
|
|
341
|
-
problemId,
|
|
365
|
+
problemId: context.problemId,
|
|
342
366
|
language: language || "python"
|
|
343
367
|
});
|
|
344
368
|
}
|
package/dist/commands/init.js
CHANGED
|
@@ -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-
|
|
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("
|
|
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 [
|
|
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.
|
|
75
|
-
|
|
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 "
|
|
97
|
-
return "\
|
|
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 "
|
|
118
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
176
|
-
const
|
|
175
|
+
if (archiveDir !== "." && archiveDir !== "") {
|
|
176
|
+
const archiveDirPath = join(cwd, archiveDir);
|
|
177
177
|
try {
|
|
178
|
-
await mkdir(
|
|
179
|
-
setCreated((prev) => [...prev, `${
|
|
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
|
-
|
|
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
|
-
"
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 "
|
|
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
|
-
|
|
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__ */
|
|
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
|
": ",
|
package/dist/commands/open.js
CHANGED
|
@@ -7,9 +7,8 @@ import {
|
|
|
7
7
|
Command,
|
|
8
8
|
CommandBuilder,
|
|
9
9
|
CommandDef,
|
|
10
|
-
getProblemId,
|
|
11
10
|
resolveProblemContext
|
|
12
|
-
} from "../chunk-
|
|
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
|
|
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>`);
|
package/dist/commands/run.js
CHANGED
|
@@ -6,10 +6,9 @@ import {
|
|
|
6
6
|
Command,
|
|
7
7
|
CommandBuilder,
|
|
8
8
|
CommandDef,
|
|
9
|
-
getProblemId,
|
|
10
9
|
resolveLanguage,
|
|
11
10
|
resolveProblemContext
|
|
12
|
-
} from "../chunk-
|
|
11
|
+
} from "../chunk-F4LZ6ENP.js";
|
|
13
12
|
import {
|
|
14
13
|
__decorateClass,
|
|
15
14
|
getSupportedLanguagesString
|
|
@@ -123,17 +122,14 @@ function RunView({
|
|
|
123
122
|
}
|
|
124
123
|
var RunCommand = class extends Command {
|
|
125
124
|
async execute(args, flags) {
|
|
126
|
-
const
|
|
127
|
-
const
|
|
128
|
-
problemId !== null ? [problemId.toString()] : []
|
|
129
|
-
);
|
|
130
|
-
const inputPath = flags.input ? join(context.problemDir, flags.input) : await this.findInputFile(context.problemDir);
|
|
125
|
+
const context = await resolveProblemContext(args);
|
|
126
|
+
const inputPath = flags.input ? join(context.archiveDir, flags.input) : await this.findInputFile(context.archiveDir);
|
|
131
127
|
const detectedLanguage = await resolveLanguage(
|
|
132
|
-
context.
|
|
128
|
+
context.archiveDir,
|
|
133
129
|
flags.language
|
|
134
130
|
);
|
|
135
131
|
await this.renderView(RunView, {
|
|
136
|
-
problemDir: context.
|
|
132
|
+
problemDir: context.archiveDir,
|
|
137
133
|
language: detectedLanguage,
|
|
138
134
|
inputFile: inputPath
|
|
139
135
|
});
|
package/dist/commands/search.js
CHANGED
|
@@ -16,10 +16,10 @@ import {
|
|
|
16
16
|
Command,
|
|
17
17
|
CommandBuilder,
|
|
18
18
|
CommandDef,
|
|
19
|
-
|
|
19
|
+
getArchiveDirPath,
|
|
20
20
|
getTierColor,
|
|
21
21
|
getTierName
|
|
22
|
-
} from "../chunk-
|
|
22
|
+
} from "../chunk-F4LZ6ENP.js";
|
|
23
23
|
import {
|
|
24
24
|
__decorateClass
|
|
25
25
|
} from "../chunk-7MQMPJ3X.js";
|
|
@@ -270,7 +270,7 @@ function WorkbookSearchView({
|
|
|
270
270
|
] });
|
|
271
271
|
}
|
|
272
272
|
const problemsWithSolvedStatus = problems.map((problem) => {
|
|
273
|
-
const problemDirPath =
|
|
273
|
+
const problemDirPath = getArchiveDirPath(problem.problemId);
|
|
274
274
|
const isSolved = existsSync(problemDirPath);
|
|
275
275
|
return {
|
|
276
276
|
problemId: problem.problemId,
|
|
@@ -362,7 +362,7 @@ function SearchView({ query, onComplete }) {
|
|
|
362
362
|
const searchResults = await searchProblems(query, currentPage);
|
|
363
363
|
const resultsWithSolvedStatus = searchResults.problems.map(
|
|
364
364
|
(problem) => {
|
|
365
|
-
const problemDirPath =
|
|
365
|
+
const problemDirPath = getArchiveDirPath(problem.problemId);
|
|
366
366
|
const isSolved = existsSync(problemDirPath);
|
|
367
367
|
return {
|
|
368
368
|
...problem,
|
package/dist/commands/stats.js
CHANGED
package/dist/commands/submit.js
CHANGED
|
@@ -8,10 +8,9 @@ import {
|
|
|
8
8
|
CommandDef,
|
|
9
9
|
detectProblemIdFromPath,
|
|
10
10
|
findSolutionFile,
|
|
11
|
-
getProblemId,
|
|
12
11
|
resolveLanguage,
|
|
13
12
|
resolveProblemContext
|
|
14
|
-
} from "../chunk-
|
|
13
|
+
} from "../chunk-F4LZ6ENP.js";
|
|
15
14
|
import {
|
|
16
15
|
__decorateClass,
|
|
17
16
|
getSupportedLanguagesString
|
|
@@ -195,19 +194,15 @@ function SubmitView({
|
|
|
195
194
|
}
|
|
196
195
|
var SubmitCommand = class extends Command {
|
|
197
196
|
async execute(args, flags) {
|
|
198
|
-
const
|
|
199
|
-
const
|
|
200
|
-
problemId !== null ? [problemId.toString()] : [],
|
|
201
|
-
{ requireId: true }
|
|
202
|
-
);
|
|
203
|
-
const sourcePath = await findSolutionFile(context.problemDir);
|
|
197
|
+
const context = await resolveProblemContext(args, { requireId: true });
|
|
198
|
+
const sourcePath = await findSolutionFile(context.archiveDir);
|
|
204
199
|
const detectedLanguage = await resolveLanguage(
|
|
205
|
-
context.
|
|
200
|
+
context.archiveDir,
|
|
206
201
|
flags.language
|
|
207
202
|
);
|
|
208
203
|
let finalProblemId = context.problemId;
|
|
209
204
|
if (finalProblemId === null) {
|
|
210
|
-
finalProblemId = detectProblemIdFromPath(context.
|
|
205
|
+
finalProblemId = detectProblemIdFromPath(context.archiveDir);
|
|
211
206
|
}
|
|
212
207
|
if (finalProblemId === null) {
|
|
213
208
|
throw new Error(
|
package/dist/commands/test.js
CHANGED
|
@@ -6,10 +6,9 @@ import {
|
|
|
6
6
|
Command,
|
|
7
7
|
CommandBuilder,
|
|
8
8
|
CommandDef,
|
|
9
|
-
getProblemId,
|
|
10
9
|
resolveLanguage,
|
|
11
10
|
resolveProblemContext
|
|
12
|
-
} from "../chunk-
|
|
11
|
+
} from "../chunk-F4LZ6ENP.js";
|
|
13
12
|
import {
|
|
14
13
|
__decorateClass,
|
|
15
14
|
getSupportedLanguagesString
|
|
@@ -317,16 +316,13 @@ function TestView({
|
|
|
317
316
|
}
|
|
318
317
|
var TestCommand = class extends Command {
|
|
319
318
|
async execute(args, flags) {
|
|
320
|
-
const
|
|
321
|
-
const context = await resolveProblemContext(
|
|
322
|
-
problemId !== null && problemId !== void 0 ? [problemId.toString()] : []
|
|
323
|
-
);
|
|
319
|
+
const context = await resolveProblemContext(args);
|
|
324
320
|
const language = await resolveLanguage(
|
|
325
|
-
context.
|
|
321
|
+
context.archiveDir,
|
|
326
322
|
flags.language
|
|
327
323
|
);
|
|
328
324
|
await this.renderView(TestView, {
|
|
329
|
-
problemDir: context.
|
|
325
|
+
problemDir: context.archiveDir,
|
|
330
326
|
language,
|
|
331
327
|
watch: Boolean(flags.watch),
|
|
332
328
|
timeoutMs: flags.timeoutMs
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rhseung/ps-cli",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.4",
|
|
4
4
|
"description": "백준(BOJ) 문제 해결을 위한 통합 CLI 도구",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -8,11 +8,14 @@
|
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"dist",
|
|
11
|
-
"templates"
|
|
11
|
+
"templates",
|
|
12
|
+
"LICENSE"
|
|
12
13
|
],
|
|
13
14
|
"scripts": {
|
|
14
15
|
"build": "tsup && node scripts/add-shebang.js",
|
|
16
|
+
"prepublishOnly": "npm run build",
|
|
15
17
|
"dev": "tsup --watch",
|
|
18
|
+
"ps": "node dist/index.js",
|
|
16
19
|
"typecheck": "tsc --noEmit",
|
|
17
20
|
"lint": "eslint",
|
|
18
21
|
"format": "prettier --check .",
|