@byh3071/vhk 0.5.3 โ 0.6.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 +32 -3
- package/dist/chunk-IU37BEQA.js +298 -0
- package/dist/index.js +85 -32
- package/dist/mcp/index.js +10 -0
- package/package.json +65 -62
package/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: vhk-readme
|
|
3
|
-
date: 2026-05-
|
|
4
|
-
tags: [vhk, cli, readme, v0.
|
|
3
|
+
date: 2026-05-24
|
|
4
|
+
tags: [vhk, cli, readme, v0.6.0]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# ๐ง VHK โ Vibe Harness Kit
|
|
8
8
|
|
|
9
|
-
> AI ์ฝ๋ฉ ์์ด์ ํธ๋ฅผ ๋ถ๋ฆฌ๋ ์ฌ๋์ ์ํ **ํ๊ตญ์ด ํ์ฌ์ดํด CLI** (v0.
|
|
9
|
+
> AI ์ฝ๋ฉ ์์ด์ ํธ๋ฅผ ๋ถ๋ฆฌ๋ ์ฌ๋์ ์ํ **ํ๊ตญ์ด ํ์ฌ์ดํด CLI** (v0.6.0)
|
|
10
10
|
>
|
|
11
11
|
> ๐ฝ๏ธ **VHK๋ VHK๋ก ๋ถํธ์คํธ๋ฉ๋จ** โ ์ด ๋ ํฌ์ `docs/`, `CLAUDE.md`, `.cursorrules`๋ `vhk init`์ด ๋ง๋ค์์ต๋๋ค.
|
|
12
12
|
|
|
@@ -89,6 +89,8 @@ vhk ๊ธฐํ ๋๋ฌ๊ณ ๋ฐ๋ก ์์
|
|
|
89
89
|
| `vhk undo` | `๋๋๋ฆฌ๊ธฐ`, `์ทจ์` | ์ต๊ทผ ์ปค๋ฐ soft reset (๋ณ๊ฒฝ์ staged ์ ์ง) |
|
|
90
90
|
| `vhk diff` | `๋ณ๊ฒฝ`, `์ฐจ์ด` | staged / unstaged / ์ ํ์ผ ์์ฝ (์ค ์ ํฉ๊ณ๋ trackedยทHEAD ๊ธฐ์ค) |
|
|
91
91
|
| `vhk status` | `์ํ`, `ํํฉ` | ๋ธ๋์นยท๋ณ๊ฒฝยท์ปค๋ฐยท์๊ฒฉยท๋ฒ์ ๋์๋ณด๋ |
|
|
92
|
+
| `vhk mcp` | โ | MCP ์๋ฒ ์์ (Cursor ๋ฑ MCP ํด๋ผ์ด์ธํธ์ฉ, stdio) |
|
|
93
|
+
| `vhk mcp-init` | `mcp์ค์ ` | Cursor `.cursor/mcp.json` ์๋ ์์ฑ |
|
|
92
94
|
|
|
93
95
|
### init ์ต์
|
|
94
96
|
|
|
@@ -105,6 +107,33 @@ vhk ๊ธฐํ ๋๋ฌ๊ณ ๋ฐ๋ก ์์
|
|
|
105
107
|
|------|------|
|
|
106
108
|
| `--since YYYY-MM-DD` | ๋ถ์ ์์์ผ (๊ธฐ๋ณธ: ์ค๋) |
|
|
107
109
|
|
|
110
|
+
## Cursor์ MCP๋ก ์ฐ๋ํ๊ธฐ
|
|
111
|
+
|
|
112
|
+
`v0.6.0`๋ถํฐ vhk๋ [Model Context Protocol](https://modelcontextprotocol.io) ์๋ฒ๋ฅผ ๋ด์ฅํฉ๋๋ค. Cursor ์ฑํ
์์ ์์ฐ์ด๋ก vhk ๋๊ตฌ๋ฅผ ํธ์ถํ ์ ์์ต๋๋ค.
|
|
113
|
+
|
|
114
|
+
```powershell
|
|
115
|
+
vhk mcp-init # .cursor/mcp.json ์๋ ์์ฑ
|
|
116
|
+
# โ Cursor ์ฌ์์ ํ ์ฑํ
์์ ์์ฐ์ด๋ก vhk ๋๊ตฌ ํธ์ถ ๊ฐ๋ฅ
|
|
117
|
+
# ์: "์ํ ์๋ ค์ค" โ Cursor๊ฐ vhk status ๋๊ตฌ ํธ์ถ
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
๋
ธ์ถ๋๋ MCP ๋๊ตฌ 8๊ฐ: `save`, `undo`, `status`, `diff`, `ship`, `doctor`, `check`, `recap`.
|
|
121
|
+
|
|
122
|
+
MCP ์๋ฒ๋ฅผ ์๋์ผ๋ก ๋์ฐ๋ ค๋ฉด:
|
|
123
|
+
|
|
124
|
+
```powershell
|
|
125
|
+
vhk mcp # stdio ์๋ฒ ์์ (Cursor๊ฐ ์๋์ผ๋ก ํธ์ถ)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## v0.6.0 ํ์ด๋ผ์ดํธ
|
|
129
|
+
|
|
130
|
+
| ๊ธฐ๋ฅ | ์ค๋ช
|
|
|
131
|
+
|------|------|
|
|
132
|
+
| **MCP ์๋ฒ** | `vhk mcp` โ 8๊ฐ ๋๊ตฌ(save/undo/status/diff/ship/doctor/check/recap)๋ฅผ stdio๋ก ๋
ธ์ถ. Cursor ๋ฑ MCP ํด๋ผ์ด์ธํธ์์ ์์ฐ์ด๋ก ํธ์ถ |
|
|
133
|
+
| **mcp-init** | `vhk mcp-init` โ Cursor `.cursor/mcp.json` ์๋ ์์ฑ. ์ฌ์์ ํ ๋ฒ์ผ๋ก ์ฐ๋ ์๋ฃ |
|
|
134
|
+
| **์์ฐ์ด ๋ผ์ฐํ
ํ์ฅ** | `vhk mcp์ค์ ` โ `vhk mcp-init` ๋ณ์นญ |
|
|
135
|
+
| **๋ณด์** | MCP save ๋๊ตฌ์ shell injection ์ฐจ๋จ โ ๋ชจ๋ git ํธ์ถ์ `execFileSync` ์ฌ์ฉ |
|
|
136
|
+
|
|
108
137
|
## v0.5.3 ํ์ด๋ผ์ดํธ
|
|
109
138
|
|
|
110
139
|
| ๊ธฐ๋ฅ | ์ค๋ช
|
|
|
@@ -0,0 +1,298 @@
|
|
|
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 { execFileSync } from "child_process";
|
|
33
|
+
import { existsSync, readFileSync } from "fs";
|
|
34
|
+
var SERVER_VERSION = "0.6.0";
|
|
35
|
+
var SHIM_BINARIES = /* @__PURE__ */ new Set(["pnpm", "npm", "npx", "yarn"]);
|
|
36
|
+
function platformCmd(cmd) {
|
|
37
|
+
if (process.platform === "win32" && SHIM_BINARIES.has(cmd)) {
|
|
38
|
+
return `${cmd}.cmd`;
|
|
39
|
+
}
|
|
40
|
+
return cmd;
|
|
41
|
+
}
|
|
42
|
+
function safeExecFile(cmd, args) {
|
|
43
|
+
try {
|
|
44
|
+
const out = execFileSync(platformCmd(cmd), args, {
|
|
45
|
+
encoding: "utf-8",
|
|
46
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
47
|
+
}).toString();
|
|
48
|
+
return { ok: true, out: out.trim() };
|
|
49
|
+
} catch (err) {
|
|
50
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
51
|
+
return { ok: false, err: msg };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function isGitRepo() {
|
|
55
|
+
return safeExecFile("git", ["rev-parse", "--is-inside-work-tree"]).ok;
|
|
56
|
+
}
|
|
57
|
+
function createVhkMcpServer() {
|
|
58
|
+
const server = new McpServer({
|
|
59
|
+
name: "vhk",
|
|
60
|
+
version: SERVER_VERSION
|
|
61
|
+
});
|
|
62
|
+
server.registerTool(
|
|
63
|
+
"save",
|
|
64
|
+
{
|
|
65
|
+
description: "\uBCC0\uACBD\uC0AC\uD56D \uC800\uC7A5 (git add \u2192 commit \u2192 push)",
|
|
66
|
+
inputSchema: {
|
|
67
|
+
message: z.string().optional().describe("\uCEE4\uBC0B \uBA54\uC2DC\uC9C0 (\uBE44\uC6B0\uBA74 \uC790\uB3D9 \uC0DD\uC131)")
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
async ({ message }) => {
|
|
71
|
+
if (!isGitRepo()) {
|
|
72
|
+
return { content: [{ type: "text", text: "\u274C git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2D9\uB2C8\uB2E4." }] };
|
|
73
|
+
}
|
|
74
|
+
const status = safeExecFile("git", ["status", "--porcelain"]);
|
|
75
|
+
if (!status.ok) {
|
|
76
|
+
return { content: [{ type: "text", text: `\u274C git status \uC2E4\uD328: ${status.err}` }] };
|
|
77
|
+
}
|
|
78
|
+
if (!status.out) {
|
|
79
|
+
return { content: [{ type: "text", text: "\u{1F4ED} \uC800\uC7A5\uD560 \uBCC0\uACBD\uC0AC\uD56D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4." }] };
|
|
80
|
+
}
|
|
81
|
+
const files = status.out.split("\n");
|
|
82
|
+
const now = /* @__PURE__ */ new Date();
|
|
83
|
+
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")}`;
|
|
84
|
+
const commitMsg = message?.trim() || `\u2728 vhk save: ${ts}`;
|
|
85
|
+
const add = safeExecFile("git", ["add", "."]);
|
|
86
|
+
if (!add.ok) {
|
|
87
|
+
return { content: [{ type: "text", text: `\u274C git add \uC2E4\uD328: ${add.err}` }] };
|
|
88
|
+
}
|
|
89
|
+
const commit = safeExecFile("git", ["commit", "-m", commitMsg]);
|
|
90
|
+
if (!commit.ok) {
|
|
91
|
+
return { content: [{ type: "text", text: `\u274C commit \uC2E4\uD328: ${commit.err}` }] };
|
|
92
|
+
}
|
|
93
|
+
const push = safeExecFile("git", ["push"]);
|
|
94
|
+
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)";
|
|
95
|
+
return {
|
|
96
|
+
content: [
|
|
97
|
+
{
|
|
98
|
+
type: "text",
|
|
99
|
+
text: `\u2705 ${files.length}\uAC1C \uD30C\uC77C \uC800\uC7A5 \uC644\uB8CC! ${pushResult}
|
|
100
|
+
\uCEE4\uBC0B: ${commitMsg}`
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
);
|
|
106
|
+
server.registerTool("undo", { description: "\uCD5C\uADFC \uCEE4\uBC0B \uB418\uB3CC\uB9AC\uAE30 (soft reset, \uBCC0\uACBD\uC0AC\uD56D\uC740 \uC720\uC9C0)" }, async () => {
|
|
107
|
+
if (!isGitRepo()) {
|
|
108
|
+
return { content: [{ type: "text", text: "\u274C git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2D9\uB2C8\uB2E4." }] };
|
|
109
|
+
}
|
|
110
|
+
const last = safeExecFile("git", ["log", "--oneline", "-1"]);
|
|
111
|
+
if (!last.ok || !last.out) {
|
|
112
|
+
return { content: [{ type: "text", text: "\u{1F4ED} \uB418\uB3CC\uB9B4 \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4." }] };
|
|
113
|
+
}
|
|
114
|
+
const reset = safeExecFile("git", ["reset", "--soft", "HEAD~1"]);
|
|
115
|
+
if (!reset.ok) {
|
|
116
|
+
return { content: [{ type: "text", text: `\u274C reset \uC2E4\uD328: ${reset.err}` }] };
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
content: [
|
|
120
|
+
{
|
|
121
|
+
type: "text",
|
|
122
|
+
text: `\u2705 \uB418\uB3CC\uB9AC\uAE30 \uC644\uB8CC!
|
|
123
|
+
\uCDE8\uC18C\uB41C \uCEE4\uBC0B: ${last.out}
|
|
124
|
+
\u{1F4A1} \uBCC0\uACBD\uC0AC\uD56D\uC740 \uC2A4\uD14C\uC774\uC9D5 \uC601\uC5ED\uC5D0 \uB0A8\uC544\uC788\uC2B5\uB2C8\uB2E4.`
|
|
125
|
+
}
|
|
126
|
+
]
|
|
127
|
+
};
|
|
128
|
+
});
|
|
129
|
+
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 () => {
|
|
130
|
+
const lines = [];
|
|
131
|
+
if (existsSync("package.json")) {
|
|
132
|
+
try {
|
|
133
|
+
const pkg = JSON.parse(readFileSync("package.json", "utf-8"));
|
|
134
|
+
lines.push(`\u{1F4E6} \uD504\uB85C\uC81D\uD2B8: ${pkg.name ?? "(\uC774\uB984 \uC5C6\uC74C)"} v${pkg.version ?? "?"}`);
|
|
135
|
+
} catch {
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (!isGitRepo()) {
|
|
139
|
+
lines.push("\u26A0\uFE0F git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2D9\uB2C8\uB2E4");
|
|
140
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
141
|
+
}
|
|
142
|
+
const branch = safeExecFile("git", ["branch", "--show-current"]);
|
|
143
|
+
if (branch.ok) lines.push(`\u{1F33F} \uBE0C\uB79C\uCE58: ${branch.out || "(detached)"}`);
|
|
144
|
+
const status = safeExecFile("git", ["status", "--porcelain"]);
|
|
145
|
+
if (status.ok) {
|
|
146
|
+
if (!status.out) {
|
|
147
|
+
lines.push("\u{1F4DD} \uBCC0\uACBD\uC0AC\uD56D: \u2705 \uAE68\uB057\uD568");
|
|
148
|
+
} else {
|
|
149
|
+
const fileLines = status.out.split("\n");
|
|
150
|
+
const staged = fileLines.filter((l) => l[0] !== " " && l[0] !== "?").length;
|
|
151
|
+
const unstaged = fileLines.filter((l) => l[1] === "M" || l[1] === "D").length;
|
|
152
|
+
const untracked = fileLines.filter((l) => l.startsWith("??")).length;
|
|
153
|
+
const parts = [];
|
|
154
|
+
if (staged) parts.push(`\uC2A4\uD14C\uC774\uC9D5 ${staged}\uAC1C`);
|
|
155
|
+
if (unstaged) parts.push(`\uC218\uC815 ${unstaged}\uAC1C`);
|
|
156
|
+
if (untracked) parts.push(`\uC0C8\uD30C\uC77C ${untracked}\uAC1C`);
|
|
157
|
+
lines.push(`\u{1F4DD} \uBCC0\uACBD\uC0AC\uD56D: ${parts.join(", ")}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
const log = safeExecFile("git", ["log", "--oneline", "-3"]);
|
|
161
|
+
if (log.ok && log.out) {
|
|
162
|
+
lines.push("\u{1F4DC} \uCD5C\uADFC \uCEE4\uBC0B:");
|
|
163
|
+
log.out.split("\n").forEach((l) => lines.push(` ${l}`));
|
|
164
|
+
}
|
|
165
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
166
|
+
});
|
|
167
|
+
server.registerTool("diff", { description: "\uBCC0\uACBD\uC0AC\uD56D \uD655\uC778 (staged/unstaged/\uC0C8\uD30C\uC77C + \uCD1D \uBCC0\uACBD \uC694\uC57D)" }, async () => {
|
|
168
|
+
if (!isGitRepo()) {
|
|
169
|
+
return { content: [{ type: "text", text: "\u274C git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2D9\uB2C8\uB2E4." }] };
|
|
170
|
+
}
|
|
171
|
+
const unstaged = safeExecFile("git", ["diff", "--stat"]);
|
|
172
|
+
const staged = safeExecFile("git", ["diff", "--cached", "--stat"]);
|
|
173
|
+
const untracked = safeExecFile("git", ["ls-files", "--others", "--exclude-standard"]);
|
|
174
|
+
const unstagedOut = unstaged.ok ? unstaged.out : "";
|
|
175
|
+
const stagedOut = staged.ok ? staged.out : "";
|
|
176
|
+
const untrackedOut = untracked.ok ? untracked.out : "";
|
|
177
|
+
if (!unstagedOut && !stagedOut && !untrackedOut) {
|
|
178
|
+
return { content: [{ type: "text", text: "\u2705 \uBCC0\uACBD\uC0AC\uD56D \uC5C6\uC74C! \uAE68\uB057\uD569\uB2C8\uB2E4." }] };
|
|
179
|
+
}
|
|
180
|
+
const lines = [];
|
|
181
|
+
if (stagedOut) {
|
|
182
|
+
lines.push("\u{1F4E6} \uCEE4\uBC0B \uB300\uAE30 (staged):");
|
|
183
|
+
lines.push(stagedOut);
|
|
184
|
+
}
|
|
185
|
+
if (unstagedOut) {
|
|
186
|
+
lines.push("\u270F\uFE0F \uC218\uC815\uB428 (unstaged):");
|
|
187
|
+
lines.push(unstagedOut);
|
|
188
|
+
}
|
|
189
|
+
if (untrackedOut) {
|
|
190
|
+
const files = untrackedOut.split("\n");
|
|
191
|
+
lines.push(`\u2795 \uC0C8 \uD30C\uC77C (${files.length}\uAC1C):`);
|
|
192
|
+
files.forEach((f) => lines.push(` + ${f}`));
|
|
193
|
+
}
|
|
194
|
+
const numstat = safeExecFile("git", ["diff", "--numstat", "HEAD"]);
|
|
195
|
+
if (numstat.ok && numstat.out) {
|
|
196
|
+
let totalAdd = 0;
|
|
197
|
+
let totalDel = 0;
|
|
198
|
+
let fileCount = 0;
|
|
199
|
+
numstat.out.split("\n").forEach((line) => {
|
|
200
|
+
const [add, del] = line.split(" ");
|
|
201
|
+
totalAdd += parseInt(add, 10) || 0;
|
|
202
|
+
totalDel += parseInt(del, 10) || 0;
|
|
203
|
+
fileCount += 1;
|
|
204
|
+
});
|
|
205
|
+
lines.push(`
|
|
206
|
+
\u{1F4CA} \uCD1D \uBCC0\uACBD: ${fileCount}\uAC1C \uD30C\uC77C, +${totalAdd}\uC904 -${totalDel}\uC904`);
|
|
207
|
+
}
|
|
208
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
209
|
+
});
|
|
210
|
+
server.registerTool("ship", { description: "\uBC30\uD3EC \uCCB4\uD06C\uB9AC\uC2A4\uD2B8 \uC2E4\uD589 (\uBE4C\uB4DC + \uD14C\uC2A4\uD2B8 + \uBC84\uC804 + git \uC0C1\uD0DC)" }, async () => {
|
|
211
|
+
const checks = [];
|
|
212
|
+
const build = safeExecFile("pnpm", ["build"]);
|
|
213
|
+
checks.push(build.ok ? "\u2705 \uBE4C\uB4DC \uC131\uACF5" : "\u274C \uBE4C\uB4DC \uC2E4\uD328");
|
|
214
|
+
const test = safeExecFile("pnpm", ["test", "--run"]);
|
|
215
|
+
checks.push(test.ok ? "\u2705 \uD14C\uC2A4\uD2B8 \uD1B5\uACFC" : "\u274C \uD14C\uC2A4\uD2B8 \uC2E4\uD328");
|
|
216
|
+
if (existsSync("package.json")) {
|
|
217
|
+
try {
|
|
218
|
+
const pkg = JSON.parse(readFileSync("package.json", "utf-8"));
|
|
219
|
+
checks.push(`\u{1F4E6} \uBC84\uC804: ${pkg.version}`);
|
|
220
|
+
} catch {
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (isGitRepo()) {
|
|
224
|
+
const status = safeExecFile("git", ["status", "--porcelain"]);
|
|
225
|
+
if (status.ok) {
|
|
226
|
+
if (status.out) {
|
|
227
|
+
checks.push(`\u26A0\uFE0F \uCEE4\uBC0B\uB418\uC9C0 \uC54A\uC740 \uBCC0\uACBD\uC0AC\uD56D ${status.out.split("\n").length}\uAC1C`);
|
|
228
|
+
} else {
|
|
229
|
+
checks.push("\u2705 \uC6CC\uD0B9 \uB514\uB809\uD1A0\uB9AC \uAE68\uB057\uD568");
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return { content: [{ type: "text", text: "\u{1F680} \uBC30\uD3EC \uCCB4\uD06C\uB9AC\uC2A4\uD2B8\n" + checks.join("\n") }] };
|
|
234
|
+
});
|
|
235
|
+
server.registerTool("doctor", { description: "\uAC1C\uBC1C \uD658\uACBD \uC810\uAC80 (Node/Git/npm/pnpm/TypeScript)" }, async () => {
|
|
236
|
+
const checks = [];
|
|
237
|
+
const node = safeExecFile("node", ["--version"]);
|
|
238
|
+
checks.push(node.ok ? `\u2705 Node.js: ${node.out}` : "\u274C Node.js: \uC124\uCE58 \uC548 \uB428");
|
|
239
|
+
const git = safeExecFile("git", ["--version"]);
|
|
240
|
+
checks.push(git.ok ? `\u2705 Git: ${git.out}` : "\u274C Git: \uC124\uCE58 \uC548 \uB428");
|
|
241
|
+
const pnpm = safeExecFile("pnpm", ["--version"]);
|
|
242
|
+
checks.push(pnpm.ok ? `\u2705 pnpm: v${pnpm.out}` : "\u26A0\uFE0F pnpm: \uC124\uCE58 \uC548 \uB428");
|
|
243
|
+
const npm = safeExecFile("npm", ["--version"]);
|
|
244
|
+
checks.push(npm.ok ? `\u2705 npm: v${npm.out}` : "\u274C npm: \uC124\uCE58 \uC548 \uB428");
|
|
245
|
+
const tsc = safeExecFile("npx", ["tsc", "--version"]);
|
|
246
|
+
checks.push(tsc.ok ? `\u2705 TypeScript: ${tsc.out}` : "\u26A0\uFE0F TypeScript: \uD504\uB85C\uC81D\uD2B8\uC5D0 \uC5C6\uC74C");
|
|
247
|
+
return { content: [{ type: "text", text: "\u{1FA7A} \uD658\uACBD \uC810\uAC80 \uACB0\uACFC\n" + checks.join("\n") }] };
|
|
248
|
+
});
|
|
249
|
+
server.registerTool("check", { description: "\uD504\uB85C\uC81D\uD2B8 \uAD6C\uC870 \uC810\uAC80 (\uD544\uC218 \uD30C\uC77C + VHK \uD558\uB124\uC2A4 \uD30C\uC77C)" }, async () => {
|
|
250
|
+
const required = ["package.json", "tsconfig.json", "README.md", ".gitignore"];
|
|
251
|
+
const recommended = ["CLAUDE.md", ".cursorrules", "docs/PRD.md", "docs/ARCHITECTURE.md"];
|
|
252
|
+
const lines = ["\u{1F50D} \uD504\uB85C\uC81D\uD2B8 \uC810\uAC80", "", "\uD544\uC218:"];
|
|
253
|
+
required.forEach((f) => {
|
|
254
|
+
lines.push(` ${existsSync(f) ? "\u2705" : "\u274C"} ${f}`);
|
|
255
|
+
});
|
|
256
|
+
lines.push("", "\uAD8C\uC7A5 (VHK \uD558\uB124\uC2A4):");
|
|
257
|
+
recommended.forEach((f) => {
|
|
258
|
+
lines.push(` ${existsSync(f) ? "\u2705" : "\u26A0\uFE0F"} ${f}`);
|
|
259
|
+
});
|
|
260
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
261
|
+
});
|
|
262
|
+
server.registerTool(
|
|
263
|
+
"recap",
|
|
264
|
+
{
|
|
265
|
+
description: "\uCD5C\uADFC \uC791\uC5C5 \uC694\uC57D (\uCEE4\uBC0B \uD788\uC2A4\uD1A0\uB9AC \uAE30\uBC18, \uB0A0\uC9DC \uD3EC\uD568)",
|
|
266
|
+
inputSchema: {
|
|
267
|
+
count: z.number().optional().describe("\uD45C\uC2DC\uD560 \uCEE4\uBC0B \uC218 (\uAE30\uBCF8: 10)")
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
async ({ count }) => {
|
|
271
|
+
if (!isGitRepo()) {
|
|
272
|
+
return { content: [{ type: "text", text: "\u274C git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2D9\uB2C8\uB2E4." }] };
|
|
273
|
+
}
|
|
274
|
+
const n = count && count > 0 ? Math.floor(count) : 10;
|
|
275
|
+
const log = safeExecFile("git", ["log", "--format=%h %ad %s", "--date=short", `-${n}`]);
|
|
276
|
+
if (!log.ok) {
|
|
277
|
+
return { content: [{ type: "text", text: `\u274C git log \uC2E4\uD328: ${log.err}` }] };
|
|
278
|
+
}
|
|
279
|
+
if (!log.out) {
|
|
280
|
+
return { content: [{ type: "text", text: "\u{1F4ED} \uCEE4\uBC0B \uD788\uC2A4\uD1A0\uB9AC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4." }] };
|
|
281
|
+
}
|
|
282
|
+
return { content: [{ type: "text", text: `\u{1F4CB} \uCD5C\uADFC \uC791\uC5C5 (${n}\uAC1C):
|
|
283
|
+
${log.out}` }] };
|
|
284
|
+
}
|
|
285
|
+
);
|
|
286
|
+
return server;
|
|
287
|
+
}
|
|
288
|
+
async function startMcpServer() {
|
|
289
|
+
const server = createVhkMcpServer();
|
|
290
|
+
const transport = new StdioServerTransport();
|
|
291
|
+
await server.connect(transport);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export {
|
|
295
|
+
__commonJS,
|
|
296
|
+
__toESM,
|
|
297
|
+
startMcpServer
|
|
298
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -1,29 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
));
|
|
2
|
+
import {
|
|
3
|
+
__commonJS,
|
|
4
|
+
__toESM,
|
|
5
|
+
startMcpServer
|
|
6
|
+
} from "./chunk-IU37BEQA.js";
|
|
27
7
|
|
|
28
8
|
// node_modules/.pnpm/ignore@7.0.5/node_modules/ignore/index.js
|
|
29
9
|
var require_ignore = __commonJS({
|
|
@@ -523,6 +503,12 @@ var RULES = [
|
|
|
523
503
|
confidence: "high",
|
|
524
504
|
test: (t2) => /ํ๋ก์ ํธ.*(๋ง๋ค|์์)|ํด๋.*๋ง๋ค|๋ง๋ค๊ณ \s*์ถ|ํ๋ค์ค|์ด๊ธฐํ/.test(t2) || /^์์$/.test(t2)
|
|
525
505
|
},
|
|
506
|
+
{
|
|
507
|
+
command: "mcp-init",
|
|
508
|
+
explanation: "Cursor MCP \uC5F0\uB3D9 \uC124\uC815 (vhk mcp-init)",
|
|
509
|
+
confidence: "high",
|
|
510
|
+
test: (t2) => /mcp.*(์ค์ |์ฐ๋|์ด๊ธฐ|init)|์ปค์.*(์ฐ๋|์ค์ |mcp)|cursor.*mcp/.test(t2)
|
|
511
|
+
},
|
|
526
512
|
{
|
|
527
513
|
command: "secure",
|
|
528
514
|
explanation: "\uBCF4\uC548 \uC2A4\uCE94 (vhk \uBCF4\uC548)",
|
|
@@ -647,6 +633,9 @@ var KNOWN_COMMAND_TOKENS = /* @__PURE__ */ new Set([
|
|
|
647
633
|
"diff",
|
|
648
634
|
"\uBCC0\uACBD",
|
|
649
635
|
"\uCC28\uC774",
|
|
636
|
+
"mcp",
|
|
637
|
+
"mcp-init",
|
|
638
|
+
"mcp\uC124\uC815",
|
|
650
639
|
"help"
|
|
651
640
|
]);
|
|
652
641
|
function isOptionToken(token) {
|
|
@@ -670,7 +659,7 @@ function detectNaturalLanguageInput(argv) {
|
|
|
670
659
|
}
|
|
671
660
|
|
|
672
661
|
// src/lib/nlp-run.ts
|
|
673
|
-
import
|
|
662
|
+
import chalk17 from "chalk";
|
|
674
663
|
import inquirer7 from "inquirer";
|
|
675
664
|
|
|
676
665
|
// src/i18n/ko.ts
|
|
@@ -915,6 +904,10 @@ var ko = {
|
|
|
915
904
|
changelogUpdated: (version) => `CHANGELOG.md \uAC31\uC2E0\uB428 \u2014 [Unreleased] \u2192 [${version}] \uC139\uC158\uC73C\uB85C \uC774\uB3D9`,
|
|
916
905
|
changelogNoUnreleased: "CHANGELOG.md\uC5D0 [Unreleased] \uC139\uC158\uC774 \uC5C6\uC5B4 \uC790\uB3D9 \uAC31\uC2E0\uC744 \uC2A4\uD0B5\uD588\uC5B4\uC694",
|
|
917
906
|
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."
|
|
907
|
+
},
|
|
908
|
+
mcp: {
|
|
909
|
+
initTitle: "Cursor MCP \uC5F0\uB3D9 \uC124\uC815",
|
|
910
|
+
serverStarted: "VHK MCP \uC11C\uBC84 \uC2DC\uC791\uB428"
|
|
918
911
|
}
|
|
919
912
|
};
|
|
920
913
|
function lookup(path15) {
|
|
@@ -3553,6 +3546,55 @@ ${t("diff.summaryHeader")}`));
|
|
|
3553
3546
|
console.log("");
|
|
3554
3547
|
}
|
|
3555
3548
|
|
|
3549
|
+
// src/commands/mcp-init.ts
|
|
3550
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
3551
|
+
import { join } from "path";
|
|
3552
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3553
|
+
import chalk16 from "chalk";
|
|
3554
|
+
function resolveVhkMcpPath() {
|
|
3555
|
+
try {
|
|
3556
|
+
const url = import.meta.resolve?.("@byh3071/vhk/dist/mcp/index.js");
|
|
3557
|
+
if (typeof url === "string") return fileURLToPath2(url);
|
|
3558
|
+
} catch {
|
|
3559
|
+
}
|
|
3560
|
+
return join(process.cwd(), "node_modules", "@byh3071", "vhk", "dist", "mcp", "index.js");
|
|
3561
|
+
}
|
|
3562
|
+
async function mcpInit() {
|
|
3563
|
+
console.log(chalk16.bold("\n\u{1F50C} " + t("mcp.initTitle")));
|
|
3564
|
+
console.log(chalk16.gray("\u2500".repeat(40)));
|
|
3565
|
+
const cursorDir = join(process.cwd(), ".cursor");
|
|
3566
|
+
if (!existsSync(cursorDir)) {
|
|
3567
|
+
mkdirSync(cursorDir, { recursive: true });
|
|
3568
|
+
}
|
|
3569
|
+
const configPath = join(cursorDir, "mcp.json");
|
|
3570
|
+
const vhkEntry = {
|
|
3571
|
+
command: "node",
|
|
3572
|
+
args: [resolveVhkMcpPath()]
|
|
3573
|
+
};
|
|
3574
|
+
let config;
|
|
3575
|
+
if (existsSync(configPath)) {
|
|
3576
|
+
try {
|
|
3577
|
+
const parsed = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
3578
|
+
config = {
|
|
3579
|
+
mcpServers: { ...parsed.mcpServers ?? {}, vhk: vhkEntry }
|
|
3580
|
+
};
|
|
3581
|
+
} catch {
|
|
3582
|
+
console.log(chalk16.yellow("\u26A0\uFE0F \uAE30\uC874 .cursor/mcp.json \uD30C\uC2F1 \uC2E4\uD328 \u2014 \uC0C8 \uD30C\uC77C\uB85C \uB36E\uC5B4\uC501\uB2C8\uB2E4."));
|
|
3583
|
+
config = { mcpServers: { vhk: vhkEntry } };
|
|
3584
|
+
}
|
|
3585
|
+
} else {
|
|
3586
|
+
config = { mcpServers: { vhk: vhkEntry } };
|
|
3587
|
+
}
|
|
3588
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
3589
|
+
console.log(chalk16.green("\n\u2705 Cursor MCP \uC124\uC815 \uC644\uB8CC!"));
|
|
3590
|
+
console.log(chalk16.cyan("\u{1F4C1} \uC0DD\uC131\uB41C \uD30C\uC77C:"));
|
|
3591
|
+
console.log(` ${configPath}`);
|
|
3592
|
+
console.log(chalk16.cyan("\n\u{1F504} \uB2E4\uC74C \uB2E8\uACC4:"));
|
|
3593
|
+
console.log(" 1. Cursor\uB97C \uC7AC\uC2DC\uC791\uD558\uC138\uC694");
|
|
3594
|
+
console.log(" 2. Cursor \uCC44\uD305\uC5D0\uC11C vhk \uB3C4\uAD6C\uB97C \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4");
|
|
3595
|
+
console.log(chalk16.gray('\n\u{1F4A1} \uC608: "\uD504\uB85C\uC81D\uD2B8 \uC0C1\uD0DC \uC54C\uB824\uC918" \u2192 Cursor\uAC00 vhk status \uD638\uCD9C'));
|
|
3596
|
+
}
|
|
3597
|
+
|
|
3556
3598
|
// src/lib/nlp-run.ts
|
|
3557
3599
|
async function dispatchNlpRoute(route, input) {
|
|
3558
3600
|
switch (route.command) {
|
|
@@ -3583,19 +3625,21 @@ async function dispatchNlpRoute(route, input) {
|
|
|
3583
3625
|
return status();
|
|
3584
3626
|
case "diff":
|
|
3585
3627
|
return diff();
|
|
3628
|
+
case "mcp-init":
|
|
3629
|
+
return mcpInit();
|
|
3586
3630
|
}
|
|
3587
3631
|
}
|
|
3588
3632
|
async function runNaturalLanguageRoute(input) {
|
|
3589
3633
|
const route = routeNaturalLanguage(input);
|
|
3590
3634
|
if (!route) {
|
|
3591
|
-
console.log(
|
|
3635
|
+
console.log(chalk17.yellow(`
|
|
3592
3636
|
\u2753 "${input}" \u2014 ${ko.nlp.notMatched}
|
|
3593
3637
|
`));
|
|
3594
3638
|
return;
|
|
3595
3639
|
}
|
|
3596
3640
|
console.log("");
|
|
3597
|
-
console.log(
|
|
3598
|
-
console.log(
|
|
3641
|
+
console.log(chalk17.cyan(` \u{1F4AC} "${input}"`));
|
|
3642
|
+
console.log(chalk17.cyan(` \u2192 ${route.explanation}`));
|
|
3599
3643
|
if (route.confidence === "low") {
|
|
3600
3644
|
const { confirm } = await inquirer7.prompt([{
|
|
3601
3645
|
type: "confirm",
|
|
@@ -3604,7 +3648,7 @@ async function runNaturalLanguageRoute(input) {
|
|
|
3604
3648
|
default: true
|
|
3605
3649
|
}]);
|
|
3606
3650
|
if (!confirm) {
|
|
3607
|
-
console.log(
|
|
3651
|
+
console.log(chalk17.dim(` ${ko.nlp.menuHint}`));
|
|
3608
3652
|
return;
|
|
3609
3653
|
}
|
|
3610
3654
|
}
|
|
@@ -3629,14 +3673,17 @@ var KO_ALIASES = {
|
|
|
3629
3673
|
status: "\uC0C1\uD0DC",
|
|
3630
3674
|
diff: "\uBCC0\uACBD"
|
|
3631
3675
|
};
|
|
3632
|
-
program.name("vhk").description("VHK \u2014 \uBC14\uC774\uBE0C\uCF54\uB529 \uD504\uB85C\uC81D\uD2B8 \uCF54\uCE58 (\uD55C\uAD6D\uC5B4\uB85C \uC548\uB0B4\uD569\uB2C8\uB2E4)").version("0.
|
|
3676
|
+
program.name("vhk").description("VHK \u2014 \uBC14\uC774\uBE0C\uCF54\uB529 \uD504\uB85C\uC81D\uD2B8 \uCF54\uCE58 (\uD55C\uAD6D\uC5B4\uB85C \uC548\uB0B4\uD569\uB2C8\uB2E4)").version("0.6.0");
|
|
3633
3677
|
program.configureHelp({
|
|
3634
3678
|
formatHelp(cmd, helper) {
|
|
3635
3679
|
if (cmd.parent) {
|
|
3636
3680
|
return defaultHelp.formatHelp(cmd, helper);
|
|
3637
3681
|
}
|
|
3638
3682
|
const subs = helper.visibleCommands(cmd).filter((c) => c.name() !== "help");
|
|
3639
|
-
const terms = subs.map((c) =>
|
|
3683
|
+
const terms = subs.map((c) => {
|
|
3684
|
+
const alias = KO_ALIASES[c.name()];
|
|
3685
|
+
return alias ? `${c.name()} (${alias})` : c.name();
|
|
3686
|
+
});
|
|
3640
3687
|
const termWidth = Math.max(...terms.map((t2) => t2.length), 0);
|
|
3641
3688
|
const lines = [
|
|
3642
3689
|
helper.commandDescription(cmd),
|
|
@@ -3669,6 +3716,12 @@ program.command("status").alias("\uC0C1\uD0DC").description("\uD504\uB85C\uC81D\
|
|
|
3669
3716
|
await status();
|
|
3670
3717
|
});
|
|
3671
3718
|
program.command("diff").alias("\uBCC0\uACBD").alias("\uCC28\uC774").description("Git \uBCC0\uACBD\uC0AC\uD56D \uD55C\uAD6D\uC5B4 \uC694\uC57D (staged / unstaged / \uC0C8 \uD30C\uC77C)").action(diff);
|
|
3719
|
+
program.command("mcp").description("MCP \uC11C\uBC84 \uC2DC\uC791 (Cursor \uB4F1 MCP \uD074\uB77C\uC774\uC5B8\uD2B8\uC6A9)").action(async () => {
|
|
3720
|
+
await startMcpServer();
|
|
3721
|
+
});
|
|
3722
|
+
program.command("mcp-init").alias("mcp\uC124\uC815").description("Cursor MCP \uC5F0\uB3D9 \uC124\uC815 \uC790\uB3D9 \uC0DD\uC131 (.cursor/mcp.json)").action(async () => {
|
|
3723
|
+
await mcpInit();
|
|
3724
|
+
});
|
|
3672
3725
|
program.on("command:*", async (operands) => {
|
|
3673
3726
|
const unknown = operands[0] ?? "";
|
|
3674
3727
|
const rest = operands.slice(1);
|
package/package.json
CHANGED
|
@@ -1,62 +1,65 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@byh3071/vhk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Vibe Harness Kit โ ๋ฐ์ด๋ธ์ฝ๋ฉ ํ์ฌ์ดํด CLI",
|
|
5
|
-
"bin": {
|
|
6
|
-
"vhk": "dist/index.js"
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"test
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
"
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
|
|
62
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@byh3071/vhk",
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "Vibe Harness Kit โ ๋ฐ์ด๋ธ์ฝ๋ฉ ํ์ฌ์ดํด CLI",
|
|
5
|
+
"bin": {
|
|
6
|
+
"vhk": "dist/index.js",
|
|
7
|
+
"vhk-mcp": "dist/mcp/index.js"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"dev": "tsx src/index.ts",
|
|
12
|
+
"build": "tsup",
|
|
13
|
+
"test": "vitest",
|
|
14
|
+
"test:run": "vitest --run",
|
|
15
|
+
"prepublishOnly": "pnpm build && pnpm test:run",
|
|
16
|
+
"save": "vhk save",
|
|
17
|
+
"check": "vhk check",
|
|
18
|
+
"scan": "vhk secure scan",
|
|
19
|
+
"recap": "vhk recap",
|
|
20
|
+
"ship": "vhk ship",
|
|
21
|
+
"doctor": "vhk doctor"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"README.md",
|
|
26
|
+
"LICENSE"
|
|
27
|
+
],
|
|
28
|
+
"keywords": [
|
|
29
|
+
"vibe-coding",
|
|
30
|
+
"harness",
|
|
31
|
+
"cli",
|
|
32
|
+
"scaffold",
|
|
33
|
+
"session-log",
|
|
34
|
+
"rules-sync"
|
|
35
|
+
],
|
|
36
|
+
"author": "byh3071 <byh3071@gmail.com>",
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/byh3071-cpu/vhk.git"
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=20"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
47
|
+
"@notionhq/client": "^5.22.0",
|
|
48
|
+
"chalk": "^5.6.2",
|
|
49
|
+
"commander": "^14.0.3",
|
|
50
|
+
"handlebars": "^4.7.9",
|
|
51
|
+
"inquirer": "^9.3.8",
|
|
52
|
+
"ora": "^9.4.0",
|
|
53
|
+
"simple-git": "^3.36.0",
|
|
54
|
+
"zod": "^4.4.3"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@types/inquirer": "^9.0.9",
|
|
58
|
+
"@types/node": "^25.9.1",
|
|
59
|
+
"ignore": "^7.0.5",
|
|
60
|
+
"tsup": "^8.5.1",
|
|
61
|
+
"tsx": "^4.22.3",
|
|
62
|
+
"typescript": "^6.0.3",
|
|
63
|
+
"vitest": "^4.1.7"
|
|
64
|
+
}
|
|
65
|
+
}
|