@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 +52 -3
- package/dist/{chunk-UPXCLOBF.js โ chunk-SD7O6HO7.js} +29 -7
- package/dist/index.js +559 -99
- package/dist/mcp/index.js +1 -1
- package/package.json +1 -1
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,
|
|
4
|
+
tags: [vhk, cli, readme, v1.0.0, ga]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# ๐ง VHK โ Vibe Harness Kit
|
|
8
8
|
|
|
9
|
-
>
|
|
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
|
|
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 =
|
|
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 =
|
|
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(
|
|
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 =
|
|
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(
|
|
768
|
-
const currentKeys = existsSync2(".env") ? parseEnvKeys(
|
|
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-
|
|
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(
|
|
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(
|
|
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 = (
|
|
344
|
-
if (!isString(
|
|
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 (!
|
|
351
|
+
if (!path15) {
|
|
351
352
|
return doThrow(`path must not be empty`, TypeError);
|
|
352
353
|
}
|
|
353
|
-
if (checkPath.isNotRelative(
|
|
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 = (
|
|
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
|
|
393
|
+
const path15 = originalPath && checkPath.convert(originalPath);
|
|
393
394
|
checkPath(
|
|
394
|
-
|
|
395
|
+
path15,
|
|
395
396
|
originalPath,
|
|
396
397
|
this._strictPathCheck ? throwError : RETURN_FALSE
|
|
397
398
|
);
|
|
398
|
-
return this._t(
|
|
399
|
+
return this._t(path15, cache, checkUnignored, slices);
|
|
399
400
|
}
|
|
400
|
-
checkIgnore(
|
|
401
|
-
if (!REGEX_TEST_TRAILING_SLASH.test(
|
|
402
|
-
return this.test(
|
|
401
|
+
checkIgnore(path15) {
|
|
402
|
+
if (!REGEX_TEST_TRAILING_SLASH.test(path15)) {
|
|
403
|
+
return this.test(path15);
|
|
403
404
|
}
|
|
404
|
-
const slices =
|
|
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(
|
|
418
|
+
return this._rules.test(path15, false, MODE_CHECK_IGNORE);
|
|
418
419
|
}
|
|
419
|
-
_t(
|
|
420
|
-
if (
|
|
421
|
-
return cache[
|
|
420
|
+
_t(path15, cache, checkUnignored, slices) {
|
|
421
|
+
if (path15 in cache) {
|
|
422
|
+
return cache[path15];
|
|
422
423
|
}
|
|
423
424
|
if (!slices) {
|
|
424
|
-
slices =
|
|
425
|
+
slices = path15.split(SLASH).filter(Boolean);
|
|
425
426
|
}
|
|
426
427
|
slices.pop();
|
|
427
428
|
if (!slices.length) {
|
|
428
|
-
return cache[
|
|
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[
|
|
437
|
+
return cache[path15] = parent.ignored ? parent : this._rules.test(path15, checkUnignored, MODE_IGNORE);
|
|
437
438
|
}
|
|
438
|
-
ignores(
|
|
439
|
-
return this._test(
|
|
439
|
+
ignores(path15) {
|
|
440
|
+
return this._test(path15, this._ignoreCache, false).ignored;
|
|
440
441
|
}
|
|
441
442
|
createFilter() {
|
|
442
|
-
return (
|
|
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(
|
|
449
|
-
return this._test(
|
|
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 = (
|
|
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 = (
|
|
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
|
|
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
|
|
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 =
|
|
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
|
-
|
|
1619
|
-
|
|
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
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
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,
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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 (!
|
|
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,
|
|
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
|
|
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 =
|
|
3425
|
+
const pkg = readJsonFile(pkgPath);
|
|
3370
3426
|
if (pkg.name === "@byh3071/vhk") {
|
|
3371
|
-
|
|
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
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3433
|
+
return null;
|
|
3434
|
+
}
|
|
3435
|
+
function resolveVhkMcpEntry() {
|
|
3436
|
+
const entryPath = resolveMcpEntryPoint();
|
|
3437
|
+
if (entryPath) {
|
|
3438
|
+
return { command: "node", args: [entryPath] };
|
|
3380
3439
|
}
|
|
3381
|
-
return
|
|
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 =
|
|
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,
|
|
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 =
|
|
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,
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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(
|
|
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(
|
|
4397
|
-
console.log(
|
|
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(
|
|
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/
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
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 (
|
|
4420
|
-
const pkg =
|
|
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(
|
|
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