@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 CHANGED
@@ -1,57 +1,90 @@
1
1
  ---
2
2
  id: vhk-readme
3
- date: 2026-05-24
4
- tags: [vhk, cli, readme, v1.0.0, ga]
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.0.0 GA** โ€” ๋ฐ”์ด๋ธŒ์ฝ”๋”์˜ ์˜ฌ์ธ์› CLI. ๊ณต๊ฐœ API ์•ˆ์ •์„ฑ ๋ณด์žฅ.
9
+ > ๐ŸŽ‰ **v1.4.0** โ€” ๋ฐ”์ด๋ธŒ์ฝ”๋”์˜ ์˜ฌ์ธ์› CLI. ์ปจํ…์ŠคํŠธ + ์ž์œจ ํ•˜๋„ค์Šค + ํฌํ„ฐ๋นŒ๋ฆฌํ‹ฐ.
10
10
  >
11
- > AI ์ฝ”๋”ฉ ์—์ด์ „ํŠธ๋ฅผ ๋ถ€๋ฆฌ๋Š” ์‚ฌ๋žŒ์„ ์œ„ํ•œ **ํ•œ๊ตญ์–ด ํ’€์‚ฌ์ดํด CLI** (v1.0.0)
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
- npx @byh3071/vhk
31
+ mkdir my-app && cd my-app
32
+ vhk start
26
33
  ```
27
34
 
28
- ๋กœ์ปฌ ๊ฐœ๋ฐœ ์ค‘:
35
+ `vhk start` ํ•œ ๋ฒˆ์ด๋ฉด **git init โ†’ ๋ฌธ์„œ ์ƒ์„ฑ โ†’ MCP ๋“ฑ๋ก โ†’ ์ปจํ…์ŠคํŠธ ํŒŒ์ผ** ๊นŒ์ง€ ์ž๋™์œผ๋กœ ๋๋‚ฉ๋‹ˆ๋‹ค.
29
36
 
30
- ```powershell
31
- cd vhk-cli
32
- pnpm install
33
- pnpm build
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
- ```bash
41
- vhk
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๊ฐœ ๋„๊ตฌ(save/undo/status/diff/ship/doctor/check/recap)๋ฅผ stdio๋กœ ๋…ธ์ถœ. Cursor ๋“ฑ MCP ํด๋ผ์ด์–ธํŠธ์—์„œ ์ž์—ฐ์–ด๋กœ ํ˜ธ์ถœ |
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 existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3, appendFileSync as appendFileSync2 } from "fs";
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 = "1.3.0";
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 (existsSync5("package.json")) {
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 (existsSync5("package.json")) {
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(` ${existsSync5(f) ? "\u2705" : "\u274C"} ${f}`);
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(` ${existsSync5(f) ? "\u2705" : "\u26A0\uFE0F"} ${f}`);
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 (!existsSync5(".env")) {
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 (existsSync5(".gitignore")) {
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 (!existsSync5(".env.example")) {
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 = existsSync5(".env") ? parseEnvKeys(readFileSync4(".env", "utf-8")) : [];
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 (!existsSync5("package.json")) {
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 = existsSync5("pnpm-lock.yaml") ? "pnpm" : existsSync5("yarn.lock") ? "yarn" : existsSync5("package-lock.json") ? "npm" : null;
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-3HGOQLRT.js";
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|๋ฆฌ์…‹|๋กค๋ฐฑ|์›๋ž˜๋Œ€๋กœ/.test(t2)
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 chalk26 from "chalk";
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 r = safeExecFile("bash", [scriptPath]);
2057
- return { ok: r.ok, out: r.out, err: r.ok ? "" : r.err };
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 = join2(SCRIPTS_DIR, `check-goal-${id}.sh`);
2073
- if (!existsSync2(scriptPath)) {
2074
- console.log(chalk6.red(` \u274C \uAC8C\uC774\uD2B8 \uC2A4\uD06C\uB9BD\uD2B8 \uC5C6\uC74C: ${scriptPath}`));
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 = join2(SCRIPTS_DIR, `check-goal-${id}.sh`);
2112
- if (!existsSync2(scriptPath)) {
2113
- console.log(chalk6.red(` \u274C \uAC8C\uC774\uD2B8 \uC2A4\uD06C\uB9BD\uD2B8 \uC5C6\uC74C \u2014 done \uCC98\uB9AC \uAC70\uBD80: ${scriptPath}`));
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 getVhkVersion() {
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 = getVhkVersion();
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
- console.log("");
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
- console.log(chalk15.cyan("\n\u{1F504} \uB2E4\uC74C \uB2E8\uACC4:"));
3118
- console.log(" 1. Cursor\uB97C \uC7AC\uC2DC\uC791\uD558\uC138\uC694");
3119
- console.log(" 2. Cursor \uCC44\uD305\uC5D0\uC11C vhk \uB3C4\uAD6C\uB97C \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4");
3120
- console.log(chalk15.gray('\n\u{1F4A1} \uC608: "\uD504\uB85C\uC81D\uD2B8 \uC0C1\uD0DC \uC54C\uB824\uC918" \u2192 Cursor\uAC00 vhk status \uD638\uCD9C'));
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(chalk26.yellow(`
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(chalk26.cyan(` \u{1F4AC} "${input}"`));
4396
- console.log(chalk26.cyan(` \u2192 ${route.explanation}`));
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(chalk26.dim(` ${ko.nlp.menuHint}`));
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 chalk27 from "chalk";
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(chalk27.bold(`
4827
+ console.log(chalk28.bold(`
4444
4828
  ${ko.agent.blockerTitle}
4445
4829
  `));
4446
4830
  if (!description || !description.trim()) {
4447
- console.log(chalk27.red(" \u274C \uBE14\uB85C\uCEE4 \uC124\uBA85\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694."));
4448
- console.log(chalk27.dim(' \uC608: vhk blocker "tsc \uC5D0\uB7EC \u2014 simple-git \uD0C0\uC785 \uD638\uD658"'));
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(chalk27.green(` \u2705 blocker \uAE30\uB85D (\uD604\uC7AC \uD65C\uC131 ${r.count}\uAC74)`));
4838
+ console.log(chalk28.green(` \u2705 blocker \uAE30\uB85D (\uD604\uC7AC \uD65C\uC131 ${r.count}\uAC74)`));
4455
4839
  if (r.hardStopTripped) {
4456
- console.log(chalk27.red.bold(" \u{1F6D1} HARD_STOP \uC790\uB3D9 \uC0DD\uC131 \u2014 \uBAA8\uB4E0 \uC790\uB3D9\uD654 \uC911\uB2E8."));
4457
- console.log(chalk27.yellow(" \uC0AC\uB78C \uAC80\uD1A0 \uD6C4 `vhk resume --confirm` \uC73C\uB85C\uB9CC \uD574\uC81C."));
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(chalk27.bold(`
4846
+ console.log(chalk28.bold(`
4463
4847
  ${ko.agent.learnTitle}
4464
4848
  `));
4465
4849
  if (!lesson || !lesson.trim()) {
4466
- console.log(chalk27.red(" \u274C \uAD50\uD6C8 \uB0B4\uC6A9\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694."));
4467
- console.log(chalk27.dim(' \uC608: vhk learn "PowerShell \uC5D0\uC11C\uB294 ; \uC0AC\uC6A9 (&& \uBBF8\uC9C0\uC6D0)"'));
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(chalk27.green(" \u2705 learnings.md append."));
4857
+ console.log(chalk28.green(" \u2705 learnings.md append."));
4474
4858
  console.log(
4475
- chalk27.dim(" \uACB0\uC815\uC0AC\uD56D(decision)\uC740 `vhk memory add` \uB85C \uBCC4\uB3C4 \uAE30\uB85D \u2014 SoT \uBD84\uB9AC.")
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(chalk27.bold(`
4863
+ console.log(chalk28.bold(`
4480
4864
  ${ko.agent.resumeTitle}
4481
4865
  `));
4482
4866
  if (!isHardStopActive()) {
4483
- console.log(chalk27.dim(" HARD_STOP \uD65C\uC131 \uC544\uB2D8 \u2014 \uD560 \uC77C \uC5C6\uC74C."));
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(chalk27.yellow(" \u{1F4CB} HARD_STOP \uC0AC\uC720:"));
4489
- console.log(chalk27.dim(` ${reason.split("\n").join("\n ")}`));
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
- chalk27.red(
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(chalk27.yellow(" \uC0AC\uC720\uB97C \uD655\uC778\uD55C \uD6C4 \uB2E4\uC2DC: vhk resume --confirm"));
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(chalk27.green(" \u2705 HARD_STOP \uD574\uC81C. \uC790\uB3D9\uD654 \uC7AC\uAC1C \uAC00\uB2A5."));
4888
+ console.log(chalk28.green(" \u2705 HARD_STOP \uD574\uC81C. \uC790\uB3D9\uD654 \uC7AC\uAC1C \uAC00\uB2A5."));
4505
4889
  } else {
4506
- console.log(chalk27.dim(" \uD30C\uC77C\uC774 \uC774\uBBF8 \uC5C6\uC74C \u2014 no-op."));
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(getVhkVersion2());
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 (Cursor \uB4F1 MCP \uD074\uB77C\uC774\uC5B8\uD2B8\uC6A9)").action(async () => {
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
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startMcpServer
4
- } from "../chunk-3HGOQLRT.js";
4
+ } from "../chunk-3DV7AEN4.js";
5
5
 
6
6
  // src/mcp/index.ts
7
7
  startMcpServer().catch((err) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@byh3071/vhk",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Vibe Harness Kit โ€” ๋ฐ”์ด๋ธŒ์ฝ”๋”ฉ ํ’€์‚ฌ์ดํด CLI",
5
5
  "bin": {
6
6
  "vhk": "dist/index.js",