@byh3071/vhk 1.6.0 โ†’ 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  ---
2
2
  id: vhk-readme
3
3
  date: 2026-05-28
4
- tags: [vhk, cli, readme, v1.6.0, ga]
4
+ tags: [vhk, cli, readme, v1.6.1, ga]
5
5
  ---
6
6
 
7
7
  # ๐Ÿ”ง VHK โ€” Vibe Harness Kit
8
8
 
9
- > ๐ŸŽ‰ **v1.6.0** โ€” **๊ทœ์น™์€ ํ•œ ๋ฒŒ๋กœ CursorยทClaudeยทWindsurfยทCopilotยทAntigravity์—, ๋งฅ๋ฝ์€ ํด๋ผ์šฐ๋“œ๋กœ.**
9
+ > ๐ŸŽ‰ **v1.6.1** โ€” **๊ทœ์น™์€ ํ•œ ๋ฒŒ๋กœ CursorยทClaudeยทWindsurfยทCopilotยทAntigravity์—, ๋งฅ๋ฝ์€ ํด๋ผ์šฐ๋“œ๋กœ.**
10
10
  > ๋„๊ตฌยท๊ธฐ๊ธฐ๋ฅผ ์˜ฎ๊ฒจ๋„ `vhk` ๋ช…๋ น์œผ๋กœ ๊ทธ๋Œ€๋กœ ๋ถˆ๋Ÿฌ์˜ต๋‹ˆ๋‹ค. (ํฌํ„ฐ๋นŒ๋ฆฌํ‹ฐ)
11
11
  >
12
12
  > AI ์ฝ”๋”ฉ ์—์ด์ „ํŠธ๋ฅผ ๋ถ€๋ฆฌ๋Š” ์‚ฌ๋žŒ์„ ์œ„ํ•œ **ํ•œ๊ตญ์–ด ํ’€์‚ฌ์ดํด CLI**.
@@ -29,6 +29,20 @@ AI ์ฝ”๋”ฉ ๋„๊ตฌ๋Š” ์ €๋งˆ๋‹ค ๊ทœ์น™ ํŒŒ์ผ์ด ๋‹ค๋ฅด๊ณ (`.cursorrules`ยท`CLAUDE
29
29
  >
30
30
  > โ„น๏ธ WindsurfยทCopilotยทAntigravity ์ถœ๋ ฅ ๊ฒฝ๋กœยทํฌ๋งท์€ ๊ฐ ๋„๊ตฌ์˜ **๊ณต์‹ ๋ฌธ์„œ ๊ธฐ์ค€**์œผ๋กœ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค(`.windsurfrules` ยท `.github/copilot-instructions.md` ยท `.agents/rules/`). Antigravity ๋Š” ํŒŒ์ผ๋‹น 12,000์ž ์ œํ•œ์ด ์žˆ์–ด ์ดˆ๊ณผ ์‹œ ์•ˆ์ „ํ•˜๊ฒŒ ์ ˆ์‚ญํ•˜๊ณ  ์ „์ฒด๋Š” `RULES.md` ์— ๋‚จ์Šต๋‹ˆ๋‹ค.
31
31
 
32
+ ### Cursor / Copilot / Antigravity ์—์„œ ์ด๋ ‡๊ฒŒ ๋งํ•˜์„ธ์š”
33
+
34
+ ๊ทœ์น™ ํŒŒ์ผ์„ ๋™๊ธฐํ™”ํ•˜๋ฉด, ์—์ด์ „ํŠธ ์ฑ„ํŒ…์ฐฝ์—์„œ ๋ช…๋ น์„ ์™ธ์šฐ์ง€ ์•Š๊ณ  ํ•œ๊ตญ์–ด๋กœ ๋งํ•ด๋„ ๋ฉ๋‹ˆ๋‹ค:
35
+
36
+ | ํ•˜๊ณ  ์‹ถ์€ ์ผ | ์ฑ„ํŒ…์ฐฝ์— ์ด๋ ‡๊ฒŒ | ์‹คํ–‰๋˜๋Š” ๋ช…๋ น |
37
+ |------|------|------|
38
+ | ๊ทœ์น™ ํ•œ ๋ฒŒ๋กœ ๋™๊ธฐํ™” | "๊ทœ์น™ ๋™๊ธฐํ™”ํ•ด์ค˜" | `vhk sync` |
39
+ | ์ง€๊ธˆ ์ƒํƒœ ๋ณด๊ธฐ | "์ƒํƒœ ์•Œ๋ ค์ค˜" | `vhk status` |
40
+ | ๋ญ ๋ฐ”๋€Œ์—ˆ๋Š”์ง€ | "๋ญ ๋ฐ”๋€Œ์—ˆ์–ด?" | `vhk diff` |
41
+ | ์ฒ˜์Œ์ด๋ผ ๋ง‰๋ง‰ํ•จ | "์ฒ˜์Œ ๋ญ ํ•ด?" / "๋„์›€๋ง" | `vhk start` |
42
+ | ์ €์žฅ(์ปค๋ฐ‹) | "์ €์žฅํ•ด์ค˜" | `vhk save` |
43
+
44
+ > MCP๋ฅผ ๋“ฑ๋กํ•˜๋ฉด(`vhk mcp-init`) ์œ„ ๋ฌธ์žฅ์ด ๊ณง๋ฐ”๋กœ vhk ๋„๊ตฌ ํ˜ธ์ถœ๋กœ ์ด์–ด์ง‘๋‹ˆ๋‹ค. RULES.md ํ•œ ๋ฒŒ์ด ๋‹ค์„ฏ ๋„๊ตฌ์— ๊ฐ™์€ ๊ทœ์น™์„ ๊น”์•„์ค๋‹ˆ๋‹ค.
45
+
32
46
  ## 3๋ถ„ ์•ˆ์— ์‹œ์ž‘ํ•˜๊ธฐ (Getting Started)
33
47
 
34
48
  ### 1. ์„ค์น˜
@@ -156,7 +170,7 @@ vhk ๊ธฐํš ๋๋‚ฌ๊ณ  ๋ฐ”๋กœ ์‹œ์ž‘
156
170
  | `vhk audit` | `๊ฐ์‚ฌ` | npm/pnpm/yarn ๋ณด์•ˆ ์ทจ์•ฝ์  ๊ฐ์‚ฌ (`--fix`๋กœ ์ž๋™ ์ˆ˜์ •, npm๋งŒ) |
157
171
  | `vhk migrate [target]` | `์ „ํ™˜` | ํŒจํ‚ค์ง€ ๋งค๋‹ˆ์ € ์ „ํ™˜ (`npm` / `yarn` / `pnpm`, lockfile + node_modules ์žฌ๊ตฌ์„ฑ) |
158
172
  | `vhk update` | `์—…๋ฐ์ดํŠธ` | VHK CLI ์ตœ์‹  ๋ฒ„์ „์œผ๋กœ ์…€ํ”„ ์—…๋ฐ์ดํŠธ |
159
- | `vhk context` | `๋งฅ๋ฝ` | ํ”„๋กœ์ ํŠธ ํŠธ๋ฆฌยท์ŠคํƒยทCLI ๋ช…๋ น ๋ชฉ๋ก์„ `.vhk/context.md`๋กœ ์ž๋™ ์ƒ์„ฑ (AI ์–ด์‹œ์Šคํ„ดํŠธ์šฉ) |
173
+ | `vhk context` | `๋งฅ๋ฝ` | ํ”„๋กœ์ ํŠธ ํŠธ๋ฆฌยท์ŠคํƒยทCLI ๋ช…๋ น ๋ชฉ๋ก์„ `.vhk/context.md`๋กœ ์ž๋™ ์ƒ์„ฑ (AI ์–ด์‹œ์Šคํ„ดํŠธ์šฉ). `--compact` ๋กœ ํ† ํฐ ์ ˆ๊ฐํ˜•(Active Goal + ์ตœ๊ทผ blockers/learnings/memories + ์ฐธ์กฐ ๋งํฌ) ์ถœ๋ ฅ |
160
174
  | `vhk context-show` | `๋งฅ๋ฝ๋ณด๊ธฐ` | ํ˜„์žฌ ์ปจํ…์ŠคํŠธ ํŒŒ์ผ ๋‚ด์šฉ ์ถœ๋ ฅ |
161
175
  | `vhk memory` | `๊ธฐ์–ต` | ๊ฒฐ์ •์‚ฌํ•ญ ๊ธฐ์–ต ๊ด€๋ฆฌ (`add` / `list` / `remove`, `.vhk/memory.json` ๊ธฐ๋ฐ˜, ํƒœ๊ทธ ์ง€์›) |
162
176
  | `vhk brief` | `๋ธŒ๋ฆฌํ•‘` | ํ”„๋กœ์ ํŠธ ์ •๋ณด + git ์ƒํƒœ + ๊ฒฐ์ •์‚ฌํ•ญ + ๋ ˆํผ๋Ÿฐ์Šค ํ†ตํ•ฉ ๋ณด๊ณ ์„œ `.vhk/brief.md` |
@@ -562,7 +562,7 @@ var ko = {
562
562
  notGitRepo: "Git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2C8\uC5D0\uC694. \uBA3C\uC800 git init\uC744 \uC2E4\uD589\uD558\uC138\uC694.",
563
563
  branch: "\uBE0C\uB79C\uCE58:",
564
564
  changes: "\uBCC0\uACBD:",
565
- recentCommits: "\uCD5C\uADFC \uCEE4\uBC0B (3):",
565
+ recentCommits: (n) => `\uCD5C\uADFC \uCEE4\uBC0B (${n}):`,
566
566
  noCommits: "\uCEE4\uBC0B \uC5C6\uC74C",
567
567
  remote: "\uC6D0\uACA9:",
568
568
  noUpstream: "upstream \uC5C6\uC74C",
@@ -573,8 +573,9 @@ var ko = {
573
573
  noPackage: "package.json \uC5C6\uC74C",
574
574
  detached: "(detached HEAD)",
575
575
  unknownBranch: "(\uC54C \uC218 \uC5C6\uC74C)",
576
- nextWithChangesMessage: "\uBCC0\uACBD\uC0AC\uD56D\uC774 \uC788\uC5B4\uC694. \uCEE4\uBC0B\xB7\uD478\uC2DC\uB97C \uC9C4\uD589\uD558\uC138\uC694.",
577
- nextWithChangesCursor: "\uC800\uC7A5\uD574\uC918",
576
+ nextWithChangesMessage: "\uBCC0\uACBD\uC0AC\uD56D\uC774 \uC788\uC5B4\uC694. \uBA3C\uC800 \uBB34\uC5C7\uC774 \uBC14\uB00C\uC5C8\uB294\uC9C0 \uD655\uC778\uD558\uC138\uC694.",
577
+ nextWithChangesCursor: "\uBB50 \uBC14\uB00C\uC5C8\uC5B4?",
578
+ nextWithChangesAlt: "\uD655\uC778\uD588\uC73C\uBA74 vhk save \uB85C \uC800\uC7A5\uD558\uC138\uC694",
578
579
  nextCleanMessage: "\uD074\uB9B0 \uC0C1\uD0DC! \uB2E4\uC74C \uBBF8\uC158\uC73C\uB85C \uB118\uC5B4\uAC00\uC138\uC694.",
579
580
  nextCleanCursor: "\uB2E4\uC74C \uBAA9\uD45C \uC54C\uB824\uC918"
580
581
  },
@@ -719,7 +720,10 @@ var ko = {
719
720
  commandsMdDone: "\u{1F4CB} COMMANDS.md \uC0DD\uC131",
720
721
  scriptsDone: "\u{1F4E6} package.json scripts \uCD94\uAC00",
721
722
  gitignoreCreated: "\u{1F512} .gitignore \uC0DD\uC131 (.env\xB7node_modules\xB7dist \uC81C\uC678)",
722
- gitignoreUpdated: "\u{1F512} .gitignore \uBCF4\uAC15 (\uB204\uB77D \uD56D\uBAA9 \uCD94\uAC00)"
723
+ gitignoreUpdated: "\u{1F512} .gitignore \uBCF4\uAC15 (\uB204\uB77D \uD56D\uBAA9 \uCD94\uAC00)",
724
+ adoptPrompt: (n, list) => `\u{1F4E5} \uAE30\uC874 \uADDC\uCE59 \uD30C\uC77C ${n}\uAC1C \uBC1C\uACAC (${list}). RULES.md\uB85C \uAC00\uC838\uC62C\uAE4C\uC694?`,
725
+ adoptPreview: (n) => `\uAE30\uC874 \uADDC\uCE59 ${n}\uAC1C\uB97C RULES.md \uD45C\uC900 \uC139\uC158\uC73C\uB85C \uBCD1\uD569\uD588\uC5B4\uC694 (\uCD9C\uCC98 \uC8FC\uC11D \uD3EC\uD568).`,
726
+ adoptDone: "\u{1F4E5} RULES.md \u2014 \uAE30\uC874 \uADDC\uCE59 adopt \uC644\uB8CC"
723
727
  },
724
728
  recap: {
725
729
  title: "\u{1F4DD} \uC624\uB298 \uD55C \uC77C \uC815\uB9AC",
@@ -783,8 +787,29 @@ var ko = {
783
787
  windsurfDone: "\u2705 .windsurfrules \uB9DE\uCDA4 \uC644\uB8CC",
784
788
  copilotDone: "\u2705 .github/copilot-instructions.md \uB9DE\uCDA4 \uC644\uB8CC",
785
789
  antigravityDone: "\u2705 .agents/rules/vhk-rules.md \uB9DE\uCDA4 \uC644\uB8CC",
790
+ agentsDone: "\u2705 AGENTS.md \uB9DE\uCDA4 \uC644\uB8CC",
786
791
  antigravityTruncated: "Antigravity 12,000\uC790 \uC81C\uD55C\uC73C\uB85C \uC77C\uBD80 \uC808\uC0AD\uB428 \u2014 \uC804\uCCB4\uB294 RULES.md \uCC38\uC870",
787
- done: "\u{1F504} \uB9DE\uCD94\uAE30 \uC644\uB8CC!"
792
+ done: "\u{1F504} \uB9DE\uCD94\uAE30 \uC644\uB8CC!",
793
+ // ์•ˆ์ „ ๊ฐ€๋“œ (๋ฐฐ์น˜ 0) โ€” ๋ฎ์–ด์“ฐ๊ธฐ ์ „ ๋ฐฑ์—…ยท๋“œ๋ฆฌํ”„ํŠธ ํ™•์ธยท๋ฏธ๋ฆฌ๋ณด๊ธฐ
794
+ backupSaved: (n, id) => `\u{1F6DF} \uB36E\uC5B4\uC4F0\uAE30 \uC804 ${n}\uAC1C \uD30C\uC77C \uBC31\uC5C5\uD568 \u2192 .vhk/backups/${id} (\uBCF5\uC6D0: vhk restore)`,
795
+ firstSync: "\u{1F6DF} \uCCAB sync \u2014 \uAE30\uC874 \uD30C\uC77C\uC744 \uBC31\uC5C5\uD55C \uB4A4 \uC0DD\uC131\uD569\uB2C8\uB2E4.",
796
+ driftWarn: (p) => `\u26A0\uFE0F ${p} \uAC00 RULES.md \uC0DD\uC131\uBCF8\uACFC \uB2E4\uB985\uB2C8\uB2E4 (\uC9C1\uC811 \uC218\uC815\uD588\uC744 \uC218 \uC788\uC5B4\uC694).`,
797
+ driftConfirm: (n) => `\uC704 ${n}\uAC1C \uD30C\uC77C\uC758 \uAE30\uC874 \uB0B4\uC6A9\uC744 \uB36E\uC5B4\uC4F8\uAE4C\uC694? (\uBC31\uC5C5\uC740 \uC774\uBBF8 \uC800\uC7A5\uB428)`,
798
+ skipped: (p) => `\u23ED\uFE0F \uAC74\uB108\uB700: ${p} (\uB36E\uC5B4\uC4F0\uAE30 \uAC70\uBD80 \u2014 \uBC31\uC5C5\uB9CC \uBCF4\uAD00)`,
799
+ dryRunHeader: "\u{1F50E} \uBBF8\uB9AC\uBCF4\uAE30 (--dry-run) \u2014 \uC2E4\uC81C \uD30C\uC77C \uBCC0\uACBD \uC5C6\uC74C",
800
+ dryRunWouldWrite: (p, drift) => ` ${drift ? "\u270F\uFE0F \uBCC0\uACBD\uB428" : "\xB7 \uB3D9\uC77C"} : ${p}`,
801
+ nonTtyAuto: (n, id) => `\u{1F916} \uBE44\uB300\uD654\uD615(CI/\uC5D0\uC774\uC804\uD2B8) \u2014 ${n}\uAC1C \uBC31\uC5C5 \uD6C4 \uC9C4\uD589. \uBCF5\uC6D0: vhk restore ${id}`
802
+ },
803
+ restore: {
804
+ title: "\u{1F6DF} \uBC31\uC5C5 \uBCF5\uC6D0",
805
+ notGitNote: "\uBC31\uC5C5\uC740 .vhk/backups/ \uC758 \uB85C\uCEEC \uBCF5\uC0AC\uBCF8\uC5D0\uC11C \uBCF5\uC6D0\uB429\uB2C8\uB2E4 (git \uBB34\uAD00).",
806
+ noBackups: "\uBCF5\uC6D0\uD560 \uBC31\uC5C5\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. (vhk sync \uAC00 \uB36E\uC5B4\uC4F0\uAE30 \uC804 \uC790\uB3D9 \uC0DD\uC131)",
807
+ selectPrompt: "\uBCF5\uC6D0\uD560 \uBC31\uC5C5\uC744 \uC120\uD0DD\uD558\uC138\uC694:",
808
+ listHeader: "\u{1F4CB} \uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uBC31\uC5C5 (\uCD5C\uC2E0\uC21C):",
809
+ restored: (n, id) => `\u2705 ${n}\uAC1C \uD30C\uC77C \uBCF5\uC6D0 \uC644\uB8CC (\uBC31\uC5C5 ${id})`,
810
+ notFound: (id) => `\u274C \uBC31\uC5C5\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${id}`,
811
+ nonTtyHint: "\uBE44\uB300\uD654\uD615 \uBAA8\uB4DC \u2014 \uBCF5\uC6D0\uD560 \uBC31\uC5C5 id \uB97C \uC778\uC790\uB85C \uC9C0\uC815\uD558\uC138\uC694: vhk restore <id>",
812
+ cancelled: "\uBCF5\uC6D0 \uCDE8\uC18C\uB428"
788
813
  },
789
814
  cloud: {
790
815
  pushTitle: "\u2601\uFE0F .vhk \uD074\uB77C\uC6B0\uB4DC \uBC31\uC5C5 (gist \uC62C\uB9AC\uAE30)",
@@ -1064,11 +1089,30 @@ ${t("deploy.deploying")}
1064
1089
  }
1065
1090
 
1066
1091
  // src/commands/env.ts
1067
- import { existsSync as existsSync2, readFileSync, writeFileSync, appendFileSync } from "fs";
1092
+ import { existsSync as existsSync2, readFileSync, writeFileSync, appendFileSync, readdirSync } from "fs";
1093
+ import { join } from "path";
1068
1094
  import chalk3 from "chalk";
1069
1095
  function parseEnvKeys(content) {
1070
1096
  return content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).map((line) => line.split("=")[0].trim()).filter(Boolean);
1071
1097
  }
1098
+ function loadDefinedEnvKeys(dir = ".") {
1099
+ const keys = /* @__PURE__ */ new Set();
1100
+ let entries;
1101
+ try {
1102
+ entries = readdirSync(dir);
1103
+ } catch {
1104
+ return [];
1105
+ }
1106
+ for (const name of entries) {
1107
+ if (!name.startsWith(".env")) continue;
1108
+ if (name.endsWith(".example") || name.endsWith(".sample")) continue;
1109
+ try {
1110
+ for (const k of parseEnvKeys(readFileSync(join(dir, name), "utf-8"))) keys.add(k);
1111
+ } catch {
1112
+ }
1113
+ }
1114
+ return [...keys];
1115
+ }
1072
1116
  function ensureGitignore() {
1073
1117
  const gitignorePath = ".gitignore";
1074
1118
  if (existsSync2(gitignorePath)) {
@@ -1116,7 +1160,7 @@ async function envCheck() {
1116
1160
  return;
1117
1161
  }
1118
1162
  const requiredKeys = parseEnvKeys(readFileSync(".env.example", "utf-8"));
1119
- const currentKeys = existsSync2(".env") ? parseEnvKeys(readFileSync(".env", "utf-8")) : [];
1163
+ const currentKeys = loadDefinedEnvKeys();
1120
1164
  const missing = requiredKeys.filter((k) => !currentKeys.includes(k));
1121
1165
  const extra = currentKeys.filter((k) => !requiredKeys.includes(k));
1122
1166
  console.log(chalk3.cyan(`
@@ -1127,6 +1171,7 @@ async function envCheck() {
1127
1171
  console.log(chalk3.red(`
1128
1172
  \u274C \uB204\uB77D\uB41C \uD658\uACBD\uBCC0\uC218 (${missing.length}\uAC1C):`));
1129
1173
  missing.forEach((k) => console.log(chalk3.red(` \u2022 ${k}`)));
1174
+ process.exitCode = 1;
1130
1175
  }
1131
1176
  if (extra.length > 0) {
1132
1177
  console.log(chalk3.yellow(`
@@ -1403,13 +1448,13 @@ async function audit(autoFix = false) {
1403
1448
 
1404
1449
  // src/lib/version.ts
1405
1450
  import { existsSync as existsSync5 } from "fs";
1406
- import { dirname, join } from "path";
1451
+ import { dirname, join as join2 } from "path";
1407
1452
  import { fileURLToPath } from "url";
1408
1453
  function getVhkVersion() {
1409
1454
  const dir = dirname(fileURLToPath(import.meta.url));
1410
1455
  for (const pkgPath of [
1411
- join(dir, "../../package.json"),
1412
- join(dir, "../package.json")
1456
+ join2(dir, "../../package.json"),
1457
+ join2(dir, "../package.json")
1413
1458
  ]) {
1414
1459
  try {
1415
1460
  if (existsSync5(pkgPath)) {
@@ -1423,11 +1468,33 @@ function getVhkVersion() {
1423
1468
  return "0.0.0";
1424
1469
  }
1425
1470
 
1471
+ // src/mcp/cli-path.ts
1472
+ import { existsSync as existsSync6 } from "fs";
1473
+ import { fileURLToPath as fileURLToPath2 } from "url";
1474
+ import { dirname as dirname2, resolve } from "path";
1475
+ function pickCliInvocation(globalAvailable, localCli, localExists) {
1476
+ if (globalAvailable) return { bin: "vhk", prefixArgs: [], fallback: false };
1477
+ if (localExists) return { bin: process.execPath, prefixArgs: [localCli], fallback: true };
1478
+ return { bin: "vhk", prefixArgs: [], fallback: false };
1479
+ }
1480
+ function composeInvocation(cli, args) {
1481
+ return { bin: cli.bin, args: [...cli.prefixArgs, ...args] };
1482
+ }
1483
+ function localCliPath() {
1484
+ const here = dirname2(fileURLToPath2(import.meta.url));
1485
+ return resolve(here, "..", "index.js");
1486
+ }
1487
+ function resolveVhkCliInvocation() {
1488
+ const globalAvailable = safeExecFile("vhk", ["--version"]).ok;
1489
+ const local = localCliPath();
1490
+ return pickCliInvocation(globalAvailable, local, existsSync6(local));
1491
+ }
1492
+
1426
1493
  // src/mcp/server.ts
1427
1494
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
1428
1495
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
1429
1496
  import { z } from "zod";
1430
- import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync3, appendFileSync as appendFileSync2 } from "fs";
1497
+ import { existsSync as existsSync7, readFileSync as readFileSync4, writeFileSync as writeFileSync3, appendFileSync as appendFileSync2 } from "fs";
1431
1498
 
1432
1499
  // src/lib/scan-secrets.ts
1433
1500
  import fs3 from "fs";
@@ -1545,6 +1612,7 @@ function findExposedSensitiveFiles(rootDir, ig = loadGitignore(rootDir), maxDept
1545
1612
  }
1546
1613
  function isSensitiveName(name) {
1547
1614
  const lower = name.toLowerCase();
1615
+ if (lower.endsWith(".example") || lower.endsWith(".sample")) return false;
1548
1616
  if (lower === ".env" || lower.startsWith(".env.")) return true;
1549
1617
  if (lower.endsWith(".pem") || lower.endsWith(".key")) return true;
1550
1618
  if (lower === "credentials.json" || lower === "secrets.json") return true;
@@ -1721,8 +1789,15 @@ var ANSI_RE = /\x1B\[[0-9;?]*[ -/]*[@-~]/g;
1721
1789
  function stripAnsi(s) {
1722
1790
  return s.replace(ANSI_RE, "");
1723
1791
  }
1792
+ var cachedCli = null;
1793
+ function getVhkCli() {
1794
+ return cachedCli ??= resolveVhkCliInvocation();
1795
+ }
1724
1796
  function runVhkCli(args, headline) {
1725
- const result = safeExecFile("vhk", args, { env: { FORCE_COLOR: "0", NO_COLOR: "1" } });
1797
+ const { bin, args: fullArgs } = composeInvocation(getVhkCli(), args);
1798
+ const result = safeExecFile(bin, fullArgs, {
1799
+ env: { FORCE_COLOR: "0", NO_COLOR: "1" }
1800
+ });
1726
1801
  const body = stripAnsi(result.out || (result.ok ? "" : `(stdout \uC5C6\uC74C)
1727
1802
  ${result.err}`));
1728
1803
  const prefix = result.ok ? `\u2705 ${headline}` : `\u274C ${headline} \uC2E4\uD328`;
@@ -1795,32 +1870,56 @@ ${preview}${more}
1795
1870
  };
1796
1871
  }
1797
1872
  );
1798
- server.registerTool("undo", { description: "\uCD5C\uADFC \uCEE4\uBC0B \uB418\uB3CC\uB9AC\uAE30 (soft reset, \uBCC0\uACBD\uC0AC\uD56D\uC740 \uC720\uC9C0)" }, async () => {
1799
- if (!isGitRepo()) {
1800
- return { content: [{ type: "text", text: "\u274C git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2D9\uB2C8\uB2E4." }] };
1801
- }
1802
- const last = safeExecFile("git", ["log", "--oneline", "-1"]);
1803
- if (!last.ok || !last.out) {
1804
- return { content: [{ type: "text", text: "\u{1F4ED} \uB418\uB3CC\uB9B4 \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4." }] };
1805
- }
1806
- const reset = safeExecFile("git", ["reset", "--soft", "HEAD~1"]);
1807
- if (!reset.ok) {
1808
- return { content: [{ type: "text", text: `\u274C reset \uC2E4\uD328: ${reset.err}` }] };
1809
- }
1810
- return {
1811
- content: [
1812
- {
1813
- type: "text",
1814
- text: `\u2705 \uB418\uB3CC\uB9AC\uAE30 \uC644\uB8CC!
1873
+ server.registerTool(
1874
+ "undo",
1875
+ {
1876
+ description: "\uCD5C\uADFC \uCEE4\uBC0B \uB418\uB3CC\uB9AC\uAE30 (soft reset). \uAE30\uBCF8\uC740 \uBBF8\uB9AC\uBCF4\uAE30 \u2014 confirm:true \uC77C \uB54C\uB9CC \uC2E4\uC81C \uC2E4\uD589.",
1877
+ inputSchema: {
1878
+ confirm: z.boolean().optional().describe("true \uC77C \uB54C\uB9CC \uC2E4\uC81C git reset \uC2E4\uD589 (\uAE30\uBCF8 false = \uBBF8\uB9AC\uBCF4\uAE30\uB9CC)")
1879
+ }
1880
+ },
1881
+ async ({ confirm }) => {
1882
+ if (!isGitRepo()) {
1883
+ return { content: [{ type: "text", text: "\u274C git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2D9\uB2C8\uB2E4." }] };
1884
+ }
1885
+ const last = safeExecFile("git", ["log", "--oneline", "-1"]);
1886
+ if (!last.ok || !last.out) {
1887
+ return { content: [{ type: "text", text: "\u{1F4ED} \uB418\uB3CC\uB9B4 \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4." }] };
1888
+ }
1889
+ if (!confirm) {
1890
+ return {
1891
+ content: [
1892
+ {
1893
+ type: "text",
1894
+ text: `\u{1F50E} \uBBF8\uB9AC\uBCF4\uAE30 \u2014 \uB418\uB3CC\uB9B4 \uCEE4\uBC0B:
1895
+ ${last.out}
1896
+
1897
+ \uC2E4\uC81C\uB85C \uB418\uB3CC\uB9AC\uB824\uBA74 confirm: true \uB85C \uB2E4\uC2DC \uD638\uCD9C\uD558\uC138\uC694.
1898
+ (soft reset \u2014 \uBCC0\uACBD\uC0AC\uD56D\uC740 \uC2A4\uD14C\uC774\uC9D5 \uC601\uC5ED\uC5D0 \uBCF4\uC874\uB429\uB2C8\uB2E4.)
1899
+ \uB610\uB294 \uD130\uBBF8\uB110\uC5D0\uC11C \`vhk undo\` (\uB300\uD654\uD615 \uD655\uC778).`
1900
+ }
1901
+ ]
1902
+ };
1903
+ }
1904
+ const reset = safeExecFile("git", ["reset", "--soft", "HEAD~1"]);
1905
+ if (!reset.ok) {
1906
+ return { content: [{ type: "text", text: `\u274C reset \uC2E4\uD328: ${reset.err}` }] };
1907
+ }
1908
+ return {
1909
+ content: [
1910
+ {
1911
+ type: "text",
1912
+ text: `\u2705 \uB418\uB3CC\uB9AC\uAE30 \uC644\uB8CC!
1815
1913
  \uCDE8\uC18C\uB41C \uCEE4\uBC0B: ${last.out}
1816
1914
  \u{1F4A1} \uBCC0\uACBD\uC0AC\uD56D\uC740 \uC2A4\uD14C\uC774\uC9D5 \uC601\uC5ED\uC5D0 \uB0A8\uC544\uC788\uC2B5\uB2C8\uB2E4.`
1817
- }
1818
- ]
1819
- };
1820
- });
1915
+ }
1916
+ ]
1917
+ };
1918
+ }
1919
+ );
1821
1920
  server.registerTool("status", { description: "\uD504\uB85C\uC81D\uD2B8 \uC0C1\uD0DC \uB300\uC2DC\uBCF4\uB4DC (\uBE0C\uB79C\uCE58/\uBCC0\uACBD\uC0AC\uD56D/\uCD5C\uADFC \uCEE4\uBC0B)" }, async () => {
1822
1921
  const lines = [];
1823
- if (existsSync6("package.json")) {
1922
+ if (existsSync7("package.json")) {
1824
1923
  try {
1825
1924
  const pkg = readJsonFile("package.json");
1826
1925
  lines.push(`\u{1F4E6} \uD504\uB85C\uC81D\uD2B8: ${pkg.name ?? "(\uC774\uB984 \uC5C6\uC74C)"} v${pkg.version ?? "?"}`);
@@ -1905,7 +2004,7 @@ ${preview}${more}
1905
2004
  checks.push(build.ok ? "\u2705 \uBE4C\uB4DC \uC131\uACF5" : "\u274C \uBE4C\uB4DC \uC2E4\uD328");
1906
2005
  const test = safeExecFile("pnpm", ["test", "--run"]);
1907
2006
  checks.push(test.ok ? "\u2705 \uD14C\uC2A4\uD2B8 \uD1B5\uACFC" : "\u274C \uD14C\uC2A4\uD2B8 \uC2E4\uD328");
1908
- if (existsSync6("package.json")) {
2007
+ if (existsSync7("package.json")) {
1909
2008
  try {
1910
2009
  const pkg = readJsonFile("package.json");
1911
2010
  checks.push(`\u{1F4E6} \uBC84\uC804: ${pkg.version}`);
@@ -1943,11 +2042,11 @@ ${preview}${more}
1943
2042
  const recommended = ["CLAUDE.md", ".cursorrules", "docs/PRD.md", "docs/ARCHITECTURE.md"];
1944
2043
  const lines = ["\u{1F50D} \uD504\uB85C\uC81D\uD2B8 \uC810\uAC80", "", "\uD544\uC218:"];
1945
2044
  required.forEach((f) => {
1946
- lines.push(` ${existsSync6(f) ? "\u2705" : "\u274C"} ${f}`);
2045
+ lines.push(` ${existsSync7(f) ? "\u2705" : "\u274C"} ${f}`);
1947
2046
  });
1948
2047
  lines.push("", "\uAD8C\uC7A5 (VHK \uD558\uB124\uC2A4):");
1949
2048
  recommended.forEach((f) => {
1950
- lines.push(` ${existsSync6(f) ? "\u2705" : "\u26A0\uFE0F"} ${f}`);
2049
+ lines.push(` ${existsSync7(f) ? "\u2705" : "\u26A0\uFE0F"} ${f}`);
1951
2050
  });
1952
2051
  return { content: [{ type: "text", text: lines.join("\n") }] };
1953
2052
  });
@@ -1981,7 +2080,7 @@ ${log.out}` }] };
1981
2080
  description: ".env \u2192 .env.example \uB3D9\uAE30\uD654 + .gitignore\uC5D0 .env \uC790\uB3D9 \uCD94\uAC00"
1982
2081
  },
1983
2082
  async () => {
1984
- if (!existsSync6(".env")) {
2083
+ if (!existsSync7(".env")) {
1985
2084
  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." }] };
1986
2085
  }
1987
2086
  const keys = parseEnvKeys(readFileSync4(".env", "utf-8"));
@@ -1991,7 +2090,7 @@ ${log.out}` }] };
1991
2090
  const exampleContent = keys.map((k) => `${k}=`).join("\n") + "\n";
1992
2091
  writeFileSync3(".env.example", exampleContent, "utf-8");
1993
2092
  const gitignoreLines = [];
1994
- if (existsSync6(".gitignore")) {
2093
+ if (existsSync7(".gitignore")) {
1995
2094
  const content = readFileSync4(".gitignore", "utf-8");
1996
2095
  if (!content.split("\n").some((l) => l.trim() === ".env")) {
1997
2096
  appendFileSync2(".gitignore", "\n.env\n");
@@ -2012,11 +2111,11 @@ ${log.out}` }] };
2012
2111
  description: "\uD544\uC218 \uD658\uACBD\uBCC0\uC218 \uB204\uB77D \uAC80\uC0AC (.env.example \uAE30\uC900)"
2013
2112
  },
2014
2113
  async () => {
2015
- if (!existsSync6(".env.example")) {
2114
+ if (!existsSync7(".env.example")) {
2016
2115
  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." }] };
2017
2116
  }
2018
2117
  const requiredKeys = parseEnvKeys(readFileSync4(".env.example", "utf-8"));
2019
- const currentKeys = existsSync6(".env") ? parseEnvKeys(readFileSync4(".env", "utf-8")) : [];
2118
+ const currentKeys = existsSync7(".env") ? parseEnvKeys(readFileSync4(".env", "utf-8")) : [];
2020
2119
  const missing = requiredKeys.filter((k) => !currentKeys.includes(k));
2021
2120
  const extra = currentKeys.filter((k) => !requiredKeys.includes(k));
2022
2121
  const lines = [`\u{1F4CB} \uD544\uC218 \uD658\uACBD\uBCC0\uC218: ${requiredKeys.length}\uAC1C`];
@@ -2035,7 +2134,9 @@ ${log.out}` }] };
2035
2134
  );
2036
2135
  server.registerTool(
2037
2136
  "sync",
2038
- { description: "RULES.md \u2192 .cursorrules + CLAUDE.md \uC790\uB3D9 \uB3D9\uAE30\uD654" },
2137
+ {
2138
+ description: "RULES.md \u2192 Cursor\xB7Claude\xB7Windsurf\xB7Copilot\xB7Antigravity\xB7AGENTS.md \uADDC\uCE59 \uB3D9\uAE30\uD654. \uB36E\uC5B4\uC4F0\uAE30 \uC804 \uAE30\uC874 \uD30C\uC77C\uC744 \uC790\uB3D9 \uBC31\uC5C5\uD558\uBBC0\uB85C \uC548\uC804\uD558\uBA70, \uB418\uB3CC\uB9AC\uB824\uBA74 \uC0AC\uC6A9\uC790\uC5D0\uAC8C `vhk restore` \uB97C \uC548\uB0B4\uD558\uC138\uC694."
2139
+ },
2039
2140
  async () => runVhkCli(["sync"], "sync")
2040
2141
  );
2041
2142
  server.registerTool(
@@ -2130,7 +2231,7 @@ ${cliStatus}
2130
2231
  description: "\uD604\uC7AC \uBC84\uC804 + bump \uD6C4\uBCF4 \uD45C\uC2DC (MCP \uBAA8\uB4DC: \uC2E4\uC81C npm publish \uBBF8\uC218\uD589 \u2014 `vhk publish` \uC548\uB0B4)"
2131
2232
  },
2132
2233
  async () => {
2133
- if (!existsSync6("package.json")) {
2234
+ if (!existsSync7("package.json")) {
2134
2235
  return { content: [{ type: "text", text: "\u274C package.json \uC5C6\uC74C." }] };
2135
2236
  }
2136
2237
  try {
@@ -2158,7 +2259,7 @@ ${cliStatus}
2158
2259
  description: "\uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800 \uAC10\uC9C0 + \uC804\uD658 \uD6C4\uBCF4 \uAC00\uC6A9\uC131 (MCP \uBAA8\uB4DC: \uC2E4\uC81C \uC804\uD658 \uBBF8\uC218\uD589 \u2014 `vhk migrate <target>` \uC548\uB0B4)"
2159
2260
  },
2160
2261
  async () => {
2161
- const current = existsSync6("pnpm-lock.yaml") ? "pnpm" : existsSync6("yarn.lock") ? "yarn" : existsSync6("package-lock.json") ? "npm" : null;
2262
+ const current = existsSync7("pnpm-lock.yaml") ? "pnpm" : existsSync7("yarn.lock") ? "yarn" : existsSync7("package-lock.json") ? "npm" : null;
2162
2263
  const candidates = ["npm", "yarn", "pnpm"].filter((pm) => pm !== current);
2163
2264
  const lines = [`\uD604\uC7AC PM: ${current ?? "\uAC10\uC9C0 \uBD88\uAC00 (lock \uD30C\uC77C \uC5C6\uC74C)"}`];
2164
2265
  for (const pm of candidates) {
@@ -2241,5 +2342,6 @@ export {
2241
2342
  publish,
2242
2343
  audit,
2243
2344
  getVhkVersion,
2345
+ resolveVhkCliInvocation,
2244
2346
  startMcpServer
2245
2347
  };
package/dist/index.d.ts CHANGED
@@ -1,2 +1,5 @@
1
+ import { Command } from 'commander';
1
2
 
2
- export { }
3
+ declare const program: Command;
4
+
5
+ export { program };