@byh3071/vhk 1.0.2 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,826 +0,0 @@
1
- #!/usr/bin/env node
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __commonJS = (cb, mod) => function __require() {
9
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
10
- };
11
- var __copyProps = (to, from, except, desc) => {
12
- if (from && typeof from === "object" || typeof from === "function") {
13
- for (let key of __getOwnPropNames(from))
14
- if (!__hasOwnProp.call(to, key) && key !== except)
15
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
- }
17
- return to;
18
- };
19
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
- // If the importer is in node compatibility mode or this is not an ESM
21
- // file that has been converted to a CommonJS file using a Babel-
22
- // compatible transform (i.e. "__esModule" has not been set), then set
23
- // "default" to the CommonJS "module.exports" for node compatibility.
24
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
- mod
26
- ));
27
-
28
- // src/commands/env.ts
29
- import { existsSync, readFileSync, writeFileSync, appendFileSync } from "fs";
30
- import chalk2 from "chalk";
31
-
32
- // src/i18n/ko.ts
33
- var ko = {
34
- status: {
35
- title: "\uD504\uB85C\uC81D\uD2B8 \uC0C1\uD0DC",
36
- notGitRepo: "Git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2C8\uC5D0\uC694. \uBA3C\uC800 git init\uC744 \uC2E4\uD589\uD558\uC138\uC694.",
37
- branch: "\uBE0C\uB79C\uCE58:",
38
- changes: "\uBCC0\uACBD:",
39
- recentCommits: "\uCD5C\uADFC \uCEE4\uBC0B (3):",
40
- noCommits: "\uCEE4\uBC0B \uC5C6\uC74C",
41
- remote: "\uC6D0\uACA9:",
42
- noUpstream: "upstream \uC5C6\uC74C",
43
- inSync: "\uB3D9\uAE30\uD654\uB428",
44
- ahead: (n) => `\u2191${n} ahead`,
45
- behind: (n) => `\u2193${n} behind`,
46
- package: "package.json:",
47
- noPackage: "package.json \uC5C6\uC74C",
48
- detached: "(detached HEAD)",
49
- unknownBranch: "(\uC54C \uC218 \uC5C6\uC74C)"
50
- },
51
- save: {
52
- title: "\uC800\uC7A5\uD558\uAE30",
53
- notGitRepo: "git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2D9\uB2C8\uB2E4. \uBA3C\uC800 git init\uC744 \uC2E4\uD589\uD558\uC138\uC694.",
54
- noChanges: "\uC800\uC7A5\uD560 \uBCC0\uACBD\uC0AC\uD56D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
55
- filesHeader: (n) => `\uBCC0\uACBD\uB41C \uD30C\uC77C (${n}\uAC1C):`,
56
- commitMessage: "\uCEE4\uBC0B \uBA54\uC2DC\uC9C0 (Enter\uB85C \uAE30\uBCF8\uAC12 \uC0AC\uC6A9):",
57
- saving: "\uC800\uC7A5 \uC911...",
58
- pushing: "\uC6D0\uACA9 \uC800\uC7A5\uC18C\uC5D0 \uC62C\uB9AC\uB294 \uC911...",
59
- successWithPush: "\uC800\uC7A5 + \uC6D0\uACA9 \uC5C5\uB85C\uB4DC \uC644\uB8CC!",
60
- successLocal: "\uB85C\uCEEC \uC800\uC7A5 \uC644\uB8CC!",
61
- noRemote: "\uC6D0\uACA9 \uC800\uC7A5\uC18C\uAC00 \uC124\uC815\uB418\uC9C0 \uC54A\uC544 push\uB97C \uAC74\uB108\uB6F0\uC5C8\uC2B5\uB2C8\uB2E4.",
62
- failed: "\uC800\uC7A5 \uC2E4\uD328",
63
- stagedAfterFail: "\uCEE4\uBC0B\uC740 \uC2E4\uD328\uD588\uC9C0\uB9CC \uD30C\uC77C\uC740 \uC2A4\uD14C\uC774\uC9D5\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4. \uD655\uC778: git status / \uCDE8\uC18C: git reset HEAD",
64
- securityWarnHeader: "\uC800\uC7A5 \uC804 \uBCF4\uC548 \uD655\uC778:",
65
- secretsFound: (n) => `\uCF54\uB4DC\uC5D0\uC11C CRITICAL/HIGH \uC2DC\uD06C\uB9BF \uD328\uD134 ${n}\uAC74 \uAC10\uC9C0`,
66
- secretsConfirm: "\uADF8\uB798\uB3C4 \uCEE4\uBC0B\xB7push\uB97C \uC9C4\uD589\uD560\uAE4C\uC694?",
67
- cancelled: "\uC800\uC7A5\uC744 \uCDE8\uC18C\uD588\uC2B5\uB2C8\uB2E4.",
68
- pushFailed: "push \uC2E4\uD328 (\uB85C\uCEEC \uCEE4\uBC0B\uC740 \uC644\uB8CC\uB428)",
69
- commitOkPushFailed: "\uB85C\uCEEC \uCEE4\uBC0B\uC740 \uB410\uC9C0\uB9CC \uC6D0\uACA9 push\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4. git push\uB97C \uC9C1\uC811 \uD655\uC778\uD558\uC138\uC694.",
70
- done: (n) => `${n}\uAC1C \uD30C\uC77C \uC800\uC7A5 \uC644\uB8CC!`,
71
- doneLocalOnly: (n) => `${n}\uAC1C \uD30C\uC77C \uB85C\uCEEC \uC800\uC7A5\uB428 (push\uB294 \uC2E4\uD328)`
72
- },
73
- undo: {
74
- title: "\uB418\uB3CC\uB9AC\uAE30",
75
- notGitRepo: "git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2D9\uB2C8\uB2E4.",
76
- noCommits: "\uB418\uB3CC\uB9B4 \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
77
- recentHeader: "\u{1F4CB} \uCD5C\uADFC \uCEE4\uBC0B:",
78
- howMany: "\uBA87 \uAC1C\uC758 \uCEE4\uBC0B\uC744 \uB418\uB3CC\uB9B4\uAE4C\uC694?",
79
- alreadyPushed: "\uC774 \uCEE4\uBC0B\uC740 \uC774\uBBF8 \uC6D0\uACA9\uC5D0 \uC62C\uB77C\uAC14\uC2B5\uB2C8\uB2E4. \uB418\uB3CC\uB9AC\uBA74 \uCDA9\uB3CC\uC774 \uC0DD\uAE38 \uC218 \uC788\uC5B4\uC694.",
80
- noUpstreamWarning: "upstream \uBE0C\uB79C\uCE58\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uC774\uBBF8 push\uD55C \uCEE4\uBC0B\uC77C \uC218 \uC788\uC5B4\uC694. \uB418\uB3CC\uB9B0 \uB4A4 force push\uAC00 \uD544\uC694\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.",
81
- confirmMessage: "\uCD5C\uADFC \uCEE4\uBC0B\uC744 \uB418\uB3CC\uB9AC\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
82
- confirmRisky: (n) => `\u26A0\uFE0F \uC704\uD5D8: \uCD5C\uADFC ${n}\uAC1C \uCEE4\uBC0B\uC744 soft reset\uD569\uB2C8\uB2E4. \uC6D0\uACA9\uACFC \uC5B4\uAE0B\uB0A0 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uACC4\uC18D\uD560\uAE4C\uC694?`,
83
- cancelled: "\uCDE8\uC18C\uB428",
84
- success: "\uB418\uB3CC\uB9AC\uAE30 \uC644\uB8CC! \uBCC0\uACBD\uC0AC\uD56D\uC740 \uADF8\uB300\uB85C \uB0A8\uC544\uC788\uC2B5\uB2C8\uB2E4.",
85
- stagedHint: "\uBCC0\uACBD\uC0AC\uD56D\uC740 \uC2A4\uD14C\uC774\uC9D5 \uC601\uC5ED\uC5D0 \uB0A8\uC544 \uC788\uC5B4\uC694.",
86
- rootCommit: "\uCCAB \uCEE4\uBC0B\uB9CC \uC788\uC5B4\uC11C \uB354 \uB418\uB3CC\uB9B4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
87
- forcePushHint: "\uC6D0\uACA9\uACFC \uB9DE\uCD94\uB824\uBA74: git push --force-with-lease (\uD63C\uC790 \uC791\uC5C5\uD55C \uBE0C\uB79C\uCE58\uC5D0\uC11C\uB9CC, \uD300\uACFC \uD569\uC758 \uD6C4)",
88
- failed: "\uB418\uB3CC\uB9AC\uAE30 \uC2E4\uD328"
89
- },
90
- diff: {
91
- title: "\uBCC0\uACBD\uC0AC\uD56D \uD655\uC778",
92
- notGitRepo: "git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2D9\uB2C8\uB2E4.",
93
- noChanges: "\uBCC0\uACBD\uC0AC\uD56D \uC5C6\uC74C! \uAE68\uB057\uD569\uB2C8\uB2E4.",
94
- stagedHeader: "\u{1F4E6} \uCEE4\uBC0B \uB300\uAE30 (staged):",
95
- unstagedHeader: "\u270F\uFE0F \uC218\uC815\uB428 (unstaged):",
96
- untrackedHeader: (n) => `\u2795 \uC0C8 \uD30C\uC77C (${n}\uAC1C):`,
97
- summaryHeader: "\u{1F4CA} \uCD1D \uBCC0\uACBD \uC694\uC57D (\uC791\uC5C5 \uD2B8\uB9AC vs HEAD)",
98
- filesLine: (n) => `\uD30C\uC77C: ${n}\uAC1C`
99
- },
100
- start: {
101
- title: "\u{1F527} VHK \u2014 \uBB34\uC5C7\uC744 \uB3C4\uC640\uB4DC\uB9B4\uAE4C\uC694?",
102
- subtitle: "\uBC88\uD638\uB9CC \uACE0\uB974\uBA74 \uB429\uB2C8\uB2E4. \uBA85\uB839\uC5B4\uB97C \uC678\uC6B8 \uD544\uC694 \uC5C6\uC5B4\uC694.",
103
- menuPrompt: "\uC5B4\uB5A4 \uC791\uC5C5\uC744 \uD560\uAE4C\uC694?",
104
- choiceGate: "1) \uC0C8 \uC544\uC774\uB514\uC5B4 \uAC80\uC99D\uD558\uAE30",
105
- choiceInit: "2) \uD504\uB85C\uC81D\uD2B8 \uC2DC\uC791\uD558\uAE30 \u2014 \uD3F4\uB354\xB7\uD30C\uC77C \uB9CC\uB4E4\uAE30",
106
- choiceInitSkipGate: "3) \uAE30\uD68D\uC740 \uB05D\uB0AC\uC5B4\uC694 \u2014 \uBC14\uB85C \uD504\uB85C\uC81D\uD2B8 \uB9CC\uB4E4\uAE30",
107
- choiceRecap: "4) \uC624\uB298 \uD55C \uC77C \uC815\uB9AC\uD558\uAE30",
108
- choiceSync: "5) \uADDC\uCE59 \uD30C\uC77C \uB9DE\uCD94\uAE30",
109
- choiceCheck: "6) \uD504\uB85C\uC81D\uD2B8 \uADDC\uCE59 \uC810\uAC80\uD558\uAE30",
110
- choiceSecure: "7) \uBE44\uBC00\uBC88\uD638\xB7\uD0A4 \uC720\uCD9C \uAC80\uC0AC\uD558\uAE30",
111
- choiceExit: "\uB098\uAC00\uAE30",
112
- goodbye: "\uB2E4\uC74C\uC5D0 \uB610 \uBD88\uB7EC\uC8FC\uC138\uC694."
113
- },
114
- gate: {
115
- title: "\u{1F4A1} \uC544\uC774\uB514\uC5B4 \uAC80\uC99D",
116
- welcome: "\uC0C8 \uC544\uC774\uB514\uC5B4\uB97C \uAC80\uC99D\uD569\uB2C8\uB2E4. \uC9C8\uBB38\uC5D0 \uB2F5\uD574\uC8FC\uC138\uC694.",
117
- modePrompt: "\uC5B4\uB5BB\uAC8C \uAC80\uC99D\uD560\uAE4C\uC694?",
118
- modeQuickLabel: "\u26A1 \uC9E7\uAC8C (\uD575\uC2EC 5\uBB38\uD56D) \u2014 \uB9C9 \uB5A0\uC62C\uB790\uC744 \uB54C",
119
- modeFullLabel: "\u{1F50D} \uC790\uC138\uD788 (13\uBB38\uD56D) \u2014 \uAE30\uD68D\uC774 \uC5B4\uB290 \uC815\uB3C4 \uC7A1\uD614\uC744 \uB54C",
120
- modeSkipLabel: "\u23ED\uFE0F \uAC74\uB108\uB6F0\uAE30 \u2014 \uB178\uC158\xB7\uBB38\uC11C\uC5D0 \uC774\uBBF8 \uAE30\uD68D\uD574 \uB460",
121
- skipSourcePrompt: "\u{1F4C4} \uAE30\uD68D \uBB38\uC11C \uC704\uCE58 (\uB178\uC158 \uC8FC\uC18C, \uD30C\uC77C \uACBD\uB85C \uB4F1):",
122
- skipGo: "\u2705 \uC2DC\uC791\uD574\uB3C4 \uB3FC\uC694! \uC774\uC81C \uD504\uB85C\uC81D\uD2B8\uB97C \uB9CC\uB4E4\uC5B4 \uBCF4\uC138\uC694 (vhk init)",
123
- skipSourceLabel: (source) => `\uAE30\uD68D \uBB38\uC11C: ${source}`,
124
- quickHeader: "\u26A1 \uC9E7\uC740 \uAC80\uC99D",
125
- fullHeader: "\u{1F50D} \uC790\uC138\uD55C \uAC80\uC99D",
126
- modeCountSuffix: (total) => `\u2014 ${total}\uBB38\uD56D`,
127
- idea: "\u{1F4A1} \uC5B4\uB5A4 \uAC78 \uB9CC\uB4E4 \uAC74\uAC00\uC694? (\uD55C \uC904)",
128
- ideaHint: '\uC608: "\uD300 \uD560 \uC77C\uC744 3\uCD08\uC5D0 \uCD94\uAC00\uD558\uB294 \uC571"',
129
- painPoint: "\u{1F624} \uC774 \uBB38\uC81C, \uB204\uAC00 \uC5BC\uB9C8\uB098 \uC544\uD30C\uD574\uC694?",
130
- painPointHint: '\uC608: "\uB9E4\uC77C \uC5D1\uC140\uC5D0 \uBCF5\uBD99\uD558\uB290\uB77C 30\uBD84\uC529 \uB0A0\uB9BC"',
131
- edge: "\u{1F4AA} \uB098\uB9CC\uC758 \uAC15\uC810\uC740? (\uBE44\uC2B7\uD55C \uAC8C \uC788\uB294\uB370 \uC65C \uC774\uAC78?)",
132
- edgeHint: '\uC608: "\uD55C\uAD6D\uC5B4\uB85C \uB41C \uAC00\uC774\uB4DC + \uBC14\uB85C \uC4F0\uB294 \uD15C\uD50C\uB9BF"',
133
- checklistStart: "\u2500\u2500\u2500 \uC774\uC5B4\uC11C \uC9C8\uBB38\uD569\uB2C8\uB2E4 \u2500\u2500\u2500",
134
- hintPrefix: " \u{1F4A1}",
135
- verdictPrompt: (_failIf) => " \u2192 \uC9C0\uAE08 \uC0C1\uD0DC\uB294?",
136
- statusPassChoice: "\u2705 \uAD1C\uCC2E\uC544\uC694",
137
- statusHoldChoice: "\u{1F7E1} \uC544\uC9C1 \uBAA8\uB974\uACA0\uC5B4\uC694 (\uB098\uC911\uC5D0 \uCC44\uC6CC\uB3C4 \uB429\uB2C8\uB2E4)",
138
- statusFailChoice: "\u{1F504} \uBC94\uC704\uB97C \uC904\uC5EC\uBCFC\uAC8C\uC694",
139
- statusPassLine: " \u2705 \uAD1C\uCC2E\uC544\uC694",
140
- statusHoldLine: " \u{1F7E1} \uBCF4\uB958 \u2014 \uAC1C\uBC1C\uD558\uBA74\uC11C \uCC44\uC6CC\uB3C4 \uB429\uB2C8\uB2E4",
141
- statusFailLine: " \u{1F504} \uBC94\uC704 \uC870\uC815\uC774 \uD544\uC694\uD574 \uBCF4\uC5EC\uC694",
142
- verdictTitle: "\u2550\u2550\u2550 \uACB0\uACFC \u2550\u2550\u2550",
143
- ideaLabel: "\uB9CC\uB4E4 \uAC83:",
144
- painPointLabel: "\uC544\uD508 \uC810:",
145
- edgeLabel: "\uB098\uB9CC\uC758 \uAC15\uC810:",
146
- countLine: (failCount, holdCount, total) => `\uBC94\uC704 \uC870\uC815 ${failCount}\uAC1C \xB7 \uBCF4\uB958 ${holdCount}\uAC1C / ${total}\uBB38\uD56D`,
147
- go: "\u2705 \uC2DC\uC791\uD574\uB3C4 \uB3FC\uC694! \uB2E4\uC74C \uB2E8\uACC4(\uD504\uB85C\uC81D\uD2B8 \uB9CC\uB4E4\uAE30)\uB85C \uB118\uC5B4\uAC00\uC138\uC694.",
148
- refine: "\u{1F504} \uC870\uAE08 \uB354 \uB2E4\uB4EC\uC73C\uBA74 \uC88B\uACA0\uC5B4\uC694. \uC704 \uD56D\uBAA9\uC744 \uBCF4\uC644\uD574 \uBCF4\uC138\uC694.",
149
- drop: "\u{1F4A1} \uB2E4\uB978 \uC544\uC774\uB514\uC5B4\uB97C \uAC80\uD1A0\uD574 \uBCF4\uB294 \uAC74 \uC5B4\uB5A8\uAE4C\uC694?",
150
- nextCommand: "\uB2E4\uC74C: vhk init (\uD504\uB85C\uC81D\uD2B8 \uC2DC\uC791\uD558\uAE30)",
151
- holdRemainHint: "\u{1F4A1} \uBCF4\uB958\uD55C \uD56D\uBAA9\uC740 \uAC1C\uBC1C\uD558\uBA74\uC11C \uCC44\uC6CC\uB3C4 \uAD1C\uCC2E\uC544\uC694.",
152
- failMessage: "\uC544\uC9C1 \uBAA8\uB974\uACA0\uC5B4\uC694 \u2192 \uAD1C\uCC2E\uC544\uC694, \uAC1C\uBC1C\uD558\uBA74\uC11C \uCC44\uC6CC\uB3C4 \uB429\uB2C8\uB2E4."
153
- },
154
- init: {
155
- title: "\u{1F6E0}\uFE0F \uD504\uB85C\uC81D\uD2B8 \uC2DC\uC791\uD558\uAE30",
156
- skipGate: "\u23ED\uFE0F 1\uB2E8\uACC4(\uC544\uC774\uB514\uC5B4 \uAC80\uC99D) \uAC74\uB108\uB6F0\uAE30 \u2014 \uAE30\uD68D\xB7\uC124\uACC4\uAC00 \uC774\uBBF8 \uC788\uC5B4\uC694",
157
- projectName: "\u{1F4E6} \uD504\uB85C\uC81D\uD2B8 \uC774\uB984\uC740?",
158
- projectNameHint: '\uC608: "\uD300 \uD560 \uC77C \uC571"',
159
- description: "\u{1F4DD} \uD55C \uC904\uB85C \uC124\uBA85\uD558\uBA74?",
160
- descriptionHint: '\uC608: "3\uCD08 \uB9CC\uC5D0 \uD560 \uC77C \uCD94\uAC00"',
161
- projectType: "\u{1F3D7}\uFE0F \uC5B4\uB5A4 \uC885\uB958\uC778\uAC00\uC694?",
162
- confirmStack: "\uC774 \uAE30\uC220 \uBB36\uC74C\uC73C\uB85C \uC9C4\uD589\uD560\uAE4C\uC694?",
163
- canceled: "\uCDE8\uC18C\uD588\uC5B4\uC694. \uAE30\uC220 \uBB36\uC74C\uC744 \uBC14\uAFB8\uB824\uBA74 \uB2E4\uC2DC vhk init\uC744 \uC2E4\uD589\uD558\uC138\uC694.",
164
- recommendedStack: "\uCD94\uCC9C \uAE30\uC220 \uBB36\uC74C:",
165
- filesGenerating: "\u{1F4C2} \uD544\uC694\uD55C \uD30C\uC77C \uB9CC\uB4DC\uB294 \uC911...",
166
- overwrite: (filePath) => ` \u26A0\uFE0F ${filePath} \uD30C\uC77C\uC774 \uC788\uC5B4\uC694. \uB36E\uC5B4\uC4F8\uAE4C\uC694?`,
167
- skipped: (filePath) => `${filePath} \u2014 \uAC74\uB108\uB700`,
168
- done: "\u{1F389} \uD504\uB85C\uC81D\uD2B8 \uBF08\uB300\uAC00 \uC900\uBE44\uB410\uC5B4\uC694!",
169
- nextSteps: "\uB2E4\uC74C\uC5D0 \uD560 \uC77C:",
170
- fillHint: "CLAUDE.md \xB7 .cursorrules\uC5D0\uC11C \u{1F449} \uC5EC\uAE30\uB97C \uCC44\uC6CC\uC8FC\uC138\uC694 \uD45C\uC2DC\uB97C \uCC3E\uC544 \uCC44\uC6B0\uC138\uC694",
171
- prdHint: "docs/PRD.md\uC5D0 1\uCC28 \uBC84\uC804\uC5D0 \uB123\uC744 \uAE30\uB2A5\xB7\uBE7C\uB294 \uAE30\uB2A5\uC744 \uC801\uC5B4 \uBCF4\uC138\uC694",
172
- notionFetching: "\u{1F4E1} \uB178\uC158 \uAE30\uD68D \uD398\uC774\uC9C0 \uBD88\uB7EC\uC624\uB294 \uC911...",
173
- notionDone: (name) => `\uB178\uC158\uC5D0\uC11C \uAC00\uC838\uC624\uAE30 \uC644\uB8CC: ${name}`,
174
- notionReviewHint: "docs/PRD.md\uB97C \uC77D\uACE0 \u{1F449} \uC5EC\uAE30\uB97C \uCC44\uC6CC\uC8FC\uC138\uC694 \uD56D\uBAA9\uC744 \uCC44\uC6B0\uC138\uC694",
175
- gitHintLabel: "\uD130\uBBF8\uB110\uC5D0 \uBCF5\uC0AC\uD560 \uBA85\uB839 (\uC544\uB798 \uBC15\uC2A4 \uBCF5\uBD99):",
176
- gitHintCommand: 'git init && git add . && git commit -m "feat: \uD504\uB85C\uC81D\uD2B8 \uC2DC\uC791"',
177
- startDev: "\uC774\uC81C \uAC1C\uBC1C\uD574 \uBCF4\uC138\uC694! \u{1F680}",
178
- commandsMdDone: "\u{1F4CB} COMMANDS.md \uC0DD\uC131",
179
- scriptsDone: "\u{1F4E6} package.json scripts \uCD94\uAC00"
180
- },
181
- recap: {
182
- title: "\u{1F4DD} \uC624\uB298 \uD55C \uC77C \uC815\uB9AC",
183
- analyzing: "\u{1F4CA} \uC624\uB298 \uBC14\uB010 \uD30C\uC77C\xB7\uCEE4\uBC0B\uC744 \uC0B4\uD3B4\uBCF4\uB294 \uC911...",
184
- noRepo: "\u274C Git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2C8\uC5D0\uC694. \uBA3C\uC800 git init\uC744 \uC2E4\uD589\uD558\uC138\uC694.",
185
- noChanges: "\u26A0\uFE0F \uC624\uB298 \uBC14\uB010 \uB0B4\uC6A9\uC774 \uC5C6\uC5B4\uC694.",
186
- summary: "\u{1F4DD} \uC774\uBC88\uC5D0 \uBB58 \uD588\uB098\uC694? (1~3\uC904)",
187
- summaryHint: '\uC608: "\uB85C\uADF8\uC778 \uD654\uBA74 \uB9CC\uB4E4\uACE0 \uBC84\uD2BC \uC0C9 \uACE0\uCE68"',
188
- decisions: "\u{1F9ED} \uC815\uD55C \uACB0\uC815\uC774 \uC788\uB098\uC694? (\uC5C6\uC73C\uBA74 Enter)",
189
- nextTodo: "\u23ED\uFE0F \uB2E4\uC74C\uC5D0 \uD560 \uC77C\uC740?",
190
- blockers: "\u{1F6A7} \uB9C9\uD78C \uAC8C \uC788\uB098\uC694? (\uC5C6\uC73C\uBA74 Enter)",
191
- done: "\u2705 \uC624\uB298 \uAE30\uB85D\uC744 \uC800\uC7A5\uD588\uC5B4\uC694!",
192
- updateClaude: 'CLAUDE.md "\uC9C0\uAE08 \uC0C1\uD0DC"\uB3C4 \uAC19\uC774 \uACE0\uCE60\uAE4C\uC694?',
193
- adrDetected: "\u{1F4D0} \uC4F0\uB294 \uAE30\uC220\xB7\uC124\uC815\uC774 \uBC14\uB010 \uAC83 \uAC19\uC544\uC694!",
194
- createAdr: "\uC65C \uADF8\uB807\uAC8C \uD588\uB294\uC9C0 \uAE30\uB85D \uBB38\uC11C\uB97C \uB9CC\uB4E4\uAE4C\uC694?",
195
- troubleDetected: "\u{1F527} \uBC84\uADF8\xB7\uC624\uB958\uB97C \uACE0\uCE5C \uCEE4\uBC0B\uC774 \uBCF4\uC5EC\uC694!",
196
- createTroubleshoot: "\uC5B4\uB5BB\uAC8C \uACE0\uCCE4\uB294\uC9C0 \uBA54\uBAA8\uB97C \uB0A8\uAE38\uAE4C\uC694?"
197
- },
198
- check: {
199
- title: "\u{1F50D} \uD504\uB85C\uC81D\uD2B8 \uADDC\uCE59 \uC810\uAC80",
200
- noRules: "\u26A0\uFE0F RULES.md \uD30C\uC77C\uC774 \uC5C6\uC5B4\uC694.",
201
- noAutoRules: "\u26A0\uFE0F \uC790\uB3D9\uC73C\uB85C \uAC80\uC0AC\uD560 \uADDC\uCE59\uC774 \uC5C6\uC5B4\uC694.",
202
- allPassed: "\u{1F389} \uADDC\uCE59\uC744 \uBAA8\uB450 \uC9C0\uCF30\uC5B4\uC694!",
203
- summary: "\u{1F4CA} \uC810\uAC80 \uACB0\uACFC:"
204
- },
205
- doctor: {
206
- title: "\u{1FA7A} \uAC1C\uBC1C \uD658\uACBD \uC810\uAC80",
207
- allOk: "\u{1F389} \uAC1C\uBC1C \uD658\uACBD \uC900\uBE44 \uC644\uB8CC!",
208
- missing: "\u26A0\uFE0F \uC77C\uBD80 \uB3C4\uAD6C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
209
- missingHint: "\uC704 \uC548\uB0B4\uB97C \uB530\uB77C \uC124\uCE58\uD558\uC138\uC694.",
210
- projectFiles: "\u{1F4C1} \uD504\uB85C\uC81D\uD2B8 \uD30C\uC77C \uD655\uC778:",
211
- envNotIgnored: "\u26A0\uFE0F .env\uAC00 .gitignore\uC5D0 \uC5C6\uC74C! \uCD94\uAC00\uD558\uC138\uC694",
212
- nextOkMessage: "\uD658\uACBD \uC810\uAC80 \uD1B5\uACFC! \uC774\uC81C \uD504\uB85C\uC81D\uD2B8\uB97C \uC2DC\uC791\uD558\uC138\uC694.",
213
- nextRetryMessage: "\uC704 \uB3C4\uAD6C\uB97C \uC124\uCE58\uD55C \uD6C4 \uB2E4\uC2DC \uC810\uAC80\uD558\uC138\uC694.",
214
- updateAvailable: (latest) => `\u{1F195} v${latest} \uC0AC\uC6A9 \uAC00\uB2A5 \u2014 npm i -g @byh3071/vhk`,
215
- updateCurrent: "\uCD5C\uC2E0 \uBC84\uC804\uC744 \uC4F0\uACE0 \uC788\uC5B4\uC694"
216
- },
217
- nlp: {
218
- matched: "\uC774\uAC8C \uB9DE\uB098\uC694?",
219
- notMatched: "\uBB34\uC2A8 \uB73B\uC778\uC9C0 \uBAA8\uB974\uACA0\uC5B4\uC694. vhk\uB97C \uC785\uB825\uD558\uBA74 \uBA54\uB274\uC5D0\uC11C \uC120\uD0DD\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.",
220
- menuHint: "vhk\uB97C \uC785\uB825\uD558\uBA74 \uBA54\uB274\uC5D0\uC11C \uC120\uD0DD\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4."
221
- },
222
- secure: {
223
- title: "\u{1F512} \uBE44\uBC00\uBC88\uD638\xB7\uD0A4 \uC720\uCD9C \uAC80\uC0AC",
224
- noGitignore: "\u26A0\uFE0F .gitignore \uD30C\uC77C\uC774 \uC5C6\uC5B4\uC694!",
225
- noEnvInGitignore: "\u26A0\uFE0F .gitignore\uC5D0 .env\uAC00 \uC5C6\uC5B4\uC694!",
226
- scanning: "\u{1F50D} \uD30C\uC77C\uC744 \uC0B4\uD3B4\uBCF4\uB294 \uC911...",
227
- clean: "\u{1F389} \uBE44\uBC00\uBC88\uD638\xB7\uD0A4\uAC00 \uCF54\uB4DC\uC5D0 \uBCF4\uC774\uC9C0 \uC54A\uC544\uC694!",
228
- summary: "\u{1F4CA} \uAC80\uC0AC \uC694\uC57D:"
229
- },
230
- sync: {
231
- title: "\u{1F504} \uADDC\uCE59 \uD30C\uC77C \uB9DE\uCD94\uAE30",
232
- noRules: "\u26A0\uFE0F RULES.md \uD30C\uC77C\uC774 \uC5C6\uC5B4\uC694.",
233
- cursorrulesDone: "\u2705 .cursorrules \uB9DE\uCDA4 \uC644\uB8CC",
234
- claudeDone: "\u2705 CLAUDE.md \uB9DE\uCDA4 \uC644\uB8CC",
235
- done: "\u{1F504} \uB9DE\uCD94\uAE30 \uC644\uB8CC!"
236
- },
237
- ship: {
238
- title: "\u{1F680} \uBC30\uD3EC \uCCB4\uD06C\uB9AC\uC2A4\uD2B8",
239
- checklist: "\u{1F4CB} \uBC30\uD3EC \uC804 \uCCB4\uD06C\uB9AC\uC2A4\uD2B8",
240
- retro: "\u{1F50D} \uBC30\uD3EC \uD68C\uACE0",
241
- buildLogCreated: "\u2705 \uBE4C\uB4DC \uB85C\uADF8 \uC0DD\uC131 \uC644\uB8CC",
242
- buildLogDone: (rel) => `\u2705 \uBE4C\uB4DC \uB85C\uADF8 \uC0DD\uC131 \uC644\uB8CC: ${rel}`,
243
- questionWell: "\uC798\uB41C \uC810\uC740?",
244
- questionWrong: "\uC5B4\uB824\uC6E0\uB358 \uC810\uC740?",
245
- questionLearned: "\uBC30\uC6B4 \uC810\uC740?",
246
- questionNext: "\uB2E4\uC74C \uBC84\uC804\uC5D0\uC11C \uD560 \uAC83\uC740?",
247
- checkboxPrompt: "\uC644\uB8CC\uD55C \uD56D\uBAA9\uC744 \uC120\uD0DD\uD558\uC138\uC694:",
248
- incompleteHeader: "\u26A0\uFE0F \uC544\uC9C1 \uC644\uB8CC\uD558\uC9C0 \uC54A\uC740 \uD56D\uBAA9:",
249
- proceedConfirm: "\uADF8\uB798\uB3C4 \uACC4\uC18D \uC9C4\uD589\uD560\uAE4C\uC694?",
250
- allPassed: "\u2705 \uBAA8\uB4E0 \uCCB4\uD06C\uB9AC\uC2A4\uD2B8 \uD1B5\uACFC!",
251
- retryMessage: "\uCCB4\uD06C\uB9AC\uC2A4\uD2B8\uB97C \uB9C8\uCE5C \uB4A4 \uB2E4\uC2DC \uC2E4\uD589\uD574 \uBCF4\uC138\uC694.",
252
- retryCursorHint: "\uBE4C\uB4DC\uD558\uACE0 \uD14C\uC2A4\uD2B8 \uB3CC\uB824\uC918",
253
- versionPrompt: "\uBC30\uD3EC \uBC84\uC804\uC740?",
254
- versionHint: "\uC608: 0.4.0",
255
- emptySection: "(\uBBF8\uC791\uC131)",
256
- emptyNext: "(\uBBF8\uC815)",
257
- deployMessage: "\uBE4C\uB4DC \uB85C\uADF8\uB97C \uC800\uC7A5\uD588\uC5B4\uC694! \uC774\uC81C \uC2E4\uC81C \uBC30\uD3EC\uB97C \uC9C4\uD589\uD558\uC138\uC694.",
258
- deployCursorHint: "\uBC30\uD3EC\uD574\uC918",
259
- checkBuild: "\uBE4C\uB4DC\uAC00 \uC131\uACF5\uD588\uB098\uC694?",
260
- hintBuild: "pnpm build",
261
- checkTest: "\uBAA8\uB4E0 \uD14C\uC2A4\uD2B8\uAC00 \uD1B5\uACFC\uD588\uB098\uC694?",
262
- hintTest: "pnpm test --run",
263
- checkVersion: "package.json \uBC84\uC804\uC744 \uC62C\uB838\uB098\uC694?",
264
- hintVersion: "version \uD544\uB4DC \uD655\uC778",
265
- checkChangelog: "\uBCC0\uACBD \uB0B4\uC6A9\uC744 \uAE30\uB85D\uD588\uB098\uC694?",
266
- hintChangelog: "README \uB610\uB294 CHANGELOG",
267
- checkSecurity: "\uBCF4\uC548 \uC2A4\uCE94\uC744 \uB3CC\uB838\uB098\uC694?",
268
- hintSecurity: "vhk \uBCF4\uC548 scan",
269
- checkCommit: "\uBAA8\uB4E0 \uBCC0\uACBD\uC774 \uCEE4\uBC0B\uB418\uC5C8\uB098\uC694?",
270
- hintCommit: "git status \uD655\uC778",
271
- changelogUpdated: (version) => `CHANGELOG.md \uAC31\uC2E0\uB428 \u2014 [Unreleased] \u2192 [${version}] \uC139\uC158\uC73C\uB85C \uC774\uB3D9`,
272
- changelogNoUnreleased: "CHANGELOG.md\uC5D0 [Unreleased] \uC139\uC158\uC774 \uC5C6\uC5B4 \uC790\uB3D9 \uAC31\uC2E0\uC744 \uC2A4\uD0B5\uD588\uC5B4\uC694",
273
- changelogMissing: "CHANGELOG.md\uAC00 \uC5C6\uC5B4\uC694. \uB9CC\uB4E4\uBA74 ship\uC774 \uC790\uB3D9\uC73C\uB85C [Unreleased] \u2192 \uBC84\uC804 \uC139\uC158\uC73C\uB85C \uC62E\uACA8\uC90D\uB2C8\uB2E4."
274
- },
275
- design: {
276
- title: "\uB514\uC790\uC778 \uD1A0\uD070 \uC0DD\uC131",
277
- selectPalette: "\uCEEC\uB7EC \uD314\uB808\uD2B8\uB97C \uC120\uD0DD\uD558\uC138\uC694:"
278
- },
279
- theme: {
280
- title: "\uD14C\uB9C8 \uC124\uC815 (\uB2E4\uD06C/\uB77C\uC774\uD2B8 \uBAA8\uB4DC)"
281
- },
282
- ref: {
283
- addTitle: "\uB808\uD37C\uB7F0\uC2A4 \uCD94\uAC00",
284
- listTitle: "\uB808\uD37C\uB7F0\uC2A4 \uBAA9\uB85D"
285
- },
286
- memory: {
287
- addTitle: "\uAE30\uC5B5 \uCD94\uAC00",
288
- listTitle: "\uAE30\uC5B5 \uBAA9\uB85D"
289
- },
290
- mcp: {
291
- initTitle: "Cursor MCP \uC5F0\uB3D9 \uC124\uC815",
292
- serverStarted: "VHK MCP \uC11C\uBC84 \uC2DC\uC791\uB428"
293
- },
294
- deploy: {
295
- title: "\uBC30\uD3EC\uD558\uAE30",
296
- selectPlatform: "\uC5B4\uB5A4 \uD50C\uB7AB\uD3FC\uC5D0 \uBC30\uD3EC\uD560\uAE4C\uC694?",
297
- deploying: "\uBC30\uD3EC \uC911...",
298
- success: "\uBC30\uD3EC \uC131\uACF5!",
299
- failed: "\uBC30\uD3EC \uC2E4\uD328"
300
- },
301
- env: {
302
- title: "\uD658\uACBD\uBCC0\uC218 \uAD00\uB9AC",
303
- checkTitle: "\uD658\uACBD\uBCC0\uC218 \uC810\uAC80"
304
- },
305
- publish: {
306
- title: "npm \uBC30\uD3EC",
307
- selectBump: "\uBC84\uC804\uC744 \uC5B4\uB5BB\uAC8C \uC62C\uB9B4\uAE4C\uC694?",
308
- building: "\uBE4C\uB4DC \uC911...",
309
- buildSuccess: "\uBE4C\uB4DC \uC131\uACF5",
310
- buildFailed: "\uBE4C\uB4DC \uC2E4\uD328",
311
- testing: "\uD14C\uC2A4\uD2B8 \uC911...",
312
- testSuccess: "\uD14C\uC2A4\uD2B8 \uD1B5\uACFC",
313
- testFailed: "\uD14C\uC2A4\uD2B8 \uC2E4\uD328",
314
- publishing: "npm \uBC30\uD3EC \uC911...",
315
- publishSuccess: "npm \uBC30\uD3EC \uC131\uACF5!",
316
- publishFailed: "npm \uBC30\uD3EC \uC2E4\uD328"
317
- },
318
- harness: {
319
- title: "\uD1B5\uD569 \uD488\uC9C8 \uC810\uAC80"
320
- },
321
- audit: {
322
- title: "\uBCF4\uC548 \uAC10\uC0AC"
323
- },
324
- migrate: {
325
- title: "\uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800 \uC804\uD658",
326
- selectTarget: "\uC5B4\uB5A4 \uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800\uB85C \uC804\uD658\uD560\uAE4C\uC694?"
327
- },
328
- update: {
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"
337
- }
338
- };
339
- function lookup(path) {
340
- const parts = path.split(".");
341
- let cur = ko;
342
- for (const part of parts) {
343
- if (cur === null || typeof cur !== "object") return void 0;
344
- cur = cur[part];
345
- }
346
- return cur;
347
- }
348
- function t(key, ...args) {
349
- const value = lookup(key);
350
- if (typeof value === "function") {
351
- return value(...args);
352
- }
353
- if (typeof value === "string") return value;
354
- return key;
355
- }
356
-
357
- // src/lib/next-step.ts
358
- import chalk from "chalk";
359
- function printNextStep(step) {
360
- console.log("");
361
- console.log(chalk.cyan.bold("\u2501\u2501\u2501 \uB2E4\uC74C\uC5D0 \uC774\uAC83\uB9CC \uD558\uC138\uC694 \u2501\u2501\u2501"));
362
- console.log("");
363
- console.log(` ${step.message}`);
364
- if (step.command) {
365
- console.log("");
366
- console.log(chalk.white.bgGray(" \uD130\uBBF8\uB110\uC5D0 \uBCF5\uBD99 "));
367
- console.log(chalk.green(` ${step.command}`));
368
- }
369
- if (step.cursorHint) {
370
- console.log("");
371
- console.log(chalk.white.bgBlue(" Cursor\uC5D0\uAC8C \uB9D0\uD558\uAE30 "));
372
- console.log(chalk.blue(` "${step.cursorHint}"`));
373
- }
374
- if (step.alternative) {
375
- console.log("");
376
- console.log(chalk.dim(` \uB610\uB294: ${step.alternative}`));
377
- }
378
- console.log("");
379
- console.log(chalk.cyan.bold("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
380
- console.log("");
381
- }
382
-
383
- // src/commands/env.ts
384
- function parseEnvKeys(content) {
385
- return content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).map((line) => line.split("=")[0].trim()).filter(Boolean);
386
- }
387
- function ensureGitignore() {
388
- const gitignorePath = ".gitignore";
389
- if (existsSync(gitignorePath)) {
390
- const content = readFileSync(gitignorePath, "utf-8");
391
- if (!content.split("\n").some((l) => l.trim() === ".env")) {
392
- appendFileSync(gitignorePath, "\n.env\n");
393
- console.log(chalk2.green("\n\u{1F512} .gitignore\uC5D0 .env \uCD94\uAC00\uB428"));
394
- }
395
- } else {
396
- writeFileSync(gitignorePath, ".env\nnode_modules/\ndist/\n");
397
- console.log(chalk2.green("\n\u{1F512} .gitignore \uC0DD\uC131 (.env \uD3EC\uD568)"));
398
- }
399
- }
400
- async function env() {
401
- console.log(chalk2.bold("\n\u{1F510} " + t("env.title")));
402
- console.log(chalk2.gray("\u2500".repeat(40)));
403
- if (!existsSync(".env")) {
404
- console.log(chalk2.yellow("\n\u26A0\uFE0F .env \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
405
- console.log(chalk2.gray(" .env \uD30C\uC77C\uC744 \uBA3C\uC800 \uB9CC\uB4E4\uC5B4\uC8FC\uC138\uC694."));
406
- return;
407
- }
408
- const envContent = readFileSync(".env", "utf-8");
409
- const keys = parseEnvKeys(envContent);
410
- if (keys.length === 0) {
411
- console.log(chalk2.yellow("\n\u{1F4ED} .env\uC5D0 \uD658\uACBD\uBCC0\uC218\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4."));
412
- return;
413
- }
414
- const exampleContent = keys.map((k) => `${k}=`).join("\n") + "\n";
415
- writeFileSync(".env.example", exampleContent, "utf-8");
416
- console.log(chalk2.green(`
417
- \u2705 .env.example \uC0DD\uC131 (${keys.length}\uAC1C \uD0A4)`));
418
- keys.forEach((k) => console.log(chalk2.gray(` ${k}`)));
419
- ensureGitignore();
420
- printNextStep({
421
- message: ".env.example \uC0DD\uC131 \uC644\uB8CC!",
422
- command: "vhk env-check",
423
- cursorHint: "\uD658\uACBD\uBCC0\uC218 \uC810\uAC80\uD574\uC918"
424
- });
425
- }
426
- async function envCheck() {
427
- console.log(chalk2.bold("\n\u{1F50D} " + t("env.checkTitle")));
428
- console.log(chalk2.gray("\u2500".repeat(40)));
429
- if (!existsSync(".env.example")) {
430
- console.log(chalk2.yellow("\n\u26A0\uFE0F .env.example\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 vhk env\uB97C \uC2E4\uD589\uD558\uC138\uC694."));
431
- return;
432
- }
433
- const requiredKeys = parseEnvKeys(readFileSync(".env.example", "utf-8"));
434
- const currentKeys = existsSync(".env") ? parseEnvKeys(readFileSync(".env", "utf-8")) : [];
435
- const missing = requiredKeys.filter((k) => !currentKeys.includes(k));
436
- const extra = currentKeys.filter((k) => !requiredKeys.includes(k));
437
- console.log(chalk2.cyan(`
438
- \u{1F4CB} \uD544\uC218 \uD658\uACBD\uBCC0\uC218: ${requiredKeys.length}\uAC1C`));
439
- if (missing.length === 0) {
440
- console.log(chalk2.green("\n\u2705 \uBAA8\uB4E0 \uD544\uC218 \uD658\uACBD\uBCC0\uC218\uAC00 \uC124\uC815\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4!"));
441
- } else {
442
- console.log(chalk2.red(`
443
- \u274C \uB204\uB77D\uB41C \uD658\uACBD\uBCC0\uC218 (${missing.length}\uAC1C):`));
444
- missing.forEach((k) => console.log(chalk2.red(` \u2022 ${k}`)));
445
- }
446
- if (extra.length > 0) {
447
- console.log(chalk2.yellow(`
448
- \u{1F4A1} .env.example\uC5D0 \uC5C6\uB294 \uCD94\uAC00 \uBCC0\uC218 (${extra.length}\uAC1C):`));
449
- extra.forEach((k) => console.log(chalk2.yellow(` \u2022 ${k}`)));
450
- }
451
- ensureGitignore();
452
- }
453
-
454
- // src/mcp/server.ts
455
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
456
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
457
- import { z } from "zod";
458
- import { existsSync as existsSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2, appendFileSync as appendFileSync2 } from "fs";
459
-
460
- // src/lib/exec.ts
461
- import { execFileSync } from "child_process";
462
- var SHIM_BINARIES = /* @__PURE__ */ new Set(["pnpm", "npm", "npx", "yarn"]);
463
- function platformCmd(cmd) {
464
- if (process.platform === "win32" && SHIM_BINARIES.has(cmd)) {
465
- return `${cmd}.cmd`;
466
- }
467
- return cmd;
468
- }
469
- function resolveCmd(cmd, args) {
470
- if (process.platform === "win32" && SHIM_BINARIES.has(cmd)) {
471
- return { bin: "cmd.exe", argv: ["/d", "/s", "/c", `${cmd}.cmd`, ...args] };
472
- }
473
- return { bin: platformCmd(cmd), argv: args };
474
- }
475
- function safeExecFile(cmd, args) {
476
- const { bin, argv } = resolveCmd(cmd, args);
477
- try {
478
- const out = execFileSync(bin, argv, {
479
- encoding: "utf-8",
480
- stdio: ["pipe", "pipe", "pipe"]
481
- }).toString();
482
- return { ok: true, out: out.trim() };
483
- } catch (err) {
484
- const e = err;
485
- const stdout = e.stdout ? e.stdout.toString() : "";
486
- const msg = e.message ?? String(err);
487
- return { ok: false, err: msg, out: stdout.trim() };
488
- }
489
- }
490
- function safeExecFileStream(cmd, args) {
491
- const { bin, argv } = resolveCmd(cmd, args);
492
- try {
493
- execFileSync(bin, argv, {
494
- encoding: "utf-8",
495
- stdio: "inherit"
496
- });
497
- return { ok: true };
498
- } catch (err) {
499
- const msg = err instanceof Error ? err.message : String(err);
500
- return { ok: false, err: msg };
501
- }
502
- }
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
-
514
- // src/mcp/server.ts
515
- var SERVER_VERSION = "0.7.1";
516
- function isGitRepo() {
517
- return safeExecFile("git", ["rev-parse", "--is-inside-work-tree"]).ok;
518
- }
519
- function createVhkMcpServer() {
520
- const server = new McpServer({
521
- name: "vhk",
522
- version: SERVER_VERSION
523
- });
524
- server.registerTool(
525
- "save",
526
- {
527
- description: "\uBCC0\uACBD\uC0AC\uD56D \uC800\uC7A5 (git add \u2192 commit \u2192 push)",
528
- inputSchema: {
529
- message: z.string().optional().describe("\uCEE4\uBC0B \uBA54\uC2DC\uC9C0 (\uBE44\uC6B0\uBA74 \uC790\uB3D9 \uC0DD\uC131)")
530
- }
531
- },
532
- async ({ message }) => {
533
- if (!isGitRepo()) {
534
- return { content: [{ type: "text", text: "\u274C git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2D9\uB2C8\uB2E4." }] };
535
- }
536
- const status = safeExecFile("git", ["status", "--porcelain"]);
537
- if (!status.ok) {
538
- return { content: [{ type: "text", text: `\u274C git status \uC2E4\uD328: ${status.err}` }] };
539
- }
540
- if (!status.out) {
541
- return { content: [{ type: "text", text: "\u{1F4ED} \uC800\uC7A5\uD560 \uBCC0\uACBD\uC0AC\uD56D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4." }] };
542
- }
543
- const files = status.out.split("\n");
544
- const now = /* @__PURE__ */ new Date();
545
- const ts = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")} ${String(now.getHours()).padStart(2, "0")}:${String(now.getMinutes()).padStart(2, "0")}`;
546
- const commitMsg = message?.trim() || `\u2728 vhk save: ${ts}`;
547
- const add = safeExecFile("git", ["add", "."]);
548
- if (!add.ok) {
549
- return { content: [{ type: "text", text: `\u274C git add \uC2E4\uD328: ${add.err}` }] };
550
- }
551
- const commit = safeExecFile("git", ["commit", "-m", commitMsg]);
552
- if (!commit.ok) {
553
- return { content: [{ type: "text", text: `\u274C commit \uC2E4\uD328: ${commit.err}` }] };
554
- }
555
- const push = safeExecFile("git", ["push"]);
556
- const pushResult = push.ok ? "+ \uC6D0\uACA9 \uC5C5\uB85C\uB4DC \uC644\uB8CC" : "(\uC6D0\uACA9 \uC800\uC7A5\uC18C \uC5C6\uAC70\uB098 push \uC2E4\uD328 \u2192 \uC2A4\uD0B5)";
557
- return {
558
- content: [
559
- {
560
- type: "text",
561
- text: `\u2705 ${files.length}\uAC1C \uD30C\uC77C \uC800\uC7A5 \uC644\uB8CC! ${pushResult}
562
- \uCEE4\uBC0B: ${commitMsg}`
563
- }
564
- ]
565
- };
566
- }
567
- );
568
- server.registerTool("undo", { description: "\uCD5C\uADFC \uCEE4\uBC0B \uB418\uB3CC\uB9AC\uAE30 (soft reset, \uBCC0\uACBD\uC0AC\uD56D\uC740 \uC720\uC9C0)" }, async () => {
569
- if (!isGitRepo()) {
570
- return { content: [{ type: "text", text: "\u274C git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2D9\uB2C8\uB2E4." }] };
571
- }
572
- const last = safeExecFile("git", ["log", "--oneline", "-1"]);
573
- if (!last.ok || !last.out) {
574
- return { content: [{ type: "text", text: "\u{1F4ED} \uB418\uB3CC\uB9B4 \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4." }] };
575
- }
576
- const reset = safeExecFile("git", ["reset", "--soft", "HEAD~1"]);
577
- if (!reset.ok) {
578
- return { content: [{ type: "text", text: `\u274C reset \uC2E4\uD328: ${reset.err}` }] };
579
- }
580
- return {
581
- content: [
582
- {
583
- type: "text",
584
- text: `\u2705 \uB418\uB3CC\uB9AC\uAE30 \uC644\uB8CC!
585
- \uCDE8\uC18C\uB41C \uCEE4\uBC0B: ${last.out}
586
- \u{1F4A1} \uBCC0\uACBD\uC0AC\uD56D\uC740 \uC2A4\uD14C\uC774\uC9D5 \uC601\uC5ED\uC5D0 \uB0A8\uC544\uC788\uC2B5\uB2C8\uB2E4.`
587
- }
588
- ]
589
- };
590
- });
591
- 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 () => {
592
- const lines = [];
593
- if (existsSync2("package.json")) {
594
- try {
595
- const pkg = readJsonFile("package.json");
596
- lines.push(`\u{1F4E6} \uD504\uB85C\uC81D\uD2B8: ${pkg.name ?? "(\uC774\uB984 \uC5C6\uC74C)"} v${pkg.version ?? "?"}`);
597
- } catch {
598
- }
599
- }
600
- if (!isGitRepo()) {
601
- lines.push("\u26A0\uFE0F git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2D9\uB2C8\uB2E4");
602
- return { content: [{ type: "text", text: lines.join("\n") }] };
603
- }
604
- const branch = safeExecFile("git", ["branch", "--show-current"]);
605
- if (branch.ok) lines.push(`\u{1F33F} \uBE0C\uB79C\uCE58: ${branch.out || "(detached)"}`);
606
- const status = safeExecFile("git", ["status", "--porcelain"]);
607
- if (status.ok) {
608
- if (!status.out) {
609
- lines.push("\u{1F4DD} \uBCC0\uACBD\uC0AC\uD56D: \u2705 \uAE68\uB057\uD568");
610
- } else {
611
- const fileLines = status.out.split("\n");
612
- const staged = fileLines.filter((l) => l[0] !== " " && l[0] !== "?").length;
613
- const unstaged = fileLines.filter((l) => l[1] === "M" || l[1] === "D").length;
614
- const untracked = fileLines.filter((l) => l.startsWith("??")).length;
615
- const parts = [];
616
- if (staged) parts.push(`\uC2A4\uD14C\uC774\uC9D5 ${staged}\uAC1C`);
617
- if (unstaged) parts.push(`\uC218\uC815 ${unstaged}\uAC1C`);
618
- if (untracked) parts.push(`\uC0C8\uD30C\uC77C ${untracked}\uAC1C`);
619
- lines.push(`\u{1F4DD} \uBCC0\uACBD\uC0AC\uD56D: ${parts.join(", ")}`);
620
- }
621
- }
622
- const log = safeExecFile("git", ["log", "--oneline", "-3"]);
623
- if (log.ok && log.out) {
624
- lines.push("\u{1F4DC} \uCD5C\uADFC \uCEE4\uBC0B:");
625
- log.out.split("\n").forEach((l) => lines.push(` ${l}`));
626
- }
627
- return { content: [{ type: "text", text: lines.join("\n") }] };
628
- });
629
- server.registerTool("diff", { description: "\uBCC0\uACBD\uC0AC\uD56D \uD655\uC778 (staged/unstaged/\uC0C8\uD30C\uC77C + \uCD1D \uBCC0\uACBD \uC694\uC57D)" }, async () => {
630
- if (!isGitRepo()) {
631
- return { content: [{ type: "text", text: "\u274C git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2D9\uB2C8\uB2E4." }] };
632
- }
633
- const unstaged = safeExecFile("git", ["diff", "--stat"]);
634
- const staged = safeExecFile("git", ["diff", "--cached", "--stat"]);
635
- const untracked = safeExecFile("git", ["ls-files", "--others", "--exclude-standard"]);
636
- const unstagedOut = unstaged.ok ? unstaged.out : "";
637
- const stagedOut = staged.ok ? staged.out : "";
638
- const untrackedOut = untracked.ok ? untracked.out : "";
639
- if (!unstagedOut && !stagedOut && !untrackedOut) {
640
- return { content: [{ type: "text", text: "\u2705 \uBCC0\uACBD\uC0AC\uD56D \uC5C6\uC74C! \uAE68\uB057\uD569\uB2C8\uB2E4." }] };
641
- }
642
- const lines = [];
643
- if (stagedOut) {
644
- lines.push("\u{1F4E6} \uCEE4\uBC0B \uB300\uAE30 (staged):");
645
- lines.push(stagedOut);
646
- }
647
- if (unstagedOut) {
648
- lines.push("\u270F\uFE0F \uC218\uC815\uB428 (unstaged):");
649
- lines.push(unstagedOut);
650
- }
651
- if (untrackedOut) {
652
- const files = untrackedOut.split("\n");
653
- lines.push(`\u2795 \uC0C8 \uD30C\uC77C (${files.length}\uAC1C):`);
654
- files.forEach((f) => lines.push(` + ${f}`));
655
- }
656
- const numstat = safeExecFile("git", ["diff", "--numstat", "HEAD"]);
657
- if (numstat.ok && numstat.out) {
658
- let totalAdd = 0;
659
- let totalDel = 0;
660
- let fileCount = 0;
661
- numstat.out.split("\n").forEach((line) => {
662
- const [add, del] = line.split(" ");
663
- totalAdd += parseInt(add, 10) || 0;
664
- totalDel += parseInt(del, 10) || 0;
665
- fileCount += 1;
666
- });
667
- lines.push(`
668
- \u{1F4CA} \uCD1D \uBCC0\uACBD: ${fileCount}\uAC1C \uD30C\uC77C, +${totalAdd}\uC904 -${totalDel}\uC904`);
669
- }
670
- return { content: [{ type: "text", text: lines.join("\n") }] };
671
- });
672
- server.registerTool("ship", { description: "\uBC30\uD3EC \uCCB4\uD06C\uB9AC\uC2A4\uD2B8 \uC2E4\uD589 (\uBE4C\uB4DC + \uD14C\uC2A4\uD2B8 + \uBC84\uC804 + git \uC0C1\uD0DC)" }, async () => {
673
- const checks = [];
674
- const build = safeExecFile("pnpm", ["build"]);
675
- checks.push(build.ok ? "\u2705 \uBE4C\uB4DC \uC131\uACF5" : "\u274C \uBE4C\uB4DC \uC2E4\uD328");
676
- const test = safeExecFile("pnpm", ["test", "--run"]);
677
- checks.push(test.ok ? "\u2705 \uD14C\uC2A4\uD2B8 \uD1B5\uACFC" : "\u274C \uD14C\uC2A4\uD2B8 \uC2E4\uD328");
678
- if (existsSync2("package.json")) {
679
- try {
680
- const pkg = readJsonFile("package.json");
681
- checks.push(`\u{1F4E6} \uBC84\uC804: ${pkg.version}`);
682
- } catch {
683
- }
684
- }
685
- if (isGitRepo()) {
686
- const status = safeExecFile("git", ["status", "--porcelain"]);
687
- if (status.ok) {
688
- if (status.out) {
689
- checks.push(`\u26A0\uFE0F \uCEE4\uBC0B\uB418\uC9C0 \uC54A\uC740 \uBCC0\uACBD\uC0AC\uD56D ${status.out.split("\n").length}\uAC1C`);
690
- } else {
691
- checks.push("\u2705 \uC6CC\uD0B9 \uB514\uB809\uD1A0\uB9AC \uAE68\uB057\uD568");
692
- }
693
- }
694
- }
695
- return { content: [{ type: "text", text: "\u{1F680} \uBC30\uD3EC \uCCB4\uD06C\uB9AC\uC2A4\uD2B8\n" + checks.join("\n") }] };
696
- });
697
- server.registerTool("doctor", { description: "\uAC1C\uBC1C \uD658\uACBD \uC810\uAC80 (Node/Git/npm/pnpm/TypeScript)" }, async () => {
698
- const checks = [];
699
- const node = safeExecFile("node", ["--version"]);
700
- checks.push(node.ok ? `\u2705 Node.js: ${node.out}` : "\u274C Node.js: \uC124\uCE58 \uC548 \uB428");
701
- const git = safeExecFile("git", ["--version"]);
702
- checks.push(git.ok ? `\u2705 Git: ${git.out}` : "\u274C Git: \uC124\uCE58 \uC548 \uB428");
703
- const pnpm = safeExecFile("pnpm", ["--version"]);
704
- checks.push(pnpm.ok ? `\u2705 pnpm: v${pnpm.out}` : "\u26A0\uFE0F pnpm: \uC124\uCE58 \uC548 \uB428");
705
- const npm = safeExecFile("npm", ["--version"]);
706
- checks.push(npm.ok ? `\u2705 npm: v${npm.out}` : "\u274C npm: \uC124\uCE58 \uC548 \uB428");
707
- const tsc = safeExecFile("npx", ["tsc", "--version"]);
708
- checks.push(tsc.ok ? `\u2705 TypeScript: ${tsc.out}` : "\u26A0\uFE0F TypeScript: \uD504\uB85C\uC81D\uD2B8\uC5D0 \uC5C6\uC74C");
709
- return { content: [{ type: "text", text: "\u{1FA7A} \uD658\uACBD \uC810\uAC80 \uACB0\uACFC\n" + checks.join("\n") }] };
710
- });
711
- server.registerTool("check", { description: "\uD504\uB85C\uC81D\uD2B8 \uAD6C\uC870 \uC810\uAC80 (\uD544\uC218 \uD30C\uC77C + VHK \uD558\uB124\uC2A4 \uD30C\uC77C)" }, async () => {
712
- const required = ["package.json", "tsconfig.json", "README.md", ".gitignore"];
713
- const recommended = ["CLAUDE.md", ".cursorrules", "docs/PRD.md", "docs/ARCHITECTURE.md"];
714
- const lines = ["\u{1F50D} \uD504\uB85C\uC81D\uD2B8 \uC810\uAC80", "", "\uD544\uC218:"];
715
- required.forEach((f) => {
716
- lines.push(` ${existsSync2(f) ? "\u2705" : "\u274C"} ${f}`);
717
- });
718
- lines.push("", "\uAD8C\uC7A5 (VHK \uD558\uB124\uC2A4):");
719
- recommended.forEach((f) => {
720
- lines.push(` ${existsSync2(f) ? "\u2705" : "\u26A0\uFE0F"} ${f}`);
721
- });
722
- return { content: [{ type: "text", text: lines.join("\n") }] };
723
- });
724
- server.registerTool(
725
- "recap",
726
- {
727
- description: "\uCD5C\uADFC \uC791\uC5C5 \uC694\uC57D (\uCEE4\uBC0B \uD788\uC2A4\uD1A0\uB9AC \uAE30\uBC18, \uB0A0\uC9DC \uD3EC\uD568)",
728
- inputSchema: {
729
- count: z.number().optional().describe("\uD45C\uC2DC\uD560 \uCEE4\uBC0B \uC218 (\uAE30\uBCF8: 10)")
730
- }
731
- },
732
- async ({ count }) => {
733
- if (!isGitRepo()) {
734
- return { content: [{ type: "text", text: "\u274C git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2D9\uB2C8\uB2E4." }] };
735
- }
736
- const n = count && count > 0 ? Math.floor(count) : 10;
737
- const log = safeExecFile("git", ["log", "--format=%h %ad %s", "--date=short", `-${n}`]);
738
- if (!log.ok) {
739
- return { content: [{ type: "text", text: `\u274C git log \uC2E4\uD328: ${log.err}` }] };
740
- }
741
- if (!log.out) {
742
- return { content: [{ type: "text", text: "\u{1F4ED} \uCEE4\uBC0B \uD788\uC2A4\uD1A0\uB9AC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4." }] };
743
- }
744
- return { content: [{ type: "text", text: `\u{1F4CB} \uCD5C\uADFC \uC791\uC5C5 (${n}\uAC1C):
745
- ${log.out}` }] };
746
- }
747
- );
748
- server.registerTool(
749
- "env",
750
- {
751
- description: ".env \u2192 .env.example \uB3D9\uAE30\uD654 + .gitignore\uC5D0 .env \uC790\uB3D9 \uCD94\uAC00"
752
- },
753
- async () => {
754
- if (!existsSync2(".env")) {
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." }] };
756
- }
757
- const keys = parseEnvKeys(readFileSync3(".env", "utf-8"));
758
- if (keys.length === 0) {
759
- return { content: [{ type: "text", text: "\u{1F4ED} .env\uC5D0 \uD658\uACBD\uBCC0\uC218\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4." }] };
760
- }
761
- const exampleContent = keys.map((k) => `${k}=`).join("\n") + "\n";
762
- writeFileSync2(".env.example", exampleContent, "utf-8");
763
- const gitignoreLines = [];
764
- if (existsSync2(".gitignore")) {
765
- const content = readFileSync3(".gitignore", "utf-8");
766
- if (!content.split("\n").some((l) => l.trim() === ".env")) {
767
- appendFileSync2(".gitignore", "\n.env\n");
768
- gitignoreLines.push("\u{1F512} .gitignore\uC5D0 .env \uCD94\uAC00\uB428");
769
- }
770
- } else {
771
- writeFileSync2(".gitignore", ".env\nnode_modules/\ndist/\n");
772
- gitignoreLines.push("\u{1F512} .gitignore \uC0DD\uC131 (.env \uD3EC\uD568)");
773
- }
774
- const lines = [`\u2705 .env.example \uC0DD\uC131 (${keys.length}\uAC1C \uD0A4)`, ...keys.map((k) => ` ${k}`)];
775
- if (gitignoreLines.length) lines.push("", ...gitignoreLines);
776
- return { content: [{ type: "text", text: lines.join("\n") }] };
777
- }
778
- );
779
- server.registerTool(
780
- "env-check",
781
- {
782
- description: "\uD544\uC218 \uD658\uACBD\uBCC0\uC218 \uB204\uB77D \uAC80\uC0AC (.env.example \uAE30\uC900)"
783
- },
784
- async () => {
785
- if (!existsSync2(".env.example")) {
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." }] };
787
- }
788
- const requiredKeys = parseEnvKeys(readFileSync3(".env.example", "utf-8"));
789
- const currentKeys = existsSync2(".env") ? parseEnvKeys(readFileSync3(".env", "utf-8")) : [];
790
- const missing = requiredKeys.filter((k) => !currentKeys.includes(k));
791
- const extra = currentKeys.filter((k) => !requiredKeys.includes(k));
792
- const lines = [`\u{1F4CB} \uD544\uC218 \uD658\uACBD\uBCC0\uC218: ${requiredKeys.length}\uAC1C`];
793
- if (missing.length === 0) {
794
- lines.push("\u2705 \uBAA8\uB4E0 \uD544\uC218 \uD658\uACBD\uBCC0\uC218\uAC00 \uC124\uC815\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4!");
795
- } else {
796
- lines.push(`\u274C \uB204\uB77D\uB41C \uD658\uACBD\uBCC0\uC218 (${missing.length}\uAC1C):`);
797
- missing.forEach((k) => lines.push(` \u2022 ${k}`));
798
- }
799
- if (extra.length > 0) {
800
- lines.push(`\u{1F4A1} .env.example\uC5D0 \uC5C6\uB294 \uCD94\uAC00 \uBCC0\uC218 (${extra.length}\uAC1C):`);
801
- extra.forEach((k) => lines.push(` \u2022 ${k}`));
802
- }
803
- return { content: [{ type: "text", text: lines.join("\n") }] };
804
- }
805
- );
806
- return server;
807
- }
808
- async function startMcpServer() {
809
- const server = createVhkMcpServer();
810
- const transport = new StdioServerTransport();
811
- await server.connect(transport);
812
- }
813
-
814
- export {
815
- __commonJS,
816
- __toESM,
817
- ko,
818
- t,
819
- printNextStep,
820
- readJsonFile,
821
- safeExecFile,
822
- safeExecFileStream,
823
- env,
824
- envCheck,
825
- startMcpServer
826
- };