@byh3071/vhk 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -2
- package/dist/chunk-NQ4V3VN4.js +781 -0
- package/dist/index.js +652 -650
- package/dist/mcp/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-NJDRNI3S.js +0 -316
|
@@ -0,0 +1,781 @@
|
|
|
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
|
+
mcp: {
|
|
287
|
+
initTitle: "Cursor MCP \uC5F0\uB3D9 \uC124\uC815",
|
|
288
|
+
serverStarted: "VHK MCP \uC11C\uBC84 \uC2DC\uC791\uB428"
|
|
289
|
+
},
|
|
290
|
+
deploy: {
|
|
291
|
+
title: "\uBC30\uD3EC\uD558\uAE30",
|
|
292
|
+
selectPlatform: "\uC5B4\uB5A4 \uD50C\uB7AB\uD3FC\uC5D0 \uBC30\uD3EC\uD560\uAE4C\uC694?",
|
|
293
|
+
deploying: "\uBC30\uD3EC \uC911...",
|
|
294
|
+
success: "\uBC30\uD3EC \uC131\uACF5!",
|
|
295
|
+
failed: "\uBC30\uD3EC \uC2E4\uD328"
|
|
296
|
+
},
|
|
297
|
+
env: {
|
|
298
|
+
title: "\uD658\uACBD\uBCC0\uC218 \uAD00\uB9AC",
|
|
299
|
+
checkTitle: "\uD658\uACBD\uBCC0\uC218 \uC810\uAC80"
|
|
300
|
+
},
|
|
301
|
+
publish: {
|
|
302
|
+
title: "npm \uBC30\uD3EC",
|
|
303
|
+
selectBump: "\uBC84\uC804\uC744 \uC5B4\uB5BB\uAC8C \uC62C\uB9B4\uAE4C\uC694?",
|
|
304
|
+
building: "\uBE4C\uB4DC \uC911...",
|
|
305
|
+
buildSuccess: "\uBE4C\uB4DC \uC131\uACF5",
|
|
306
|
+
buildFailed: "\uBE4C\uB4DC \uC2E4\uD328",
|
|
307
|
+
testing: "\uD14C\uC2A4\uD2B8 \uC911...",
|
|
308
|
+
testSuccess: "\uD14C\uC2A4\uD2B8 \uD1B5\uACFC",
|
|
309
|
+
testFailed: "\uD14C\uC2A4\uD2B8 \uC2E4\uD328",
|
|
310
|
+
publishing: "npm \uBC30\uD3EC \uC911...",
|
|
311
|
+
publishSuccess: "npm \uBC30\uD3EC \uC131\uACF5!",
|
|
312
|
+
publishFailed: "npm \uBC30\uD3EC \uC2E4\uD328"
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
function lookup(path) {
|
|
316
|
+
const parts = path.split(".");
|
|
317
|
+
let cur = ko;
|
|
318
|
+
for (const part of parts) {
|
|
319
|
+
if (cur === null || typeof cur !== "object") return void 0;
|
|
320
|
+
cur = cur[part];
|
|
321
|
+
}
|
|
322
|
+
return cur;
|
|
323
|
+
}
|
|
324
|
+
function t(key, ...args) {
|
|
325
|
+
const value = lookup(key);
|
|
326
|
+
if (typeof value === "function") {
|
|
327
|
+
return value(...args);
|
|
328
|
+
}
|
|
329
|
+
if (typeof value === "string") return value;
|
|
330
|
+
return key;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// src/lib/next-step.ts
|
|
334
|
+
import chalk from "chalk";
|
|
335
|
+
function printNextStep(step) {
|
|
336
|
+
console.log("");
|
|
337
|
+
console.log(chalk.cyan.bold("\u2501\u2501\u2501 \uB2E4\uC74C\uC5D0 \uC774\uAC83\uB9CC \uD558\uC138\uC694 \u2501\u2501\u2501"));
|
|
338
|
+
console.log("");
|
|
339
|
+
console.log(` ${step.message}`);
|
|
340
|
+
if (step.command) {
|
|
341
|
+
console.log("");
|
|
342
|
+
console.log(chalk.white.bgGray(" \uD130\uBBF8\uB110\uC5D0 \uBCF5\uBD99 "));
|
|
343
|
+
console.log(chalk.green(` ${step.command}`));
|
|
344
|
+
}
|
|
345
|
+
if (step.cursorHint) {
|
|
346
|
+
console.log("");
|
|
347
|
+
console.log(chalk.white.bgBlue(" Cursor\uC5D0\uAC8C \uB9D0\uD558\uAE30 "));
|
|
348
|
+
console.log(chalk.blue(` "${step.cursorHint}"`));
|
|
349
|
+
}
|
|
350
|
+
if (step.alternative) {
|
|
351
|
+
console.log("");
|
|
352
|
+
console.log(chalk.dim(` \uB610\uB294: ${step.alternative}`));
|
|
353
|
+
}
|
|
354
|
+
console.log("");
|
|
355
|
+
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"));
|
|
356
|
+
console.log("");
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// src/commands/env.ts
|
|
360
|
+
function parseEnvKeys(content) {
|
|
361
|
+
return content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).map((line) => line.split("=")[0].trim()).filter(Boolean);
|
|
362
|
+
}
|
|
363
|
+
function ensureGitignore() {
|
|
364
|
+
const gitignorePath = ".gitignore";
|
|
365
|
+
if (existsSync(gitignorePath)) {
|
|
366
|
+
const content = readFileSync(gitignorePath, "utf-8");
|
|
367
|
+
if (!content.split("\n").some((l) => l.trim() === ".env")) {
|
|
368
|
+
appendFileSync(gitignorePath, "\n.env\n");
|
|
369
|
+
console.log(chalk2.green("\n\u{1F512} .gitignore\uC5D0 .env \uCD94\uAC00\uB428"));
|
|
370
|
+
}
|
|
371
|
+
} else {
|
|
372
|
+
writeFileSync(gitignorePath, ".env\nnode_modules/\ndist/\n");
|
|
373
|
+
console.log(chalk2.green("\n\u{1F512} .gitignore \uC0DD\uC131 (.env \uD3EC\uD568)"));
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
async function env() {
|
|
377
|
+
console.log(chalk2.bold("\n\u{1F510} " + t("env.title")));
|
|
378
|
+
console.log(chalk2.gray("\u2500".repeat(40)));
|
|
379
|
+
if (!existsSync(".env")) {
|
|
380
|
+
console.log(chalk2.yellow("\n\u26A0\uFE0F .env \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
|
|
381
|
+
console.log(chalk2.gray(" .env \uD30C\uC77C\uC744 \uBA3C\uC800 \uB9CC\uB4E4\uC5B4\uC8FC\uC138\uC694."));
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
const envContent = readFileSync(".env", "utf-8");
|
|
385
|
+
const keys = parseEnvKeys(envContent);
|
|
386
|
+
if (keys.length === 0) {
|
|
387
|
+
console.log(chalk2.yellow("\n\u{1F4ED} .env\uC5D0 \uD658\uACBD\uBCC0\uC218\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4."));
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
const exampleContent = keys.map((k) => `${k}=`).join("\n") + "\n";
|
|
391
|
+
writeFileSync(".env.example", exampleContent, "utf-8");
|
|
392
|
+
console.log(chalk2.green(`
|
|
393
|
+
\u2705 .env.example \uC0DD\uC131 (${keys.length}\uAC1C \uD0A4)`));
|
|
394
|
+
keys.forEach((k) => console.log(chalk2.gray(` ${k}`)));
|
|
395
|
+
ensureGitignore();
|
|
396
|
+
printNextStep({
|
|
397
|
+
message: ".env.example \uC0DD\uC131 \uC644\uB8CC!",
|
|
398
|
+
command: "vhk env-check",
|
|
399
|
+
cursorHint: "\uD658\uACBD\uBCC0\uC218 \uC810\uAC80\uD574\uC918"
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
async function envCheck() {
|
|
403
|
+
console.log(chalk2.bold("\n\u{1F50D} " + t("env.checkTitle")));
|
|
404
|
+
console.log(chalk2.gray("\u2500".repeat(40)));
|
|
405
|
+
if (!existsSync(".env.example")) {
|
|
406
|
+
console.log(chalk2.yellow("\n\u26A0\uFE0F .env.example\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 vhk env\uB97C \uC2E4\uD589\uD558\uC138\uC694."));
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
const requiredKeys = parseEnvKeys(readFileSync(".env.example", "utf-8"));
|
|
410
|
+
const currentKeys = existsSync(".env") ? parseEnvKeys(readFileSync(".env", "utf-8")) : [];
|
|
411
|
+
const missing = requiredKeys.filter((k) => !currentKeys.includes(k));
|
|
412
|
+
const extra = currentKeys.filter((k) => !requiredKeys.includes(k));
|
|
413
|
+
console.log(chalk2.cyan(`
|
|
414
|
+
\u{1F4CB} \uD544\uC218 \uD658\uACBD\uBCC0\uC218: ${requiredKeys.length}\uAC1C`));
|
|
415
|
+
if (missing.length === 0) {
|
|
416
|
+
console.log(chalk2.green("\n\u2705 \uBAA8\uB4E0 \uD544\uC218 \uD658\uACBD\uBCC0\uC218\uAC00 \uC124\uC815\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4!"));
|
|
417
|
+
} else {
|
|
418
|
+
console.log(chalk2.red(`
|
|
419
|
+
\u274C \uB204\uB77D\uB41C \uD658\uACBD\uBCC0\uC218 (${missing.length}\uAC1C):`));
|
|
420
|
+
missing.forEach((k) => console.log(chalk2.red(` \u2022 ${k}`)));
|
|
421
|
+
}
|
|
422
|
+
if (extra.length > 0) {
|
|
423
|
+
console.log(chalk2.yellow(`
|
|
424
|
+
\u{1F4A1} .env.example\uC5D0 \uC5C6\uB294 \uCD94\uAC00 \uBCC0\uC218 (${extra.length}\uAC1C):`));
|
|
425
|
+
extra.forEach((k) => console.log(chalk2.yellow(` \u2022 ${k}`)));
|
|
426
|
+
}
|
|
427
|
+
ensureGitignore();
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// src/mcp/server.ts
|
|
431
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
432
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
433
|
+
import { z } from "zod";
|
|
434
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, appendFileSync as appendFileSync2 } from "fs";
|
|
435
|
+
|
|
436
|
+
// src/lib/exec.ts
|
|
437
|
+
import { execFileSync } from "child_process";
|
|
438
|
+
var SHIM_BINARIES = /* @__PURE__ */ new Set(["pnpm", "npm", "npx", "yarn"]);
|
|
439
|
+
function platformCmd(cmd) {
|
|
440
|
+
if (process.platform === "win32" && SHIM_BINARIES.has(cmd)) {
|
|
441
|
+
return `${cmd}.cmd`;
|
|
442
|
+
}
|
|
443
|
+
return cmd;
|
|
444
|
+
}
|
|
445
|
+
function safeExecFile(cmd, args) {
|
|
446
|
+
try {
|
|
447
|
+
const out = execFileSync(platformCmd(cmd), args, {
|
|
448
|
+
encoding: "utf-8",
|
|
449
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
450
|
+
}).toString();
|
|
451
|
+
return { ok: true, out: out.trim() };
|
|
452
|
+
} catch (err) {
|
|
453
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
454
|
+
return { ok: false, err: msg };
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
function safeExecFileStream(cmd, args) {
|
|
458
|
+
try {
|
|
459
|
+
execFileSync(platformCmd(cmd), args, {
|
|
460
|
+
encoding: "utf-8",
|
|
461
|
+
stdio: "inherit"
|
|
462
|
+
});
|
|
463
|
+
return { ok: true };
|
|
464
|
+
} catch (err) {
|
|
465
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
466
|
+
return { ok: false, err: msg };
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// src/mcp/server.ts
|
|
471
|
+
var SERVER_VERSION = "0.7.1";
|
|
472
|
+
function isGitRepo() {
|
|
473
|
+
return safeExecFile("git", ["rev-parse", "--is-inside-work-tree"]).ok;
|
|
474
|
+
}
|
|
475
|
+
function createVhkMcpServer() {
|
|
476
|
+
const server = new McpServer({
|
|
477
|
+
name: "vhk",
|
|
478
|
+
version: SERVER_VERSION
|
|
479
|
+
});
|
|
480
|
+
server.registerTool(
|
|
481
|
+
"save",
|
|
482
|
+
{
|
|
483
|
+
description: "\uBCC0\uACBD\uC0AC\uD56D \uC800\uC7A5 (git add \u2192 commit \u2192 push)",
|
|
484
|
+
inputSchema: {
|
|
485
|
+
message: z.string().optional().describe("\uCEE4\uBC0B \uBA54\uC2DC\uC9C0 (\uBE44\uC6B0\uBA74 \uC790\uB3D9 \uC0DD\uC131)")
|
|
486
|
+
}
|
|
487
|
+
},
|
|
488
|
+
async ({ message }) => {
|
|
489
|
+
if (!isGitRepo()) {
|
|
490
|
+
return { content: [{ type: "text", text: "\u274C git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2D9\uB2C8\uB2E4." }] };
|
|
491
|
+
}
|
|
492
|
+
const status = safeExecFile("git", ["status", "--porcelain"]);
|
|
493
|
+
if (!status.ok) {
|
|
494
|
+
return { content: [{ type: "text", text: `\u274C git status \uC2E4\uD328: ${status.err}` }] };
|
|
495
|
+
}
|
|
496
|
+
if (!status.out) {
|
|
497
|
+
return { content: [{ type: "text", text: "\u{1F4ED} \uC800\uC7A5\uD560 \uBCC0\uACBD\uC0AC\uD56D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4." }] };
|
|
498
|
+
}
|
|
499
|
+
const files = status.out.split("\n");
|
|
500
|
+
const now = /* @__PURE__ */ new Date();
|
|
501
|
+
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")}`;
|
|
502
|
+
const commitMsg = message?.trim() || `\u2728 vhk save: ${ts}`;
|
|
503
|
+
const add = safeExecFile("git", ["add", "."]);
|
|
504
|
+
if (!add.ok) {
|
|
505
|
+
return { content: [{ type: "text", text: `\u274C git add \uC2E4\uD328: ${add.err}` }] };
|
|
506
|
+
}
|
|
507
|
+
const commit = safeExecFile("git", ["commit", "-m", commitMsg]);
|
|
508
|
+
if (!commit.ok) {
|
|
509
|
+
return { content: [{ type: "text", text: `\u274C commit \uC2E4\uD328: ${commit.err}` }] };
|
|
510
|
+
}
|
|
511
|
+
const push = safeExecFile("git", ["push"]);
|
|
512
|
+
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)";
|
|
513
|
+
return {
|
|
514
|
+
content: [
|
|
515
|
+
{
|
|
516
|
+
type: "text",
|
|
517
|
+
text: `\u2705 ${files.length}\uAC1C \uD30C\uC77C \uC800\uC7A5 \uC644\uB8CC! ${pushResult}
|
|
518
|
+
\uCEE4\uBC0B: ${commitMsg}`
|
|
519
|
+
}
|
|
520
|
+
]
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
);
|
|
524
|
+
server.registerTool("undo", { description: "\uCD5C\uADFC \uCEE4\uBC0B \uB418\uB3CC\uB9AC\uAE30 (soft reset, \uBCC0\uACBD\uC0AC\uD56D\uC740 \uC720\uC9C0)" }, async () => {
|
|
525
|
+
if (!isGitRepo()) {
|
|
526
|
+
return { content: [{ type: "text", text: "\u274C git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2D9\uB2C8\uB2E4." }] };
|
|
527
|
+
}
|
|
528
|
+
const last = safeExecFile("git", ["log", "--oneline", "-1"]);
|
|
529
|
+
if (!last.ok || !last.out) {
|
|
530
|
+
return { content: [{ type: "text", text: "\u{1F4ED} \uB418\uB3CC\uB9B4 \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4." }] };
|
|
531
|
+
}
|
|
532
|
+
const reset = safeExecFile("git", ["reset", "--soft", "HEAD~1"]);
|
|
533
|
+
if (!reset.ok) {
|
|
534
|
+
return { content: [{ type: "text", text: `\u274C reset \uC2E4\uD328: ${reset.err}` }] };
|
|
535
|
+
}
|
|
536
|
+
return {
|
|
537
|
+
content: [
|
|
538
|
+
{
|
|
539
|
+
type: "text",
|
|
540
|
+
text: `\u2705 \uB418\uB3CC\uB9AC\uAE30 \uC644\uB8CC!
|
|
541
|
+
\uCDE8\uC18C\uB41C \uCEE4\uBC0B: ${last.out}
|
|
542
|
+
\u{1F4A1} \uBCC0\uACBD\uC0AC\uD56D\uC740 \uC2A4\uD14C\uC774\uC9D5 \uC601\uC5ED\uC5D0 \uB0A8\uC544\uC788\uC2B5\uB2C8\uB2E4.`
|
|
543
|
+
}
|
|
544
|
+
]
|
|
545
|
+
};
|
|
546
|
+
});
|
|
547
|
+
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 () => {
|
|
548
|
+
const lines = [];
|
|
549
|
+
if (existsSync2("package.json")) {
|
|
550
|
+
try {
|
|
551
|
+
const pkg = JSON.parse(readFileSync2("package.json", "utf-8"));
|
|
552
|
+
lines.push(`\u{1F4E6} \uD504\uB85C\uC81D\uD2B8: ${pkg.name ?? "(\uC774\uB984 \uC5C6\uC74C)"} v${pkg.version ?? "?"}`);
|
|
553
|
+
} catch {
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
if (!isGitRepo()) {
|
|
557
|
+
lines.push("\u26A0\uFE0F git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2D9\uB2C8\uB2E4");
|
|
558
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
559
|
+
}
|
|
560
|
+
const branch = safeExecFile("git", ["branch", "--show-current"]);
|
|
561
|
+
if (branch.ok) lines.push(`\u{1F33F} \uBE0C\uB79C\uCE58: ${branch.out || "(detached)"}`);
|
|
562
|
+
const status = safeExecFile("git", ["status", "--porcelain"]);
|
|
563
|
+
if (status.ok) {
|
|
564
|
+
if (!status.out) {
|
|
565
|
+
lines.push("\u{1F4DD} \uBCC0\uACBD\uC0AC\uD56D: \u2705 \uAE68\uB057\uD568");
|
|
566
|
+
} else {
|
|
567
|
+
const fileLines = status.out.split("\n");
|
|
568
|
+
const staged = fileLines.filter((l) => l[0] !== " " && l[0] !== "?").length;
|
|
569
|
+
const unstaged = fileLines.filter((l) => l[1] === "M" || l[1] === "D").length;
|
|
570
|
+
const untracked = fileLines.filter((l) => l.startsWith("??")).length;
|
|
571
|
+
const parts = [];
|
|
572
|
+
if (staged) parts.push(`\uC2A4\uD14C\uC774\uC9D5 ${staged}\uAC1C`);
|
|
573
|
+
if (unstaged) parts.push(`\uC218\uC815 ${unstaged}\uAC1C`);
|
|
574
|
+
if (untracked) parts.push(`\uC0C8\uD30C\uC77C ${untracked}\uAC1C`);
|
|
575
|
+
lines.push(`\u{1F4DD} \uBCC0\uACBD\uC0AC\uD56D: ${parts.join(", ")}`);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
const log = safeExecFile("git", ["log", "--oneline", "-3"]);
|
|
579
|
+
if (log.ok && log.out) {
|
|
580
|
+
lines.push("\u{1F4DC} \uCD5C\uADFC \uCEE4\uBC0B:");
|
|
581
|
+
log.out.split("\n").forEach((l) => lines.push(` ${l}`));
|
|
582
|
+
}
|
|
583
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
584
|
+
});
|
|
585
|
+
server.registerTool("diff", { description: "\uBCC0\uACBD\uC0AC\uD56D \uD655\uC778 (staged/unstaged/\uC0C8\uD30C\uC77C + \uCD1D \uBCC0\uACBD \uC694\uC57D)" }, async () => {
|
|
586
|
+
if (!isGitRepo()) {
|
|
587
|
+
return { content: [{ type: "text", text: "\u274C git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2D9\uB2C8\uB2E4." }] };
|
|
588
|
+
}
|
|
589
|
+
const unstaged = safeExecFile("git", ["diff", "--stat"]);
|
|
590
|
+
const staged = safeExecFile("git", ["diff", "--cached", "--stat"]);
|
|
591
|
+
const untracked = safeExecFile("git", ["ls-files", "--others", "--exclude-standard"]);
|
|
592
|
+
const unstagedOut = unstaged.ok ? unstaged.out : "";
|
|
593
|
+
const stagedOut = staged.ok ? staged.out : "";
|
|
594
|
+
const untrackedOut = untracked.ok ? untracked.out : "";
|
|
595
|
+
if (!unstagedOut && !stagedOut && !untrackedOut) {
|
|
596
|
+
return { content: [{ type: "text", text: "\u2705 \uBCC0\uACBD\uC0AC\uD56D \uC5C6\uC74C! \uAE68\uB057\uD569\uB2C8\uB2E4." }] };
|
|
597
|
+
}
|
|
598
|
+
const lines = [];
|
|
599
|
+
if (stagedOut) {
|
|
600
|
+
lines.push("\u{1F4E6} \uCEE4\uBC0B \uB300\uAE30 (staged):");
|
|
601
|
+
lines.push(stagedOut);
|
|
602
|
+
}
|
|
603
|
+
if (unstagedOut) {
|
|
604
|
+
lines.push("\u270F\uFE0F \uC218\uC815\uB428 (unstaged):");
|
|
605
|
+
lines.push(unstagedOut);
|
|
606
|
+
}
|
|
607
|
+
if (untrackedOut) {
|
|
608
|
+
const files = untrackedOut.split("\n");
|
|
609
|
+
lines.push(`\u2795 \uC0C8 \uD30C\uC77C (${files.length}\uAC1C):`);
|
|
610
|
+
files.forEach((f) => lines.push(` + ${f}`));
|
|
611
|
+
}
|
|
612
|
+
const numstat = safeExecFile("git", ["diff", "--numstat", "HEAD"]);
|
|
613
|
+
if (numstat.ok && numstat.out) {
|
|
614
|
+
let totalAdd = 0;
|
|
615
|
+
let totalDel = 0;
|
|
616
|
+
let fileCount = 0;
|
|
617
|
+
numstat.out.split("\n").forEach((line) => {
|
|
618
|
+
const [add, del] = line.split(" ");
|
|
619
|
+
totalAdd += parseInt(add, 10) || 0;
|
|
620
|
+
totalDel += parseInt(del, 10) || 0;
|
|
621
|
+
fileCount += 1;
|
|
622
|
+
});
|
|
623
|
+
lines.push(`
|
|
624
|
+
\u{1F4CA} \uCD1D \uBCC0\uACBD: ${fileCount}\uAC1C \uD30C\uC77C, +${totalAdd}\uC904 -${totalDel}\uC904`);
|
|
625
|
+
}
|
|
626
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
627
|
+
});
|
|
628
|
+
server.registerTool("ship", { description: "\uBC30\uD3EC \uCCB4\uD06C\uB9AC\uC2A4\uD2B8 \uC2E4\uD589 (\uBE4C\uB4DC + \uD14C\uC2A4\uD2B8 + \uBC84\uC804 + git \uC0C1\uD0DC)" }, async () => {
|
|
629
|
+
const checks = [];
|
|
630
|
+
const build = safeExecFile("pnpm", ["build"]);
|
|
631
|
+
checks.push(build.ok ? "\u2705 \uBE4C\uB4DC \uC131\uACF5" : "\u274C \uBE4C\uB4DC \uC2E4\uD328");
|
|
632
|
+
const test = safeExecFile("pnpm", ["test", "--run"]);
|
|
633
|
+
checks.push(test.ok ? "\u2705 \uD14C\uC2A4\uD2B8 \uD1B5\uACFC" : "\u274C \uD14C\uC2A4\uD2B8 \uC2E4\uD328");
|
|
634
|
+
if (existsSync2("package.json")) {
|
|
635
|
+
try {
|
|
636
|
+
const pkg = JSON.parse(readFileSync2("package.json", "utf-8"));
|
|
637
|
+
checks.push(`\u{1F4E6} \uBC84\uC804: ${pkg.version}`);
|
|
638
|
+
} catch {
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
if (isGitRepo()) {
|
|
642
|
+
const status = safeExecFile("git", ["status", "--porcelain"]);
|
|
643
|
+
if (status.ok) {
|
|
644
|
+
if (status.out) {
|
|
645
|
+
checks.push(`\u26A0\uFE0F \uCEE4\uBC0B\uB418\uC9C0 \uC54A\uC740 \uBCC0\uACBD\uC0AC\uD56D ${status.out.split("\n").length}\uAC1C`);
|
|
646
|
+
} else {
|
|
647
|
+
checks.push("\u2705 \uC6CC\uD0B9 \uB514\uB809\uD1A0\uB9AC \uAE68\uB057\uD568");
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
return { content: [{ type: "text", text: "\u{1F680} \uBC30\uD3EC \uCCB4\uD06C\uB9AC\uC2A4\uD2B8\n" + checks.join("\n") }] };
|
|
652
|
+
});
|
|
653
|
+
server.registerTool("doctor", { description: "\uAC1C\uBC1C \uD658\uACBD \uC810\uAC80 (Node/Git/npm/pnpm/TypeScript)" }, async () => {
|
|
654
|
+
const checks = [];
|
|
655
|
+
const node = safeExecFile("node", ["--version"]);
|
|
656
|
+
checks.push(node.ok ? `\u2705 Node.js: ${node.out}` : "\u274C Node.js: \uC124\uCE58 \uC548 \uB428");
|
|
657
|
+
const git = safeExecFile("git", ["--version"]);
|
|
658
|
+
checks.push(git.ok ? `\u2705 Git: ${git.out}` : "\u274C Git: \uC124\uCE58 \uC548 \uB428");
|
|
659
|
+
const pnpm = safeExecFile("pnpm", ["--version"]);
|
|
660
|
+
checks.push(pnpm.ok ? `\u2705 pnpm: v${pnpm.out}` : "\u26A0\uFE0F pnpm: \uC124\uCE58 \uC548 \uB428");
|
|
661
|
+
const npm = safeExecFile("npm", ["--version"]);
|
|
662
|
+
checks.push(npm.ok ? `\u2705 npm: v${npm.out}` : "\u274C npm: \uC124\uCE58 \uC548 \uB428");
|
|
663
|
+
const tsc = safeExecFile("npx", ["tsc", "--version"]);
|
|
664
|
+
checks.push(tsc.ok ? `\u2705 TypeScript: ${tsc.out}` : "\u26A0\uFE0F TypeScript: \uD504\uB85C\uC81D\uD2B8\uC5D0 \uC5C6\uC74C");
|
|
665
|
+
return { content: [{ type: "text", text: "\u{1FA7A} \uD658\uACBD \uC810\uAC80 \uACB0\uACFC\n" + checks.join("\n") }] };
|
|
666
|
+
});
|
|
667
|
+
server.registerTool("check", { description: "\uD504\uB85C\uC81D\uD2B8 \uAD6C\uC870 \uC810\uAC80 (\uD544\uC218 \uD30C\uC77C + VHK \uD558\uB124\uC2A4 \uD30C\uC77C)" }, async () => {
|
|
668
|
+
const required = ["package.json", "tsconfig.json", "README.md", ".gitignore"];
|
|
669
|
+
const recommended = ["CLAUDE.md", ".cursorrules", "docs/PRD.md", "docs/ARCHITECTURE.md"];
|
|
670
|
+
const lines = ["\u{1F50D} \uD504\uB85C\uC81D\uD2B8 \uC810\uAC80", "", "\uD544\uC218:"];
|
|
671
|
+
required.forEach((f) => {
|
|
672
|
+
lines.push(` ${existsSync2(f) ? "\u2705" : "\u274C"} ${f}`);
|
|
673
|
+
});
|
|
674
|
+
lines.push("", "\uAD8C\uC7A5 (VHK \uD558\uB124\uC2A4):");
|
|
675
|
+
recommended.forEach((f) => {
|
|
676
|
+
lines.push(` ${existsSync2(f) ? "\u2705" : "\u26A0\uFE0F"} ${f}`);
|
|
677
|
+
});
|
|
678
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
679
|
+
});
|
|
680
|
+
server.registerTool(
|
|
681
|
+
"recap",
|
|
682
|
+
{
|
|
683
|
+
description: "\uCD5C\uADFC \uC791\uC5C5 \uC694\uC57D (\uCEE4\uBC0B \uD788\uC2A4\uD1A0\uB9AC \uAE30\uBC18, \uB0A0\uC9DC \uD3EC\uD568)",
|
|
684
|
+
inputSchema: {
|
|
685
|
+
count: z.number().optional().describe("\uD45C\uC2DC\uD560 \uCEE4\uBC0B \uC218 (\uAE30\uBCF8: 10)")
|
|
686
|
+
}
|
|
687
|
+
},
|
|
688
|
+
async ({ count }) => {
|
|
689
|
+
if (!isGitRepo()) {
|
|
690
|
+
return { content: [{ type: "text", text: "\u274C git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2D9\uB2C8\uB2E4." }] };
|
|
691
|
+
}
|
|
692
|
+
const n = count && count > 0 ? Math.floor(count) : 10;
|
|
693
|
+
const log = safeExecFile("git", ["log", "--format=%h %ad %s", "--date=short", `-${n}`]);
|
|
694
|
+
if (!log.ok) {
|
|
695
|
+
return { content: [{ type: "text", text: `\u274C git log \uC2E4\uD328: ${log.err}` }] };
|
|
696
|
+
}
|
|
697
|
+
if (!log.out) {
|
|
698
|
+
return { content: [{ type: "text", text: "\u{1F4ED} \uCEE4\uBC0B \uD788\uC2A4\uD1A0\uB9AC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4." }] };
|
|
699
|
+
}
|
|
700
|
+
return { content: [{ type: "text", text: `\u{1F4CB} \uCD5C\uADFC \uC791\uC5C5 (${n}\uAC1C):
|
|
701
|
+
${log.out}` }] };
|
|
702
|
+
}
|
|
703
|
+
);
|
|
704
|
+
server.registerTool(
|
|
705
|
+
"env",
|
|
706
|
+
{
|
|
707
|
+
description: ".env \u2192 .env.example \uB3D9\uAE30\uD654 + .gitignore\uC5D0 .env \uC790\uB3D9 \uCD94\uAC00"
|
|
708
|
+
},
|
|
709
|
+
async () => {
|
|
710
|
+
if (!existsSync2(".env")) {
|
|
711
|
+
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." }] };
|
|
712
|
+
}
|
|
713
|
+
const keys = parseEnvKeys(readFileSync2(".env", "utf-8"));
|
|
714
|
+
if (keys.length === 0) {
|
|
715
|
+
return { content: [{ type: "text", text: "\u{1F4ED} .env\uC5D0 \uD658\uACBD\uBCC0\uC218\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4." }] };
|
|
716
|
+
}
|
|
717
|
+
const exampleContent = keys.map((k) => `${k}=`).join("\n") + "\n";
|
|
718
|
+
writeFileSync2(".env.example", exampleContent, "utf-8");
|
|
719
|
+
const gitignoreLines = [];
|
|
720
|
+
if (existsSync2(".gitignore")) {
|
|
721
|
+
const content = readFileSync2(".gitignore", "utf-8");
|
|
722
|
+
if (!content.split("\n").some((l) => l.trim() === ".env")) {
|
|
723
|
+
appendFileSync2(".gitignore", "\n.env\n");
|
|
724
|
+
gitignoreLines.push("\u{1F512} .gitignore\uC5D0 .env \uCD94\uAC00\uB428");
|
|
725
|
+
}
|
|
726
|
+
} else {
|
|
727
|
+
writeFileSync2(".gitignore", ".env\nnode_modules/\ndist/\n");
|
|
728
|
+
gitignoreLines.push("\u{1F512} .gitignore \uC0DD\uC131 (.env \uD3EC\uD568)");
|
|
729
|
+
}
|
|
730
|
+
const lines = [`\u2705 .env.example \uC0DD\uC131 (${keys.length}\uAC1C \uD0A4)`, ...keys.map((k) => ` ${k}`)];
|
|
731
|
+
if (gitignoreLines.length) lines.push("", ...gitignoreLines);
|
|
732
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
733
|
+
}
|
|
734
|
+
);
|
|
735
|
+
server.registerTool(
|
|
736
|
+
"env-check",
|
|
737
|
+
{
|
|
738
|
+
description: "\uD544\uC218 \uD658\uACBD\uBCC0\uC218 \uB204\uB77D \uAC80\uC0AC (.env.example \uAE30\uC900)"
|
|
739
|
+
},
|
|
740
|
+
async () => {
|
|
741
|
+
if (!existsSync2(".env.example")) {
|
|
742
|
+
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." }] };
|
|
743
|
+
}
|
|
744
|
+
const requiredKeys = parseEnvKeys(readFileSync2(".env.example", "utf-8"));
|
|
745
|
+
const currentKeys = existsSync2(".env") ? parseEnvKeys(readFileSync2(".env", "utf-8")) : [];
|
|
746
|
+
const missing = requiredKeys.filter((k) => !currentKeys.includes(k));
|
|
747
|
+
const extra = currentKeys.filter((k) => !requiredKeys.includes(k));
|
|
748
|
+
const lines = [`\u{1F4CB} \uD544\uC218 \uD658\uACBD\uBCC0\uC218: ${requiredKeys.length}\uAC1C`];
|
|
749
|
+
if (missing.length === 0) {
|
|
750
|
+
lines.push("\u2705 \uBAA8\uB4E0 \uD544\uC218 \uD658\uACBD\uBCC0\uC218\uAC00 \uC124\uC815\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4!");
|
|
751
|
+
} else {
|
|
752
|
+
lines.push(`\u274C \uB204\uB77D\uB41C \uD658\uACBD\uBCC0\uC218 (${missing.length}\uAC1C):`);
|
|
753
|
+
missing.forEach((k) => lines.push(` \u2022 ${k}`));
|
|
754
|
+
}
|
|
755
|
+
if (extra.length > 0) {
|
|
756
|
+
lines.push(`\u{1F4A1} .env.example\uC5D0 \uC5C6\uB294 \uCD94\uAC00 \uBCC0\uC218 (${extra.length}\uAC1C):`);
|
|
757
|
+
extra.forEach((k) => lines.push(` \u2022 ${k}`));
|
|
758
|
+
}
|
|
759
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
760
|
+
}
|
|
761
|
+
);
|
|
762
|
+
return server;
|
|
763
|
+
}
|
|
764
|
+
async function startMcpServer() {
|
|
765
|
+
const server = createVhkMcpServer();
|
|
766
|
+
const transport = new StdioServerTransport();
|
|
767
|
+
await server.connect(transport);
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
export {
|
|
771
|
+
__commonJS,
|
|
772
|
+
__toESM,
|
|
773
|
+
ko,
|
|
774
|
+
t,
|
|
775
|
+
printNextStep,
|
|
776
|
+
safeExecFile,
|
|
777
|
+
safeExecFileStream,
|
|
778
|
+
env,
|
|
779
|
+
envCheck,
|
|
780
|
+
startMcpServer
|
|
781
|
+
};
|