@byh3071/vhk 1.3.1 โ†’ 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,12 +1,12 @@
1
1
  ---
2
2
  id: vhk-readme
3
3
  date: 2026-05-28
4
- tags: [vhk, cli, readme, v1.3.0, ga]
4
+ tags: [vhk, cli, readme, v1.4.0, ga]
5
5
  ---
6
6
 
7
7
  # ๐Ÿ”ง VHK โ€” Vibe Harness Kit
8
8
 
9
- > ๐ŸŽ‰ **v1.3.0** โ€” ๋ฐ”์ด๋ธŒ์ฝ”๋”์˜ ์˜ฌ์ธ์› CLI. ์ปจํ…์ŠคํŠธ + ์ž์œจ ํ•˜๋„ค์Šค.
9
+ > ๐ŸŽ‰ **v1.4.0** โ€” ๋ฐ”์ด๋ธŒ์ฝ”๋”์˜ ์˜ฌ์ธ์› CLI. ์ปจํ…์ŠคํŠธ + ์ž์œจ ํ•˜๋„ค์Šค + ํฌํ„ฐ๋นŒ๋ฆฌํ‹ฐ.
10
10
  >
11
11
  > AI ์ฝ”๋”ฉ ์—์ด์ „ํŠธ๋ฅผ ๋ถ€๋ฆฌ๋Š” ์‚ฌ๋žŒ์„ ์œ„ํ•œ **ํ•œ๊ตญ์–ด ํ’€์‚ฌ์ดํด CLI**.
12
12
  >
@@ -115,11 +115,13 @@ vhk ๊ธฐํš ๋๋‚ฌ๊ณ  ๋ฐ”๋กœ ์‹œ์ž‘
115
115
  | `vhk gate` | `๊ฒ€์ฆ`, `์•„์ด๋””์–ด` | ์•„์ด๋””์–ด ๊ฒ€์ฆ (ํ€ต 5๋ฌธํ•ญ / ํ’€ 13๋ฌธํ•ญ / ์Šคํ‚ต) |
116
116
  | `vhk init` | `์‹œ์ž‘`, `๋งŒ๋“ค๊ธฐ` | ํ”„๋กœ์ ํŠธ ์ดˆ๊ธฐํ™” + ํ•˜๋„ค์Šค ์ƒ์„ฑ |
117
117
  | `vhk recap` | `์ •๋ฆฌ`, `์˜ค๋Š˜` | Git ๋ณ€๊ฒฝ โ†’ `docs/log/` ์„ธ์…˜ ๋กœ๊ทธ |
118
- | `vhk sync` | `๊ทœ์น™`, `๋งž์ถ”๊ธฐ` | RULES.md โ†’ `.cursorrules` + CLAUDE.md |
118
+ | `vhk sync` | `๊ทœ์น™`, `๋งž์ถ”๊ธฐ` | RULES.md โ†’ `.cursorrules` + CLAUDE.md + `.windsurfrules` |
119
119
  | `vhk check` | `์ ๊ฒ€`, `๋ฆฐํŠธ` | RULES.md ๊ทœ์น™ ์œ„๋ฐ˜ ๊ฒ€์‚ฌ |
120
120
  | `vhk secure` | `๋ณด์•ˆ` | ์‹œํฌ๋ฆฟยทํ‚ค ์œ ์ถœ ์Šค์บ” (`scan` / `์Šค์บ”` ๋™์ผ). **CRITICAL/HIGH ๋ฐœ๊ฒฌ ์‹œ exit code 1** (CI์šฉ) |
121
121
  | `vhk ship` | `์ถœํ•˜` | ๋ฐฐํฌ ์ฒดํฌ๋ฆฌ์ŠคํŠธ + ํšŒ๊ณ  + ๋นŒ๋“œ ๋กœ๊ทธ |
122
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) |
123
125
  | `vhk save` | `์ €์žฅ`, `์ปค๋ฐ‹` | git add ยท commit ยท push ํ•œ ๋ฒˆ์— |
124
126
  | `vhk undo` | `๋˜๋Œ๋ฆฌ๊ธฐ`, `์ทจ์†Œ` | ์ตœ๊ทผ ์ปค๋ฐ‹ soft reset (๋ณ€๊ฒฝ์€ staged ์œ ์ง€) |
125
127
  | `vhk diff` | `๋ณ€๊ฒฝ`, `์ฐจ์ด` | staged / unstaged / ์ƒˆ ํŒŒ์ผ ์š”์•ฝ (์ค„ ์ˆ˜ ํ•ฉ๊ณ„๋Š” trackedยทHEAD ๊ธฐ์ค€) |
@@ -340,8 +342,26 @@ vhk ref open 1 # 1๋ฒˆ ๋ ˆํผ๋Ÿฐ์Šค๋ฅผ ๋ธŒ๋ผ์šฐ์ €๋กœ ์—ด๊ธฐ
340
342
  - `docs/PRD.md`, `docs/ARCHITECTURE.md`
341
343
  - `docs/adr/`, `docs/log/`, `docs/troubleshooting/`
342
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` ๋ณดํ˜ธ โ€” ๊ธฐ์กด ํŒŒ์ผ์€ ๋ณด์กดํ•˜๊ณ  ๋ˆ„๋ฝ๋ถ„๋งŒ ์ถ”๊ฐ€)
343
348
  - `package.json` scripts: `save`, `check`, `scan`, `recap`, `ship`, `doctor` โ†’ `vhk` ํ˜ธ์ถœ
344
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
+
345
365
  ## ์ž์—ฐ์–ด ์˜ˆ์‹œ
346
366
 
347
367
  | ๋งํ•˜๋ฉด | ์‹คํ–‰ |
@@ -696,7 +696,9 @@ var ko = {
696
696
  gitHintCommand: 'git init && git add . && git commit -m "feat: \uD504\uB85C\uC81D\uD2B8 \uC2DC\uC791"',
697
697
  startDev: "\uC774\uC81C \uAC1C\uBC1C\uD574 \uBCF4\uC138\uC694! \u{1F680}",
698
698
  commandsMdDone: "\u{1F4CB} COMMANDS.md \uC0DD\uC131",
699
- 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)"
700
702
  },
701
703
  recap: {
702
704
  title: "\u{1F4DD} \uC624\uB298 \uD55C \uC77C \uC815\uB9AC",
@@ -752,8 +754,22 @@ var ko = {
752
754
  noRules: "\u26A0\uFE0F RULES.md \uD30C\uC77C\uC774 \uC5C6\uC5B4\uC694.",
753
755
  cursorrulesDone: "\u2705 .cursorrules \uB9DE\uCDA4 \uC644\uB8CC",
754
756
  claudeDone: "\u2705 CLAUDE.md \uB9DE\uCDA4 \uC644\uB8CC",
757
+ windsurfDone: "\u2705 .windsurfrules \uB9DE\uCDA4 \uC644\uB8CC",
755
758
  done: "\u{1F504} \uB9DE\uCD94\uAE30 \uC644\uB8CC!"
756
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
+ },
757
773
  ship: {
758
774
  title: "\u{1F680} \uBC30\uD3EC \uCCB4\uD06C\uB9AC\uC2A4\uD2B8",
759
775
  checklist: "\u{1F4CB} \uBC30\uD3EC \uC804 \uCCB4\uD06C\uB9AC\uC2A4\uD2B8",
@@ -2134,9 +2150,11 @@ async function startMcpServer() {
2134
2150
  }
2135
2151
 
2136
2152
  export {
2153
+ __toESM,
2137
2154
  ko,
2138
2155
  t,
2139
2156
  printNextStep,
2157
+ require_ignore,
2140
2158
  printSecurityWarnings,
2141
2159
  filterTrackedPaths,
2142
2160
  readJsonFile,
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@
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,
@@ -14,11 +15,12 @@ import {
14
15
  printSecurityWarnings,
15
16
  publish,
16
17
  readJsonFile,
18
+ require_ignore,
17
19
  safeExecFile,
18
20
  scanProjectForSecrets,
19
21
  startMcpServer,
20
22
  t
21
- } from "./chunk-6S3JYYZ3.js";
23
+ } from "./chunk-3DV7AEN4.js";
22
24
 
23
25
  // src/index.ts
24
26
  import { Command, Help } from "commander";
@@ -40,6 +42,19 @@ function matchesKeywords(text, command) {
40
42
  return keywords.some((kw) => text.includes(kw.toLowerCase()));
41
43
  }
42
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
+ },
43
58
  {
44
59
  command: "start",
45
60
  explanation: "\uB178\uC158\uC5D0\uC11C \uAC00\uC838\uC640 \uC0C8 \uD504\uB85C\uC81D\uD2B8 \uC2DC\uC791 \uB9C8\uBC95\uC0AC (vhk start --from-notion)",
@@ -177,7 +192,7 @@ var RULES = [
177
192
  command: "save",
178
193
  explanation: "Git\uC5D0 \uC800\uC7A5 (vhk \uC800\uC7A5)",
179
194
  confidence: "high",
180
- 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)
181
196
  },
182
197
  {
183
198
  command: "recap",
@@ -352,6 +367,8 @@ var KNOWN_COMMAND_TOKENS = /* @__PURE__ */ new Set([
352
367
  "\uAE30\uC5B5",
353
368
  "brief",
354
369
  "\uBE0C\uB9AC\uD551",
370
+ "cloud",
371
+ "\uD074\uB77C\uC6B0\uB4DC",
355
372
  "goal",
356
373
  "\uBAA9\uD45C",
357
374
  "blocker",
@@ -383,7 +400,7 @@ function detectNaturalLanguageInput(argv) {
383
400
  }
384
401
 
385
402
  // src/lib/nlp-run.ts
386
- import chalk26 from "chalk";
403
+ import chalk27 from "chalk";
387
404
  import inquirer11 from "inquirer";
388
405
 
389
406
  // src/commands/gate.ts
@@ -738,6 +755,70 @@ function COMMANDS_MD_TEMPLATE() {
738
755
  ].join("\n");
739
756
  }
740
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
+
741
822
  // src/utils/logger.ts
742
823
  import chalk2 from "chalk";
743
824
  var log = {
@@ -995,7 +1076,7 @@ ${ko.init.recommendedStack} ${stack.join(" + ")}
995
1076
  }
996
1077
  }
997
1078
  const cwd = process.cwd();
998
- const files = generateFiles(answers.name, answers.description, stack, prdContent);
1079
+ const files = generateFiles(answers.name, answers.description, stack, prdContent, answers.type);
999
1080
  log.step(ko.init.filesGenerating);
1000
1081
  for (const [filePath, content] of Object.entries(files)) {
1001
1082
  const fullPath = path2.join(cwd, filePath);
@@ -1040,7 +1121,7 @@ ${ko.init.nextSteps}`));
1040
1121
  alternative: "VS Code/Cursor\uC5D0\uC11C \uD3F4\uB354\uB97C \uC5F4\uC5B4\uB3C4 \uB429\uB2C8\uB2E4"
1041
1122
  });
1042
1123
  }
1043
- function generateFiles(name, description, stack, prdContent = {}) {
1124
+ function generateFiles(name, description, stack, prdContent = {}, type = "") {
1044
1125
  const stackStr = stack.join(" + ");
1045
1126
  const prd = {
1046
1127
  tagline: description,
@@ -1065,7 +1146,12 @@ function generateFiles(name, description, stack, prdContent = {}) {
1065
1146
  ## v1.1 \uD6C4\uBCF4
1066
1147
 
1067
1148
  -
1068
- `
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()
1069
1155
  };
1070
1156
  }
1071
1157
  var VHK_PACKAGE_SCRIPTS = {
@@ -1076,6 +1162,32 @@ var VHK_PACKAGE_SCRIPTS = {
1076
1162
  ship: "vhk ship",
1077
1163
  doctor: "vhk doctor"
1078
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
+ }
1079
1191
  function enhancePackageScripts(projectDir) {
1080
1192
  const pkgPath = path2.join(projectDir, "package.json");
1081
1193
  if (!fs2.existsSync(pkgPath)) return false;
@@ -1106,6 +1218,12 @@ async function writeInitExtras(projectDir) {
1106
1218
  if (enhancePackageScripts(projectDir)) {
1107
1219
  log.success(ko.init.scriptsDone);
1108
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
+ }
1109
1227
  }
1110
1228
 
1111
1229
  // src/commands/recap.ts
@@ -1579,6 +1697,27 @@ function toCursorrules(sections, projectName) {
1579
1697
  }
1580
1698
  return lines.join("\n");
1581
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
+ }
1582
1721
  function toClaudeMd(sections, existing) {
1583
1722
  const recordSections = sections.filter(
1584
1723
  (s) => CLAUDE_MD_KEYS.some((k) => s.title.includes(k))
@@ -1638,9 +1777,12 @@ ${ko.sync.title}
1638
1777
  - **\uB9C8\uC9C0\uB9C9 \uC5C5\uB370\uC774\uD2B8:** ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}`;
1639
1778
  fs5.writeFileSync(claudePath, toClaudeMd(sections, existingClaude), "utf-8");
1640
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}`));
1641
1783
  console.log(chalk5.bold.green(`
1642
1784
  ${ko.sync.done}`));
1643
- 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)"));
1644
1786
  console.log(chalk5.dim(" \uADDC\uCE59 \uBCC0\uACBD\uC740 \uD56D\uC0C1 RULES.md\uC5D0\uC11C\uB9CC \uD558\uC138\uC694."));
1645
1787
  printNextStep({
1646
1788
  message: "\uADDC\uCE59 \uB3D9\uAE30\uD654 \uC644\uB8CC! \uC774\uC81C Cursor\uAC00 \uC0C8 \uADDC\uCE59\uC744 \uB530\uB985\uB2C8\uB2E4.",
@@ -4361,6 +4503,211 @@ ${ko.start.allDone}
4361
4503
  });
4362
4504
  }
4363
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
+
4364
4711
  // src/lib/nlp-run.ts
4365
4712
  async function dispatchNlpRoute(route, input) {
4366
4713
  switch (route.command) {
@@ -4429,6 +4776,10 @@ async function dispatchNlpRoute(route, input) {
4429
4776
  return memoryList();
4430
4777
  case "brief":
4431
4778
  return brief();
4779
+ case "cloud-push":
4780
+ return cloudPush();
4781
+ case "cloud-pull":
4782
+ return cloudPull();
4432
4783
  case "goal": {
4433
4784
  const sub = route.args?.[0];
4434
4785
  if (sub === "next") return goalNext();
@@ -4441,14 +4792,14 @@ async function dispatchNlpRoute(route, input) {
4441
4792
  async function runNaturalLanguageRoute(input) {
4442
4793
  const route = routeNaturalLanguage(input);
4443
4794
  if (!route) {
4444
- console.log(chalk26.yellow(`
4795
+ console.log(chalk27.yellow(`
4445
4796
  \u2753 "${input}" \u2014 ${ko.nlp.notMatched}
4446
4797
  `));
4447
4798
  return;
4448
4799
  }
4449
4800
  console.log("");
4450
- console.log(chalk26.cyan(` \u{1F4AC} "${input}"`));
4451
- console.log(chalk26.cyan(` \u2192 ${route.explanation}`));
4801
+ console.log(chalk27.cyan(` \u{1F4AC} "${input}"`));
4802
+ console.log(chalk27.cyan(` \u2192 ${route.explanation}`));
4452
4803
  if (route.confidence === "low") {
4453
4804
  const { confirm } = await inquirer11.prompt([{
4454
4805
  type: "confirm",
@@ -4457,7 +4808,7 @@ async function runNaturalLanguageRoute(input) {
4457
4808
  default: true
4458
4809
  }]);
4459
4810
  if (!confirm) {
4460
- console.log(chalk26.dim(` ${ko.nlp.menuHint}`));
4811
+ console.log(chalk27.dim(` ${ko.nlp.menuHint}`));
4461
4812
  return;
4462
4813
  }
4463
4814
  }
@@ -4466,77 +4817,77 @@ async function runNaturalLanguageRoute(input) {
4466
4817
  }
4467
4818
 
4468
4819
  // src/commands/agent.ts
4469
- import chalk27 from "chalk";
4820
+ import chalk28 from "chalk";
4470
4821
  function activeGoalId() {
4471
4822
  const goals = listGoals("goals");
4472
4823
  const id = selectActiveId(goals);
4473
4824
  return id ?? void 0;
4474
4825
  }
4475
4826
  async function blocker(description) {
4476
- console.log(chalk27.bold(`
4827
+ console.log(chalk28.bold(`
4477
4828
  ${ko.agent.blockerTitle}
4478
4829
  `));
4479
4830
  if (!description || !description.trim()) {
4480
- console.log(chalk27.red(" \u274C \uBE14\uB85C\uCEE4 \uC124\uBA85\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694."));
4481
- 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"'));
4482
4833
  process.exitCode = 1;
4483
4834
  return;
4484
4835
  }
4485
4836
  const goalId = activeGoalId();
4486
4837
  const r = appendBlocker(description, goalId);
4487
- 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)`));
4488
4839
  if (r.hardStopTripped) {
4489
- console.log(chalk27.red.bold(" \u{1F6D1} HARD_STOP \uC790\uB3D9 \uC0DD\uC131 \u2014 \uBAA8\uB4E0 \uC790\uB3D9\uD654 \uC911\uB2E8."));
4490
- 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."));
4491
4842
  process.exitCode = 2;
4492
4843
  }
4493
4844
  }
4494
4845
  async function learn(lesson) {
4495
- console.log(chalk27.bold(`
4846
+ console.log(chalk28.bold(`
4496
4847
  ${ko.agent.learnTitle}
4497
4848
  `));
4498
4849
  if (!lesson || !lesson.trim()) {
4499
- console.log(chalk27.red(" \u274C \uAD50\uD6C8 \uB0B4\uC6A9\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694."));
4500
- 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)"'));
4501
4852
  process.exitCode = 1;
4502
4853
  return;
4503
4854
  }
4504
4855
  const goalId = activeGoalId();
4505
4856
  appendLearning(lesson, goalId);
4506
- console.log(chalk27.green(" \u2705 learnings.md append."));
4857
+ console.log(chalk28.green(" \u2705 learnings.md append."));
4507
4858
  console.log(
4508
- 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.")
4509
4860
  );
4510
4861
  }
4511
4862
  async function resume(opts = {}) {
4512
- console.log(chalk27.bold(`
4863
+ console.log(chalk28.bold(`
4513
4864
  ${ko.agent.resumeTitle}
4514
4865
  `));
4515
4866
  if (!isHardStopActive()) {
4516
- 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."));
4517
4868
  return;
4518
4869
  }
4519
4870
  const reason = readHardStopReason();
4520
4871
  if (reason) {
4521
- console.log(chalk27.yellow(" \u{1F4CB} HARD_STOP \uC0AC\uC720:"));
4522
- 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 ")}`));
4523
4874
  console.log("");
4524
4875
  }
4525
4876
  if (!opts.confirm) {
4526
4877
  console.log(
4527
- chalk27.red(
4878
+ chalk28.red(
4528
4879
  " \u274C --confirm \uD50C\uB798\uADF8 \uC5C6\uC774\uB294 \uD574\uC81C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uC790\uB3D9 \uD638\uCD9C \uAE08\uC9C0)."
4529
4880
  )
4530
4881
  );
4531
- 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"));
4532
4883
  process.exitCode = 1;
4533
4884
  return;
4534
4885
  }
4535
4886
  const removed = clearHardStop();
4536
4887
  if (removed) {
4537
- 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."));
4538
4889
  } else {
4539
- 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."));
4540
4891
  }
4541
4892
  }
4542
4893
 
@@ -4612,6 +4963,15 @@ program.command("check").alias("\uC810\uAC80").alias("\uB9B0\uD2B8").option("--g
4612
4963
  });
4613
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);
4614
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
+ });
4615
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);
4616
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);
4617
4977
  program.command("save").alias("\uC800\uC7A5").description("\uBCC0\uACBD\uC0AC\uD56D \uC800\uC7A5 (git add \u2192 commit \u2192 push)").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-6S3JYYZ3.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.1",
3
+ "version": "1.4.0",
4
4
  "description": "Vibe Harness Kit โ€” ๋ฐ”์ด๋ธŒ์ฝ”๋”ฉ ํ’€์‚ฌ์ดํด CLI",
5
5
  "bin": {
6
6
  "vhk": "dist/index.js",