@byh3071/vhk 0.7.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/mcp/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startMcpServer
4
- } from "../chunk-NJDRNI3S.js";
4
+ } from "../chunk-3HHU7V77.js";
5
5
 
6
6
  // src/mcp/index.ts
7
7
  startMcpServer().catch((err) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@byh3071/vhk",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "description": "Vibe Harness Kit — 바이브코딩 풀사이클 CLI",
5
5
  "bin": {
6
6
  "vhk": "dist/index.js",
@@ -1,316 +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/mcp/server.ts
29
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
30
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
31
- import { z } from "zod";
32
- import { existsSync, readFileSync } from "fs";
33
-
34
- // src/lib/exec.ts
35
- import { execFileSync } from "child_process";
36
- var SHIM_BINARIES = /* @__PURE__ */ new Set(["pnpm", "npm", "npx", "yarn"]);
37
- function platformCmd(cmd) {
38
- if (process.platform === "win32" && SHIM_BINARIES.has(cmd)) {
39
- return `${cmd}.cmd`;
40
- }
41
- return cmd;
42
- }
43
- function safeExecFile(cmd, args) {
44
- try {
45
- const out = execFileSync(platformCmd(cmd), args, {
46
- encoding: "utf-8",
47
- stdio: ["pipe", "pipe", "pipe"]
48
- }).toString();
49
- return { ok: true, out: out.trim() };
50
- } catch (err) {
51
- const msg = err instanceof Error ? err.message : String(err);
52
- return { ok: false, err: msg };
53
- }
54
- }
55
- function safeExecFileStream(cmd, args) {
56
- try {
57
- execFileSync(platformCmd(cmd), args, {
58
- encoding: "utf-8",
59
- stdio: "inherit"
60
- });
61
- return { ok: true };
62
- } catch (err) {
63
- const msg = err instanceof Error ? err.message : String(err);
64
- return { ok: false, err: msg };
65
- }
66
- }
67
-
68
- // src/mcp/server.ts
69
- var SERVER_VERSION = "0.7.0";
70
- function isGitRepo() {
71
- return safeExecFile("git", ["rev-parse", "--is-inside-work-tree"]).ok;
72
- }
73
- function createVhkMcpServer() {
74
- const server = new McpServer({
75
- name: "vhk",
76
- version: SERVER_VERSION
77
- });
78
- server.registerTool(
79
- "save",
80
- {
81
- description: "\uBCC0\uACBD\uC0AC\uD56D \uC800\uC7A5 (git add \u2192 commit \u2192 push)",
82
- inputSchema: {
83
- message: z.string().optional().describe("\uCEE4\uBC0B \uBA54\uC2DC\uC9C0 (\uBE44\uC6B0\uBA74 \uC790\uB3D9 \uC0DD\uC131)")
84
- }
85
- },
86
- async ({ message }) => {
87
- if (!isGitRepo()) {
88
- return { content: [{ type: "text", text: "\u274C git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2D9\uB2C8\uB2E4." }] };
89
- }
90
- const status = safeExecFile("git", ["status", "--porcelain"]);
91
- if (!status.ok) {
92
- return { content: [{ type: "text", text: `\u274C git status \uC2E4\uD328: ${status.err}` }] };
93
- }
94
- if (!status.out) {
95
- return { content: [{ type: "text", text: "\u{1F4ED} \uC800\uC7A5\uD560 \uBCC0\uACBD\uC0AC\uD56D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4." }] };
96
- }
97
- const files = status.out.split("\n");
98
- const now = /* @__PURE__ */ new Date();
99
- 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")}`;
100
- const commitMsg = message?.trim() || `\u2728 vhk save: ${ts}`;
101
- const add = safeExecFile("git", ["add", "."]);
102
- if (!add.ok) {
103
- return { content: [{ type: "text", text: `\u274C git add \uC2E4\uD328: ${add.err}` }] };
104
- }
105
- const commit = safeExecFile("git", ["commit", "-m", commitMsg]);
106
- if (!commit.ok) {
107
- return { content: [{ type: "text", text: `\u274C commit \uC2E4\uD328: ${commit.err}` }] };
108
- }
109
- const push = safeExecFile("git", ["push"]);
110
- 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)";
111
- return {
112
- content: [
113
- {
114
- type: "text",
115
- text: `\u2705 ${files.length}\uAC1C \uD30C\uC77C \uC800\uC7A5 \uC644\uB8CC! ${pushResult}
116
- \uCEE4\uBC0B: ${commitMsg}`
117
- }
118
- ]
119
- };
120
- }
121
- );
122
- server.registerTool("undo", { description: "\uCD5C\uADFC \uCEE4\uBC0B \uB418\uB3CC\uB9AC\uAE30 (soft reset, \uBCC0\uACBD\uC0AC\uD56D\uC740 \uC720\uC9C0)" }, async () => {
123
- if (!isGitRepo()) {
124
- return { content: [{ type: "text", text: "\u274C git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2D9\uB2C8\uB2E4." }] };
125
- }
126
- const last = safeExecFile("git", ["log", "--oneline", "-1"]);
127
- if (!last.ok || !last.out) {
128
- return { content: [{ type: "text", text: "\u{1F4ED} \uB418\uB3CC\uB9B4 \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4." }] };
129
- }
130
- const reset = safeExecFile("git", ["reset", "--soft", "HEAD~1"]);
131
- if (!reset.ok) {
132
- return { content: [{ type: "text", text: `\u274C reset \uC2E4\uD328: ${reset.err}` }] };
133
- }
134
- return {
135
- content: [
136
- {
137
- type: "text",
138
- text: `\u2705 \uB418\uB3CC\uB9AC\uAE30 \uC644\uB8CC!
139
- \uCDE8\uC18C\uB41C \uCEE4\uBC0B: ${last.out}
140
- \u{1F4A1} \uBCC0\uACBD\uC0AC\uD56D\uC740 \uC2A4\uD14C\uC774\uC9D5 \uC601\uC5ED\uC5D0 \uB0A8\uC544\uC788\uC2B5\uB2C8\uB2E4.`
141
- }
142
- ]
143
- };
144
- });
145
- 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 () => {
146
- const lines = [];
147
- if (existsSync("package.json")) {
148
- try {
149
- const pkg = JSON.parse(readFileSync("package.json", "utf-8"));
150
- lines.push(`\u{1F4E6} \uD504\uB85C\uC81D\uD2B8: ${pkg.name ?? "(\uC774\uB984 \uC5C6\uC74C)"} v${pkg.version ?? "?"}`);
151
- } catch {
152
- }
153
- }
154
- if (!isGitRepo()) {
155
- lines.push("\u26A0\uFE0F git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2D9\uB2C8\uB2E4");
156
- return { content: [{ type: "text", text: lines.join("\n") }] };
157
- }
158
- const branch = safeExecFile("git", ["branch", "--show-current"]);
159
- if (branch.ok) lines.push(`\u{1F33F} \uBE0C\uB79C\uCE58: ${branch.out || "(detached)"}`);
160
- const status = safeExecFile("git", ["status", "--porcelain"]);
161
- if (status.ok) {
162
- if (!status.out) {
163
- lines.push("\u{1F4DD} \uBCC0\uACBD\uC0AC\uD56D: \u2705 \uAE68\uB057\uD568");
164
- } else {
165
- const fileLines = status.out.split("\n");
166
- const staged = fileLines.filter((l) => l[0] !== " " && l[0] !== "?").length;
167
- const unstaged = fileLines.filter((l) => l[1] === "M" || l[1] === "D").length;
168
- const untracked = fileLines.filter((l) => l.startsWith("??")).length;
169
- const parts = [];
170
- if (staged) parts.push(`\uC2A4\uD14C\uC774\uC9D5 ${staged}\uAC1C`);
171
- if (unstaged) parts.push(`\uC218\uC815 ${unstaged}\uAC1C`);
172
- if (untracked) parts.push(`\uC0C8\uD30C\uC77C ${untracked}\uAC1C`);
173
- lines.push(`\u{1F4DD} \uBCC0\uACBD\uC0AC\uD56D: ${parts.join(", ")}`);
174
- }
175
- }
176
- const log = safeExecFile("git", ["log", "--oneline", "-3"]);
177
- if (log.ok && log.out) {
178
- lines.push("\u{1F4DC} \uCD5C\uADFC \uCEE4\uBC0B:");
179
- log.out.split("\n").forEach((l) => lines.push(` ${l}`));
180
- }
181
- return { content: [{ type: "text", text: lines.join("\n") }] };
182
- });
183
- server.registerTool("diff", { description: "\uBCC0\uACBD\uC0AC\uD56D \uD655\uC778 (staged/unstaged/\uC0C8\uD30C\uC77C + \uCD1D \uBCC0\uACBD \uC694\uC57D)" }, async () => {
184
- if (!isGitRepo()) {
185
- return { content: [{ type: "text", text: "\u274C git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2D9\uB2C8\uB2E4." }] };
186
- }
187
- const unstaged = safeExecFile("git", ["diff", "--stat"]);
188
- const staged = safeExecFile("git", ["diff", "--cached", "--stat"]);
189
- const untracked = safeExecFile("git", ["ls-files", "--others", "--exclude-standard"]);
190
- const unstagedOut = unstaged.ok ? unstaged.out : "";
191
- const stagedOut = staged.ok ? staged.out : "";
192
- const untrackedOut = untracked.ok ? untracked.out : "";
193
- if (!unstagedOut && !stagedOut && !untrackedOut) {
194
- return { content: [{ type: "text", text: "\u2705 \uBCC0\uACBD\uC0AC\uD56D \uC5C6\uC74C! \uAE68\uB057\uD569\uB2C8\uB2E4." }] };
195
- }
196
- const lines = [];
197
- if (stagedOut) {
198
- lines.push("\u{1F4E6} \uCEE4\uBC0B \uB300\uAE30 (staged):");
199
- lines.push(stagedOut);
200
- }
201
- if (unstagedOut) {
202
- lines.push("\u270F\uFE0F \uC218\uC815\uB428 (unstaged):");
203
- lines.push(unstagedOut);
204
- }
205
- if (untrackedOut) {
206
- const files = untrackedOut.split("\n");
207
- lines.push(`\u2795 \uC0C8 \uD30C\uC77C (${files.length}\uAC1C):`);
208
- files.forEach((f) => lines.push(` + ${f}`));
209
- }
210
- const numstat = safeExecFile("git", ["diff", "--numstat", "HEAD"]);
211
- if (numstat.ok && numstat.out) {
212
- let totalAdd = 0;
213
- let totalDel = 0;
214
- let fileCount = 0;
215
- numstat.out.split("\n").forEach((line) => {
216
- const [add, del] = line.split(" ");
217
- totalAdd += parseInt(add, 10) || 0;
218
- totalDel += parseInt(del, 10) || 0;
219
- fileCount += 1;
220
- });
221
- lines.push(`
222
- \u{1F4CA} \uCD1D \uBCC0\uACBD: ${fileCount}\uAC1C \uD30C\uC77C, +${totalAdd}\uC904 -${totalDel}\uC904`);
223
- }
224
- return { content: [{ type: "text", text: lines.join("\n") }] };
225
- });
226
- server.registerTool("ship", { description: "\uBC30\uD3EC \uCCB4\uD06C\uB9AC\uC2A4\uD2B8 \uC2E4\uD589 (\uBE4C\uB4DC + \uD14C\uC2A4\uD2B8 + \uBC84\uC804 + git \uC0C1\uD0DC)" }, async () => {
227
- const checks = [];
228
- const build = safeExecFile("pnpm", ["build"]);
229
- checks.push(build.ok ? "\u2705 \uBE4C\uB4DC \uC131\uACF5" : "\u274C \uBE4C\uB4DC \uC2E4\uD328");
230
- const test = safeExecFile("pnpm", ["test", "--run"]);
231
- checks.push(test.ok ? "\u2705 \uD14C\uC2A4\uD2B8 \uD1B5\uACFC" : "\u274C \uD14C\uC2A4\uD2B8 \uC2E4\uD328");
232
- if (existsSync("package.json")) {
233
- try {
234
- const pkg = JSON.parse(readFileSync("package.json", "utf-8"));
235
- checks.push(`\u{1F4E6} \uBC84\uC804: ${pkg.version}`);
236
- } catch {
237
- }
238
- }
239
- if (isGitRepo()) {
240
- const status = safeExecFile("git", ["status", "--porcelain"]);
241
- if (status.ok) {
242
- if (status.out) {
243
- checks.push(`\u26A0\uFE0F \uCEE4\uBC0B\uB418\uC9C0 \uC54A\uC740 \uBCC0\uACBD\uC0AC\uD56D ${status.out.split("\n").length}\uAC1C`);
244
- } else {
245
- checks.push("\u2705 \uC6CC\uD0B9 \uB514\uB809\uD1A0\uB9AC \uAE68\uB057\uD568");
246
- }
247
- }
248
- }
249
- return { content: [{ type: "text", text: "\u{1F680} \uBC30\uD3EC \uCCB4\uD06C\uB9AC\uC2A4\uD2B8\n" + checks.join("\n") }] };
250
- });
251
- server.registerTool("doctor", { description: "\uAC1C\uBC1C \uD658\uACBD \uC810\uAC80 (Node/Git/npm/pnpm/TypeScript)" }, async () => {
252
- const checks = [];
253
- const node = safeExecFile("node", ["--version"]);
254
- checks.push(node.ok ? `\u2705 Node.js: ${node.out}` : "\u274C Node.js: \uC124\uCE58 \uC548 \uB428");
255
- const git = safeExecFile("git", ["--version"]);
256
- checks.push(git.ok ? `\u2705 Git: ${git.out}` : "\u274C Git: \uC124\uCE58 \uC548 \uB428");
257
- const pnpm = safeExecFile("pnpm", ["--version"]);
258
- checks.push(pnpm.ok ? `\u2705 pnpm: v${pnpm.out}` : "\u26A0\uFE0F pnpm: \uC124\uCE58 \uC548 \uB428");
259
- const npm = safeExecFile("npm", ["--version"]);
260
- checks.push(npm.ok ? `\u2705 npm: v${npm.out}` : "\u274C npm: \uC124\uCE58 \uC548 \uB428");
261
- const tsc = safeExecFile("npx", ["tsc", "--version"]);
262
- checks.push(tsc.ok ? `\u2705 TypeScript: ${tsc.out}` : "\u26A0\uFE0F TypeScript: \uD504\uB85C\uC81D\uD2B8\uC5D0 \uC5C6\uC74C");
263
- return { content: [{ type: "text", text: "\u{1FA7A} \uD658\uACBD \uC810\uAC80 \uACB0\uACFC\n" + checks.join("\n") }] };
264
- });
265
- server.registerTool("check", { description: "\uD504\uB85C\uC81D\uD2B8 \uAD6C\uC870 \uC810\uAC80 (\uD544\uC218 \uD30C\uC77C + VHK \uD558\uB124\uC2A4 \uD30C\uC77C)" }, async () => {
266
- const required = ["package.json", "tsconfig.json", "README.md", ".gitignore"];
267
- const recommended = ["CLAUDE.md", ".cursorrules", "docs/PRD.md", "docs/ARCHITECTURE.md"];
268
- const lines = ["\u{1F50D} \uD504\uB85C\uC81D\uD2B8 \uC810\uAC80", "", "\uD544\uC218:"];
269
- required.forEach((f) => {
270
- lines.push(` ${existsSync(f) ? "\u2705" : "\u274C"} ${f}`);
271
- });
272
- lines.push("", "\uAD8C\uC7A5 (VHK \uD558\uB124\uC2A4):");
273
- recommended.forEach((f) => {
274
- lines.push(` ${existsSync(f) ? "\u2705" : "\u26A0\uFE0F"} ${f}`);
275
- });
276
- return { content: [{ type: "text", text: lines.join("\n") }] };
277
- });
278
- server.registerTool(
279
- "recap",
280
- {
281
- description: "\uCD5C\uADFC \uC791\uC5C5 \uC694\uC57D (\uCEE4\uBC0B \uD788\uC2A4\uD1A0\uB9AC \uAE30\uBC18, \uB0A0\uC9DC \uD3EC\uD568)",
282
- inputSchema: {
283
- count: z.number().optional().describe("\uD45C\uC2DC\uD560 \uCEE4\uBC0B \uC218 (\uAE30\uBCF8: 10)")
284
- }
285
- },
286
- async ({ count }) => {
287
- if (!isGitRepo()) {
288
- return { content: [{ type: "text", text: "\u274C git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2D9\uB2C8\uB2E4." }] };
289
- }
290
- const n = count && count > 0 ? Math.floor(count) : 10;
291
- const log = safeExecFile("git", ["log", "--format=%h %ad %s", "--date=short", `-${n}`]);
292
- if (!log.ok) {
293
- return { content: [{ type: "text", text: `\u274C git log \uC2E4\uD328: ${log.err}` }] };
294
- }
295
- if (!log.out) {
296
- return { content: [{ type: "text", text: "\u{1F4ED} \uCEE4\uBC0B \uD788\uC2A4\uD1A0\uB9AC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4." }] };
297
- }
298
- return { content: [{ type: "text", text: `\u{1F4CB} \uCD5C\uADFC \uC791\uC5C5 (${n}\uAC1C):
299
- ${log.out}` }] };
300
- }
301
- );
302
- return server;
303
- }
304
- async function startMcpServer() {
305
- const server = createVhkMcpServer();
306
- const transport = new StdioServerTransport();
307
- await server.connect(transport);
308
- }
309
-
310
- export {
311
- __commonJS,
312
- __toESM,
313
- safeExecFile,
314
- safeExecFileStream,
315
- startMcpServer
316
- };