@byh3071/vhk 0.9.1 โ†’ 1.0.1

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,14 @@
1
1
  ---
2
2
  id: vhk-readme
3
3
  date: 2026-05-24
4
- tags: [vhk, cli, readme, v0.9.0]
4
+ tags: [vhk, cli, readme, v1.0.0, ga]
5
5
  ---
6
6
 
7
7
  # ๐Ÿ”ง VHK โ€” Vibe Harness Kit
8
8
 
9
- > AI ์ฝ”๋”ฉ ์—์ด์ „ํŠธ๋ฅผ ๋ถ€๋ฆฌ๋Š” ์‚ฌ๋žŒ์„ ์œ„ํ•œ **ํ•œ๊ตญ์–ด ํ’€์‚ฌ์ดํด CLI** (v0.9.0)
9
+ > ๐ŸŽ‰ **v1.0.0 GA** โ€” ๋ฐ”์ด๋ธŒ์ฝ”๋”์˜ ์˜ฌ์ธ์› CLI. ๊ณต๊ฐœ API ์•ˆ์ •์„ฑ ๋ณด์žฅ.
10
+ >
11
+ > AI ์ฝ”๋”ฉ ์—์ด์ „ํŠธ๋ฅผ ๋ถ€๋ฆฌ๋Š” ์‚ฌ๋žŒ์„ ์œ„ํ•œ **ํ•œ๊ตญ์–ด ํ’€์‚ฌ์ดํด CLI** (v1.0.0)
10
12
  >
11
13
  > ๐Ÿฝ๏ธ **VHK๋Š” VHK๋กœ ๋ถ€ํŠธ์ŠคํŠธ๋žฉ๋จ** โ€” ์ด ๋ ˆํฌ์˜ `docs/`, `CLAUDE.md`, `.cursorrules`๋„ `vhk init`์ด ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.
12
14
 
@@ -100,9 +102,13 @@ vhk ๊ธฐํš ๋๋‚ฌ๊ณ  ๋ฐ”๋กœ ์‹œ์ž‘
100
102
  | `vhk theme` | `ํ…Œ๋งˆ` | ๋‹คํฌ/๋ผ์ดํŠธ ๋ชจ๋“œ CSS + ํ† ๊ธ€ ์œ ํ‹ธ๋ฆฌํ‹ฐ ์ƒ์„ฑ |
101
103
  | `vhk ref` | `๋ ˆํผ๋Ÿฐ์Šค` | ๋ ˆํผ๋Ÿฐ์Šค URL ๊ด€๋ฆฌ (`add` / `list` / `open`) |
102
104
  | `vhk harness` | `ํ•˜๋„ค์Šค` | ํ†ตํ•ฉ ํ’ˆ์งˆ ์ ๊ฒ€ (lint + type-check + test + build ์ˆœ์ฐจ ์‹คํ–‰ + ๋ฆฌํฌํŠธ) |
103
- | `vhk audit` | `๊ฐ์‚ฌ` | npm ๋ณด์•ˆ ์ทจ์•ฝ์  ๊ฐ์‚ฌ (`--fix`๋กœ ์ž๋™ ์ˆ˜์ •) |
105
+ | `vhk audit` | `๊ฐ์‚ฌ` | npm/pnpm/yarn ๋ณด์•ˆ ์ทจ์•ฝ์  ๊ฐ์‚ฌ (`--fix`๋กœ ์ž๋™ ์ˆ˜์ •, npm๋งŒ) |
104
106
  | `vhk migrate [target]` | `์ „ํ™˜` | ํŒจํ‚ค์ง€ ๋งค๋‹ˆ์ € ์ „ํ™˜ (`npm` / `yarn` / `pnpm`, lockfile + node_modules ์žฌ๊ตฌ์„ฑ) |
105
107
  | `vhk update` | `์—…๋ฐ์ดํŠธ` | VHK CLI ์ตœ์‹  ๋ฒ„์ „์œผ๋กœ ์…€ํ”„ ์—…๋ฐ์ดํŠธ |
108
+ | `vhk context` | `๋งฅ๋ฝ` | ํ”„๋กœ์ ํŠธ ํŠธ๋ฆฌยท์ŠคํƒยทCLI ๋ช…๋ น ๋ชฉ๋ก์„ `.vhk/context.md`๋กœ ์ž๋™ ์ƒ์„ฑ (AI ์–ด์‹œ์Šคํ„ดํŠธ์šฉ) |
109
+ | `vhk context-show` | `๋งฅ๋ฝ๋ณด๊ธฐ` | ํ˜„์žฌ ์ปจํ…์ŠคํŠธ ํŒŒ์ผ ๋‚ด์šฉ ์ถœ๋ ฅ |
110
+ | `vhk memory` | `๊ธฐ์–ต` | ๊ฒฐ์ •์‚ฌํ•ญ ๊ธฐ์–ต ๊ด€๋ฆฌ (`add` / `list` / `remove`, `.vhk/memory.json` ๊ธฐ๋ฐ˜, ํƒœ๊ทธ ์ง€์›) |
111
+ | `vhk brief` | `๋ธŒ๋ฆฌํ•‘` | ํ”„๋กœ์ ํŠธ ์ •๋ณด + git ์ƒํƒœ + ๊ฒฐ์ •์‚ฌํ•ญ + ๋ ˆํผ๋Ÿฐ์Šค ํ†ตํ•ฉ ๋ณด๊ณ ์„œ `.vhk/brief.md` |
106
112
 
107
113
  ### init ์˜ต์…˜
108
114
 
@@ -137,6 +143,45 @@ MCP ์„œ๋ฒ„๋ฅผ ์ˆ˜๋™์œผ๋กœ ๋„์šฐ๋ ค๋ฉด:
137
143
  vhk mcp # stdio ์„œ๋ฒ„ ์‹œ์ž‘ (Cursor๊ฐ€ ์ž๋™์œผ๋กœ ํ˜ธ์ถœ)
138
144
  ```
139
145
 
146
+ ## v1.0.0 GA ํ•˜์ด๋ผ์ดํŠธ ๐ŸŽ‰
147
+
148
+ > **๊ณต๊ฐœ API ์•ˆ์ •์„ฑ ์•ฝ์†**. ๋ช…๋ น์–ด ์ด๋ฆ„, CLI ์ธ์ž, `.vhk/` ํŒŒ์ผ ํฌ๋งท์€ v2.0๊นŒ์ง€ breaking change ์—†์Œ.
149
+
150
+ | ๊ธฐ๋Šฅ | ์„ค๋ช… |
151
+ |------|------|
152
+ | **context** | ํ”„๋กœ์ ํŠธ ๋””๋ ‰ํ† ๋ฆฌ ํŠธ๋ฆฌ(3-depth) + ๊ธฐ์ˆ  ์Šคํƒ(Next/Nuxt/Vue/Svelte/TS/Tailwind/tsup/Vite/...) ์ž๋™ ๊ฐ์ง€ + 29๊ฐœ+ VHK ๋ช…๋ น์–ด ๋ชฉ๋ก์„ `.vhk/context.md` ๋งˆํฌ๋‹ค์šด์œผ๋กœ ์ƒ์„ฑ. AI ์–ด์‹œ์Šคํ„ดํŠธ๊ฐ€ ํ”„๋กœ์ ํŠธ ๋งฅ๋ฝ์„ ์ฆ‰์‹œ ํŒŒ์•… |
153
+ | **memory** | `.vhk/memory.json` ๊ฒฐ์ •์‚ฌํ•ญ ๊ธฐ์–ต ๊ด€๋ฆฌ. `add <content> --tags X,Y` / `list` / `remove <๋ฒˆํ˜ธ>`. NL์€ list๋งŒ (add/remove๋Š” ์ธ์ž ํ•„์ˆ˜ โ†’ commander ์ „์šฉ) |
154
+ | **brief** | ํ”„๋กœ์ ํŠธ ์ •๋ณด + git ์ƒํƒœ(๋ธŒ๋žœ์น˜ยท๋งˆ์ง€๋ง‰ ์ปค๋ฐ‹ยท๋ฏธ์ปค๋ฐ‹ ๋ณ€๊ฒฝ) + ์ตœ๊ทผ ๊ฒฐ์ •์‚ฌํ•ญ + ๋ ˆํผ๋Ÿฐ์Šค๋ฅผ ํ•œ ํ™”๋ฉด์— + `.vhk/brief.md` ์ €์žฅ. `safeExecFile` ๊ธฐ๋ฐ˜ (Windows .cmd shim ์•ˆ์ „) |
155
+ | **์ž์—ฐ์–ด ํ™•์žฅ** | `"๋งฅ๋ฝ ๋งŒ๋“ค์–ด์ค˜"` โ†’ context ยท `"์ปจํ…์ŠคํŠธ ๋ณด์—ฌ์ค˜"` โ†’ context-show ยท `"๊ธฐ์–ต ๋ชฉ๋ก"` โ†’ memory ยท `"ํ”„๋กœ์ ํŠธ ๋ธŒ๋ฆฌํ•‘ ๋งŒ๋“ค์–ด์ค˜"` / `"์ƒํƒœ ์š”์•ฝ"` โ†’ brief |
156
+
157
+ ```powershell
158
+ vhk context # .vhk/context.md (ํŠธ๋ฆฌ + ์Šคํƒ + ๋ช…๋ น ๋ชฉ๋ก)
159
+ vhk memory add "API๋Š” tRPC ์‚ฌ์šฉ" --tags decision,arch
160
+ vhk memory list
161
+ vhk brief # ์ฝ˜์†” ์ถœ๋ ฅ + .vhk/brief.md
162
+ ```
163
+
164
+ ### Cursor ๊ถŒ์žฅ ์‹œํ€€์Šค (v1.0 GA)
165
+
166
+ ```text
167
+ vhk init # ํ”„๋กœ์ ํŠธ ์…‹์—…
168
+ vhk design + theme # ๋””์ž์ธ ์‹œ์Šคํ…œ
169
+ vhk context # AI ๋งฅ๋ฝ ํŒŒ์ผ ์ƒ์„ฑ
170
+ ... ๊ฐœ๋ฐœ ...
171
+ vhk memory add "<๊ฒฐ์ •>" # ๊ฒฐ์ • ๋ˆ„์ 
172
+ vhk brief # ์„ธ์…˜ ์ข…๋ฃŒ ์‹œ ์ƒํƒœ ๋ณด๊ณ ์„œ
173
+ ๋‹ค์Œ ์„ธ์…˜ ์‹œ์ž‘: "์ปจํ…์ŠคํŠธ ๋ณด์—ฌ์ค˜" โ†’ ์–ด์ œ ๋งฅ๋ฝ ๋ณต์›
174
+ ```
175
+
176
+ ### v1.0.0 GA ์ •์ฑ…
177
+
178
+ - **๊ณต๊ฐœ API ์•ˆ์ •์„ฑ**: ๋ช…๋ น์–ด ์ด๋ฆ„, CLI ์ธ์ž, `.vhk/` ํŒŒ์ผ ํฌ๋งท์€ v2.0๊นŒ์ง€ breaking change ์—†์Œ
179
+ - **deprecation ์ ˆ์ฐจ**: ๋ช…๋ น์–ด/์˜ต์…˜ ์ œ๊ฑฐ ์ „ 1๊ฐœ ๋งˆ์ด๋„ˆ ๋ฒ„์ „(1.x.0)์—์„œ deprecation ๊ฒฝ๊ณ 
180
+ - **i18n ํ‚ค**: `ko.ts`์˜ `t()` ํ‚ค ์ด๋ฆ„์€ ์•ˆ์ •. ์‹ ๊ทœ ํ‚ค ๋ˆ„์ , ๊ธฐ์กด ํ‚ค ๋ฏธ์ œ๊ฑฐ
181
+ - **MCP ์„œ๋ฒ„ ๋„๊ตฌ**: 8๊ฐœ ๋„๊ตฌ(save/undo/status/diff/ship/doctor/check/recap) ์ธํ„ฐํŽ˜์ด์Šค ์•ˆ์ •
182
+
183
+ > **`vhk memory` vs Claude Code `auto memory`** โ€” `vhk memory`๋Š” **ํ”„๋กœ์ ํŠธ ๋‹จ์œ„** ๊ฒฐ์ •์‚ฌํ•ญ(`.vhk/memory.json`, ํŒ€ ๊ณต์œ ). Claude Code์˜ `auto memory`๋Š” **์‚ฌ์šฉ์ž ๋‹จ์œ„** (`~/.claude/projects/.../memory/`, ๊ฐœ์ธ ์ปจํ…์ŠคํŠธ). ๋‘˜์€ ๋ณ„๊ฐœ.
184
+
140
185
  ## v0.9.0 ํ•˜์ด๋ผ์ดํŠธ
141
186
 
142
187
  | ๊ธฐ๋Šฅ | ์„ค๋ช… |
@@ -250,6 +295,10 @@ vhk ref open 1 # 1๋ฒˆ ๋ ˆํผ๋Ÿฐ์Šค๋ฅผ ๋ธŒ๋ผ์šฐ์ €๋กœ ์—ด๊ธฐ
250
295
  | ๋ณด์•ˆ ๊ฐ์‚ฌ ํ•ด์ค˜ / ์ทจ์•ฝ์  ํ™•์ธ | `vhk audit` |
251
296
  | ํŒจํ‚ค์ง€ ๋งค๋‹ˆ์ € ์ „ํ™˜ | `vhk migrate` |
252
297
  | vhk ์—…๋ฐ์ดํŠธ ํ•ด์ค˜ | `vhk update` |
298
+ | ๋งฅ๋ฝ ๋งŒ๋“ค์–ด์ค˜ / ์ปจํ…์ŠคํŠธ ์ƒ์„ฑ | `vhk context` |
299
+ | ์ปจํ…์ŠคํŠธ ๋ณด์—ฌ์ค˜ / ๋งฅ๋ฝ ๋ณด์—ฌ์ค˜ | `vhk context-show` |
300
+ | ๊ธฐ์–ต ๋ชฉ๋ก / ๊ฒฐ์ •์‚ฌํ•ญ ํ™•์ธ | `vhk memory` (list) |
301
+ | ํ”„๋กœ์ ํŠธ ๋ธŒ๋ฆฌํ•‘ / ์ƒํƒœ ์š”์•ฝ | `vhk brief` |
253
302
 
254
303
  ## ํŠน์ง•
255
304
 
@@ -283,6 +283,10 @@ var ko = {
283
283
  addTitle: "\uB808\uD37C\uB7F0\uC2A4 \uCD94\uAC00",
284
284
  listTitle: "\uB808\uD37C\uB7F0\uC2A4 \uBAA9\uB85D"
285
285
  },
286
+ memory: {
287
+ addTitle: "\uAE30\uC5B5 \uCD94\uAC00",
288
+ listTitle: "\uAE30\uC5B5 \uBAA9\uB85D"
289
+ },
286
290
  mcp: {
287
291
  initTitle: "Cursor MCP \uC5F0\uB3D9 \uC124\uC815",
288
292
  serverStarted: "VHK MCP \uC11C\uBC84 \uC2DC\uC791\uB428"
@@ -323,6 +327,13 @@ var ko = {
323
327
  },
324
328
  update: {
325
329
  title: "VHK CLI \uC5C5\uB370\uC774\uD2B8"
330
+ },
331
+ context: {
332
+ title: "\uD504\uB85C\uC81D\uD2B8 \uCEE8\uD14D\uC2A4\uD2B8 \uC0DD\uC131",
333
+ showTitle: "\uCEE8\uD14D\uC2A4\uD2B8 \uD30C\uC77C"
334
+ },
335
+ brief: {
336
+ title: "\uD504\uB85C\uC81D\uD2B8 \uBE0C\uB9AC\uD551"
326
337
  }
327
338
  };
328
339
  function lookup(path) {
@@ -444,7 +455,7 @@ async function envCheck() {
444
455
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
445
456
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
446
457
  import { z } from "zod";
447
- import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, appendFileSync as appendFileSync2 } from "fs";
458
+ import { existsSync as existsSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2, appendFileSync as appendFileSync2 } from "fs";
448
459
 
449
460
  // src/lib/exec.ts
450
461
  import { execFileSync } from "child_process";
@@ -490,6 +501,16 @@ function safeExecFileStream(cmd, args) {
490
501
  }
491
502
  }
492
503
 
504
+ // src/lib/read-json.ts
505
+ import { readFileSync as readFileSync2 } from "fs";
506
+ function stripBom(text) {
507
+ return text.charCodeAt(0) === 65279 ? text.slice(1) : text;
508
+ }
509
+ function readJsonFile(filePath) {
510
+ const raw = stripBom(readFileSync2(filePath, "utf-8"));
511
+ return JSON.parse(raw);
512
+ }
513
+
493
514
  // src/mcp/server.ts
494
515
  var SERVER_VERSION = "0.7.1";
495
516
  function isGitRepo() {
@@ -571,7 +592,7 @@ function createVhkMcpServer() {
571
592
  const lines = [];
572
593
  if (existsSync2("package.json")) {
573
594
  try {
574
- const pkg = JSON.parse(readFileSync2("package.json", "utf-8"));
595
+ const pkg = readJsonFile("package.json");
575
596
  lines.push(`\u{1F4E6} \uD504\uB85C\uC81D\uD2B8: ${pkg.name ?? "(\uC774\uB984 \uC5C6\uC74C)"} v${pkg.version ?? "?"}`);
576
597
  } catch {
577
598
  }
@@ -656,7 +677,7 @@ function createVhkMcpServer() {
656
677
  checks.push(test.ok ? "\u2705 \uD14C\uC2A4\uD2B8 \uD1B5\uACFC" : "\u274C \uD14C\uC2A4\uD2B8 \uC2E4\uD328");
657
678
  if (existsSync2("package.json")) {
658
679
  try {
659
- const pkg = JSON.parse(readFileSync2("package.json", "utf-8"));
680
+ const pkg = readJsonFile("package.json");
660
681
  checks.push(`\u{1F4E6} \uBC84\uC804: ${pkg.version}`);
661
682
  } catch {
662
683
  }
@@ -733,7 +754,7 @@ ${log.out}` }] };
733
754
  if (!existsSync2(".env")) {
734
755
  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." }] };
735
756
  }
736
- const keys = parseEnvKeys(readFileSync2(".env", "utf-8"));
757
+ const keys = parseEnvKeys(readFileSync3(".env", "utf-8"));
737
758
  if (keys.length === 0) {
738
759
  return { content: [{ type: "text", text: "\u{1F4ED} .env\uC5D0 \uD658\uACBD\uBCC0\uC218\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4." }] };
739
760
  }
@@ -741,7 +762,7 @@ ${log.out}` }] };
741
762
  writeFileSync2(".env.example", exampleContent, "utf-8");
742
763
  const gitignoreLines = [];
743
764
  if (existsSync2(".gitignore")) {
744
- const content = readFileSync2(".gitignore", "utf-8");
765
+ const content = readFileSync3(".gitignore", "utf-8");
745
766
  if (!content.split("\n").some((l) => l.trim() === ".env")) {
746
767
  appendFileSync2(".gitignore", "\n.env\n");
747
768
  gitignoreLines.push("\u{1F512} .gitignore\uC5D0 .env \uCD94\uAC00\uB428");
@@ -764,8 +785,8 @@ ${log.out}` }] };
764
785
  if (!existsSync2(".env.example")) {
765
786
  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." }] };
766
787
  }
767
- const requiredKeys = parseEnvKeys(readFileSync2(".env.example", "utf-8"));
768
- const currentKeys = existsSync2(".env") ? parseEnvKeys(readFileSync2(".env", "utf-8")) : [];
788
+ const requiredKeys = parseEnvKeys(readFileSync3(".env.example", "utf-8"));
789
+ const currentKeys = existsSync2(".env") ? parseEnvKeys(readFileSync3(".env", "utf-8")) : [];
769
790
  const missing = requiredKeys.filter((k) => !currentKeys.includes(k));
770
791
  const extra = currentKeys.filter((k) => !requiredKeys.includes(k));
771
792
  const lines = [`\u{1F4CB} \uD544\uC218 \uD658\uACBD\uBCC0\uC218: ${requiredKeys.length}\uAC1C`];
@@ -796,6 +817,7 @@ export {
796
817
  ko,
797
818
  t,
798
819
  printNextStep,
820
+ readJsonFile,
799
821
  safeExecFile,
800
822
  safeExecFileStream,
801
823
  env,
package/dist/index.js CHANGED
@@ -6,11 +6,12 @@ import {
6
6
  envCheck,
7
7
  ko,
8
8
  printNextStep,
9
+ readJsonFile,
9
10
  safeExecFile,
10
11
  safeExecFileStream,
11
12
  startMcpServer,
12
13
  t
13
- } from "./chunk-UPXCLOBF.js";
14
+ } from "./chunk-SD7O6HO7.js";
14
15
 
15
16
  // node_modules/.pnpm/ignore@7.0.5/node_modules/ignore/index.js
16
17
  var require_ignore = __commonJS({
@@ -310,7 +311,7 @@ var require_ignore = __commonJS({
310
311
  // path matching.
311
312
  // - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
312
313
  // @returns {TestResult} true if a file is ignored
313
- test(path16, checkUnignored, mode) {
314
+ test(path15, checkUnignored, mode) {
314
315
  let ignored = false;
315
316
  let unignored = false;
316
317
  let matchedRule;
@@ -319,7 +320,7 @@ var require_ignore = __commonJS({
319
320
  if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
320
321
  return;
321
322
  }
322
- const matched = rule[mode].test(path16);
323
+ const matched = rule[mode].test(path15);
323
324
  if (!matched) {
324
325
  return;
325
326
  }
@@ -340,17 +341,17 @@ var require_ignore = __commonJS({
340
341
  var throwError = (message, Ctor) => {
341
342
  throw new Ctor(message);
342
343
  };
343
- var checkPath = (path16, originalPath, doThrow) => {
344
- if (!isString(path16)) {
344
+ var checkPath = (path15, originalPath, doThrow) => {
345
+ if (!isString(path15)) {
345
346
  return doThrow(
346
347
  `path must be a string, but got \`${originalPath}\``,
347
348
  TypeError
348
349
  );
349
350
  }
350
- if (!path16) {
351
+ if (!path15) {
351
352
  return doThrow(`path must not be empty`, TypeError);
352
353
  }
353
- if (checkPath.isNotRelative(path16)) {
354
+ if (checkPath.isNotRelative(path15)) {
354
355
  const r = "`path.relative()`d";
355
356
  return doThrow(
356
357
  `path should be a ${r} string, but got "${originalPath}"`,
@@ -359,7 +360,7 @@ var require_ignore = __commonJS({
359
360
  }
360
361
  return true;
361
362
  };
362
- var isNotRelative = (path16) => REGEX_TEST_INVALID_PATH.test(path16);
363
+ var isNotRelative = (path15) => REGEX_TEST_INVALID_PATH.test(path15);
363
364
  checkPath.isNotRelative = isNotRelative;
364
365
  checkPath.convert = (p) => p;
365
366
  var Ignore = class {
@@ -389,19 +390,19 @@ var require_ignore = __commonJS({
389
390
  }
390
391
  // @returns {TestResult}
391
392
  _test(originalPath, cache, checkUnignored, slices) {
392
- const path16 = originalPath && checkPath.convert(originalPath);
393
+ const path15 = originalPath && checkPath.convert(originalPath);
393
394
  checkPath(
394
- path16,
395
+ path15,
395
396
  originalPath,
396
397
  this._strictPathCheck ? throwError : RETURN_FALSE
397
398
  );
398
- return this._t(path16, cache, checkUnignored, slices);
399
+ return this._t(path15, cache, checkUnignored, slices);
399
400
  }
400
- checkIgnore(path16) {
401
- if (!REGEX_TEST_TRAILING_SLASH.test(path16)) {
402
- return this.test(path16);
401
+ checkIgnore(path15) {
402
+ if (!REGEX_TEST_TRAILING_SLASH.test(path15)) {
403
+ return this.test(path15);
403
404
  }
404
- const slices = path16.split(SLASH).filter(Boolean);
405
+ const slices = path15.split(SLASH).filter(Boolean);
405
406
  slices.pop();
406
407
  if (slices.length) {
407
408
  const parent = this._t(
@@ -414,18 +415,18 @@ var require_ignore = __commonJS({
414
415
  return parent;
415
416
  }
416
417
  }
417
- return this._rules.test(path16, false, MODE_CHECK_IGNORE);
418
+ return this._rules.test(path15, false, MODE_CHECK_IGNORE);
418
419
  }
419
- _t(path16, cache, checkUnignored, slices) {
420
- if (path16 in cache) {
421
- return cache[path16];
420
+ _t(path15, cache, checkUnignored, slices) {
421
+ if (path15 in cache) {
422
+ return cache[path15];
422
423
  }
423
424
  if (!slices) {
424
- slices = path16.split(SLASH).filter(Boolean);
425
+ slices = path15.split(SLASH).filter(Boolean);
425
426
  }
426
427
  slices.pop();
427
428
  if (!slices.length) {
428
- return cache[path16] = this._rules.test(path16, checkUnignored, MODE_IGNORE);
429
+ return cache[path15] = this._rules.test(path15, checkUnignored, MODE_IGNORE);
429
430
  }
430
431
  const parent = this._t(
431
432
  slices.join(SLASH) + SLASH,
@@ -433,29 +434,29 @@ var require_ignore = __commonJS({
433
434
  checkUnignored,
434
435
  slices
435
436
  );
436
- return cache[path16] = parent.ignored ? parent : this._rules.test(path16, checkUnignored, MODE_IGNORE);
437
+ return cache[path15] = parent.ignored ? parent : this._rules.test(path15, checkUnignored, MODE_IGNORE);
437
438
  }
438
- ignores(path16) {
439
- return this._test(path16, this._ignoreCache, false).ignored;
439
+ ignores(path15) {
440
+ return this._test(path15, this._ignoreCache, false).ignored;
440
441
  }
441
442
  createFilter() {
442
- return (path16) => !this.ignores(path16);
443
+ return (path15) => !this.ignores(path15);
443
444
  }
444
445
  filter(paths) {
445
446
  return makeArray(paths).filter(this.createFilter());
446
447
  }
447
448
  // @returns {TestResult}
448
- test(path16) {
449
- return this._test(path16, this._testCache, true);
449
+ test(path15) {
450
+ return this._test(path15, this._testCache, true);
450
451
  }
451
452
  };
452
453
  var factory = (options) => new Ignore(options);
453
- var isPathValid = (path16) => checkPath(path16 && checkPath.convert(path16), path16, RETURN_FALSE);
454
+ var isPathValid = (path15) => checkPath(path15 && checkPath.convert(path15), path15, RETURN_FALSE);
454
455
  var setupWindows = () => {
455
456
  const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
456
457
  checkPath.convert = makePosix;
457
458
  const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
458
- checkPath.isNotRelative = (path16) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path16) || isNotRelative(path16);
459
+ checkPath.isNotRelative = (path15) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path15) || isNotRelative(path15);
459
460
  };
460
461
  if (
461
462
  // Detect `process` so that it can run in browsers.
@@ -473,9 +474,6 @@ var require_ignore = __commonJS({
473
474
  // src/index.ts
474
475
  import { Command, Help } from "commander";
475
476
  import inquirer14 from "inquirer";
476
- import fs16 from "fs";
477
- import path15 from "path";
478
- import { fileURLToPath as fileURLToPath4 } from "url";
479
477
 
480
478
  // src/lib/nlp-router.ts
481
479
  function normalize(input) {
@@ -511,7 +509,7 @@ var RULES = [
511
509
  command: "init",
512
510
  explanation: "\uD504\uB85C\uC81D\uD2B8 \uC2DC\uC791 (vhk \uC2DC\uC791)",
513
511
  confidence: "high",
514
- test: (t2) => (/ํ”„๋กœ์ ํŠธ.*(๋งŒ๋“ค|์‹œ์ž‘)|ํด๋”.*๋งŒ๋“ค|๋งŒ๋“ค๊ณ \s*์‹ถ|ํ•˜๋„ค์Šค|์ดˆ๊ธฐํ™”/.test(t2) || /^์‹œ์ž‘$/.test(t2)) && !/๋””์ž์ธ|design|ํŒ”๋ ˆํŠธ|palette|ํ…Œ๋งˆ|theme|๋ ˆํผ๋Ÿฐ์Šค|reference|๋‹คํฌ\s*๋ชจ๋“œ|๋ผ์ดํŠธ\s*๋ชจ๋“œ|์ƒ‰์ƒ\s*๋ชจ๋“œ/.test(t2)
512
+ test: (t2) => (/ํ”„๋กœ์ ํŠธ.*(๋งŒ๋“ค|์‹œ์ž‘)|ํด๋”.*๋งŒ๋“ค|๋งŒ๋“ค๊ณ \s*์‹ถ|ํ•˜๋„ค์Šค|์ดˆ๊ธฐํ™”/.test(t2) || /^์‹œ์ž‘$/.test(t2)) && !/๋””์ž์ธ|design|ํŒ”๋ ˆํŠธ|palette|ํ…Œ๋งˆ|theme|๋ ˆํผ๋Ÿฐ์Šค|reference|๋‹คํฌ\s*๋ชจ๋“œ|๋ผ์ดํŠธ\s*๋ชจ๋“œ|์ƒ‰์ƒ\s*๋ชจ๋“œ|๋ธŒ๋ฆฌํ•‘|brief|์ปจํ…์ŠคํŠธ|context|๋งฅ๋ฝ|๊ธฐ์–ต|memory/.test(t2)
515
513
  },
516
514
  {
517
515
  command: "mcp-init",
@@ -567,6 +565,30 @@ var RULES = [
567
565
  confidence: "high",
568
566
  test: (t2) => /์—…๋ฐ์ดํŠธ|update|๋ฒ„์ „\s*์—…|์ตœ์‹ \s*๋ฒ„์ „|์…€ํ”„\s*์—…๋ฐ์ดํŠธ|vhk.*์ตœ์‹ |vhk.*์—…๋ฐ์ดํŠธ/.test(t2)
569
567
  },
568
+ {
569
+ command: "context-show",
570
+ explanation: "\uCEE8\uD14D\uC2A4\uD2B8 \uD30C\uC77C \uBCF4\uAE30 (vhk context-show)",
571
+ confidence: "high",
572
+ test: (t2) => /๋งฅ๋ฝ\s*(๋ณด|ํ™•์ธ|๋ณด์—ฌ)|์ปจํ…์ŠคํŠธ\s*(๋ณด|ํ™•์ธ|๋ณด์—ฌ)|context\s*show/.test(t2)
573
+ },
574
+ {
575
+ command: "context",
576
+ explanation: "\uD504\uB85C\uC81D\uD2B8 \uB9E5\uB77D \uC0DD\uC131 (vhk context)",
577
+ confidence: "high",
578
+ test: (t2) => /(^๋งฅ๋ฝ$|^์ปจํ…์ŠคํŠธ$|^context$|๋งฅ๋ฝ\s*(๋งŒ๋“ค|์ƒ์„ฑ|๊ฐฑ์‹ |์—…๋ฐ์ดํŠธ)|์ปจํ…์ŠคํŠธ\s*(๋งŒ๋“ค|์ƒ์„ฑ|๊ฐฑ์‹ |์—…๋ฐ์ดํŠธ)|ํ”„๋กœ์ ํŠธ\s*๋งฅ๋ฝ|ํ”„๋กœ์ ํŠธ\s*์ •๋ณด\s*์ƒ์„ฑ)/.test(t2) && !/๋ณด|ํ™•์ธ|๋ณด์—ฌ|show/.test(t2)
579
+ },
580
+ {
581
+ command: "memory",
582
+ explanation: "\uAE30\uC5B5 \uBAA9\uB85D \uC870\uD68C (vhk memory list)",
583
+ confidence: "high",
584
+ test: (t2) => /^๊ธฐ์–ต$|๊ธฐ์–ต\s*(๋ชฉ๋ก|๋ณด|ํ™•์ธ|๋ญ)|memory.*list|๊ฒฐ์ •์‚ฌํ•ญ\s*(๋ชฉ๋ก|ํ™•์ธ|๋ณด์—ฌ)/.test(t2) && !/(์ถ”๊ฐ€|add|์‚ญ์ œ|remove|์ €์žฅ|๊ธฐ๋กํ•ด)/.test(t2)
585
+ },
586
+ {
587
+ command: "brief",
588
+ explanation: "\uD504\uB85C\uC81D\uD2B8 \uC0C1\uD0DC \uC694\uC57D (vhk brief)",
589
+ confidence: "high",
590
+ test: (t2) => /๋ธŒ๋ฆฌํ•‘|brief|์ƒํƒœ\s*์š”์•ฝ|ํ”„๋กœ์ ํŠธ\s*์š”์•ฝ|์š”์•ฝ\s*(๋ณด๊ณ |๋ฆฌํฌํŠธ|๋ณด์—ฌ|๋งŒ๋“ค)|๋ณด๊ณ ์„œ\s*(๋งŒ๋“ค|์ƒ์„ฑ|๋ณด์—ฌ)/.test(t2)
591
+ },
570
592
  {
571
593
  command: "secure",
572
594
  explanation: "\uBCF4\uC548 \uC2A4\uCE94 (vhk \uBCF4\uC548)",
@@ -741,6 +763,14 @@ var KNOWN_COMMAND_TOKENS = /* @__PURE__ */ new Set([
741
763
  "\uC804\uD658",
742
764
  "update",
743
765
  "\uC5C5\uB370\uC774\uD2B8",
766
+ "context",
767
+ "\uB9E5\uB77D",
768
+ "context-show",
769
+ "\uB9E5\uB77D\uBCF4\uAE30",
770
+ "memory",
771
+ "\uAE30\uC5B5",
772
+ "brief",
773
+ "\uBE0C\uB9AC\uD551",
744
774
  "help"
745
775
  ]);
746
776
  function isOptionToken(token) {
@@ -764,7 +794,7 @@ function detectNaturalLanguageInput(argv) {
764
794
  }
765
795
 
766
796
  // src/lib/nlp-run.ts
767
- import chalk25 from "chalk";
797
+ import chalk28 from "chalk";
768
798
  import inquirer13 from "inquirer";
769
799
 
770
800
  // src/commands/gate.ts
@@ -1538,7 +1568,7 @@ var VHK_PACKAGE_SCRIPTS = {
1538
1568
  function enhancePackageScripts(projectDir) {
1539
1569
  const pkgPath = path3.join(projectDir, "package.json");
1540
1570
  if (!fs3.existsSync(pkgPath)) return false;
1541
- const pkg = JSON.parse(fs3.readFileSync(pkgPath, "utf-8"));
1571
+ const pkg = readJsonFile(pkgPath);
1542
1572
  pkg.scripts = { ...VHK_PACKAGE_SCRIPTS, ...pkg.scripts };
1543
1573
  fs3.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
1544
1574
  return true;
@@ -1615,19 +1645,27 @@ function buildSessionDiffFromSummary(diffSummary) {
1615
1645
  }
1616
1646
  async function getSessionDiff(since) {
1617
1647
  const sinceDate = since || (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1618
- const diffSummary = await git.diffSummary([`--since=${sinceDate}`]);
1619
- return buildSessionDiffFromSummary(diffSummary);
1648
+ try {
1649
+ const diffSummary = await git.diffSummary([`--since=${sinceDate}`]);
1650
+ return buildSessionDiffFromSummary(diffSummary);
1651
+ } catch {
1652
+ return { filesChanged: 0, insertions: 0, deletions: 0, files: [] };
1653
+ }
1620
1654
  }
1621
1655
  async function getRecentCommits(count = 10, since) {
1622
1656
  const options = { maxCount: count };
1623
1657
  if (since) options["--since"] = since;
1624
- const log2 = await git.log(options);
1625
- return log2.all.map((entry) => ({
1626
- hash: entry.hash,
1627
- message: entry.message,
1628
- date: entry.date,
1629
- author: entry.author_name
1630
- }));
1658
+ try {
1659
+ const log2 = await git.log(options);
1660
+ return log2.all.map((entry) => ({
1661
+ hash: entry.hash,
1662
+ message: entry.message,
1663
+ date: entry.date,
1664
+ author: entry.author_name
1665
+ }));
1666
+ } catch {
1667
+ return [];
1668
+ }
1631
1669
  }
1632
1670
  async function isGitRepo() {
1633
1671
  try {
@@ -1637,6 +1675,14 @@ async function isGitRepo() {
1637
1675
  return false;
1638
1676
  }
1639
1677
  }
1678
+ async function hasAnyCommits() {
1679
+ try {
1680
+ await git.revparse(["HEAD"]);
1681
+ return true;
1682
+ } catch {
1683
+ return false;
1684
+ }
1685
+ }
1640
1686
 
1641
1687
  // src/lib/adr.ts
1642
1688
  import fs4 from "fs";
@@ -1690,7 +1736,7 @@ function nextAdrNumber(adrDir) {
1690
1736
  function slugify(title) {
1691
1737
  return title.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9๊ฐ€-ํžฃ-]/g, "").slice(0, 40) || "decision";
1692
1738
  }
1693
- function createAdrFile(cwd, title, context, decision, consequences) {
1739
+ function createAdrFile(cwd, title, context2, decision, consequences) {
1694
1740
  const adrDir = path5.join(cwd, "docs", "adr");
1695
1741
  if (!fs4.existsSync(adrDir)) fs4.mkdirSync(adrDir, { recursive: true });
1696
1742
  const num = nextAdrNumber(adrDir);
@@ -1708,7 +1754,7 @@ function createAdrFile(cwd, title, context, decision, consequences) {
1708
1754
  `# ADR-${String(num).padStart(3, "0")}: ${title}`,
1709
1755
  "",
1710
1756
  "## \uB9E5\uB77D (Context)",
1711
- context,
1757
+ context2,
1712
1758
  "",
1713
1759
  "## \uACB0\uC815 (Decision)",
1714
1760
  decision,
@@ -1735,6 +1781,11 @@ ${ko.recap.title}
1735
1781
  console.log(chalk5.red(ko.recap.noRepo));
1736
1782
  return;
1737
1783
  }
1784
+ if (!await hasAnyCommits()) {
1785
+ console.log(chalk5.yellow("\u26A0\uFE0F \uC544\uC9C1 \uCEE4\uBC0B\uC774 \uC5C6\uC5B4\uC694."));
1786
+ console.log(chalk5.gray(" \uD30C\uC77C\uC744 \uCD94\uAC00\uD558\uACE0 `vhk save` \uB610\uB294 `git commit`\uC73C\uB85C \uCCAB \uCEE4\uBC0B\uC744 \uB9CC\uB4E4\uC5B4 \uBCF4\uC138\uC694."));
1787
+ return;
1788
+ }
1738
1789
  printSecurityWarnings();
1739
1790
  console.log(chalk5.dim(`${ko.recap.analyzing}
1740
1791
  `));
@@ -2596,7 +2647,7 @@ function getVhkVersion() {
2596
2647
  for (const pkgPath of candidates) {
2597
2648
  try {
2598
2649
  if (fs12.existsSync(pkgPath)) {
2599
- const pkg = JSON.parse(fs12.readFileSync(pkgPath, "utf-8"));
2650
+ const pkg = readJsonFile(pkgPath);
2600
2651
  return pkg.version;
2601
2652
  }
2602
2653
  } catch {
@@ -3136,21 +3187,9 @@ ${t("undo.recentHeader")}`));
3136
3187
 
3137
3188
  // src/commands/status.ts
3138
3189
  import { execFileSync as execFileSync4 } from "child_process";
3139
- import fs15 from "fs";
3190
+ import fs14 from "fs";
3140
3191
  import path14 from "path";
3141
3192
  import chalk13 from "chalk";
3142
-
3143
- // src/lib/read-json.ts
3144
- import fs14 from "fs";
3145
- function stripBom(text) {
3146
- return text.charCodeAt(0) === 65279 ? text.slice(1) : text;
3147
- }
3148
- function readJsonFile(filePath) {
3149
- const raw = stripBom(fs14.readFileSync(filePath, "utf-8"));
3150
- return JSON.parse(raw);
3151
- }
3152
-
3153
- // src/commands/status.ts
3154
3193
  function countFileChanges(porcelain) {
3155
3194
  const lines = porcelain.split("\n").filter(Boolean);
3156
3195
  let staged = 0;
@@ -3189,7 +3228,7 @@ function parseRecentCommitLines(logOutput) {
3189
3228
  }
3190
3229
  function readProjectPackage(cwd = process.cwd()) {
3191
3230
  const pkgPath = path14.join(cwd, "package.json");
3192
- if (!fs15.existsSync(pkgPath)) return null;
3231
+ if (!fs14.existsSync(pkgPath)) return null;
3193
3232
  try {
3194
3233
  const pkg = readJsonFile(pkgPath);
3195
3234
  if (!pkg.name && !pkg.version) return null;
@@ -3358,27 +3397,47 @@ ${t("diff.summaryHeader")}`));
3358
3397
  }
3359
3398
 
3360
3399
  // src/commands/mcp-init.ts
3361
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
3362
- import { join } from "path";
3400
+ import { existsSync, mkdirSync, writeFileSync } from "fs";
3401
+ import { join, dirname } from "path";
3363
3402
  import { fileURLToPath as fileURLToPath2 } from "url";
3364
3403
  import chalk15 from "chalk";
3365
- function resolveVhkMcpPath() {
3404
+ function resolveMcpEntryPoint() {
3405
+ try {
3406
+ const here = fileURLToPath2(import.meta.url);
3407
+ const dir = dirname(here);
3408
+ for (const rel of [["mcp", "index.js"], ["..", "mcp", "index.js"]]) {
3409
+ const candidate = join(dir, ...rel);
3410
+ if (existsSync(candidate)) return candidate;
3411
+ }
3412
+ } catch {
3413
+ }
3414
+ try {
3415
+ const url = import.meta.resolve?.("@byh3071/vhk/dist/mcp/index.js");
3416
+ if (typeof url === "string") {
3417
+ const p = fileURLToPath2(url);
3418
+ if (existsSync(p)) return p;
3419
+ }
3420
+ } catch {
3421
+ }
3366
3422
  try {
3367
3423
  const pkgPath = join(process.cwd(), "package.json");
3368
3424
  if (existsSync(pkgPath)) {
3369
- const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
3425
+ const pkg = readJsonFile(pkgPath);
3370
3426
  if (pkg.name === "@byh3071/vhk") {
3371
- return join(process.cwd(), "dist", "mcp", "index.js");
3427
+ const local = join(process.cwd(), "dist", "mcp", "index.js");
3428
+ if (existsSync(local)) return local;
3372
3429
  }
3373
3430
  }
3374
3431
  } catch {
3375
3432
  }
3376
- try {
3377
- const url = import.meta.resolve?.("@byh3071/vhk/dist/mcp/index.js");
3378
- if (typeof url === "string") return fileURLToPath2(url);
3379
- } catch {
3433
+ return null;
3434
+ }
3435
+ function resolveVhkMcpEntry() {
3436
+ const entryPath = resolveMcpEntryPoint();
3437
+ if (entryPath) {
3438
+ return { command: "node", args: [entryPath] };
3380
3439
  }
3381
- return join(process.cwd(), "node_modules", "@byh3071", "vhk", "dist", "mcp", "index.js");
3440
+ return { command: "vhk-mcp", args: [] };
3382
3441
  }
3383
3442
  async function mcpInit() {
3384
3443
  console.log(chalk15.bold("\n\u{1F50C} " + t("mcp.initTitle")));
@@ -3388,14 +3447,11 @@ async function mcpInit() {
3388
3447
  mkdirSync(cursorDir, { recursive: true });
3389
3448
  }
3390
3449
  const configPath = join(cursorDir, "mcp.json");
3391
- const vhkEntry = {
3392
- command: "node",
3393
- args: [resolveVhkMcpPath()]
3394
- };
3450
+ const vhkEntry = resolveVhkMcpEntry();
3395
3451
  let config;
3396
3452
  if (existsSync(configPath)) {
3397
3453
  try {
3398
- const parsed = JSON.parse(readFileSync(configPath, "utf-8"));
3454
+ const parsed = readJsonFile(configPath);
3399
3455
  config = {
3400
3456
  mcpServers: { ...parsed.mcpServers ?? {}, vhk: vhkEntry }
3401
3457
  };
@@ -3518,7 +3574,7 @@ ${t("deploy.deploying")}
3518
3574
  }
3519
3575
 
3520
3576
  // src/commands/publish.ts
3521
- import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
3577
+ import { existsSync as existsSync3, writeFileSync as writeFileSync2 } from "fs";
3522
3578
  import chalk17 from "chalk";
3523
3579
  import inquirer8 from "inquirer";
3524
3580
  import ora2 from "ora";
@@ -3542,7 +3598,7 @@ async function publish() {
3542
3598
  }
3543
3599
  let pkg;
3544
3600
  try {
3545
- pkg = JSON.parse(readFileSync2("package.json", "utf-8"));
3601
+ pkg = readJsonFile("package.json");
3546
3602
  } catch {
3547
3603
  console.log(chalk17.red("\u274C package.json \uD30C\uC2F1 \uC2E4\uD328"));
3548
3604
  return;
@@ -3867,14 +3923,13 @@ async function theme() {
3867
3923
  }
3868
3924
 
3869
3925
  // src/commands/ref.ts
3870
- import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync5 } from "fs";
3926
+ import { existsSync as existsSync6, mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "fs";
3871
3927
  import chalk20 from "chalk";
3872
3928
  var REFS_PATH = ".vhk/refs.json";
3873
3929
  function loadRefs() {
3874
3930
  if (!existsSync6(REFS_PATH)) return [];
3875
3931
  try {
3876
- const raw = readFileSync3(REFS_PATH, "utf-8");
3877
- const parsed = JSON.parse(raw);
3932
+ const parsed = readJsonFile(REFS_PATH);
3878
3933
  return Array.isArray(parsed) ? parsed : [];
3879
3934
  } catch {
3880
3935
  return [];
@@ -3966,7 +4021,7 @@ async function refOpen(indexStr) {
3966
4021
  }
3967
4022
 
3968
4023
  // src/commands/harness.ts
3969
- import { existsSync as existsSync7, readFileSync as readFileSync4 } from "fs";
4024
+ import { existsSync as existsSync7 } from "fs";
3970
4025
  import chalk21 from "chalk";
3971
4026
  import ora3 from "ora";
3972
4027
  function detectPM() {
@@ -3981,7 +4036,7 @@ function detectChecks() {
3981
4036
  const checks = [];
3982
4037
  let pkg = {};
3983
4038
  try {
3984
- pkg = JSON.parse(readFileSync4("package.json", "utf-8"));
4039
+ pkg = readJsonFile("package.json");
3985
4040
  } catch {
3986
4041
  return checks;
3987
4042
  }
@@ -4251,18 +4306,18 @@ async function migrate(target) {
4251
4306
 
4252
4307
  // src/commands/update.ts
4253
4308
  import { execSync as execSync3 } from "child_process";
4254
- import { existsSync as existsSync10, readFileSync as readFileSync5 } from "fs";
4255
- import { dirname, join as join2 } from "path";
4309
+ import { existsSync as existsSync10 } from "fs";
4310
+ import { dirname as dirname2, join as join2 } from "path";
4256
4311
  import { fileURLToPath as fileURLToPath3 } from "url";
4257
4312
  import chalk24 from "chalk";
4258
4313
  import ora6 from "ora";
4259
4314
  var PACKAGE = "@byh3071/vhk";
4260
4315
  function getCurrentVersion() {
4261
- const dir = dirname(fileURLToPath3(import.meta.url));
4316
+ const dir = dirname2(fileURLToPath3(import.meta.url));
4262
4317
  for (const pkgPath of [join2(dir, "../package.json"), join2(dir, "../../package.json")]) {
4263
4318
  try {
4264
4319
  if (existsSync10(pkgPath)) {
4265
- const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
4320
+ const pkg = readJsonFile(pkgPath);
4266
4321
  if (pkg.version) return pkg.version;
4267
4322
  }
4268
4323
  } catch {
@@ -4326,6 +4381,369 @@ async function update() {
4326
4381
  }
4327
4382
  }
4328
4383
 
4384
+ // src/commands/context.ts
4385
+ import {
4386
+ existsSync as existsSync11,
4387
+ mkdirSync as mkdirSync5,
4388
+ readFileSync as readFileSync2,
4389
+ readdirSync,
4390
+ statSync,
4391
+ writeFileSync as writeFileSync6
4392
+ } from "fs";
4393
+ import { join as join3 } from "path";
4394
+ import chalk25 from "chalk";
4395
+ var CONTEXT_PATH = ".vhk/context.md";
4396
+ var IGNORE_DIRS2 = /* @__PURE__ */ new Set([
4397
+ "node_modules",
4398
+ ".git",
4399
+ "dist",
4400
+ ".next",
4401
+ ".nuxt",
4402
+ ".output",
4403
+ "coverage",
4404
+ ".cache",
4405
+ ".turbo",
4406
+ ".vhk"
4407
+ ]);
4408
+ function buildTree(dir, prefix = "", maxDepth = 3, depth = 0) {
4409
+ if (depth >= maxDepth) return [];
4410
+ const lines = [];
4411
+ try {
4412
+ const entries = readdirSync(dir);
4413
+ const filtered = entries.filter(
4414
+ (e) => (!e.startsWith(".") || e === ".env.example") && !IGNORE_DIRS2.has(e)
4415
+ );
4416
+ filtered.forEach((entry, index) => {
4417
+ const isLast = index === filtered.length - 1;
4418
+ const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
4419
+ const fullPath = join3(dir, entry);
4420
+ const stat = statSync(fullPath);
4421
+ const isDir = stat.isDirectory();
4422
+ lines.push(`${prefix}${connector}${entry}${isDir ? "/" : ""}`);
4423
+ if (isDir) {
4424
+ const nextPrefix = prefix + (isLast ? " " : "\u2502 ");
4425
+ lines.push(...buildTree(fullPath, nextPrefix, maxDepth, depth + 1));
4426
+ }
4427
+ });
4428
+ } catch {
4429
+ }
4430
+ return lines;
4431
+ }
4432
+ function extractTechStack() {
4433
+ const stack = {};
4434
+ try {
4435
+ const pkg = readJsonFile("package.json");
4436
+ const all = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
4437
+ if (all.next) stack["\uD504\uB808\uC784\uC6CC\uD06C"] = `Next.js ${all.next}`;
4438
+ else if (all.nuxt) stack["\uD504\uB808\uC784\uC6CC\uD06C"] = `Nuxt ${all.nuxt}`;
4439
+ else if (all.react) stack["\uD504\uB808\uC784\uC6CC\uD06C"] = `React ${all.react}`;
4440
+ else if (all.vue) stack["\uD504\uB808\uC784\uC6CC\uD06C"] = `Vue ${all.vue}`;
4441
+ else if (all.svelte) stack["\uD504\uB808\uC784\uC6CC\uD06C"] = `Svelte ${all.svelte}`;
4442
+ if (all.typescript) stack["\uC5B8\uC5B4"] = `TypeScript ${all.typescript}`;
4443
+ if (all.tailwindcss) stack["\uC2A4\uD0C0\uC77C"] = `Tailwind CSS ${all.tailwindcss}`;
4444
+ if (all.tsup) stack["\uBE4C\uB4DC"] = "tsup";
4445
+ else if (all.vite) stack["\uBE4C\uB4DC"] = `Vite ${all.vite}`;
4446
+ else if (all.webpack) stack["\uBE4C\uB4DC"] = "webpack";
4447
+ if (all.vitest) stack["\uD14C\uC2A4\uD2B8"] = "vitest";
4448
+ else if (all.jest) stack["\uD14C\uC2A4\uD2B8"] = "jest";
4449
+ if (all.commander) stack["CLI"] = "commander";
4450
+ if (all.inquirer) stack["\uC778\uD130\uB799\uD2F0\uBE0C"] = "inquirer";
4451
+ if (existsSync11("pnpm-lock.yaml")) stack["\uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800"] = "pnpm";
4452
+ else if (existsSync11("yarn.lock")) stack["\uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800"] = "yarn";
4453
+ else stack["\uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800"] = "npm";
4454
+ if (pkg.name) stack["\uD328\uD0A4\uC9C0 \uC774\uB984"] = pkg.name;
4455
+ if (pkg.version) stack["\uBC84\uC804"] = pkg.version;
4456
+ } catch {
4457
+ }
4458
+ return stack;
4459
+ }
4460
+ function getVhkCommands() {
4461
+ return [
4462
+ "gate \u2014 \uC544\uC774\uB514\uC5B4 \uAC80\uC99D",
4463
+ "init \u2014 \uD504\uB85C\uC81D\uD2B8 \uCD08\uAE30\uD654",
4464
+ "recap \u2014 \uC138\uC158 \uC694\uC57D \uC800\uC7A5",
4465
+ "sync \u2014 \uADDC\uCE59 \uD30C\uC77C \uB3D9\uAE30\uD654",
4466
+ "check \u2014 \uADDC\uCE59 \uC810\uAC80",
4467
+ "secure \u2014 \uBCF4\uC548 \uC2A4\uCE94",
4468
+ "ship \u2014 \uBC30\uD3EC \uCCB4\uD06C + \uD68C\uACE0",
4469
+ "doctor \u2014 \uD658\uACBD \uC9C4\uB2E8",
4470
+ "save \u2014 git \uC800\uC7A5 (add+commit+push)",
4471
+ "undo \u2014 \uCD5C\uADFC \uCEE4\uBC0B \uB418\uB3CC\uB9AC\uAE30",
4472
+ "status \u2014 git \uC0C1\uD0DC \uD655\uC778",
4473
+ "diff \u2014 git \uBCC0\uACBD \uC0AC\uD56D \uC694\uC57D",
4474
+ "deploy \u2014 \uD504\uB85C\uB355\uC158 \uBC30\uD3EC",
4475
+ "env \u2014 \uD658\uACBD\uBCC0\uC218 \uAD00\uB9AC",
4476
+ "publish \u2014 npm \uBC30\uD3EC \uC790\uB3D9\uD654",
4477
+ "design \u2014 \uB514\uC790\uC778 \uD1A0\uD070 \uC0DD\uC131",
4478
+ "design-palette \u2014 \uCEEC\uB7EC \uD314\uB808\uD2B8 \uC120\uD0DD",
4479
+ "theme \u2014 \uB2E4\uD06C/\uB77C\uC774\uD2B8 \uBAA8\uB4DC",
4480
+ "ref add|list|open \u2014 \uB808\uD37C\uB7F0\uC2A4 URL \uAD00\uB9AC",
4481
+ "harness \u2014 \uD1B5\uD569 \uD488\uC9C8 \uC810\uAC80",
4482
+ "audit \u2014 \uBCF4\uC548 \uCDE8\uC57D\uC810 \uAC10\uC0AC",
4483
+ "migrate \u2014 \uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800 \uC804\uD658",
4484
+ "update \u2014 VHK CLI \uC140\uD504 \uC5C5\uB370\uC774\uD2B8",
4485
+ "context \u2014 \uD504\uB85C\uC81D\uD2B8 \uB9E5\uB77D \uC0DD\uC131",
4486
+ "context-show \u2014 \uB9E5\uB77D \uD30C\uC77C \uBCF4\uAE30",
4487
+ "memory add|list|remove \u2014 \uACB0\uC815\uC0AC\uD56D \uAE30\uC5B5",
4488
+ "brief \u2014 \uD504\uB85C\uC81D\uD2B8 \uC694\uC57D \uBCF4\uACE0\uC11C",
4489
+ "mcp \u2014 MCP \uC11C\uBC84 \uC2DC\uC791",
4490
+ "mcp-init \u2014 Cursor MCP \uC124\uC815"
4491
+ ];
4492
+ }
4493
+ async function context() {
4494
+ console.log(chalk25.bold("\n\u{1F9E0} " + t("context.title")));
4495
+ console.log(chalk25.gray("\u2500".repeat(40)));
4496
+ const stack = extractTechStack();
4497
+ const tree = buildTree(".").join("\n");
4498
+ const commands = getVhkCommands();
4499
+ const lines = [];
4500
+ lines.push("# \uD504\uB85C\uC81D\uD2B8 \uCEE8\uD14D\uC2A4\uD2B8");
4501
+ lines.push("");
4502
+ lines.push("> \uC774 \uD30C\uC77C\uC740 `vhk context`\uB85C \uC790\uB3D9 \uC0DD\uC131\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
4503
+ lines.push("> AI \uC5B4\uC2DC\uC2A4\uD134\uD2B8\uC5D0\uAC8C \uD504\uB85C\uC81D\uD2B8 \uB9E5\uB77D\uC744 \uC81C\uACF5\uD569\uB2C8\uB2E4.");
4504
+ lines.push("");
4505
+ lines.push("## \uAE30\uC220 \uC2A4\uD0DD");
4506
+ lines.push("");
4507
+ for (const [key, value] of Object.entries(stack)) {
4508
+ lines.push(`- **${key}**: ${value}`);
4509
+ }
4510
+ lines.push("");
4511
+ lines.push("## \uB514\uB809\uD1A0\uB9AC \uAD6C\uC870");
4512
+ lines.push("");
4513
+ lines.push("```");
4514
+ lines.push(tree);
4515
+ lines.push("```");
4516
+ lines.push("");
4517
+ lines.push("## VHK CLI \uBA85\uB839\uC5B4");
4518
+ lines.push("");
4519
+ for (const cmd of commands) {
4520
+ lines.push(`- \`vhk ${cmd}\``);
4521
+ }
4522
+ lines.push("");
4523
+ if (existsSync11(".vhk/memory.json")) {
4524
+ try {
4525
+ const memories = readJsonFile(
4526
+ ".vhk/memory.json"
4527
+ );
4528
+ if (Array.isArray(memories) && memories.length > 0) {
4529
+ lines.push("## \uC800\uC7A5\uB41C \uACB0\uC815\uC0AC\uD56D");
4530
+ lines.push("");
4531
+ for (const m of memories) {
4532
+ const date = new Date(m.addedAt).toLocaleDateString("ko-KR");
4533
+ lines.push(`- ${m.content} _(${date})_`);
4534
+ }
4535
+ lines.push("");
4536
+ }
4537
+ } catch {
4538
+ }
4539
+ }
4540
+ lines.push("---");
4541
+ lines.push("");
4542
+ lines.push(`_\uC0DD\uC131: ${(/* @__PURE__ */ new Date()).toLocaleString("ko-KR")}_`);
4543
+ lines.push("");
4544
+ mkdirSync5(".vhk", { recursive: true });
4545
+ writeFileSync6(CONTEXT_PATH, lines.join("\n"), "utf-8");
4546
+ console.log(chalk25.green(`
4547
+ \u2705 ${CONTEXT_PATH} \uC0DD\uC131 \uC644\uB8CC!`));
4548
+ console.log(chalk25.gray(` \uAE30\uC220 \uC2A4\uD0DD ${Object.keys(stack).length}\uAC1C \uAC10\uC9C0`));
4549
+ console.log(chalk25.gray(" AI \uC5B4\uC2DC\uC2A4\uD134\uD2B8\uC5D0\uAC8C \uC774 \uD30C\uC77C\uC744 \uCC38\uC870\uD558\uAC8C \uD558\uC138\uC694."));
4550
+ printNextStep({
4551
+ message: "\uCEE8\uD14D\uC2A4\uD2B8 \uD30C\uC77C \uC0DD\uC131 \uC644\uB8CC!",
4552
+ command: "vhk context-show",
4553
+ cursorHint: "\uCEE8\uD14D\uC2A4\uD2B8 \uBCF4\uC5EC\uC918"
4554
+ });
4555
+ }
4556
+ async function contextShow() {
4557
+ console.log(chalk25.bold("\n\u{1F4C4} " + t("context.showTitle")));
4558
+ console.log(chalk25.gray("\u2500".repeat(40)));
4559
+ if (!existsSync11(CONTEXT_PATH)) {
4560
+ console.log(chalk25.yellow("\n\u26A0\uFE0F \uCEE8\uD14D\uC2A4\uD2B8 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
4561
+ console.log(chalk25.gray(" vhk context\uB97C \uBA3C\uC800 \uC2E4\uD589\uD558\uC138\uC694."));
4562
+ return;
4563
+ }
4564
+ const content = readFileSync2(CONTEXT_PATH, "utf-8");
4565
+ console.log("\n" + content);
4566
+ }
4567
+
4568
+ // src/commands/memory.ts
4569
+ import { existsSync as existsSync12, mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
4570
+ import chalk26 from "chalk";
4571
+ var MEMORY_PATH = ".vhk/memory.json";
4572
+ function loadMemories() {
4573
+ if (!existsSync12(MEMORY_PATH)) return [];
4574
+ try {
4575
+ const parsed = readJsonFile(MEMORY_PATH);
4576
+ return Array.isArray(parsed) ? parsed : [];
4577
+ } catch {
4578
+ return [];
4579
+ }
4580
+ }
4581
+ function saveMemories(memories) {
4582
+ mkdirSync6(".vhk", { recursive: true });
4583
+ writeFileSync7(MEMORY_PATH, JSON.stringify(memories, null, 2) + "\n", "utf-8");
4584
+ }
4585
+ async function memoryAdd(content, tags) {
4586
+ console.log(chalk26.bold("\n\u{1F9E0} " + t("memory.addTitle")));
4587
+ console.log(chalk26.gray("\u2500".repeat(40)));
4588
+ if (!content) {
4589
+ console.log(chalk26.red("\u274C \uAE30\uC5B5\uD560 \uB0B4\uC6A9\uC744 \uC785\uB825\uD574\uC8FC\uC138\uC694."));
4590
+ console.log(chalk26.gray(' \uC608: vhk memory add "API\uB294 tRPC \uC0AC\uC6A9\uD558\uAE30\uB85C \uACB0\uC815"'));
4591
+ return;
4592
+ }
4593
+ const memories = loadMemories();
4594
+ memories.push({
4595
+ content,
4596
+ addedAt: (/* @__PURE__ */ new Date()).toISOString(),
4597
+ tags: tags && tags.length > 0 ? tags : []
4598
+ });
4599
+ saveMemories(memories);
4600
+ console.log(chalk26.green(`
4601
+ \u2705 \uAE30\uC5B5 \uC800\uC7A5\uB428 (#${memories.length})`));
4602
+ console.log(chalk26.cyan(` \u{1F4DD} ${content}`));
4603
+ printNextStep({
4604
+ message: "\uAE30\uC5B5 \uC800\uC7A5 \uC644\uB8CC!",
4605
+ command: "vhk memory list",
4606
+ cursorHint: "\uAE30\uC5B5 \uBAA9\uB85D \uBCF4\uC5EC\uC918"
4607
+ });
4608
+ }
4609
+ async function memoryList() {
4610
+ console.log(chalk26.bold("\n\u{1F9E0} " + t("memory.listTitle")));
4611
+ console.log(chalk26.gray("\u2500".repeat(40)));
4612
+ const memories = loadMemories();
4613
+ if (memories.length === 0) {
4614
+ console.log(chalk26.yellow("\n\u{1F4ED} \uC800\uC7A5\uB41C \uAE30\uC5B5\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
4615
+ console.log(chalk26.gray(' vhk memory add "\uB0B4\uC6A9"\uC73C\uB85C \uCD94\uAC00\uD558\uC138\uC694.'));
4616
+ return;
4617
+ }
4618
+ console.log(chalk26.cyan(`
4619
+ \uCD1D ${memories.length}\uAC1C\uC758 \uAE30\uC5B5:
4620
+ `));
4621
+ memories.forEach((m, index) => {
4622
+ const date = new Date(m.addedAt).toLocaleDateString("ko-KR");
4623
+ console.log(chalk26.white(` [${index + 1}] ${m.content}`));
4624
+ if (m.tags && m.tags.length > 0) {
4625
+ console.log(chalk26.blue(` \u{1F3F7}\uFE0F ${m.tags.join(", ")}`));
4626
+ }
4627
+ console.log(chalk26.gray(` \u{1F4C5} ${date}`));
4628
+ console.log("");
4629
+ });
4630
+ }
4631
+ async function memoryRemove(indexStr) {
4632
+ const memories = loadMemories();
4633
+ const idx = parseInt(indexStr, 10) - 1;
4634
+ if (Number.isNaN(idx) || idx < 0 || idx >= memories.length) {
4635
+ console.log(chalk26.red(`\u274C \uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uBC88\uD638\uC785\uB2C8\uB2E4. (1~${memories.length || 0})`));
4636
+ return;
4637
+ }
4638
+ const removed = memories.splice(idx, 1)[0];
4639
+ saveMemories(memories);
4640
+ console.log(chalk26.green("\n\u2705 \uAE30\uC5B5 \uC0AD\uC81C\uB428:"));
4641
+ console.log(chalk26.gray(` ${removed.content}`));
4642
+ }
4643
+
4644
+ // src/commands/brief.ts
4645
+ import { existsSync as existsSync13, mkdirSync as mkdirSync7, writeFileSync as writeFileSync8 } from "fs";
4646
+ import chalk27 from "chalk";
4647
+ var BRIEF_PATH = ".vhk/brief.md";
4648
+ function git2(args) {
4649
+ const result = safeExecFile("git", args);
4650
+ return result.ok ? result.out : "";
4651
+ }
4652
+ async function brief() {
4653
+ console.log(chalk27.bold("\n\u{1F4CB} " + t("brief.title")));
4654
+ console.log(chalk27.gray("\u2500".repeat(40)));
4655
+ const lines = [];
4656
+ lines.push("# \uD504\uB85C\uC81D\uD2B8 \uBE0C\uB9AC\uD551");
4657
+ lines.push("");
4658
+ lines.push(`> \uC0DD\uC131: ${(/* @__PURE__ */ new Date()).toLocaleString("ko-KR")}`);
4659
+ lines.push("");
4660
+ try {
4661
+ const pkg = readJsonFile("package.json");
4662
+ lines.push("## \uD504\uB85C\uC81D\uD2B8 \uC815\uBCF4");
4663
+ lines.push("");
4664
+ lines.push(`- **\uC774\uB984**: ${pkg.name ?? "\uBBF8\uC815"}`);
4665
+ lines.push(`- **\uBC84\uC804**: ${pkg.version ?? "\uBBF8\uC815"}`);
4666
+ lines.push(`- **\uC124\uBA85**: ${pkg.description ?? "\uC5C6\uC74C"}`);
4667
+ const deps = Object.keys(pkg.dependencies ?? {}).length;
4668
+ const devDeps = Object.keys(pkg.devDependencies ?? {}).length;
4669
+ lines.push(`- **\uC758\uC874\uC131**: ${deps}\uAC1C (dev: ${devDeps}\uAC1C)`);
4670
+ lines.push("");
4671
+ } catch {
4672
+ lines.push("## \uD504\uB85C\uC81D\uD2B8 \uC815\uBCF4");
4673
+ lines.push("");
4674
+ lines.push("\u26A0\uFE0F package.json\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
4675
+ lines.push("");
4676
+ }
4677
+ const branch = git2(["branch", "--show-current"]);
4678
+ const lastCommit = git2(["log", "-1", "--pretty=format:%h %s (%cr)"]);
4679
+ const uncommitted = git2(["status", "--porcelain"]);
4680
+ const totalCommits = git2(["rev-list", "--count", "HEAD"]);
4681
+ lines.push("## Git \uC0C1\uD0DC");
4682
+ lines.push("");
4683
+ lines.push(`- **\uD604\uC7AC \uBE0C\uB79C\uCE58**: ${branch || "\uC54C \uC218 \uC5C6\uC74C"}`);
4684
+ lines.push(`- **\uB9C8\uC9C0\uB9C9 \uCEE4\uBC0B**: ${lastCommit || "\uC5C6\uC74C"}`);
4685
+ lines.push(`- **\uCD1D \uCEE4\uBC0B \uC218**: ${totalCommits || "\uC54C \uC218 \uC5C6\uC74C"}`);
4686
+ lines.push(
4687
+ `- **\uBBF8\uCEE4\uBC0B \uBCC0\uACBD**: ${uncommitted ? `${uncommitted.split("\n").length}\uAC1C \uD30C\uC77C` : "\uC5C6\uC74C \u2705"}`
4688
+ );
4689
+ lines.push("");
4690
+ if (existsSync13(".vhk/memory.json")) {
4691
+ try {
4692
+ const memories = readJsonFile(".vhk/memory.json");
4693
+ if (Array.isArray(memories) && memories.length > 0) {
4694
+ lines.push(`## \uC800\uC7A5\uB41C \uACB0\uC815\uC0AC\uD56D (${memories.length}\uAC1C)`);
4695
+ lines.push("");
4696
+ for (const m of memories.slice(-5)) {
4697
+ lines.push(`- ${m.content}`);
4698
+ }
4699
+ if (memories.length > 5) {
4700
+ lines.push(`- ... \uC678 ${memories.length - 5}\uAC1C`);
4701
+ }
4702
+ lines.push("");
4703
+ }
4704
+ } catch {
4705
+ }
4706
+ }
4707
+ if (existsSync13(".vhk/refs.json")) {
4708
+ try {
4709
+ const refs = readJsonFile(".vhk/refs.json");
4710
+ if (Array.isArray(refs) && refs.length > 0) {
4711
+ lines.push(`## \uB808\uD37C\uB7F0\uC2A4 (${refs.length}\uAC1C)`);
4712
+ lines.push("");
4713
+ for (const r of refs.slice(-3)) {
4714
+ const label = r.memo && r.memo.length > 0 ? r.memo : r.url;
4715
+ lines.push(`- [${label}](${r.url})`);
4716
+ }
4717
+ lines.push("");
4718
+ }
4719
+ } catch {
4720
+ }
4721
+ }
4722
+ lines.push("## \uB2E4\uC74C \uB2E8\uACC4 \uC81C\uC548");
4723
+ lines.push("");
4724
+ const steps = [];
4725
+ if (uncommitted) steps.push("\uBBF8\uCEE4\uBC0B \uBCC0\uACBD \uC0AC\uD56D\uC744 \uCEE4\uBC0B\uD558\uC138\uC694: `vhk save`");
4726
+ steps.push("\uD488\uC9C8 \uC810\uAC80 \uC2E4\uD589: `vhk harness`");
4727
+ steps.push("\uBCF4\uC548 \uAC10\uC0AC: `vhk audit`");
4728
+ steps.push("\uCEE8\uD14D\uC2A4\uD2B8 \uAC31\uC2E0: `vhk context`");
4729
+ steps.forEach((s, i) => lines.push(`${i + 1}. ${s}`));
4730
+ lines.push("");
4731
+ lines.push("---");
4732
+ lines.push("");
4733
+ lines.push("_VHK CLI \uBE0C\uB9AC\uD551_");
4734
+ lines.push("");
4735
+ mkdirSync7(".vhk", { recursive: true });
4736
+ writeFileSync8(BRIEF_PATH, lines.join("\n"), "utf-8");
4737
+ console.log("\n" + lines.join("\n"));
4738
+ console.log(chalk27.green(`
4739
+ \u2705 ${BRIEF_PATH} \uC800\uC7A5 \uC644\uB8CC`));
4740
+ printNextStep({
4741
+ message: "\uBE0C\uB9AC\uD551 \uC0DD\uC131 \uC644\uB8CC!",
4742
+ command: "vhk context",
4743
+ cursorHint: "\uCEE8\uD14D\uC2A4\uD2B8 \uC5C5\uB370\uC774\uD2B8\uD574\uC918"
4744
+ });
4745
+ }
4746
+
4329
4747
  // src/lib/nlp-run.ts
4330
4748
  async function dispatchNlpRoute(route, input) {
4331
4749
  switch (route.command) {
@@ -4382,19 +4800,27 @@ async function dispatchNlpRoute(route, input) {
4382
4800
  return migrate();
4383
4801
  case "update":
4384
4802
  return update();
4803
+ case "context":
4804
+ return context();
4805
+ case "context-show":
4806
+ return contextShow();
4807
+ case "memory":
4808
+ return memoryList();
4809
+ case "brief":
4810
+ return brief();
4385
4811
  }
4386
4812
  }
4387
4813
  async function runNaturalLanguageRoute(input) {
4388
4814
  const route = routeNaturalLanguage(input);
4389
4815
  if (!route) {
4390
- console.log(chalk25.yellow(`
4816
+ console.log(chalk28.yellow(`
4391
4817
  \u2753 "${input}" \u2014 ${ko.nlp.notMatched}
4392
4818
  `));
4393
4819
  return;
4394
4820
  }
4395
4821
  console.log("");
4396
- console.log(chalk25.cyan(` \u{1F4AC} "${input}"`));
4397
- console.log(chalk25.cyan(` \u2192 ${route.explanation}`));
4822
+ console.log(chalk28.cyan(` \u{1F4AC} "${input}"`));
4823
+ console.log(chalk28.cyan(` \u2192 ${route.explanation}`));
4398
4824
  if (route.confidence === "low") {
4399
4825
  const { confirm } = await inquirer13.prompt([{
4400
4826
  type: "confirm",
@@ -4403,7 +4829,7 @@ async function runNaturalLanguageRoute(input) {
4403
4829
  default: true
4404
4830
  }]);
4405
4831
  if (!confirm) {
4406
- console.log(chalk25.dim(` ${ko.nlp.menuHint}`));
4832
+ console.log(chalk28.dim(` ${ko.nlp.menuHint}`));
4407
4833
  return;
4408
4834
  }
4409
4835
  }
@@ -4411,13 +4837,19 @@ async function runNaturalLanguageRoute(input) {
4411
4837
  await dispatchNlpRoute(route, input);
4412
4838
  }
4413
4839
 
4414
- // src/index.ts
4415
- function getVersion() {
4416
- const dir = path15.dirname(fileURLToPath4(import.meta.url));
4417
- for (const pkgPath of [path15.join(dir, "../package.json"), path15.join(dir, "../../package.json")]) {
4840
+ // src/lib/version.ts
4841
+ import { existsSync as existsSync14 } from "fs";
4842
+ import { dirname as dirname3, join as join4 } from "path";
4843
+ import { fileURLToPath as fileURLToPath4 } from "url";
4844
+ function getVhkVersion2() {
4845
+ const dir = dirname3(fileURLToPath4(import.meta.url));
4846
+ for (const pkgPath of [
4847
+ join4(dir, "../../package.json"),
4848
+ join4(dir, "../package.json")
4849
+ ]) {
4418
4850
  try {
4419
- if (fs16.existsSync(pkgPath)) {
4420
- const pkg = JSON.parse(fs16.readFileSync(pkgPath, "utf-8"));
4851
+ if (existsSync14(pkgPath)) {
4852
+ const pkg = readJsonFile(pkgPath);
4421
4853
  if (pkg.version) return pkg.version;
4422
4854
  }
4423
4855
  } catch {
@@ -4426,6 +4858,8 @@ function getVersion() {
4426
4858
  }
4427
4859
  return "0.0.0";
4428
4860
  }
4861
+
4862
+ // src/index.ts
4429
4863
  var program = new Command();
4430
4864
  var defaultHelp = new Help();
4431
4865
  var KO_ALIASES = {
@@ -4452,9 +4886,13 @@ var KO_ALIASES = {
4452
4886
  harness: "\uD558\uB124\uC2A4",
4453
4887
  audit: "\uAC10\uC0AC",
4454
4888
  migrate: "\uC804\uD658",
4455
- update: "\uC5C5\uB370\uC774\uD2B8"
4889
+ update: "\uC5C5\uB370\uC774\uD2B8",
4890
+ context: "\uB9E5\uB77D",
4891
+ "context-show": "\uB9E5\uB77D\uBCF4\uAE30",
4892
+ memory: "\uAE30\uC5B5",
4893
+ brief: "\uBE0C\uB9AC\uD551"
4456
4894
  };
4457
- 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(getVersion());
4895
+ 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());
4458
4896
  program.configureHelp({
4459
4897
  formatHelp(cmd, helper) {
4460
4898
  if (cmd.parent) {
@@ -4548,6 +4986,28 @@ program.command("migrate [target]").alias("\uC804\uD658").description("\uD328\uD
4548
4986
  program.command("update").alias("\uC5C5\uB370\uC774\uD2B8").description("VHK CLI \uCD5C\uC2E0 \uBC84\uC804 \uC5C5\uB370\uC774\uD2B8").action(async () => {
4549
4987
  await update();
4550
4988
  });
4989
+ program.command("context").alias("\uB9E5\uB77D").description("\uD504\uB85C\uC81D\uD2B8 \uB9E5\uB77D \uD30C\uC77C \uC0DD\uC131 (.vhk/context.md)").action(async () => {
4990
+ await context();
4991
+ });
4992
+ program.command("context-show").alias("\uB9E5\uB77D\uBCF4\uAE30").description("\uD604\uC7AC \uCEE8\uD14D\uC2A4\uD2B8 \uD30C\uC77C \uB0B4\uC6A9 \uCD9C\uB825").action(async () => {
4993
+ await contextShow();
4994
+ });
4995
+ var memoryCmd = program.command("memory").alias("\uAE30\uC5B5").description("\uACB0\uC815\uC0AC\uD56D \uAE30\uC5B5 \uAD00\uB9AC (add / list / remove)").action(async () => {
4996
+ await memoryList();
4997
+ });
4998
+ memoryCmd.command("add <content>").option("--tags <tags>", "\uD0DC\uADF8 (\uC27C\uD45C \uAD6C\uBD84)").description("\uACB0\uC815\uC0AC\uD56D \uAE30\uC5B5 \uC800\uC7A5").action(async (content, opts) => {
4999
+ const tags = opts.tags ? opts.tags.split(",").map((s) => s.trim()) : void 0;
5000
+ await memoryAdd(content, tags);
5001
+ });
5002
+ memoryCmd.command("list").alias("\uBAA9\uB85D").description("\uC800\uC7A5\uB41C \uAE30\uC5B5 \uBAA9\uB85D").action(async () => {
5003
+ await memoryList();
5004
+ });
5005
+ memoryCmd.command("remove <index>").alias("\uC0AD\uC81C").description("\uAE30\uC5B5 \uC0AD\uC81C (1\uBD80\uD130 \uC2DC\uC791\uD558\uB294 \uBC88\uD638)").action(async (index) => {
5006
+ await memoryRemove(index);
5007
+ });
5008
+ program.command("brief").alias("\uBE0C\uB9AC\uD551").description("\uD504\uB85C\uC81D\uD2B8 \uC0C1\uD0DC \uC694\uC57D \uBCF4\uACE0\uC11C \uC0DD\uC131 (.vhk/brief.md)").action(async () => {
5009
+ await brief();
5010
+ });
4551
5011
  program.on("command:*", async (operands) => {
4552
5012
  const unknown = operands[0] ?? "";
4553
5013
  const rest = operands.slice(1);
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-UPXCLOBF.js";
4
+ } from "../chunk-SD7O6HO7.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": "0.9.1",
3
+ "version": "1.0.1",
4
4
  "description": "Vibe Harness Kit โ€” ๋ฐ”์ด๋ธŒ์ฝ”๋”ฉ ํ’€์‚ฌ์ดํด CLI",
5
5
  "bin": {
6
6
  "vhk": "dist/index.js",