@byh3071/vhk 1.3.0 โ 1.4.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 +75 -22
- package/dist/{chunk-3HGOQLRT.js โ chunk-3DV7AEN4.js} +75 -18
- package/dist/index.js +468 -75
- package/dist/mcp/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,57 +1,90 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: vhk-readme
|
|
3
|
-
date: 2026-05-
|
|
4
|
-
tags: [vhk, cli, readme, v1.
|
|
3
|
+
date: 2026-05-28
|
|
4
|
+
tags: [vhk, cli, readme, v1.4.0, ga]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# ๐ง VHK โ Vibe Harness Kit
|
|
8
8
|
|
|
9
|
-
> ๐ **v1.
|
|
9
|
+
> ๐ **v1.4.0** โ ๋ฐ์ด๋ธ์ฝ๋์ ์ฌ์ธ์ CLI. ์ปจํ
์คํธ + ์์จ ํ๋ค์ค + ํฌํฐ๋น๋ฆฌํฐ.
|
|
10
10
|
>
|
|
11
|
-
> AI ์ฝ๋ฉ ์์ด์ ํธ๋ฅผ ๋ถ๋ฆฌ๋ ์ฌ๋์ ์ํ **ํ๊ตญ์ด ํ์ฌ์ดํด CLI
|
|
11
|
+
> AI ์ฝ๋ฉ ์์ด์ ํธ๋ฅผ ๋ถ๋ฆฌ๋ ์ฌ๋์ ์ํ **ํ๊ตญ์ด ํ์ฌ์ดํด CLI**.
|
|
12
12
|
>
|
|
13
13
|
> ๐ฝ๏ธ **VHK๋ VHK๋ก ๋ถํธ์คํธ๋ฉ๋จ** โ ์ด ๋ ํฌ์ `docs/`, `CLAUDE.md`, `.cursorrules`๋ `vhk init`์ด ๋ง๋ค์์ต๋๋ค.
|
|
14
14
|
|
|
15
15
|
๋ช
๋ น์ด๋ฅผ ์ธ์ฐ์ง ์์๋ ๋ฉ๋๋ค. `vhk`๋ง ์น๋ฉด ๋ฉ๋ด๊ฐ ๋์ค๊ณ , ํ๊ตญ์ด๋ก ๋งํด๋ ์์๋ฃ์ต๋๋ค.
|
|
16
16
|
|
|
17
|
-
##
|
|
17
|
+
## 3๋ถ ์์ ์์ํ๊ธฐ (Getting Started)
|
|
18
|
+
|
|
19
|
+
### 1. ์ค์น
|
|
18
20
|
|
|
19
21
|
```bash
|
|
20
22
|
npm install -g @byh3071/vhk
|
|
23
|
+
vhk --version
|
|
21
24
|
```
|
|
22
25
|
|
|
26
|
+
> Node.js โฅ 20 ํ์. `npx @byh3071/vhk` ๋ก 1ํ์ฑ ์คํ๋ ๊ฐ๋ฅ.
|
|
27
|
+
|
|
28
|
+
### 2. ์ฒซ ํ๋ก์ ํธ โ `vhk start` (๋ง๋ฒ์ฌ)
|
|
29
|
+
|
|
23
30
|
```bash
|
|
24
|
-
|
|
25
|
-
|
|
31
|
+
mkdir my-app && cd my-app
|
|
32
|
+
vhk start
|
|
26
33
|
```
|
|
27
34
|
|
|
28
|
-
|
|
35
|
+
`vhk start` ํ ๋ฒ์ด๋ฉด **git init โ ๋ฌธ์ ์์ฑ โ MCP ๋ฑ๋ก โ ์ปจํ
์คํธ ํ์ผ** ๊น์ง ์๋์ผ๋ก ๋๋ฉ๋๋ค.
|
|
29
36
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
pnpm link --global
|
|
35
|
-
vhk --version
|
|
37
|
+
๊ธฐํ์ด ๋ง ๋ ์ฌ๋๋ค๋ฉด ๊ฒ์ฆ๋ถํฐ:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
vhk gate # ํต 5๋ฌธํญ โ GO / ๋ค๋ฌ๊ธฐ / ๋ค๋ฅธ ์์ด๋์ด
|
|
36
41
|
```
|
|
37
42
|
|
|
38
|
-
|
|
43
|
+
### 3. v1.3 ํต์ฌ ๊ธฐ๋ฅ ํ๋์
|
|
39
44
|
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
| ๊ธฐ๋ฅ | ํ ์ค ์์ฝ | ์ง์
๋ช
๋ น |
|
|
46
|
+
|------|-----------|-----------|
|
|
47
|
+
| ๐ฏ **Goals ์ฒด๊ณ** | ๋จ๊ณ๋ณ ๋ฏธ์
+ ๊ฒ์ดํธ ์คํฌ๋ฆฝํธ๋ก AI๊ฐ ๋ชฉํ๋ฅผ ์ค์ค๋ก ์ถ์ | `vhk goal init` |
|
|
48
|
+
| โถ๏ธ **์์จ ๋ฃจํ** | `goal next โ ์์
โ goal check โ goal done`. FAIL 3ํ๋ฉด ์๋ ๋ธ๋ก์ปค | `vhk goal next` |
|
|
49
|
+
| ๐ง **HARD_STOP ์์ ์ฅ์น** | ๋ธ๋ก์ปค 3๊ฑด ๋์ โ `.vhk/HARD_STOP` ํธ๋ฆฝ์์ด์ด. `vhk resume --confirm` ๋ง ํด์ | `vhk blocker "<์ฆ์>"` |
|
|
50
|
+
| ๐ **MCP 24 tool** | CursorยทClaude Desktop ๋ฑ์์ vhk๋ฅผ ์ฑํ
์ผ๋ก ํธ์ถ | `vhk mcp-init` |
|
|
51
|
+
| ๐ **์ปจํ
์คํธ ์์ํ** | `.vhk/context.md` + `memory.json` + `brief.md` ๋ก ์ธ์
๊ฐ ๋งฅ๋ฝ ์ ์ง | `vhk context` |
|
|
52
|
+
|
|
53
|
+
### 4. ๊ถ์ฅ ์ผ์ผ ์ฌ์ดํด
|
|
54
|
+
|
|
55
|
+
```text
|
|
56
|
+
์ธ์
์์ : vhk context # AI์ ์ค ํ๋ก์ ํธ ๋งฅ๋ฝ ๊ฐฑ์
|
|
57
|
+
vhk goal next # ์ค๋ ์์
ํ ๋ฏธ์
์๋ ์ ํ
|
|
58
|
+
|
|
59
|
+
๊ฐ๋ฐ ...
|
|
60
|
+
|
|
61
|
+
์ธ์
์ข
๋ฃ : vhk goal check # ๊ฒ์ดํธ ์คํฌ๋ฆฝํธ๋ก ํต๊ณผ ๊ฒ์ฆ
|
|
62
|
+
vhk goal done # ํต๊ณผ ์ status: DONE ์ผ๋ก ์ ์ด
|
|
63
|
+
vhk save # add โ commit โ push ํ ๋ฒ์
|
|
64
|
+
vhk recap # docs/log/ ์ ์ค๋ ๊ธฐ๋ก
|
|
42
65
|
```
|
|
43
66
|
|
|
44
|
-
|
|
67
|
+
### 5. ์์ฐ์ด๋ก๋ ๋ฉ๋๋ค
|
|
45
68
|
|
|
46
69
|
```bash
|
|
47
|
-
# ์์ฐ์ด๋ก๋ ๊ฐ๋ฅ
|
|
48
70
|
vhk ํ๋ก์ ํธ ๋ง๋ค๊ณ ์ถ์ด
|
|
49
71
|
vhk ๊ธฐํ ๋๋ฌ๊ณ ๋ฐ๋ก ์์
|
|
50
72
|
vhk ์ค๋ ํ ์ผ ์ ๋ฆฌ
|
|
51
73
|
vhk ์ ์ฅํด์ค
|
|
52
|
-
vhk
|
|
74
|
+
vhk ๋ค์ ๋ชฉํ
|
|
75
|
+
vhk ๋ธ๋ก์ปค "API ํธ์ถ ์คํจ"
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## ๋น ๋ฅธ ์์ (์ธํฐ๋ํฐ๋ธ ๋ฉ๋ด)
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
vhk
|
|
53
84
|
```
|
|
54
85
|
|
|
86
|
+
์ธ์ ์์ด ์คํํ๋ฉด **ใ๋ญ ๋์๋๋ฆด๊น์?ใ** ๋ฉ๋ด๊ฐ ์ด๋ฆฝ๋๋ค.
|
|
87
|
+
|
|
55
88
|
## ์ํฌํ๋ก์ฐ (๊ถ์ฅ ์์)
|
|
56
89
|
|
|
57
90
|
```text
|
|
@@ -82,11 +115,13 @@ vhk ๊ธฐํ ๋๋ฌ๊ณ ๋ฐ๋ก ์์
|
|
|
82
115
|
| `vhk gate` | `๊ฒ์ฆ`, `์์ด๋์ด` | ์์ด๋์ด ๊ฒ์ฆ (ํต 5๋ฌธํญ / ํ 13๋ฌธํญ / ์คํต) |
|
|
83
116
|
| `vhk init` | `์์`, `๋ง๋ค๊ธฐ` | ํ๋ก์ ํธ ์ด๊ธฐํ + ํ๋ค์ค ์์ฑ |
|
|
84
117
|
| `vhk recap` | `์ ๋ฆฌ`, `์ค๋` | Git ๋ณ๊ฒฝ โ `docs/log/` ์ธ์
๋ก๊ทธ |
|
|
85
|
-
| `vhk sync` | `๊ท์น`, `๋ง์ถ๊ธฐ` | RULES.md โ `.cursorrules` + CLAUDE.md |
|
|
118
|
+
| `vhk sync` | `๊ท์น`, `๋ง์ถ๊ธฐ` | RULES.md โ `.cursorrules` + CLAUDE.md + `.windsurfrules` |
|
|
86
119
|
| `vhk check` | `์ ๊ฒ`, `๋ฆฐํธ` | RULES.md ๊ท์น ์๋ฐ ๊ฒ์ฌ |
|
|
87
120
|
| `vhk secure` | `๋ณด์` | ์ํฌ๋ฆฟยทํค ์ ์ถ ์ค์บ (`scan` / `์ค์บ` ๋์ผ). **CRITICAL/HIGH ๋ฐ๊ฒฌ ์ exit code 1** (CI์ฉ) |
|
|
88
121
|
| `vhk ship` | `์ถํ` | ๋ฐฐํฌ ์ฒดํฌ๋ฆฌ์คํธ + ํ๊ณ + ๋น๋ ๋ก๊ทธ |
|
|
89
122
|
| `vhk doctor` | `ํ๊ฒฝ`, `์ง๋จ` | Node / npm / pnpm / Git ํ๊ฒฝ ์ ๊ฒ |
|
|
123
|
+
| `vhk cloud push` | `ํด๋ผ์ฐ๋`, `์ฌ๋ฆฌ๊ธฐ` | `.vhk/` ๋ฅผ GitHub secret gist ๋ก ๋ฐฑ์
(gh CLI ์ธ์ฆ ์ฌ์ฉ) |
|
|
124
|
+
| `vhk cloud pull` | `๋ด๋ฆฌ๊ธฐ` | gist ์์ `.vhk/` ๋ณต์ (`vhk cloud pull <gistId>` ๋๋ cloud.json) |
|
|
90
125
|
| `vhk save` | `์ ์ฅ`, `์ปค๋ฐ` | git add ยท commit ยท push ํ ๋ฒ์ |
|
|
91
126
|
| `vhk undo` | `๋๋๋ฆฌ๊ธฐ`, `์ทจ์` | ์ต๊ทผ ์ปค๋ฐ soft reset (๋ณ๊ฒฝ์ staged ์ ์ง) |
|
|
92
127
|
| `vhk diff` | `๋ณ๊ฒฝ`, `์ฐจ์ด` | staged / unstaged / ์ ํ์ผ ์์ฝ (์ค ์ ํฉ๊ณ๋ trackedยทHEAD ๊ธฐ์ค) |
|
|
@@ -265,7 +300,7 @@ vhk ref open 1 # 1๋ฒ ๋ ํผ๋ฐ์ค๋ฅผ ๋ธ๋ผ์ฐ์ ๋ก ์ด๊ธฐ
|
|
|
265
300
|
|
|
266
301
|
| ๊ธฐ๋ฅ | ์ค๋ช
|
|
|
267
302
|
|------|------|
|
|
268
|
-
| **MCP ์๋ฒ** | `vhk mcp` โ 8๊ฐ ๋๊ตฌ
|
|
303
|
+
| **MCP ์๋ฒ** | `vhk mcp` โ stdio MCP ์๋ฒ ์ฒซ ๋์
(v0.6.0 ๋น์ 8๊ฐ ๋๊ตฌ โ save/undo/status/diff/ship/doctor/check/recap). ํ์ฌ v1.3 ๊ธฐ์ค **24๊ฐ** ๋ก ํ์ฅ โ ์ "Cursor์ MCP๋ก ์ฐ๋ํ๊ธฐ" ์น์
์ฐธ์กฐ |
|
|
269
304
|
| **mcp-init** | `vhk mcp-init` โ Cursor `.cursor/mcp.json` ์๋ ์์ฑ. ์ฌ์์ ํ ๋ฒ์ผ๋ก ์ฐ๋ ์๋ฃ |
|
|
270
305
|
| **์์ฐ์ด ๋ผ์ฐํ
ํ์ฅ** | `vhk mcp์ค์ ` โ `vhk mcp-init` ๋ณ์นญ |
|
|
271
306
|
| **๋ณด์** | MCP save ๋๊ตฌ์ shell injection ์ฐจ๋จ โ ๋ชจ๋ git ํธ์ถ์ `execFileSync` ์ฌ์ฉ |
|
|
@@ -307,8 +342,26 @@ vhk ref open 1 # 1๋ฒ ๋ ํผ๋ฐ์ค๋ฅผ ๋ธ๋ผ์ฐ์ ๋ก ์ด๊ธฐ
|
|
|
307
342
|
- `docs/PRD.md`, `docs/ARCHITECTURE.md`
|
|
308
343
|
- `docs/adr/`, `docs/log/`, `docs/troubleshooting/`
|
|
309
344
|
- `COMMANDS.md`, `BACKLOG.md` (ํ๋ก์ ํธ ์ ํ์ ๋ฐ๋ผ)
|
|
345
|
+
- `.vhk/README.md` + `.vhk/context.md` (์ ํ๋ณ ์จ์ โ ๊ท๊ฒฉ: [`docs/spec.md`](docs/spec.md))
|
|
346
|
+
- `.vhk/.gitignore` + `.vhkignore` (๋ก์ปฌ ์ ์ฉยทํด๋ผ์ฐ๋ ์ ์ธ ๊ท์น)
|
|
347
|
+
- ๋ฃจํธ `.gitignore` (`.env`ยท`node_modules`ยท`dist` ๋ณดํธ โ ๊ธฐ์กด ํ์ผ์ ๋ณด์กดํ๊ณ ๋๋ฝ๋ถ๋ง ์ถ๊ฐ)
|
|
310
348
|
- `package.json` scripts: `save`, `check`, `scan`, `recap`, `ship`, `doctor` โ `vhk` ํธ์ถ
|
|
311
349
|
|
|
350
|
+
## ํด๋ผ์ฐ๋ ๋ฐฑ์
(vhk cloud)
|
|
351
|
+
|
|
352
|
+
`.vhk/` ํ๋ก์ ํธ ๋งฅ๋ฝ์ GitHub **secret gist** ๋ก ๋ฐฑ์
ยท๋ณต์ํฉ๋๋ค. ์ปดํจํฐ๋ฅผ ๋ฐ๊ฟ๋
|
|
353
|
+
๊ท์นยท๋งฅ๋ฝ์ด ๋ฐ๋ผ์ต๋๋ค. ๊ท๊ฒฉ์ [`docs/spec.md`](docs/spec.md) ์ฐธ์กฐ.
|
|
354
|
+
|
|
355
|
+
```bash
|
|
356
|
+
vhk cloud push # .vhk/ โ secret gist ๋ฐฑ์
(gist id ๋ .vhk/cloud.json ์ ์ ์ฅ)
|
|
357
|
+
vhk cloud pull # cloud.json ์ gist ์์ ๋ณต์
|
|
358
|
+
vhk cloud pull <gistId> # ์ ํ๊ฒฝ์์ gist id ๋ก ์ง์ ๋ณต์
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
- **์ธ์ฆ:** `gh` CLI ์ฌ์ฉ (`gh auth login`, gist ๊ถํ). ์ฝ๋ยท์ค์ ์ ํ ํฐ์ ์ ์ฅํ์ง ์์ต๋๋ค.
|
|
362
|
+
- **ํ๋ผ์ด๋ฒ์:** gist ๋ secret(๋น๊ณต๊ฐ). ๊ฐ์ธ ๋ฉ๋ชจ(`memory.json`)ยท์ฐธ๊ณ ๋งํฌ(`refs.json`)ยท
|
|
363
|
+
`HARD_STOP` ์ ๊ธฐ๋ณธ ์ ์ธ๋ฉ๋๋ค. ์ถ๊ฐ ์ ์ธ๋ ๋ฃจํธ `.vhkignore` ์ ํ ์ค์ฉ ์ ์ผ์ธ์.
|
|
364
|
+
|
|
312
365
|
## ์์ฐ์ด ์์
|
|
313
366
|
|
|
314
367
|
| ๋งํ๋ฉด | ์คํ |
|
|
@@ -551,7 +551,11 @@ var ko = {
|
|
|
551
551
|
package: "package.json:",
|
|
552
552
|
noPackage: "package.json \uC5C6\uC74C",
|
|
553
553
|
detached: "(detached HEAD)",
|
|
554
|
-
unknownBranch: "(\uC54C \uC218 \uC5C6\uC74C)"
|
|
554
|
+
unknownBranch: "(\uC54C \uC218 \uC5C6\uC74C)",
|
|
555
|
+
nextWithChangesMessage: "\uBCC0\uACBD\uC0AC\uD56D\uC774 \uC788\uC5B4\uC694. \uCEE4\uBC0B\xB7\uD478\uC2DC\uB97C \uC9C4\uD589\uD558\uC138\uC694.",
|
|
556
|
+
nextWithChangesCursor: "\uC800\uC7A5\uD574\uC918",
|
|
557
|
+
nextCleanMessage: "\uD074\uB9B0 \uC0C1\uD0DC! \uB2E4\uC74C \uBBF8\uC158\uC73C\uB85C \uB118\uC5B4\uAC00\uC138\uC694.",
|
|
558
|
+
nextCleanCursor: "\uB2E4\uC74C \uBAA9\uD45C \uC54C\uB824\uC918"
|
|
555
559
|
},
|
|
556
560
|
save: {
|
|
557
561
|
title: "\uC800\uC7A5\uD558\uAE30",
|
|
@@ -573,7 +577,11 @@ var ko = {
|
|
|
573
577
|
pushFailed: "push \uC2E4\uD328 (\uB85C\uCEEC \uCEE4\uBC0B\uC740 \uC644\uB8CC\uB428)",
|
|
574
578
|
commitOkPushFailed: "\uB85C\uCEEC \uCEE4\uBC0B\uC740 \uB410\uC9C0\uB9CC \uC6D0\uACA9 push\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4. git push\uB97C \uC9C1\uC811 \uD655\uC778\uD558\uC138\uC694.",
|
|
575
579
|
done: (n) => `${n}\uAC1C \uD30C\uC77C \uC800\uC7A5 \uC644\uB8CC!`,
|
|
576
|
-
doneLocalOnly: (n) => `${n}\uAC1C \uD30C\uC77C \uB85C\uCEEC \uC800\uC7A5\uB428 (push\uB294 \uC2E4\uD328)
|
|
580
|
+
doneLocalOnly: (n) => `${n}\uAC1C \uD30C\uC77C \uB85C\uCEEC \uC800\uC7A5\uB428 (push\uB294 \uC2E4\uD328)`,
|
|
581
|
+
nextOkMessage: "\uC800\uC7A5 \uC644\uB8CC! \uC624\uB298 \uC791\uC5C5\uC744 \uC815\uB9AC\uD574\uB450\uBA74 \uC88B\uC544\uC694.",
|
|
582
|
+
nextOkCursor: "\uC624\uB298 \uD55C \uC77C \uC815\uB9AC\uD574\uC918",
|
|
583
|
+
nextPushFailMessage: "\uCEE4\uBC0B\uC740 \uB410\uC9C0\uB9CC push \uC2E4\uD328. \uC6D0\uACA9\uC744 \uD655\uC778\uD558\uC138\uC694.",
|
|
584
|
+
nextPushFailCursor: "\uC65C push \uC2E4\uD328\uC778\uC9C0 \uC54C\uB824\uC918"
|
|
577
585
|
},
|
|
578
586
|
undo: {
|
|
579
587
|
title: "\uB418\uB3CC\uB9AC\uAE30",
|
|
@@ -590,7 +598,9 @@ var ko = {
|
|
|
590
598
|
stagedHint: "\uBCC0\uACBD\uC0AC\uD56D\uC740 \uC2A4\uD14C\uC774\uC9D5 \uC601\uC5ED\uC5D0 \uB0A8\uC544 \uC788\uC5B4\uC694.",
|
|
591
599
|
rootCommit: "\uCCAB \uCEE4\uBC0B\uB9CC \uC788\uC5B4\uC11C \uB354 \uB418\uB3CC\uB9B4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
592
600
|
forcePushHint: "\uC6D0\uACA9\uACFC \uB9DE\uCD94\uB824\uBA74: git push --force-with-lease (\uD63C\uC790 \uC791\uC5C5\uD55C \uBE0C\uB79C\uCE58\uC5D0\uC11C\uB9CC, \uD300\uACFC \uD569\uC758 \uD6C4)",
|
|
593
|
-
failed: "\uB418\uB3CC\uB9AC\uAE30 \uC2E4\uD328"
|
|
601
|
+
failed: "\uB418\uB3CC\uB9AC\uAE30 \uC2E4\uD328",
|
|
602
|
+
nextMessage: "\uBCC0\uACBD\uC0AC\uD56D\uC774 \uC2A4\uD14C\uC774\uC9D5 \uC601\uC5ED\uC5D0 \uB0A8\uC558\uC5B4\uC694. \uBA54\uC2DC\uC9C0 \uACE0\uCCD0\uC11C \uB2E4\uC2DC \uC800\uC7A5\uD558\uC138\uC694.",
|
|
603
|
+
nextCursor: "\uCEE4\uBC0B \uBA54\uC2DC\uC9C0 \uBC14\uAFD4\uC11C \uC800\uC7A5\uD574\uC918"
|
|
594
604
|
},
|
|
595
605
|
diff: {
|
|
596
606
|
title: "\uBCC0\uACBD\uC0AC\uD56D \uD655\uC778",
|
|
@@ -686,7 +696,9 @@ var ko = {
|
|
|
686
696
|
gitHintCommand: 'git init && git add . && git commit -m "feat: \uD504\uB85C\uC81D\uD2B8 \uC2DC\uC791"',
|
|
687
697
|
startDev: "\uC774\uC81C \uAC1C\uBC1C\uD574 \uBCF4\uC138\uC694! \u{1F680}",
|
|
688
698
|
commandsMdDone: "\u{1F4CB} COMMANDS.md \uC0DD\uC131",
|
|
689
|
-
scriptsDone: "\u{1F4E6} package.json scripts \uCD94\uAC00"
|
|
699
|
+
scriptsDone: "\u{1F4E6} package.json scripts \uCD94\uAC00",
|
|
700
|
+
gitignoreCreated: "\u{1F512} .gitignore \uC0DD\uC131 (.env\xB7node_modules\xB7dist \uC81C\uC678)",
|
|
701
|
+
gitignoreUpdated: "\u{1F512} .gitignore \uBCF4\uAC15 (\uB204\uB77D \uD56D\uBAA9 \uCD94\uAC00)"
|
|
690
702
|
},
|
|
691
703
|
recap: {
|
|
692
704
|
title: "\u{1F4DD} \uC624\uB298 \uD55C \uC77C \uC815\uB9AC",
|
|
@@ -742,8 +754,22 @@ var ko = {
|
|
|
742
754
|
noRules: "\u26A0\uFE0F RULES.md \uD30C\uC77C\uC774 \uC5C6\uC5B4\uC694.",
|
|
743
755
|
cursorrulesDone: "\u2705 .cursorrules \uB9DE\uCDA4 \uC644\uB8CC",
|
|
744
756
|
claudeDone: "\u2705 CLAUDE.md \uB9DE\uCDA4 \uC644\uB8CC",
|
|
757
|
+
windsurfDone: "\u2705 .windsurfrules \uB9DE\uCDA4 \uC644\uB8CC",
|
|
745
758
|
done: "\u{1F504} \uB9DE\uCD94\uAE30 \uC644\uB8CC!"
|
|
746
759
|
},
|
|
760
|
+
cloud: {
|
|
761
|
+
pushTitle: "\u2601\uFE0F .vhk \uD074\uB77C\uC6B0\uB4DC \uBC31\uC5C5 (gist \uC62C\uB9AC\uAE30)",
|
|
762
|
+
pullTitle: "\u2601\uFE0F .vhk \uD074\uB77C\uC6B0\uB4DC \uBCF5\uC6D0 (gist \uB0B4\uB9AC\uAE30)",
|
|
763
|
+
noGh: "gh CLI \uAC00 \uC124\uCE58\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
764
|
+
noAuth: "gh \uC778\uC99D\uC774 \uD544\uC694\uD569\uB2C8\uB2E4 (gist \uAD8C\uD55C).",
|
|
765
|
+
noVhkDir: ".vhk/ \uD3F4\uB354\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. vhk init \uB610\uB294 vhk context \uB97C \uBA3C\uC800 \uC2E4\uD589\uD558\uC138\uC694.",
|
|
766
|
+
nothingToSync: "\uBC31\uC5C5\uD560 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 (.vhkignore \uB85C \uBAA8\uB450 \uC81C\uC678\uB428).",
|
|
767
|
+
noGistId: "\uBCF5\uC6D0\uD560 gist id \uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
768
|
+
pushDone: "\u2705 \uD074\uB77C\uC6B0\uB4DC \uBC31\uC5C5 \uC644\uB8CC",
|
|
769
|
+
pullDone: "\u2705 \uD074\uB77C\uC6B0\uB4DC \uBCF5\uC6D0 \uC644\uB8CC",
|
|
770
|
+
pushFail: "\u274C \uBC31\uC5C5 \uC2E4\uD328",
|
|
771
|
+
pullFail: "\u274C \uBCF5\uC6D0 \uC2E4\uD328"
|
|
772
|
+
},
|
|
747
773
|
ship: {
|
|
748
774
|
title: "\u{1F680} \uBC30\uD3EC \uCCB4\uD06C\uB9AC\uC2A4\uD2B8",
|
|
749
775
|
checklist: "\u{1F4CB} \uBC30\uD3EC \uC804 \uCCB4\uD06C\uB9AC\uC2A4\uD2B8",
|
|
@@ -799,7 +825,9 @@ var ko = {
|
|
|
799
825
|
},
|
|
800
826
|
mcp: {
|
|
801
827
|
initTitle: "Cursor MCP \uC5F0\uB3D9 \uC124\uC815",
|
|
802
|
-
serverStarted: "VHK MCP \uC11C\uBC84 \uC2DC\uC791\uB428"
|
|
828
|
+
serverStarted: "VHK MCP \uC11C\uBC84 \uC2DC\uC791\uB428",
|
|
829
|
+
nextMessage: "Cursor / Claude Desktop \uC744 \uC7AC\uC2DC\uC791\uD55C \uB4A4 \uCC44\uD305\uC5D0\uC11C vhk \uB3C4\uAD6C\uB97C \uD638\uCD9C\uD558\uC138\uC694.",
|
|
830
|
+
nextCursor: "\uD504\uB85C\uC81D\uD2B8 \uC0C1\uD0DC \uC54C\uB824\uC918"
|
|
803
831
|
},
|
|
804
832
|
deploy: {
|
|
805
833
|
title: "\uBC30\uD3EC\uD558\uAE30",
|
|
@@ -836,7 +864,11 @@ var ko = {
|
|
|
836
864
|
selectTarget: "\uC5B4\uB5A4 \uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800\uB85C \uC804\uD658\uD560\uAE4C\uC694?"
|
|
837
865
|
},
|
|
838
866
|
update: {
|
|
839
|
-
title: "VHK CLI \uC5C5\uB370\uC774\uD2B8"
|
|
867
|
+
title: "VHK CLI \uC5C5\uB370\uC774\uD2B8",
|
|
868
|
+
nextOkMessage: "\uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC! \uC0C8 \uBC84\uC804 \uD655\uC778\uD558\uC138\uC694.",
|
|
869
|
+
nextOkCursor: "vhk \uBC84\uC804 \uC54C\uB824\uC918",
|
|
870
|
+
nextFailMessage: "\uC5C5\uB370\uC774\uD2B8 \uC2E4\uD328. \uD658\uACBD \uC810\uAC80\uBD80\uD130 \uD574\uBCF4\uC138\uC694.",
|
|
871
|
+
nextFailCursor: "vhk doctor \uC2E4\uD589\uD574\uC918"
|
|
840
872
|
},
|
|
841
873
|
context: {
|
|
842
874
|
title: "\uD504\uB85C\uC81D\uD2B8 \uCEE8\uD14D\uC2A4\uD2B8 \uC0DD\uC131",
|
|
@@ -1300,11 +1332,33 @@ async function audit(autoFix = false) {
|
|
|
1300
1332
|
});
|
|
1301
1333
|
}
|
|
1302
1334
|
|
|
1335
|
+
// src/lib/version.ts
|
|
1336
|
+
import { existsSync as existsSync5 } from "fs";
|
|
1337
|
+
import { dirname, join } from "path";
|
|
1338
|
+
import { fileURLToPath } from "url";
|
|
1339
|
+
function getVhkVersion() {
|
|
1340
|
+
const dir = dirname(fileURLToPath(import.meta.url));
|
|
1341
|
+
for (const pkgPath of [
|
|
1342
|
+
join(dir, "../../package.json"),
|
|
1343
|
+
join(dir, "../package.json")
|
|
1344
|
+
]) {
|
|
1345
|
+
try {
|
|
1346
|
+
if (existsSync5(pkgPath)) {
|
|
1347
|
+
const pkg = readJsonFile(pkgPath);
|
|
1348
|
+
if (pkg.version) return pkg.version;
|
|
1349
|
+
}
|
|
1350
|
+
} catch {
|
|
1351
|
+
continue;
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
return "0.0.0";
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1303
1357
|
// src/mcp/server.ts
|
|
1304
1358
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
1305
1359
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
1306
1360
|
import { z } from "zod";
|
|
1307
|
-
import { existsSync as
|
|
1361
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync3, appendFileSync as appendFileSync2 } from "fs";
|
|
1308
1362
|
|
|
1309
1363
|
// src/lib/scan-secrets.ts
|
|
1310
1364
|
import fs3 from "fs";
|
|
@@ -1590,7 +1644,7 @@ function filterSevereFindings(findings) {
|
|
|
1590
1644
|
}
|
|
1591
1645
|
|
|
1592
1646
|
// src/mcp/server.ts
|
|
1593
|
-
var SERVER_VERSION =
|
|
1647
|
+
var SERVER_VERSION = getVhkVersion();
|
|
1594
1648
|
function isGitRepo() {
|
|
1595
1649
|
return safeExecFile("git", ["rev-parse", "--is-inside-work-tree"]).ok;
|
|
1596
1650
|
}
|
|
@@ -1697,7 +1751,7 @@ ${preview}${more}
|
|
|
1697
1751
|
});
|
|
1698
1752
|
server.registerTool("status", { description: "\uD504\uB85C\uC81D\uD2B8 \uC0C1\uD0DC \uB300\uC2DC\uBCF4\uB4DC (\uBE0C\uB79C\uCE58/\uBCC0\uACBD\uC0AC\uD56D/\uCD5C\uADFC \uCEE4\uBC0B)" }, async () => {
|
|
1699
1753
|
const lines = [];
|
|
1700
|
-
if (
|
|
1754
|
+
if (existsSync6("package.json")) {
|
|
1701
1755
|
try {
|
|
1702
1756
|
const pkg = readJsonFile("package.json");
|
|
1703
1757
|
lines.push(`\u{1F4E6} \uD504\uB85C\uC81D\uD2B8: ${pkg.name ?? "(\uC774\uB984 \uC5C6\uC74C)"} v${pkg.version ?? "?"}`);
|
|
@@ -1782,7 +1836,7 @@ ${preview}${more}
|
|
|
1782
1836
|
checks.push(build.ok ? "\u2705 \uBE4C\uB4DC \uC131\uACF5" : "\u274C \uBE4C\uB4DC \uC2E4\uD328");
|
|
1783
1837
|
const test = safeExecFile("pnpm", ["test", "--run"]);
|
|
1784
1838
|
checks.push(test.ok ? "\u2705 \uD14C\uC2A4\uD2B8 \uD1B5\uACFC" : "\u274C \uD14C\uC2A4\uD2B8 \uC2E4\uD328");
|
|
1785
|
-
if (
|
|
1839
|
+
if (existsSync6("package.json")) {
|
|
1786
1840
|
try {
|
|
1787
1841
|
const pkg = readJsonFile("package.json");
|
|
1788
1842
|
checks.push(`\u{1F4E6} \uBC84\uC804: ${pkg.version}`);
|
|
@@ -1820,11 +1874,11 @@ ${preview}${more}
|
|
|
1820
1874
|
const recommended = ["CLAUDE.md", ".cursorrules", "docs/PRD.md", "docs/ARCHITECTURE.md"];
|
|
1821
1875
|
const lines = ["\u{1F50D} \uD504\uB85C\uC81D\uD2B8 \uC810\uAC80", "", "\uD544\uC218:"];
|
|
1822
1876
|
required.forEach((f) => {
|
|
1823
|
-
lines.push(` ${
|
|
1877
|
+
lines.push(` ${existsSync6(f) ? "\u2705" : "\u274C"} ${f}`);
|
|
1824
1878
|
});
|
|
1825
1879
|
lines.push("", "\uAD8C\uC7A5 (VHK \uD558\uB124\uC2A4):");
|
|
1826
1880
|
recommended.forEach((f) => {
|
|
1827
|
-
lines.push(` ${
|
|
1881
|
+
lines.push(` ${existsSync6(f) ? "\u2705" : "\u26A0\uFE0F"} ${f}`);
|
|
1828
1882
|
});
|
|
1829
1883
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
1830
1884
|
});
|
|
@@ -1858,7 +1912,7 @@ ${log.out}` }] };
|
|
|
1858
1912
|
description: ".env \u2192 .env.example \uB3D9\uAE30\uD654 + .gitignore\uC5D0 .env \uC790\uB3D9 \uCD94\uAC00"
|
|
1859
1913
|
},
|
|
1860
1914
|
async () => {
|
|
1861
|
-
if (!
|
|
1915
|
+
if (!existsSync6(".env")) {
|
|
1862
1916
|
return { content: [{ type: "text", text: "\u26A0\uFE0F .env \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 .env\uB97C \uB9CC\uB4E4\uC5B4\uC8FC\uC138\uC694." }] };
|
|
1863
1917
|
}
|
|
1864
1918
|
const keys = parseEnvKeys(readFileSync4(".env", "utf-8"));
|
|
@@ -1868,7 +1922,7 @@ ${log.out}` }] };
|
|
|
1868
1922
|
const exampleContent = keys.map((k) => `${k}=`).join("\n") + "\n";
|
|
1869
1923
|
writeFileSync3(".env.example", exampleContent, "utf-8");
|
|
1870
1924
|
const gitignoreLines = [];
|
|
1871
|
-
if (
|
|
1925
|
+
if (existsSync6(".gitignore")) {
|
|
1872
1926
|
const content = readFileSync4(".gitignore", "utf-8");
|
|
1873
1927
|
if (!content.split("\n").some((l) => l.trim() === ".env")) {
|
|
1874
1928
|
appendFileSync2(".gitignore", "\n.env\n");
|
|
@@ -1889,11 +1943,11 @@ ${log.out}` }] };
|
|
|
1889
1943
|
description: "\uD544\uC218 \uD658\uACBD\uBCC0\uC218 \uB204\uB77D \uAC80\uC0AC (.env.example \uAE30\uC900)"
|
|
1890
1944
|
},
|
|
1891
1945
|
async () => {
|
|
1892
|
-
if (!
|
|
1946
|
+
if (!existsSync6(".env.example")) {
|
|
1893
1947
|
return { content: [{ type: "text", text: "\u26A0\uFE0F .env.example\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 env \uB3C4\uAD6C\uB97C \uC2E4\uD589\uD558\uC138\uC694." }] };
|
|
1894
1948
|
}
|
|
1895
1949
|
const requiredKeys = parseEnvKeys(readFileSync4(".env.example", "utf-8"));
|
|
1896
|
-
const currentKeys =
|
|
1950
|
+
const currentKeys = existsSync6(".env") ? parseEnvKeys(readFileSync4(".env", "utf-8")) : [];
|
|
1897
1951
|
const missing = requiredKeys.filter((k) => !currentKeys.includes(k));
|
|
1898
1952
|
const extra = currentKeys.filter((k) => !requiredKeys.includes(k));
|
|
1899
1953
|
const lines = [`\u{1F4CB} \uD544\uC218 \uD658\uACBD\uBCC0\uC218: ${requiredKeys.length}\uAC1C`];
|
|
@@ -2007,7 +2061,7 @@ ${cliStatus}
|
|
|
2007
2061
|
description: "\uD604\uC7AC \uBC84\uC804 + bump \uD6C4\uBCF4 \uD45C\uC2DC (MCP \uBAA8\uB4DC: \uC2E4\uC81C npm publish \uBBF8\uC218\uD589 \u2014 `vhk publish` \uC548\uB0B4)"
|
|
2008
2062
|
},
|
|
2009
2063
|
async () => {
|
|
2010
|
-
if (!
|
|
2064
|
+
if (!existsSync6("package.json")) {
|
|
2011
2065
|
return { content: [{ type: "text", text: "\u274C package.json \uC5C6\uC74C." }] };
|
|
2012
2066
|
}
|
|
2013
2067
|
try {
|
|
@@ -2035,7 +2089,7 @@ ${cliStatus}
|
|
|
2035
2089
|
description: "\uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800 \uAC10\uC9C0 + \uC804\uD658 \uD6C4\uBCF4 \uAC00\uC6A9\uC131 (MCP \uBAA8\uB4DC: \uC2E4\uC81C \uC804\uD658 \uBBF8\uC218\uD589 \u2014 `vhk migrate <target>` \uC548\uB0B4)"
|
|
2036
2090
|
},
|
|
2037
2091
|
async () => {
|
|
2038
|
-
const current =
|
|
2092
|
+
const current = existsSync6("pnpm-lock.yaml") ? "pnpm" : existsSync6("yarn.lock") ? "yarn" : existsSync6("package-lock.json") ? "npm" : null;
|
|
2039
2093
|
const candidates = ["npm", "yarn", "pnpm"].filter((pm) => pm !== current);
|
|
2040
2094
|
const lines = [`\uD604\uC7AC PM: ${current ?? "\uAC10\uC9C0 \uBD88\uAC00 (lock \uD30C\uC77C \uC5C6\uC74C)"}`];
|
|
2041
2095
|
for (const pm of candidates) {
|
|
@@ -2096,9 +2150,11 @@ async function startMcpServer() {
|
|
|
2096
2150
|
}
|
|
2097
2151
|
|
|
2098
2152
|
export {
|
|
2153
|
+
__toESM,
|
|
2099
2154
|
ko,
|
|
2100
2155
|
t,
|
|
2101
2156
|
printNextStep,
|
|
2157
|
+
require_ignore,
|
|
2102
2158
|
printSecurityWarnings,
|
|
2103
2159
|
filterTrackedPaths,
|
|
2104
2160
|
readJsonFile,
|
|
@@ -2112,5 +2168,6 @@ export {
|
|
|
2112
2168
|
envCheck,
|
|
2113
2169
|
publish,
|
|
2114
2170
|
audit,
|
|
2171
|
+
getVhkVersion,
|
|
2115
2172
|
startMcpServer
|
|
2116
2173
|
};
|
package/dist/index.js
CHANGED
|
@@ -2,22 +2,25 @@
|
|
|
2
2
|
import {
|
|
3
3
|
MAX_SCAN_FILE_BYTES,
|
|
4
4
|
MAX_SECRET_FINDINGS,
|
|
5
|
+
__toESM,
|
|
5
6
|
audit,
|
|
6
7
|
deploy,
|
|
7
8
|
env,
|
|
8
9
|
envCheck,
|
|
9
10
|
filterSevereFindings,
|
|
10
11
|
filterTrackedPaths,
|
|
12
|
+
getVhkVersion,
|
|
11
13
|
ko,
|
|
12
14
|
printNextStep,
|
|
13
15
|
printSecurityWarnings,
|
|
14
16
|
publish,
|
|
15
17
|
readJsonFile,
|
|
18
|
+
require_ignore,
|
|
16
19
|
safeExecFile,
|
|
17
20
|
scanProjectForSecrets,
|
|
18
21
|
startMcpServer,
|
|
19
22
|
t
|
|
20
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-3DV7AEN4.js";
|
|
21
24
|
|
|
22
25
|
// src/index.ts
|
|
23
26
|
import { Command, Help } from "commander";
|
|
@@ -39,6 +42,19 @@ function matchesKeywords(text, command) {
|
|
|
39
42
|
return keywords.some((kw) => text.includes(kw.toLowerCase()));
|
|
40
43
|
}
|
|
41
44
|
var RULES = [
|
|
45
|
+
// ์๋ฌธ `vhk cloud push|pull [id]` ์ commander ๊ฐ ์ง์ ์ฒ๋ฆฌ(๊ฐ๋ก์ฑ๊ธฐ ๊ธ์ง) โ ํ๊ตญ์ด ํํ๋ง ๋งค์นญ.
|
|
46
|
+
{
|
|
47
|
+
command: "cloud-pull",
|
|
48
|
+
explanation: "\uD074\uB77C\uC6B0\uB4DC\uC5D0\uC11C .vhk \uBCF5\uC6D0 (vhk cloud pull)",
|
|
49
|
+
confidence: "high",
|
|
50
|
+
test: (t2) => (/(ํด๋ผ์ฐ๋|gist)\s*(์์)?\s*(๋ณต์|๋ด๋ ค๋ฐ?|๋ด๋ฆฌ|๋ฐ์)/.test(t2) || /(\.?vhk\s*)?(๋ณต์ํด|๋ณต๊ตฌํด|๋ณต์\s*ํ|๋ณต๊ตฌ\s*ํ)/.test(t2)) && !/๋ฐฑ์
|์ฌ๋ ค|์ฌ๋ฆฌ/.test(t2)
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
command: "cloud-push",
|
|
54
|
+
explanation: ".vhk \uB97C \uD074\uB77C\uC6B0\uB4DC\uC5D0 \uBC31\uC5C5 (vhk cloud push)",
|
|
55
|
+
confidence: "high",
|
|
56
|
+
test: (t2) => (/(ํด๋ผ์ฐ๋|gist)\s*(์)?\s*(๋ฐฑ์
|์ฌ๋ ค|์ฌ๋ฆฌ)/.test(t2) || /(\.?vhk\s*)?๋ฐฑ์
\s*ํด|(\.?vhk\s*)?๋ฐฑ์
ํ/.test(t2)) && !/๋ณต์|๋ด๋ ค|๋ด๋ฆฌ|๋ณต๊ตฌ/.test(t2)
|
|
57
|
+
},
|
|
42
58
|
{
|
|
43
59
|
command: "start",
|
|
44
60
|
explanation: "\uB178\uC158\uC5D0\uC11C \uAC00\uC838\uC640 \uC0C8 \uD504\uB85C\uC81D\uD2B8 \uC2DC\uC791 \uB9C8\uBC95\uC0AC (vhk start --from-notion)",
|
|
@@ -176,7 +192,7 @@ var RULES = [
|
|
|
176
192
|
command: "save",
|
|
177
193
|
explanation: "Git\uC5D0 \uC800\uC7A5 (vhk \uC800\uC7A5)",
|
|
178
194
|
confidence: "high",
|
|
179
|
-
test: (t2) => (matchesKeywords(t2, "save") || /๊นํ๋ธ|github/.test(t2)) && !/์ ๋ฆฌ|recap|๋๋|์ทจ์|rollback|reset
|
|
195
|
+
test: (t2) => (matchesKeywords(t2, "save") || /๊นํ๋ธ|github/.test(t2)) && !/์ ๋ฆฌ|recap|๋๋|์ทจ์|rollback|reset|๋ฆฌ์
|๋กค๋ฐฑ|์๋๋๋ก|ํด๋ผ์ฐ๋|cloud|gist/.test(t2)
|
|
180
196
|
},
|
|
181
197
|
{
|
|
182
198
|
command: "recap",
|
|
@@ -351,6 +367,8 @@ var KNOWN_COMMAND_TOKENS = /* @__PURE__ */ new Set([
|
|
|
351
367
|
"\uAE30\uC5B5",
|
|
352
368
|
"brief",
|
|
353
369
|
"\uBE0C\uB9AC\uD551",
|
|
370
|
+
"cloud",
|
|
371
|
+
"\uD074\uB77C\uC6B0\uB4DC",
|
|
354
372
|
"goal",
|
|
355
373
|
"\uBAA9\uD45C",
|
|
356
374
|
"blocker",
|
|
@@ -382,7 +400,7 @@ function detectNaturalLanguageInput(argv) {
|
|
|
382
400
|
}
|
|
383
401
|
|
|
384
402
|
// src/lib/nlp-run.ts
|
|
385
|
-
import
|
|
403
|
+
import chalk27 from "chalk";
|
|
386
404
|
import inquirer11 from "inquirer";
|
|
387
405
|
|
|
388
406
|
// src/commands/gate.ts
|
|
@@ -737,6 +755,70 @@ function COMMANDS_MD_TEMPLATE() {
|
|
|
737
755
|
].join("\n");
|
|
738
756
|
}
|
|
739
757
|
|
|
758
|
+
// src/templates/vhk-dir.ts
|
|
759
|
+
function VHK_README_TEMPLATE() {
|
|
760
|
+
return [
|
|
761
|
+
"# `.vhk/` \u2014 VHK runtime state",
|
|
762
|
+
"",
|
|
763
|
+
"\uC774 \uB514\uB809\uD1A0\uB9AC\uB294 VHK\uAC00 \uD504\uB85C\uC81D\uD2B8\uBCC4 \uC0C1\uD0DC\uB97C \uC800\uC7A5\uD558\uB294 \uACF3\uC785\uB2C8\uB2E4.",
|
|
764
|
+
"\uC804\uCCB4 \uADDC\uACA9\uC740 `docs/spec.md` (spec_version 1.0) \uCC38\uC870.",
|
|
765
|
+
"",
|
|
766
|
+
"## \uD2B8\uB798\uD0B9 \uC815\uCC45",
|
|
767
|
+
"",
|
|
768
|
+
"| \uD30C\uC77C | \uD2B8\uB798\uD0B9 | \uC6A9\uB3C4 |",
|
|
769
|
+
"| --- | --- | --- |",
|
|
770
|
+
"| `README.md` | \u2705 | \uBCF8 \uC548\uB0B4 |",
|
|
771
|
+
"| `context.md` | \u2705 | \uD504\uB85C\uC81D\uD2B8 \uB9E5\uB77D (`vhk context` \uB85C \uAC31\uC2E0) |",
|
|
772
|
+
"| `brief.md` | \u2705 | \uC0C1\uD0DC \uC694\uC57D \uBE0C\uB9AC\uD551 (`vhk brief`) |",
|
|
773
|
+
"| `memory.json` | \u274C \uB85C\uCEEC \uC804\uC6A9 | \uC758\uC0AC\uACB0\uC815 \uBA54\uBAA8 (`vhk memory add`) |",
|
|
774
|
+
"| `refs.json` | \u274C \uB85C\uCEEC \uC804\uC6A9 | \uCC38\uACE0 URL (`vhk ref add`) |",
|
|
775
|
+
"| `HARD_STOP` | \u274C \uB85C\uCEEC \uC804\uC6A9 | \uC874\uC7AC\uD558\uBA74 \uBAA8\uB4E0 \uC790\uB3D9\uD654 \uC989\uC2DC \uC911\uB2E8 |",
|
|
776
|
+
"",
|
|
777
|
+
"> `memory.json`\xB7`refs.json` \uC740 \uAC1C\uC778 \uBA54\uBAA8 \uB178\uCD9C \uBC29\uC9C0\uB97C \uC704\uD574 `.gitignore` \uC5D0 \uB4F1\uB85D\uB429\uB2C8\uB2E4.",
|
|
778
|
+
"> `HARD_STOP` \uD574\uC81C\uB294 `vhk resume --confirm` \uC73C\uB85C\uB9CC \uAC00\uB2A5\uD569\uB2C8\uB2E4.",
|
|
779
|
+
""
|
|
780
|
+
].join("\n");
|
|
781
|
+
}
|
|
782
|
+
function VHK_GITIGNORE_TEMPLATE() {
|
|
783
|
+
return [
|
|
784
|
+
"# VHK \uB85C\uCEEC \uC804\uC6A9 \u2014 \uAC1C\uC778 \uBA54\uBAA8/\uCC38\uACE0\uB9C1\uD06C/\uC548\uC804\uC2E0\uD638 (docs/spec.md \uD2B8\uB798\uD0B9 \uC815\uCC45)",
|
|
785
|
+
"memory.json",
|
|
786
|
+
"refs.json",
|
|
787
|
+
"HARD_STOP",
|
|
788
|
+
""
|
|
789
|
+
].join("\n");
|
|
790
|
+
}
|
|
791
|
+
function VHK_IGNORE_TEMPLATE() {
|
|
792
|
+
return [
|
|
793
|
+
"# vhk cloud push \uBC31\uC5C5\uC5D0\uC11C \uC81C\uC678\uD560 .vhk/ \uD30C\uC77C (\uD55C \uC904\uC5D0 \uD558\uB098)",
|
|
794
|
+
"# \uAE30\uBCF8 \uC81C\uC678(\uC790\uB3D9): memory.json, refs.json, HARD_STOP, cloud.json, .gitignore",
|
|
795
|
+
"# \uC608) \uC544\uB798 \uC8FC\uC11D\uC744 \uD480\uBA74 brief.md \uB3C4 \uBC31\uC5C5\uC5D0\uC11C \uC81C\uC678\uB429\uB2C8\uB2E4.",
|
|
796
|
+
"# brief.md",
|
|
797
|
+
""
|
|
798
|
+
].join("\n");
|
|
799
|
+
}
|
|
800
|
+
function VHK_CONTEXT_SEED(name, type, stack) {
|
|
801
|
+
const stackList = stack.map((s) => "- " + s).join("\n");
|
|
802
|
+
return [
|
|
803
|
+
"# " + name + " \u2014 \uD504\uB85C\uC81D\uD2B8 \uB9E5\uB77D",
|
|
804
|
+
"",
|
|
805
|
+
"> \u26A1 \uC774 \uD30C\uC77C\uC740 vhk init \uC774 \uC0DD\uC131\uD55C \uC528\uC557\uC785\uB2C8\uB2E4. `vhk context` \uB85C \uAC31\uC2E0\uD558\uC138\uC694.",
|
|
806
|
+
"",
|
|
807
|
+
"## \uD504\uB85C\uC81D\uD2B8 \uC720\uD615",
|
|
808
|
+
"- " + type,
|
|
809
|
+
"",
|
|
810
|
+
"## \uAE30\uC220 \uC2A4\uD0DD",
|
|
811
|
+
stackList,
|
|
812
|
+
"",
|
|
813
|
+
"## \uC8FC\uC694 \uACB0\uC815\uC0AC\uD56D",
|
|
814
|
+
"- (\uC544\uC9C1 \uC5C6\uC74C \u2014 `vhk memory add` \uB85C \uAE30\uB85D)",
|
|
815
|
+
"",
|
|
816
|
+
"## \uB2E4\uC74C \uB2E8\uACC4",
|
|
817
|
+
"- docs/PRD.md \uC791\uC131",
|
|
818
|
+
""
|
|
819
|
+
].join("\n");
|
|
820
|
+
}
|
|
821
|
+
|
|
740
822
|
// src/utils/logger.ts
|
|
741
823
|
import chalk2 from "chalk";
|
|
742
824
|
var log = {
|
|
@@ -994,7 +1076,7 @@ ${ko.init.recommendedStack} ${stack.join(" + ")}
|
|
|
994
1076
|
}
|
|
995
1077
|
}
|
|
996
1078
|
const cwd = process.cwd();
|
|
997
|
-
const files = generateFiles(answers.name, answers.description, stack, prdContent);
|
|
1079
|
+
const files = generateFiles(answers.name, answers.description, stack, prdContent, answers.type);
|
|
998
1080
|
log.step(ko.init.filesGenerating);
|
|
999
1081
|
for (const [filePath, content] of Object.entries(files)) {
|
|
1000
1082
|
const fullPath = path2.join(cwd, filePath);
|
|
@@ -1039,7 +1121,7 @@ ${ko.init.nextSteps}`));
|
|
|
1039
1121
|
alternative: "VS Code/Cursor\uC5D0\uC11C \uD3F4\uB354\uB97C \uC5F4\uC5B4\uB3C4 \uB429\uB2C8\uB2E4"
|
|
1040
1122
|
});
|
|
1041
1123
|
}
|
|
1042
|
-
function generateFiles(name, description, stack, prdContent = {}) {
|
|
1124
|
+
function generateFiles(name, description, stack, prdContent = {}, type = "") {
|
|
1043
1125
|
const stackStr = stack.join(" + ");
|
|
1044
1126
|
const prd = {
|
|
1045
1127
|
tagline: description,
|
|
@@ -1064,7 +1146,12 @@ function generateFiles(name, description, stack, prdContent = {}) {
|
|
|
1064
1146
|
## v1.1 \uD6C4\uBCF4
|
|
1065
1147
|
|
|
1066
1148
|
-
|
|
1067
|
-
|
|
1149
|
+
`,
|
|
1150
|
+
// .vhk/ ์จ์ โ ๊ท๊ฒฉ: docs/spec.md (spec_version 1.0)
|
|
1151
|
+
".vhk/README.md": VHK_README_TEMPLATE(),
|
|
1152
|
+
".vhk/context.md": VHK_CONTEXT_SEED(name, type || "unknown", stack),
|
|
1153
|
+
".vhk/.gitignore": VHK_GITIGNORE_TEMPLATE(),
|
|
1154
|
+
".vhkignore": VHK_IGNORE_TEMPLATE()
|
|
1068
1155
|
};
|
|
1069
1156
|
}
|
|
1070
1157
|
var VHK_PACKAGE_SCRIPTS = {
|
|
@@ -1075,6 +1162,32 @@ var VHK_PACKAGE_SCRIPTS = {
|
|
|
1075
1162
|
ship: "vhk ship",
|
|
1076
1163
|
doctor: "vhk doctor"
|
|
1077
1164
|
};
|
|
1165
|
+
var ROOT_GITIGNORE_ENTRIES = [
|
|
1166
|
+
".env",
|
|
1167
|
+
".env.local",
|
|
1168
|
+
".env.*.local",
|
|
1169
|
+
"node_modules/",
|
|
1170
|
+
"dist/",
|
|
1171
|
+
"*.tsbuildinfo",
|
|
1172
|
+
".DS_Store"
|
|
1173
|
+
];
|
|
1174
|
+
function ensureRootGitignore(projectDir) {
|
|
1175
|
+
const gitignorePath = path2.join(projectDir, ".gitignore");
|
|
1176
|
+
if (!fs2.existsSync(gitignorePath)) {
|
|
1177
|
+
fs2.writeFileSync(gitignorePath, ROOT_GITIGNORE_ENTRIES.join("\n") + "\n", "utf-8");
|
|
1178
|
+
return "created";
|
|
1179
|
+
}
|
|
1180
|
+
const content = fs2.readFileSync(gitignorePath, "utf-8");
|
|
1181
|
+
const existing = new Set(content.split("\n").map((l) => l.trim()));
|
|
1182
|
+
const missing = ROOT_GITIGNORE_ENTRIES.filter((e) => !existing.has(e));
|
|
1183
|
+
if (missing.length === 0) return "unchanged";
|
|
1184
|
+
const prefix = content.endsWith("\n") ? "" : "\n";
|
|
1185
|
+
fs2.appendFileSync(gitignorePath, `${prefix}
|
|
1186
|
+
# vhk init
|
|
1187
|
+
${missing.join("\n")}
|
|
1188
|
+
`, "utf-8");
|
|
1189
|
+
return "updated";
|
|
1190
|
+
}
|
|
1078
1191
|
function enhancePackageScripts(projectDir) {
|
|
1079
1192
|
const pkgPath = path2.join(projectDir, "package.json");
|
|
1080
1193
|
if (!fs2.existsSync(pkgPath)) return false;
|
|
@@ -1105,6 +1218,12 @@ async function writeInitExtras(projectDir) {
|
|
|
1105
1218
|
if (enhancePackageScripts(projectDir)) {
|
|
1106
1219
|
log.success(ko.init.scriptsDone);
|
|
1107
1220
|
}
|
|
1221
|
+
const gitignoreResult = ensureRootGitignore(projectDir);
|
|
1222
|
+
if (gitignoreResult === "created") {
|
|
1223
|
+
log.success(ko.init.gitignoreCreated);
|
|
1224
|
+
} else if (gitignoreResult === "updated") {
|
|
1225
|
+
log.success(ko.init.gitignoreUpdated);
|
|
1226
|
+
}
|
|
1108
1227
|
}
|
|
1109
1228
|
|
|
1110
1229
|
// src/commands/recap.ts
|
|
@@ -1578,6 +1697,27 @@ function toCursorrules(sections, projectName) {
|
|
|
1578
1697
|
}
|
|
1579
1698
|
return lines.join("\n");
|
|
1580
1699
|
}
|
|
1700
|
+
function toWindsurfrules(sections, projectName) {
|
|
1701
|
+
const codingSections = sections.filter(
|
|
1702
|
+
(s) => CURSORRULES_KEYS.some((k) => s.title.includes(k))
|
|
1703
|
+
);
|
|
1704
|
+
const lines = [
|
|
1705
|
+
`# ${projectName} \u2014 Windsurf Rules`,
|
|
1706
|
+
"",
|
|
1707
|
+
"> \uCF54\uB529/\uB514\uC790\uC778 \uC804\uC6A9. \uAE30\uB85D/\uC6B4\uC601 \u2192 CLAUDE.md \uCC38\uC870.",
|
|
1708
|
+
"> \u26A1 \uC774 \uD30C\uC77C\uC740 RULES.md\uC5D0\uC11C \uC790\uB3D9 \uC0DD\uC131\uB428 (vhk sync). \uC9C1\uC811 \uC218\uC815 \uAE08\uC9C0.",
|
|
1709
|
+
"",
|
|
1710
|
+
"## \uD544\uC218 \uCC38\uC870",
|
|
1711
|
+
"- docs/PRD.md \xB7 docs/ARCHITECTURE.md \xB7 CLAUDE.md \xB7 RULES.md",
|
|
1712
|
+
""
|
|
1713
|
+
];
|
|
1714
|
+
for (const section of codingSections) {
|
|
1715
|
+
lines.push(`## ${section.title}`);
|
|
1716
|
+
lines.push(section.content);
|
|
1717
|
+
lines.push("");
|
|
1718
|
+
}
|
|
1719
|
+
return lines.join("\n");
|
|
1720
|
+
}
|
|
1581
1721
|
function toClaudeMd(sections, existing) {
|
|
1582
1722
|
const recordSections = sections.filter(
|
|
1583
1723
|
(s) => CLAUDE_MD_KEYS.some((k) => s.title.includes(k))
|
|
@@ -1637,9 +1777,12 @@ ${ko.sync.title}
|
|
|
1637
1777
|
- **\uB9C8\uC9C0\uB9C9 \uC5C5\uB370\uC774\uD2B8:** ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}`;
|
|
1638
1778
|
fs5.writeFileSync(claudePath, toClaudeMd(sections, existingClaude), "utf-8");
|
|
1639
1779
|
console.log(chalk5.green(` ${ko.sync.claudeDone}`));
|
|
1780
|
+
const windsurfPath = path6.join(cwd, ".windsurfrules");
|
|
1781
|
+
fs5.writeFileSync(windsurfPath, toWindsurfrules(sections, projectName), "utf-8");
|
|
1782
|
+
console.log(chalk5.green(` ${ko.sync.windsurfDone}`));
|
|
1640
1783
|
console.log(chalk5.bold.green(`
|
|
1641
1784
|
${ko.sync.done}`));
|
|
1642
|
-
console.log(chalk5.dim(" RULES.md (\uC6D0\uBCF8) \u2192 .cursorrules + CLAUDE.md (\uC790\uB3D9 \uC0DD\uC131)"));
|
|
1785
|
+
console.log(chalk5.dim(" RULES.md (\uC6D0\uBCF8) \u2192 .cursorrules + CLAUDE.md + .windsurfrules (\uC790\uB3D9 \uC0DD\uC131)"));
|
|
1643
1786
|
console.log(chalk5.dim(" \uADDC\uCE59 \uBCC0\uACBD\uC740 \uD56D\uC0C1 RULES.md\uC5D0\uC11C\uB9CC \uD558\uC138\uC694."));
|
|
1644
1787
|
printNextStep({
|
|
1645
1788
|
message: "\uADDC\uCE59 \uB3D9\uAE30\uD654 \uC644\uB8CC! \uC774\uC81C Cursor\uAC00 \uC0C8 \uADDC\uCE59\uC744 \uB530\uB985\uB2C8\uB2E4.",
|
|
@@ -2052,9 +2195,18 @@ ${ko.goal.initTitle}
|
|
|
2052
2195
|
});
|
|
2053
2196
|
}
|
|
2054
2197
|
}
|
|
2198
|
+
function findGateScript(id) {
|
|
2199
|
+
const mjs = join2(SCRIPTS_DIR, `check-goal-${id}.mjs`);
|
|
2200
|
+
if (existsSync2(mjs)) return mjs;
|
|
2201
|
+
const sh = join2(SCRIPTS_DIR, `check-goal-${id}.sh`);
|
|
2202
|
+
if (existsSync2(sh)) return sh;
|
|
2203
|
+
return null;
|
|
2204
|
+
}
|
|
2055
2205
|
function runGate(scriptPath) {
|
|
2056
|
-
const
|
|
2057
|
-
|
|
2206
|
+
const isMjs = scriptPath.endsWith(".mjs");
|
|
2207
|
+
const runner = isMjs ? "node" : "bash";
|
|
2208
|
+
const r = safeExecFile(runner, [scriptPath]);
|
|
2209
|
+
return { ok: r.ok, out: r.out, err: r.ok ? "" : r.err, runner };
|
|
2058
2210
|
}
|
|
2059
2211
|
async function goalCheck(opts) {
|
|
2060
2212
|
console.log(chalk6.bold(`
|
|
@@ -2069,15 +2221,17 @@ ${ko.goal.checkTitle}
|
|
|
2069
2221
|
process.exitCode = 1;
|
|
2070
2222
|
return;
|
|
2071
2223
|
}
|
|
2072
|
-
const scriptPath =
|
|
2073
|
-
if (!
|
|
2074
|
-
console.log(
|
|
2224
|
+
const scriptPath = findGateScript(id);
|
|
2225
|
+
if (!scriptPath) {
|
|
2226
|
+
console.log(
|
|
2227
|
+
chalk6.red(` \u274C \uAC8C\uC774\uD2B8 \uC2A4\uD06C\uB9BD\uD2B8 \uC5C6\uC74C: scripts/check-goal-${id}.{mjs,sh}`)
|
|
2228
|
+
);
|
|
2075
2229
|
process.exitCode = 1;
|
|
2076
2230
|
return;
|
|
2077
2231
|
}
|
|
2078
|
-
console.log(chalk6.dim(` \u25B6 bash ${scriptPath}
|
|
2079
|
-
`));
|
|
2080
2232
|
const gate2 = runGate(scriptPath);
|
|
2233
|
+
console.log(chalk6.dim(` \u25B6 ${gate2.runner} ${scriptPath}
|
|
2234
|
+
`));
|
|
2081
2235
|
if (gate2.out) console.log(gate2.out);
|
|
2082
2236
|
if (gate2.ok) {
|
|
2083
2237
|
console.log(chalk6.green(`
|
|
@@ -2108,15 +2262,19 @@ ${ko.goal.doneTitle}
|
|
|
2108
2262
|
process.exitCode = 1;
|
|
2109
2263
|
return;
|
|
2110
2264
|
}
|
|
2111
|
-
const scriptPath =
|
|
2112
|
-
if (!
|
|
2113
|
-
console.log(
|
|
2265
|
+
const scriptPath = findGateScript(id);
|
|
2266
|
+
if (!scriptPath) {
|
|
2267
|
+
console.log(
|
|
2268
|
+
chalk6.red(
|
|
2269
|
+
` \u274C \uAC8C\uC774\uD2B8 \uC2A4\uD06C\uB9BD\uD2B8 \uC5C6\uC74C \u2014 done \uCC98\uB9AC \uAC70\uBD80: scripts/check-goal-${id}.{mjs,sh}`
|
|
2270
|
+
)
|
|
2271
|
+
);
|
|
2114
2272
|
process.exitCode = 1;
|
|
2115
2273
|
return;
|
|
2116
2274
|
}
|
|
2117
|
-
console.log(chalk6.dim(` \u25B6 \uAC8C\uC774\uD2B8 \uAC80\uC99D: bash ${scriptPath}
|
|
2118
|
-
`));
|
|
2119
2275
|
const gate2 = runGate(scriptPath);
|
|
2276
|
+
console.log(chalk6.dim(` \u25B6 \uAC8C\uC774\uD2B8 \uAC80\uC99D: ${gate2.runner} ${scriptPath}
|
|
2277
|
+
`));
|
|
2120
2278
|
if (gate2.out) console.log(gate2.out);
|
|
2121
2279
|
if (!gate2.ok) {
|
|
2122
2280
|
console.log(
|
|
@@ -2298,7 +2456,7 @@ function checkCommand(name, command, hint) {
|
|
|
2298
2456
|
const version = result.out.split("\n")[0];
|
|
2299
2457
|
return { name, command, version, ok: true, hint };
|
|
2300
2458
|
}
|
|
2301
|
-
function
|
|
2459
|
+
function getVhkVersion2() {
|
|
2302
2460
|
const dir = path10.dirname(fileURLToPath(import.meta.url));
|
|
2303
2461
|
const candidates = [
|
|
2304
2462
|
path10.join(dir, "../package.json"),
|
|
@@ -2352,7 +2510,7 @@ ${ko.doctor.title}
|
|
|
2352
2510
|
}
|
|
2353
2511
|
}
|
|
2354
2512
|
console.log("");
|
|
2355
|
-
const vhkVersion =
|
|
2513
|
+
const vhkVersion = getVhkVersion2();
|
|
2356
2514
|
if (vhkVersion) {
|
|
2357
2515
|
console.log(chalk9.green(" \u2705 VHK") + chalk9.dim(` \u2014 v${vhkVersion}`));
|
|
2358
2516
|
} else {
|
|
@@ -2709,9 +2867,19 @@ async function save() {
|
|
|
2709
2867
|
if (process.exitCode !== 1) {
|
|
2710
2868
|
console.log(chalk11.green(`
|
|
2711
2869
|
\u2705 ${t("save.done", lines.length)}`));
|
|
2870
|
+
printNextStep({
|
|
2871
|
+
message: t("save.nextOkMessage"),
|
|
2872
|
+
command: "vhk recap",
|
|
2873
|
+
cursorHint: t("save.nextOkCursor")
|
|
2874
|
+
});
|
|
2712
2875
|
} else {
|
|
2713
2876
|
console.log(chalk11.green(`
|
|
2714
2877
|
\u2705 ${t("save.doneLocalOnly", lines.length)}`));
|
|
2878
|
+
printNextStep({
|
|
2879
|
+
message: t("save.nextPushFailMessage"),
|
|
2880
|
+
command: "vhk doctor",
|
|
2881
|
+
cursorHint: t("save.nextPushFailCursor")
|
|
2882
|
+
});
|
|
2715
2883
|
}
|
|
2716
2884
|
} catch (err) {
|
|
2717
2885
|
spinner.fail(t("save.failed"));
|
|
@@ -2831,6 +2999,11 @@ ${t("undo.recentHeader")}`));
|
|
|
2831
2999
|
console.log(chalk12.yellow(`
|
|
2832
3000
|
\u{1F4A1} ${t("undo.forcePushHint")}`));
|
|
2833
3001
|
}
|
|
3002
|
+
printNextStep({
|
|
3003
|
+
message: t("undo.nextMessage"),
|
|
3004
|
+
command: "vhk save",
|
|
3005
|
+
cursorHint: t("undo.nextCursor")
|
|
3006
|
+
});
|
|
2834
3007
|
} catch (err) {
|
|
2835
3008
|
console.log(chalk12.red(`\u274C ${t("undo.failed")}`));
|
|
2836
3009
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -2954,7 +3127,20 @@ async function status() {
|
|
|
2954
3127
|
} else {
|
|
2955
3128
|
console.log(chalk13.dim(`\u{1F4E6} ${t("status.noPackage")}`));
|
|
2956
3129
|
}
|
|
2957
|
-
|
|
3130
|
+
const hasChanges = counts.staged + counts.unstaged + counts.untracked > 0;
|
|
3131
|
+
if (hasChanges) {
|
|
3132
|
+
printNextStep({
|
|
3133
|
+
message: t("status.nextWithChangesMessage"),
|
|
3134
|
+
command: "vhk save",
|
|
3135
|
+
cursorHint: t("status.nextWithChangesCursor")
|
|
3136
|
+
});
|
|
3137
|
+
} else {
|
|
3138
|
+
printNextStep({
|
|
3139
|
+
message: t("status.nextCleanMessage"),
|
|
3140
|
+
command: "vhk goal next",
|
|
3141
|
+
cursorHint: t("status.nextCleanCursor")
|
|
3142
|
+
});
|
|
3143
|
+
}
|
|
2958
3144
|
}
|
|
2959
3145
|
|
|
2960
3146
|
// src/commands/diff.ts
|
|
@@ -3114,10 +3300,11 @@ async function mcpInit() {
|
|
|
3114
3300
|
console.log(chalk15.green("\n\u2705 Cursor MCP \uC124\uC815 \uC644\uB8CC!"));
|
|
3115
3301
|
console.log(chalk15.cyan("\u{1F4C1} \uC0DD\uC131\uB41C \uD30C\uC77C:"));
|
|
3116
3302
|
console.log(` ${configPath}`);
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3303
|
+
printNextStep({
|
|
3304
|
+
message: t("mcp.nextMessage"),
|
|
3305
|
+
command: "vhk mcp",
|
|
3306
|
+
cursorHint: t("mcp.nextCursor")
|
|
3307
|
+
});
|
|
3121
3308
|
}
|
|
3122
3309
|
|
|
3123
3310
|
// src/commands/design.ts
|
|
@@ -3696,11 +3883,21 @@ async function update() {
|
|
|
3696
3883
|
console.log(chalk21.green.bold(`
|
|
3697
3884
|
\u{1F389} VHK CLI v${latest} \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC!`));
|
|
3698
3885
|
console.log(chalk21.gray(" \uBCC0\uACBD \uC0AC\uD56D\uC740 GitHub Releases\uB97C \uD655\uC778\uD558\uC138\uC694."));
|
|
3886
|
+
printNextStep({
|
|
3887
|
+
message: t("update.nextOkMessage"),
|
|
3888
|
+
command: "vhk --version",
|
|
3889
|
+
cursorHint: t("update.nextOkCursor")
|
|
3890
|
+
});
|
|
3699
3891
|
} else {
|
|
3700
3892
|
updateSpinner.fail("\uC5C5\uB370\uC774\uD2B8 \uC2E4\uD328");
|
|
3701
3893
|
console.log(chalk21.red(upd.err.slice(0, 300)));
|
|
3702
3894
|
console.log(chalk21.yellow("\n\uC218\uB3D9\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694:"));
|
|
3703
3895
|
console.log(chalk21.gray(` npm update -g ${PACKAGE}`));
|
|
3896
|
+
printNextStep({
|
|
3897
|
+
message: t("update.nextFailMessage"),
|
|
3898
|
+
command: "vhk doctor",
|
|
3899
|
+
cursorHint: t("update.nextFailCursor")
|
|
3900
|
+
});
|
|
3704
3901
|
}
|
|
3705
3902
|
}
|
|
3706
3903
|
|
|
@@ -4306,6 +4503,211 @@ ${ko.start.allDone}
|
|
|
4306
4503
|
});
|
|
4307
4504
|
}
|
|
4308
4505
|
|
|
4506
|
+
// src/commands/cloud.ts
|
|
4507
|
+
import fs13 from "fs";
|
|
4508
|
+
import path14 from "path";
|
|
4509
|
+
import chalk26 from "chalk";
|
|
4510
|
+
|
|
4511
|
+
// src/lib/vhk-cloud.ts
|
|
4512
|
+
var import_ignore = __toESM(require_ignore(), 1);
|
|
4513
|
+
import fs12 from "fs";
|
|
4514
|
+
import path13 from "path";
|
|
4515
|
+
var DEFAULT_CLOUD_EXCLUDES = [
|
|
4516
|
+
"memory.json",
|
|
4517
|
+
// ๊ฐ์ธ ์์ฌ๊ฒฐ์ ๋ฉ๋ชจ
|
|
4518
|
+
"refs.json",
|
|
4519
|
+
// ๊ฐ์ธ ์ฐธ๊ณ ๋งํฌ
|
|
4520
|
+
"HARD_STOP",
|
|
4521
|
+
// ๋ก์ปฌ ์์ ์ ํธ
|
|
4522
|
+
"cloud.json",
|
|
4523
|
+
// gist ํฌ์ธํฐ (๋ฐฑ์
๋์ ์๋)
|
|
4524
|
+
".gitignore"
|
|
4525
|
+
// .vhk/ ๋ด๋ถ gitignore
|
|
4526
|
+
];
|
|
4527
|
+
var VHK_DIR2 = ".vhk";
|
|
4528
|
+
var CLOUD_CONFIG_FILE = "cloud.json";
|
|
4529
|
+
function loadVhkignore(rootDir) {
|
|
4530
|
+
const ig = (0, import_ignore.default)();
|
|
4531
|
+
ig.add(DEFAULT_CLOUD_EXCLUDES);
|
|
4532
|
+
const ignorePath = path13.join(rootDir, ".vhkignore");
|
|
4533
|
+
if (fs12.existsSync(ignorePath)) {
|
|
4534
|
+
ig.add(fs12.readFileSync(ignorePath, "utf-8"));
|
|
4535
|
+
}
|
|
4536
|
+
return ig;
|
|
4537
|
+
}
|
|
4538
|
+
function collectVhkFiles(rootDir, ig = loadVhkignore(rootDir)) {
|
|
4539
|
+
const vhkDir = path13.join(rootDir, VHK_DIR2);
|
|
4540
|
+
let entries;
|
|
4541
|
+
try {
|
|
4542
|
+
entries = fs12.readdirSync(vhkDir, { withFileTypes: true });
|
|
4543
|
+
} catch {
|
|
4544
|
+
return [];
|
|
4545
|
+
}
|
|
4546
|
+
return entries.filter((e) => e.isFile()).map((e) => e.name).filter((name) => !ig.ignores(name)).sort();
|
|
4547
|
+
}
|
|
4548
|
+
function readCloudConfig(rootDir) {
|
|
4549
|
+
const p = path13.join(rootDir, VHK_DIR2, CLOUD_CONFIG_FILE);
|
|
4550
|
+
if (!fs12.existsSync(p)) return null;
|
|
4551
|
+
try {
|
|
4552
|
+
const parsed = JSON.parse(fs12.readFileSync(p, "utf-8"));
|
|
4553
|
+
if (parsed && typeof parsed.gistId === "string" && parsed.gistId) {
|
|
4554
|
+
return { gistId: parsed.gistId };
|
|
4555
|
+
}
|
|
4556
|
+
return null;
|
|
4557
|
+
} catch {
|
|
4558
|
+
return null;
|
|
4559
|
+
}
|
|
4560
|
+
}
|
|
4561
|
+
function writeCloudConfig(rootDir, config) {
|
|
4562
|
+
const vhkDir = path13.join(rootDir, VHK_DIR2);
|
|
4563
|
+
fs12.mkdirSync(vhkDir, { recursive: true });
|
|
4564
|
+
const p = path13.join(vhkDir, CLOUD_CONFIG_FILE);
|
|
4565
|
+
fs12.writeFileSync(p, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
4566
|
+
}
|
|
4567
|
+
|
|
4568
|
+
// src/commands/cloud.ts
|
|
4569
|
+
function ensureGhReady() {
|
|
4570
|
+
const ver = safeExecFile("gh", ["--version"]);
|
|
4571
|
+
if (!ver.ok) {
|
|
4572
|
+
console.log(chalk26.red(` ${ko.cloud.noGh}`));
|
|
4573
|
+
console.log(chalk26.dim(" \uC124\uCE58: https://cli.github.com/ (\uC124\uCE58 \uD6C4 `gh auth login`)"));
|
|
4574
|
+
return false;
|
|
4575
|
+
}
|
|
4576
|
+
const auth = safeExecFile("gh", ["auth", "status"]);
|
|
4577
|
+
if (!auth.ok) {
|
|
4578
|
+
console.log(chalk26.red(` ${ko.cloud.noAuth}`));
|
|
4579
|
+
console.log(chalk26.dim(" \uC2E4\uD589: gh auth login (gist \uAD8C\uD55C \uD544\uC694)"));
|
|
4580
|
+
return false;
|
|
4581
|
+
}
|
|
4582
|
+
return true;
|
|
4583
|
+
}
|
|
4584
|
+
function parseGistId(output) {
|
|
4585
|
+
const match = output.match(/gist\.github\.com\/(?:[^/]+\/)?([0-9a-f]+)/i);
|
|
4586
|
+
if (match) return match[1];
|
|
4587
|
+
const trimmed = output.trim();
|
|
4588
|
+
if (/^[0-9a-f]{8,}$/i.test(trimmed)) return trimmed;
|
|
4589
|
+
return null;
|
|
4590
|
+
}
|
|
4591
|
+
async function cloudPush() {
|
|
4592
|
+
console.log(chalk26.bold(`
|
|
4593
|
+
${ko.cloud.pushTitle}
|
|
4594
|
+
`));
|
|
4595
|
+
const cwd = process.cwd();
|
|
4596
|
+
if (!fs13.existsSync(path14.join(cwd, VHK_DIR2))) {
|
|
4597
|
+
console.log(chalk26.yellow(` ${ko.cloud.noVhkDir}`));
|
|
4598
|
+
return;
|
|
4599
|
+
}
|
|
4600
|
+
const files = collectVhkFiles(cwd);
|
|
4601
|
+
if (files.length === 0) {
|
|
4602
|
+
console.log(chalk26.yellow(` ${ko.cloud.nothingToSync}`));
|
|
4603
|
+
return;
|
|
4604
|
+
}
|
|
4605
|
+
if (!ensureGhReady()) {
|
|
4606
|
+
process.exitCode = 1;
|
|
4607
|
+
return;
|
|
4608
|
+
}
|
|
4609
|
+
const filePaths = files.map((f) => path14.join(cwd, VHK_DIR2, f));
|
|
4610
|
+
console.log(chalk26.dim(` \u{1F4E6} \uBC31\uC5C5 \uB300\uC0C1 ${files.length}\uAC1C: ${files.join(", ")}
|
|
4611
|
+
`));
|
|
4612
|
+
const existing = readCloudConfig(cwd);
|
|
4613
|
+
const desc = `vhk .vhk backup \u2014 ${path14.basename(cwd)}`;
|
|
4614
|
+
if (existing) {
|
|
4615
|
+
const gistFiles = listGistFiles(existing.gistId);
|
|
4616
|
+
for (let i = 0; i < files.length; i++) {
|
|
4617
|
+
const name = files[i];
|
|
4618
|
+
const src = filePaths[i];
|
|
4619
|
+
const args = gistFiles.includes(name) ? ["gist", "edit", existing.gistId, "-f", name, src] : ["gist", "edit", existing.gistId, "-a", src];
|
|
4620
|
+
const res2 = safeExecFile("gh", args);
|
|
4621
|
+
if (!res2.ok) {
|
|
4622
|
+
console.log(chalk26.red(` ${ko.cloud.pushFail}: ${name}`));
|
|
4623
|
+
console.log(chalk26.dim(` ${res2.err}`));
|
|
4624
|
+
process.exitCode = 1;
|
|
4625
|
+
return;
|
|
4626
|
+
}
|
|
4627
|
+
}
|
|
4628
|
+
console.log(chalk26.green.bold(` ${ko.cloud.pushDone}`));
|
|
4629
|
+
console.log(chalk26.dim(` gist: ${existing.gistId} (\uAC31\uC2E0)`));
|
|
4630
|
+
printPushNext();
|
|
4631
|
+
return;
|
|
4632
|
+
}
|
|
4633
|
+
const res = safeExecFile("gh", ["gist", "create", "--desc", desc, ...filePaths]);
|
|
4634
|
+
if (!res.ok) {
|
|
4635
|
+
console.log(chalk26.red(` ${ko.cloud.pushFail}`));
|
|
4636
|
+
console.log(chalk26.dim(` ${res.err || res.out}`));
|
|
4637
|
+
process.exitCode = 1;
|
|
4638
|
+
return;
|
|
4639
|
+
}
|
|
4640
|
+
const gistId = parseGistId(res.out);
|
|
4641
|
+
if (!gistId) {
|
|
4642
|
+
console.log(chalk26.red(` ${ko.cloud.pushFail} \u2014 gist id \uD30C\uC2F1 \uC2E4\uD328`));
|
|
4643
|
+
console.log(chalk26.dim(` \uCD9C\uB825: ${res.out}`));
|
|
4644
|
+
process.exitCode = 1;
|
|
4645
|
+
return;
|
|
4646
|
+
}
|
|
4647
|
+
writeCloudConfig(cwd, { gistId });
|
|
4648
|
+
console.log(chalk26.green.bold(` ${ko.cloud.pushDone}`));
|
|
4649
|
+
console.log(chalk26.dim(` gist: ${gistId} (\uC2E0\uADDC, secret) \u2192 .vhk/cloud.json \uC800\uC7A5`));
|
|
4650
|
+
printPushNext();
|
|
4651
|
+
}
|
|
4652
|
+
async function cloudPull(gistIdArg) {
|
|
4653
|
+
console.log(chalk26.bold(`
|
|
4654
|
+
${ko.cloud.pullTitle}
|
|
4655
|
+
`));
|
|
4656
|
+
const cwd = process.cwd();
|
|
4657
|
+
const gistId = gistIdArg || readCloudConfig(cwd)?.gistId;
|
|
4658
|
+
if (!gistId) {
|
|
4659
|
+
console.log(chalk26.yellow(` ${ko.cloud.noGistId}`));
|
|
4660
|
+
console.log(chalk26.dim(" \uC0AC\uC6A9\uBC95: vhk cloud pull <gistId> (\uB610\uB294 cloud.json \uC774 \uC788\uB294 \uACF3\uC5D0\uC11C \uC2E4\uD589)"));
|
|
4661
|
+
return;
|
|
4662
|
+
}
|
|
4663
|
+
if (!ensureGhReady()) {
|
|
4664
|
+
process.exitCode = 1;
|
|
4665
|
+
return;
|
|
4666
|
+
}
|
|
4667
|
+
const names = listGistFiles(gistId);
|
|
4668
|
+
if (names.length === 0) {
|
|
4669
|
+
console.log(chalk26.red(` ${ko.cloud.pullFail} \u2014 gist \uBE44\uC5C8\uAC70\uB098 \uC811\uADFC \uBD88\uAC00: ${gistId}`));
|
|
4670
|
+
process.exitCode = 1;
|
|
4671
|
+
return;
|
|
4672
|
+
}
|
|
4673
|
+
const vhkDir = path14.join(cwd, VHK_DIR2);
|
|
4674
|
+
fs13.mkdirSync(vhkDir, { recursive: true });
|
|
4675
|
+
let restored = 0;
|
|
4676
|
+
for (const name of names) {
|
|
4677
|
+
const res = safeExecFile("gh", ["gist", "view", gistId, "-f", name, "--raw"]);
|
|
4678
|
+
if (!res.ok) {
|
|
4679
|
+
console.log(chalk26.red(` ${ko.cloud.pullFail}: ${name}`));
|
|
4680
|
+
console.log(chalk26.dim(` ${res.err}`));
|
|
4681
|
+
continue;
|
|
4682
|
+
}
|
|
4683
|
+
fs13.writeFileSync(path14.join(vhkDir, name), ensureTrailingNewline(res.out), "utf-8");
|
|
4684
|
+
restored++;
|
|
4685
|
+
}
|
|
4686
|
+
writeCloudConfig(cwd, { gistId });
|
|
4687
|
+
console.log(chalk26.green.bold(` ${ko.cloud.pullDone}`));
|
|
4688
|
+
console.log(chalk26.dim(` ${restored}\uAC1C \uD30C\uC77C \uBCF5\uC6D0 (gist: ${gistId})`));
|
|
4689
|
+
printNextStep({
|
|
4690
|
+
message: "\uD074\uB77C\uC6B0\uB4DC\uC5D0\uC11C .vhk/ \uBCF5\uC6D0 \uC644\uB8CC!",
|
|
4691
|
+
command: "vhk \uB9E5\uB77D",
|
|
4692
|
+
cursorHint: "\uD504\uB85C\uC81D\uD2B8 \uB9E5\uB77D \uBCF4\uC5EC\uC918"
|
|
4693
|
+
});
|
|
4694
|
+
}
|
|
4695
|
+
function listGistFiles(gistId) {
|
|
4696
|
+
const res = safeExecFile("gh", ["gist", "view", gistId, "--files"]);
|
|
4697
|
+
if (!res.ok) return [];
|
|
4698
|
+
return res.out.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
4699
|
+
}
|
|
4700
|
+
function ensureTrailingNewline(s) {
|
|
4701
|
+
return s.endsWith("\n") ? s : s + "\n";
|
|
4702
|
+
}
|
|
4703
|
+
function printPushNext() {
|
|
4704
|
+
printNextStep({
|
|
4705
|
+
message: "\uD074\uB77C\uC6B0\uB4DC \uBC31\uC5C5 \uC644\uB8CC! \uB2E4\uB978 \uD658\uACBD\uC5D0\uC11C vhk cloud pull \uB85C \uBCF5\uC6D0\uD558\uC138\uC694.",
|
|
4706
|
+
command: "vhk cloud pull",
|
|
4707
|
+
cursorHint: "\uB2E4\uB978 \uCEF4\uD4E8\uD130\uC5D0\uC11C .vhk \uBCF5\uC6D0\uD574\uC918"
|
|
4708
|
+
});
|
|
4709
|
+
}
|
|
4710
|
+
|
|
4309
4711
|
// src/lib/nlp-run.ts
|
|
4310
4712
|
async function dispatchNlpRoute(route, input) {
|
|
4311
4713
|
switch (route.command) {
|
|
@@ -4374,6 +4776,10 @@ async function dispatchNlpRoute(route, input) {
|
|
|
4374
4776
|
return memoryList();
|
|
4375
4777
|
case "brief":
|
|
4376
4778
|
return brief();
|
|
4779
|
+
case "cloud-push":
|
|
4780
|
+
return cloudPush();
|
|
4781
|
+
case "cloud-pull":
|
|
4782
|
+
return cloudPull();
|
|
4377
4783
|
case "goal": {
|
|
4378
4784
|
const sub = route.args?.[0];
|
|
4379
4785
|
if (sub === "next") return goalNext();
|
|
@@ -4386,14 +4792,14 @@ async function dispatchNlpRoute(route, input) {
|
|
|
4386
4792
|
async function runNaturalLanguageRoute(input) {
|
|
4387
4793
|
const route = routeNaturalLanguage(input);
|
|
4388
4794
|
if (!route) {
|
|
4389
|
-
console.log(
|
|
4795
|
+
console.log(chalk27.yellow(`
|
|
4390
4796
|
\u2753 "${input}" \u2014 ${ko.nlp.notMatched}
|
|
4391
4797
|
`));
|
|
4392
4798
|
return;
|
|
4393
4799
|
}
|
|
4394
4800
|
console.log("");
|
|
4395
|
-
console.log(
|
|
4396
|
-
console.log(
|
|
4801
|
+
console.log(chalk27.cyan(` \u{1F4AC} "${input}"`));
|
|
4802
|
+
console.log(chalk27.cyan(` \u2192 ${route.explanation}`));
|
|
4397
4803
|
if (route.confidence === "low") {
|
|
4398
4804
|
const { confirm } = await inquirer11.prompt([{
|
|
4399
4805
|
type: "confirm",
|
|
@@ -4402,7 +4808,7 @@ async function runNaturalLanguageRoute(input) {
|
|
|
4402
4808
|
default: true
|
|
4403
4809
|
}]);
|
|
4404
4810
|
if (!confirm) {
|
|
4405
|
-
console.log(
|
|
4811
|
+
console.log(chalk27.dim(` ${ko.nlp.menuHint}`));
|
|
4406
4812
|
return;
|
|
4407
4813
|
}
|
|
4408
4814
|
}
|
|
@@ -4410,100 +4816,78 @@ async function runNaturalLanguageRoute(input) {
|
|
|
4410
4816
|
await dispatchNlpRoute(route, input);
|
|
4411
4817
|
}
|
|
4412
4818
|
|
|
4413
|
-
// src/lib/version.ts
|
|
4414
|
-
import { existsSync as existsSync15 } from "fs";
|
|
4415
|
-
import { dirname as dirname3, join as join8 } from "path";
|
|
4416
|
-
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
4417
|
-
function getVhkVersion2() {
|
|
4418
|
-
const dir = dirname3(fileURLToPath4(import.meta.url));
|
|
4419
|
-
for (const pkgPath of [
|
|
4420
|
-
join8(dir, "../../package.json"),
|
|
4421
|
-
join8(dir, "../package.json")
|
|
4422
|
-
]) {
|
|
4423
|
-
try {
|
|
4424
|
-
if (existsSync15(pkgPath)) {
|
|
4425
|
-
const pkg = readJsonFile(pkgPath);
|
|
4426
|
-
if (pkg.version) return pkg.version;
|
|
4427
|
-
}
|
|
4428
|
-
} catch {
|
|
4429
|
-
continue;
|
|
4430
|
-
}
|
|
4431
|
-
}
|
|
4432
|
-
return "0.0.0";
|
|
4433
|
-
}
|
|
4434
|
-
|
|
4435
4819
|
// src/commands/agent.ts
|
|
4436
|
-
import
|
|
4820
|
+
import chalk28 from "chalk";
|
|
4437
4821
|
function activeGoalId() {
|
|
4438
4822
|
const goals = listGoals("goals");
|
|
4439
4823
|
const id = selectActiveId(goals);
|
|
4440
4824
|
return id ?? void 0;
|
|
4441
4825
|
}
|
|
4442
4826
|
async function blocker(description) {
|
|
4443
|
-
console.log(
|
|
4827
|
+
console.log(chalk28.bold(`
|
|
4444
4828
|
${ko.agent.blockerTitle}
|
|
4445
4829
|
`));
|
|
4446
4830
|
if (!description || !description.trim()) {
|
|
4447
|
-
console.log(
|
|
4448
|
-
console.log(
|
|
4831
|
+
console.log(chalk28.red(" \u274C \uBE14\uB85C\uCEE4 \uC124\uBA85\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694."));
|
|
4832
|
+
console.log(chalk28.dim(' \uC608: vhk blocker "tsc \uC5D0\uB7EC \u2014 simple-git \uD0C0\uC785 \uD638\uD658"'));
|
|
4449
4833
|
process.exitCode = 1;
|
|
4450
4834
|
return;
|
|
4451
4835
|
}
|
|
4452
4836
|
const goalId = activeGoalId();
|
|
4453
4837
|
const r = appendBlocker(description, goalId);
|
|
4454
|
-
console.log(
|
|
4838
|
+
console.log(chalk28.green(` \u2705 blocker \uAE30\uB85D (\uD604\uC7AC \uD65C\uC131 ${r.count}\uAC74)`));
|
|
4455
4839
|
if (r.hardStopTripped) {
|
|
4456
|
-
console.log(
|
|
4457
|
-
console.log(
|
|
4840
|
+
console.log(chalk28.red.bold(" \u{1F6D1} HARD_STOP \uC790\uB3D9 \uC0DD\uC131 \u2014 \uBAA8\uB4E0 \uC790\uB3D9\uD654 \uC911\uB2E8."));
|
|
4841
|
+
console.log(chalk28.yellow(" \uC0AC\uB78C \uAC80\uD1A0 \uD6C4 `vhk resume --confirm` \uC73C\uB85C\uB9CC \uD574\uC81C."));
|
|
4458
4842
|
process.exitCode = 2;
|
|
4459
4843
|
}
|
|
4460
4844
|
}
|
|
4461
4845
|
async function learn(lesson) {
|
|
4462
|
-
console.log(
|
|
4846
|
+
console.log(chalk28.bold(`
|
|
4463
4847
|
${ko.agent.learnTitle}
|
|
4464
4848
|
`));
|
|
4465
4849
|
if (!lesson || !lesson.trim()) {
|
|
4466
|
-
console.log(
|
|
4467
|
-
console.log(
|
|
4850
|
+
console.log(chalk28.red(" \u274C \uAD50\uD6C8 \uB0B4\uC6A9\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694."));
|
|
4851
|
+
console.log(chalk28.dim(' \uC608: vhk learn "PowerShell \uC5D0\uC11C\uB294 ; \uC0AC\uC6A9 (&& \uBBF8\uC9C0\uC6D0)"'));
|
|
4468
4852
|
process.exitCode = 1;
|
|
4469
4853
|
return;
|
|
4470
4854
|
}
|
|
4471
4855
|
const goalId = activeGoalId();
|
|
4472
4856
|
appendLearning(lesson, goalId);
|
|
4473
|
-
console.log(
|
|
4857
|
+
console.log(chalk28.green(" \u2705 learnings.md append."));
|
|
4474
4858
|
console.log(
|
|
4475
|
-
|
|
4859
|
+
chalk28.dim(" \uACB0\uC815\uC0AC\uD56D(decision)\uC740 `vhk memory add` \uB85C \uBCC4\uB3C4 \uAE30\uB85D \u2014 SoT \uBD84\uB9AC.")
|
|
4476
4860
|
);
|
|
4477
4861
|
}
|
|
4478
4862
|
async function resume(opts = {}) {
|
|
4479
|
-
console.log(
|
|
4863
|
+
console.log(chalk28.bold(`
|
|
4480
4864
|
${ko.agent.resumeTitle}
|
|
4481
4865
|
`));
|
|
4482
4866
|
if (!isHardStopActive()) {
|
|
4483
|
-
console.log(
|
|
4867
|
+
console.log(chalk28.dim(" HARD_STOP \uD65C\uC131 \uC544\uB2D8 \u2014 \uD560 \uC77C \uC5C6\uC74C."));
|
|
4484
4868
|
return;
|
|
4485
4869
|
}
|
|
4486
4870
|
const reason = readHardStopReason();
|
|
4487
4871
|
if (reason) {
|
|
4488
|
-
console.log(
|
|
4489
|
-
console.log(
|
|
4872
|
+
console.log(chalk28.yellow(" \u{1F4CB} HARD_STOP \uC0AC\uC720:"));
|
|
4873
|
+
console.log(chalk28.dim(` ${reason.split("\n").join("\n ")}`));
|
|
4490
4874
|
console.log("");
|
|
4491
4875
|
}
|
|
4492
4876
|
if (!opts.confirm) {
|
|
4493
4877
|
console.log(
|
|
4494
|
-
|
|
4878
|
+
chalk28.red(
|
|
4495
4879
|
" \u274C --confirm \uD50C\uB798\uADF8 \uC5C6\uC774\uB294 \uD574\uC81C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uC790\uB3D9 \uD638\uCD9C \uAE08\uC9C0)."
|
|
4496
4880
|
)
|
|
4497
4881
|
);
|
|
4498
|
-
console.log(
|
|
4882
|
+
console.log(chalk28.yellow(" \uC0AC\uC720\uB97C \uD655\uC778\uD55C \uD6C4 \uB2E4\uC2DC: vhk resume --confirm"));
|
|
4499
4883
|
process.exitCode = 1;
|
|
4500
4884
|
return;
|
|
4501
4885
|
}
|
|
4502
4886
|
const removed = clearHardStop();
|
|
4503
4887
|
if (removed) {
|
|
4504
|
-
console.log(
|
|
4888
|
+
console.log(chalk28.green(" \u2705 HARD_STOP \uD574\uC81C. \uC790\uB3D9\uD654 \uC7AC\uAC1C \uAC00\uB2A5."));
|
|
4505
4889
|
} else {
|
|
4506
|
-
console.log(
|
|
4890
|
+
console.log(chalk28.dim(" \uD30C\uC77C\uC774 \uC774\uBBF8 \uC5C6\uC74C \u2014 no-op."));
|
|
4507
4891
|
}
|
|
4508
4892
|
}
|
|
4509
4893
|
|
|
@@ -4545,7 +4929,7 @@ var KO_ALIASES = {
|
|
|
4545
4929
|
learn: "\uAD50\uD6C8",
|
|
4546
4930
|
resume: "\uC7AC\uAC1C"
|
|
4547
4931
|
};
|
|
4548
|
-
program.name("vhk").description("VHK \u2014 \uBC14\uC774\uBE0C\uCF54\uB529 \uD504\uB85C\uC81D\uD2B8 \uCF54\uCE58 (\uD55C\uAD6D\uC5B4\uB85C \uC548\uB0B4\uD569\uB2C8\uB2E4)").version(
|
|
4932
|
+
program.name("vhk").description("VHK \u2014 \uBC14\uC774\uBE0C\uCF54\uB529 \uD504\uB85C\uC81D\uD2B8 \uCF54\uCE58 (\uD55C\uAD6D\uC5B4\uB85C \uC548\uB0B4\uD569\uB2C8\uB2E4)").version(getVhkVersion());
|
|
4549
4933
|
program.configureHelp({
|
|
4550
4934
|
formatHelp(cmd, helper) {
|
|
4551
4935
|
if (cmd.parent) {
|
|
@@ -4579,6 +4963,15 @@ program.command("check").alias("\uC810\uAC80").alias("\uB9B0\uD2B8").option("--g
|
|
|
4579
4963
|
});
|
|
4580
4964
|
var secureCmd = program.command("secure").alias("\uBCF4\uC548").description("\uBCF4\uC548 \uB3C4\uAD6C \uBAA8\uC74C \u2014 scan: \uC2DC\uD06C\uB9BF\xB7\uD0A4 \uC720\uCD9C \uAC80\uC0AC").action(secure);
|
|
4581
4965
|
secureCmd.command("scan").alias("\uC2A4\uCE94").description("\uC2DC\uD06C\uB9BF/\uD0A4 \uC720\uCD9C \uC2A4\uCE94").action(secure);
|
|
4966
|
+
var cloudCmd = program.command("cloud").alias("\uD074\uB77C\uC6B0\uB4DC").description(".vhk/ \uD074\uB77C\uC6B0\uB4DC \uBC31\uC5C5\xB7\uBCF5\uC6D0 (GitHub gist) \u2014 push: \uC62C\uB9AC\uAE30, pull: \uB0B4\uB9AC\uAE30").action(() => {
|
|
4967
|
+
cloudCmd.help();
|
|
4968
|
+
});
|
|
4969
|
+
cloudCmd.command("push").alias("\uC62C\uB9AC\uAE30").description(".vhk/ \uB97C secret gist \uB85C \uBC31\uC5C5").action(async () => {
|
|
4970
|
+
await cloudPush();
|
|
4971
|
+
});
|
|
4972
|
+
cloudCmd.command("pull").alias("\uB0B4\uB9AC\uAE30").argument("[gistId]", "\uBCF5\uC6D0\uD560 gist id (\uC0DD\uB7B5 \uC2DC .vhk/cloud.json \uC0AC\uC6A9)").description("gist \uC5D0\uC11C .vhk/ \uBCF5\uC6D0").action(async (gistId) => {
|
|
4973
|
+
await cloudPull(gistId);
|
|
4974
|
+
});
|
|
4582
4975
|
program.command("ship").alias("\uCD9C\uD558").description("\uBC30\uD3EC \uCCB4\uD06C\uB9AC\uC2A4\uD2B8 + \uD68C\uACE0 + \uBE4C\uB4DC \uB85C\uADF8 \uC0DD\uC131").action(ship);
|
|
4583
4976
|
program.command("doctor").alias("\uD658\uACBD").alias("\uC9C4\uB2E8").description("\uAC1C\uBC1C \uD658\uACBD \uC810\uAC80 \u2014 Node/Git/npm \uC0C1\uD0DC \uD655\uC778").action(doctor);
|
|
4584
4977
|
program.command("save").alias("\uC800\uC7A5").description("\uBCC0\uACBD\uC0AC\uD56D \uC800\uC7A5 (git add \u2192 commit \u2192 push)").action(async () => {
|
|
@@ -4591,10 +4984,10 @@ program.command("status").alias("\uC0C1\uD0DC").description("\uD504\uB85C\uC81D\
|
|
|
4591
4984
|
await status();
|
|
4592
4985
|
});
|
|
4593
4986
|
program.command("diff").alias("\uBCC0\uACBD").alias("\uCC28\uC774").description("Git \uBCC0\uACBD\uC0AC\uD56D \uD55C\uAD6D\uC5B4 \uC694\uC57D (staged / unstaged / \uC0C8 \uD30C\uC77C)").action(diff);
|
|
4594
|
-
program.command("mcp").description("MCP \uC11C\uBC84 \uC2DC\uC791 (
|
|
4987
|
+
program.command("mcp").description("MCP \uC11C\uBC84 \uC2DC\uC791 (24 tool stdio \u2014 Cursor\xB7Claude Desktop \uB4F1)").action(async () => {
|
|
4595
4988
|
await startMcpServer();
|
|
4596
4989
|
});
|
|
4597
|
-
program.command("mcp-init").alias("mcp\uC124\uC815").description("Cursor MCP \uC5F0\uB3D9 \uC124\uC815 \uC790\uB3D9 \uC0DD\uC131 (.cursor/mcp.json)").action(async () => {
|
|
4990
|
+
program.command("mcp-init").alias("mcp\uC124\uC815").description("Cursor\xB7Claude Desktop MCP \uC5F0\uB3D9 \uC124\uC815 \uC790\uB3D9 \uC0DD\uC131 (.cursor/mcp.json)").action(async () => {
|
|
4598
4991
|
await mcpInit();
|
|
4599
4992
|
});
|
|
4600
4993
|
program.command("deploy").alias("\uBC30\uD3EC").description("\uD504\uB85C\uB355\uC158 \uBC30\uD3EC (Vercel/Netlify/Cloudflare \uC790\uB3D9 \uAC10\uC9C0)").action(async () => {
|
package/dist/mcp/index.js
CHANGED