@byh3071/vhk 1.6.1 → 1.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -1
- package/dist/{chunk-O3A6SO7G.js → chunk-ACJN723Q.js} +146 -44
- package/dist/index.d.ts +4 -1
- package/dist/index.js +1957 -972
- package/dist/mcp/index.js +6 -1
- package/package.json +72 -71
package/README.md
CHANGED
|
@@ -29,6 +29,20 @@ AI 코딩 도구는 저마다 규칙 파일이 다르고(`.cursorrules`·`CLAUDE
|
|
|
29
29
|
>
|
|
30
30
|
> ℹ️ Windsurf·Copilot·Antigravity 출력 경로·포맷은 각 도구의 **공식 문서 기준**으로 생성합니다(`.windsurfrules` · `.github/copilot-instructions.md` · `.agents/rules/`). Antigravity 는 파일당 12,000자 제한이 있어 초과 시 안전하게 절삭하고 전체는 `RULES.md` 에 남습니다.
|
|
31
31
|
|
|
32
|
+
### Cursor / Copilot / Antigravity 에서 이렇게 말하세요
|
|
33
|
+
|
|
34
|
+
규칙 파일을 동기화하면, 에이전트 채팅창에서 명령을 외우지 않고 한국어로 말해도 됩니다:
|
|
35
|
+
|
|
36
|
+
| 하고 싶은 일 | 채팅창에 이렇게 | 실행되는 명령 |
|
|
37
|
+
|------|------|------|
|
|
38
|
+
| 규칙 한 벌로 동기화 | "규칙 동기화해줘" | `vhk sync` |
|
|
39
|
+
| 지금 상태 보기 | "상태 알려줘" | `vhk status` |
|
|
40
|
+
| 뭐 바뀌었는지 | "뭐 바뀌었어?" | `vhk diff` |
|
|
41
|
+
| 처음이라 막막함 | "처음 뭐 해?" / "도움말" | `vhk start` |
|
|
42
|
+
| 저장(커밋) | "저장해줘" | `vhk save` |
|
|
43
|
+
|
|
44
|
+
> MCP를 등록하면(`vhk mcp-init`) 위 문장이 곧바로 vhk 도구 호출로 이어집니다. RULES.md 한 벌이 다섯 도구에 같은 규칙을 깔아줍니다.
|
|
45
|
+
|
|
32
46
|
## 3분 안에 시작하기 (Getting Started)
|
|
33
47
|
|
|
34
48
|
### 1. 설치
|
|
@@ -156,7 +170,7 @@ vhk 기획 끝났고 바로 시작
|
|
|
156
170
|
| `vhk audit` | `감사` | npm/pnpm/yarn 보안 취약점 감사 (`--fix`로 자동 수정, npm만) |
|
|
157
171
|
| `vhk migrate [target]` | `전환` | 패키지 매니저 전환 (`npm` / `yarn` / `pnpm`, lockfile + node_modules 재구성) |
|
|
158
172
|
| `vhk update` | `업데이트` | VHK CLI 최신 버전으로 셀프 업데이트 |
|
|
159
|
-
| `vhk context` | `맥락` | 프로젝트 트리·스택·CLI 명령 목록을 `.vhk/context.md`로 자동 생성 (AI 어시스턴트용) |
|
|
173
|
+
| `vhk context` | `맥락` | 프로젝트 트리·스택·CLI 명령 목록을 `.vhk/context.md`로 자동 생성 (AI 어시스턴트용). `--compact` 로 토큰 절감형(Active Goal + 최근 blockers/learnings/memories + 참조 링크) 출력 |
|
|
160
174
|
| `vhk context-show` | `맥락보기` | 현재 컨텍스트 파일 내용 출력 |
|
|
161
175
|
| `vhk memory` | `기억` | 결정사항 기억 관리 (`add` / `list` / `remove`, `.vhk/memory.json` 기반, 태그 지원) |
|
|
162
176
|
| `vhk brief` | `브리핑` | 프로젝트 정보 + git 상태 + 결정사항 + 레퍼런스 통합 보고서 `.vhk/brief.md` |
|
|
@@ -562,7 +562,7 @@ var ko = {
|
|
|
562
562
|
notGitRepo: "Git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2C8\uC5D0\uC694. \uBA3C\uC800 git init\uC744 \uC2E4\uD589\uD558\uC138\uC694.",
|
|
563
563
|
branch: "\uBE0C\uB79C\uCE58:",
|
|
564
564
|
changes: "\uBCC0\uACBD:",
|
|
565
|
-
recentCommits:
|
|
565
|
+
recentCommits: (n) => `\uCD5C\uADFC \uCEE4\uBC0B (${n}):`,
|
|
566
566
|
noCommits: "\uCEE4\uBC0B \uC5C6\uC74C",
|
|
567
567
|
remote: "\uC6D0\uACA9:",
|
|
568
568
|
noUpstream: "upstream \uC5C6\uC74C",
|
|
@@ -573,8 +573,9 @@ var ko = {
|
|
|
573
573
|
noPackage: "package.json \uC5C6\uC74C",
|
|
574
574
|
detached: "(detached HEAD)",
|
|
575
575
|
unknownBranch: "(\uC54C \uC218 \uC5C6\uC74C)",
|
|
576
|
-
nextWithChangesMessage: "\uBCC0\uACBD\uC0AC\uD56D\uC774 \uC788\uC5B4\uC694. \
|
|
577
|
-
nextWithChangesCursor: "\
|
|
576
|
+
nextWithChangesMessage: "\uBCC0\uACBD\uC0AC\uD56D\uC774 \uC788\uC5B4\uC694. \uBA3C\uC800 \uBB34\uC5C7\uC774 \uBC14\uB00C\uC5C8\uB294\uC9C0 \uD655\uC778\uD558\uC138\uC694.",
|
|
577
|
+
nextWithChangesCursor: "\uBB50 \uBC14\uB00C\uC5C8\uC5B4?",
|
|
578
|
+
nextWithChangesAlt: "\uD655\uC778\uD588\uC73C\uBA74 vhk save \uB85C \uC800\uC7A5\uD558\uC138\uC694",
|
|
578
579
|
nextCleanMessage: "\uD074\uB9B0 \uC0C1\uD0DC! \uB2E4\uC74C \uBBF8\uC158\uC73C\uB85C \uB118\uC5B4\uAC00\uC138\uC694.",
|
|
579
580
|
nextCleanCursor: "\uB2E4\uC74C \uBAA9\uD45C \uC54C\uB824\uC918"
|
|
580
581
|
},
|
|
@@ -719,7 +720,10 @@ var ko = {
|
|
|
719
720
|
commandsMdDone: "\u{1F4CB} COMMANDS.md \uC0DD\uC131",
|
|
720
721
|
scriptsDone: "\u{1F4E6} package.json scripts \uCD94\uAC00",
|
|
721
722
|
gitignoreCreated: "\u{1F512} .gitignore \uC0DD\uC131 (.env\xB7node_modules\xB7dist \uC81C\uC678)",
|
|
722
|
-
gitignoreUpdated: "\u{1F512} .gitignore \uBCF4\uAC15 (\uB204\uB77D \uD56D\uBAA9 \uCD94\uAC00)"
|
|
723
|
+
gitignoreUpdated: "\u{1F512} .gitignore \uBCF4\uAC15 (\uB204\uB77D \uD56D\uBAA9 \uCD94\uAC00)",
|
|
724
|
+
adoptPrompt: (n, list) => `\u{1F4E5} \uAE30\uC874 \uADDC\uCE59 \uD30C\uC77C ${n}\uAC1C \uBC1C\uACAC (${list}). RULES.md\uB85C \uAC00\uC838\uC62C\uAE4C\uC694?`,
|
|
725
|
+
adoptPreview: (n) => `\uAE30\uC874 \uADDC\uCE59 ${n}\uAC1C\uB97C RULES.md \uD45C\uC900 \uC139\uC158\uC73C\uB85C \uBCD1\uD569\uD588\uC5B4\uC694 (\uCD9C\uCC98 \uC8FC\uC11D \uD3EC\uD568).`,
|
|
726
|
+
adoptDone: "\u{1F4E5} RULES.md \u2014 \uAE30\uC874 \uADDC\uCE59 adopt \uC644\uB8CC"
|
|
723
727
|
},
|
|
724
728
|
recap: {
|
|
725
729
|
title: "\u{1F4DD} \uC624\uB298 \uD55C \uC77C \uC815\uB9AC",
|
|
@@ -783,8 +787,29 @@ var ko = {
|
|
|
783
787
|
windsurfDone: "\u2705 .windsurfrules \uB9DE\uCDA4 \uC644\uB8CC",
|
|
784
788
|
copilotDone: "\u2705 .github/copilot-instructions.md \uB9DE\uCDA4 \uC644\uB8CC",
|
|
785
789
|
antigravityDone: "\u2705 .agents/rules/vhk-rules.md \uB9DE\uCDA4 \uC644\uB8CC",
|
|
790
|
+
agentsDone: "\u2705 AGENTS.md \uB9DE\uCDA4 \uC644\uB8CC",
|
|
786
791
|
antigravityTruncated: "Antigravity 12,000\uC790 \uC81C\uD55C\uC73C\uB85C \uC77C\uBD80 \uC808\uC0AD\uB428 \u2014 \uC804\uCCB4\uB294 RULES.md \uCC38\uC870",
|
|
787
|
-
done: "\u{1F504} \uB9DE\uCD94\uAE30 \uC644\uB8CC!"
|
|
792
|
+
done: "\u{1F504} \uB9DE\uCD94\uAE30 \uC644\uB8CC!",
|
|
793
|
+
// 안전 가드 (배치 0) — 덮어쓰기 전 백업·드리프트 확인·미리보기
|
|
794
|
+
backupSaved: (n, id) => `\u{1F6DF} \uB36E\uC5B4\uC4F0\uAE30 \uC804 ${n}\uAC1C \uD30C\uC77C \uBC31\uC5C5\uD568 \u2192 .vhk/backups/${id} (\uBCF5\uC6D0: vhk restore)`,
|
|
795
|
+
firstSync: "\u{1F6DF} \uCCAB sync \u2014 \uAE30\uC874 \uD30C\uC77C\uC744 \uBC31\uC5C5\uD55C \uB4A4 \uC0DD\uC131\uD569\uB2C8\uB2E4.",
|
|
796
|
+
driftWarn: (p) => `\u26A0\uFE0F ${p} \uAC00 RULES.md \uC0DD\uC131\uBCF8\uACFC \uB2E4\uB985\uB2C8\uB2E4 (\uC9C1\uC811 \uC218\uC815\uD588\uC744 \uC218 \uC788\uC5B4\uC694).`,
|
|
797
|
+
driftConfirm: (n) => `\uC704 ${n}\uAC1C \uD30C\uC77C\uC758 \uAE30\uC874 \uB0B4\uC6A9\uC744 \uB36E\uC5B4\uC4F8\uAE4C\uC694? (\uBC31\uC5C5\uC740 \uC774\uBBF8 \uC800\uC7A5\uB428)`,
|
|
798
|
+
skipped: (p) => `\u23ED\uFE0F \uAC74\uB108\uB700: ${p} (\uB36E\uC5B4\uC4F0\uAE30 \uAC70\uBD80 \u2014 \uBC31\uC5C5\uB9CC \uBCF4\uAD00)`,
|
|
799
|
+
dryRunHeader: "\u{1F50E} \uBBF8\uB9AC\uBCF4\uAE30 (--dry-run) \u2014 \uC2E4\uC81C \uD30C\uC77C \uBCC0\uACBD \uC5C6\uC74C",
|
|
800
|
+
dryRunWouldWrite: (p, drift) => ` ${drift ? "\u270F\uFE0F \uBCC0\uACBD\uB428" : "\xB7 \uB3D9\uC77C"} : ${p}`,
|
|
801
|
+
nonTtyAuto: (n, id) => `\u{1F916} \uBE44\uB300\uD654\uD615(CI/\uC5D0\uC774\uC804\uD2B8) \u2014 ${n}\uAC1C \uBC31\uC5C5 \uD6C4 \uC9C4\uD589. \uBCF5\uC6D0: vhk restore ${id}`
|
|
802
|
+
},
|
|
803
|
+
restore: {
|
|
804
|
+
title: "\u{1F6DF} \uBC31\uC5C5 \uBCF5\uC6D0",
|
|
805
|
+
notGitNote: "\uBC31\uC5C5\uC740 .vhk/backups/ \uC758 \uB85C\uCEEC \uBCF5\uC0AC\uBCF8\uC5D0\uC11C \uBCF5\uC6D0\uB429\uB2C8\uB2E4 (git \uBB34\uAD00).",
|
|
806
|
+
noBackups: "\uBCF5\uC6D0\uD560 \uBC31\uC5C5\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. (vhk sync \uAC00 \uB36E\uC5B4\uC4F0\uAE30 \uC804 \uC790\uB3D9 \uC0DD\uC131)",
|
|
807
|
+
selectPrompt: "\uBCF5\uC6D0\uD560 \uBC31\uC5C5\uC744 \uC120\uD0DD\uD558\uC138\uC694:",
|
|
808
|
+
listHeader: "\u{1F4CB} \uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uBC31\uC5C5 (\uCD5C\uC2E0\uC21C):",
|
|
809
|
+
restored: (n, id) => `\u2705 ${n}\uAC1C \uD30C\uC77C \uBCF5\uC6D0 \uC644\uB8CC (\uBC31\uC5C5 ${id})`,
|
|
810
|
+
notFound: (id) => `\u274C \uBC31\uC5C5\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${id}`,
|
|
811
|
+
nonTtyHint: "\uBE44\uB300\uD654\uD615 \uBAA8\uB4DC \u2014 \uBCF5\uC6D0\uD560 \uBC31\uC5C5 id \uB97C \uC778\uC790\uB85C \uC9C0\uC815\uD558\uC138\uC694: vhk restore <id>",
|
|
812
|
+
cancelled: "\uBCF5\uC6D0 \uCDE8\uC18C\uB428"
|
|
788
813
|
},
|
|
789
814
|
cloud: {
|
|
790
815
|
pushTitle: "\u2601\uFE0F .vhk \uD074\uB77C\uC6B0\uB4DC \uBC31\uC5C5 (gist \uC62C\uB9AC\uAE30)",
|
|
@@ -1064,11 +1089,30 @@ ${t("deploy.deploying")}
|
|
|
1064
1089
|
}
|
|
1065
1090
|
|
|
1066
1091
|
// src/commands/env.ts
|
|
1067
|
-
import { existsSync as existsSync2, readFileSync, writeFileSync, appendFileSync } from "fs";
|
|
1092
|
+
import { existsSync as existsSync2, readFileSync, writeFileSync, appendFileSync, readdirSync } from "fs";
|
|
1093
|
+
import { join } from "path";
|
|
1068
1094
|
import chalk3 from "chalk";
|
|
1069
1095
|
function parseEnvKeys(content) {
|
|
1070
1096
|
return content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).map((line) => line.split("=")[0].trim()).filter(Boolean);
|
|
1071
1097
|
}
|
|
1098
|
+
function loadDefinedEnvKeys(dir = ".") {
|
|
1099
|
+
const keys = /* @__PURE__ */ new Set();
|
|
1100
|
+
let entries;
|
|
1101
|
+
try {
|
|
1102
|
+
entries = readdirSync(dir);
|
|
1103
|
+
} catch {
|
|
1104
|
+
return [];
|
|
1105
|
+
}
|
|
1106
|
+
for (const name of entries) {
|
|
1107
|
+
if (!name.startsWith(".env")) continue;
|
|
1108
|
+
if (name.endsWith(".example") || name.endsWith(".sample")) continue;
|
|
1109
|
+
try {
|
|
1110
|
+
for (const k of parseEnvKeys(readFileSync(join(dir, name), "utf-8"))) keys.add(k);
|
|
1111
|
+
} catch {
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
return [...keys];
|
|
1115
|
+
}
|
|
1072
1116
|
function ensureGitignore() {
|
|
1073
1117
|
const gitignorePath = ".gitignore";
|
|
1074
1118
|
if (existsSync2(gitignorePath)) {
|
|
@@ -1116,7 +1160,7 @@ async function envCheck() {
|
|
|
1116
1160
|
return;
|
|
1117
1161
|
}
|
|
1118
1162
|
const requiredKeys = parseEnvKeys(readFileSync(".env.example", "utf-8"));
|
|
1119
|
-
const currentKeys =
|
|
1163
|
+
const currentKeys = loadDefinedEnvKeys();
|
|
1120
1164
|
const missing = requiredKeys.filter((k) => !currentKeys.includes(k));
|
|
1121
1165
|
const extra = currentKeys.filter((k) => !requiredKeys.includes(k));
|
|
1122
1166
|
console.log(chalk3.cyan(`
|
|
@@ -1127,6 +1171,7 @@ async function envCheck() {
|
|
|
1127
1171
|
console.log(chalk3.red(`
|
|
1128
1172
|
\u274C \uB204\uB77D\uB41C \uD658\uACBD\uBCC0\uC218 (${missing.length}\uAC1C):`));
|
|
1129
1173
|
missing.forEach((k) => console.log(chalk3.red(` \u2022 ${k}`)));
|
|
1174
|
+
process.exitCode = 1;
|
|
1130
1175
|
}
|
|
1131
1176
|
if (extra.length > 0) {
|
|
1132
1177
|
console.log(chalk3.yellow(`
|
|
@@ -1403,13 +1448,13 @@ async function audit(autoFix = false) {
|
|
|
1403
1448
|
|
|
1404
1449
|
// src/lib/version.ts
|
|
1405
1450
|
import { existsSync as existsSync5 } from "fs";
|
|
1406
|
-
import { dirname, join } from "path";
|
|
1451
|
+
import { dirname, join as join2 } from "path";
|
|
1407
1452
|
import { fileURLToPath } from "url";
|
|
1408
1453
|
function getVhkVersion() {
|
|
1409
1454
|
const dir = dirname(fileURLToPath(import.meta.url));
|
|
1410
1455
|
for (const pkgPath of [
|
|
1411
|
-
|
|
1412
|
-
|
|
1456
|
+
join2(dir, "../../package.json"),
|
|
1457
|
+
join2(dir, "../package.json")
|
|
1413
1458
|
]) {
|
|
1414
1459
|
try {
|
|
1415
1460
|
if (existsSync5(pkgPath)) {
|
|
@@ -1423,11 +1468,33 @@ function getVhkVersion() {
|
|
|
1423
1468
|
return "0.0.0";
|
|
1424
1469
|
}
|
|
1425
1470
|
|
|
1471
|
+
// src/mcp/cli-path.ts
|
|
1472
|
+
import { existsSync as existsSync6 } from "fs";
|
|
1473
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1474
|
+
import { dirname as dirname2, resolve } from "path";
|
|
1475
|
+
function pickCliInvocation(globalAvailable, localCli, localExists) {
|
|
1476
|
+
if (globalAvailable) return { bin: "vhk", prefixArgs: [], fallback: false };
|
|
1477
|
+
if (localExists) return { bin: process.execPath, prefixArgs: [localCli], fallback: true };
|
|
1478
|
+
return { bin: "vhk", prefixArgs: [], fallback: false };
|
|
1479
|
+
}
|
|
1480
|
+
function composeInvocation(cli, args) {
|
|
1481
|
+
return { bin: cli.bin, args: [...cli.prefixArgs, ...args] };
|
|
1482
|
+
}
|
|
1483
|
+
function localCliPath() {
|
|
1484
|
+
const here = dirname2(fileURLToPath2(import.meta.url));
|
|
1485
|
+
return resolve(here, "..", "index.js");
|
|
1486
|
+
}
|
|
1487
|
+
function resolveVhkCliInvocation() {
|
|
1488
|
+
const globalAvailable = safeExecFile("vhk", ["--version"]).ok;
|
|
1489
|
+
const local = localCliPath();
|
|
1490
|
+
return pickCliInvocation(globalAvailable, local, existsSync6(local));
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1426
1493
|
// src/mcp/server.ts
|
|
1427
1494
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
1428
1495
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
1429
1496
|
import { z } from "zod";
|
|
1430
|
-
import { existsSync as
|
|
1497
|
+
import { existsSync as existsSync7, readFileSync as readFileSync4, writeFileSync as writeFileSync3, appendFileSync as appendFileSync2 } from "fs";
|
|
1431
1498
|
|
|
1432
1499
|
// src/lib/scan-secrets.ts
|
|
1433
1500
|
import fs3 from "fs";
|
|
@@ -1545,6 +1612,7 @@ function findExposedSensitiveFiles(rootDir, ig = loadGitignore(rootDir), maxDept
|
|
|
1545
1612
|
}
|
|
1546
1613
|
function isSensitiveName(name) {
|
|
1547
1614
|
const lower = name.toLowerCase();
|
|
1615
|
+
if (lower.endsWith(".example") || lower.endsWith(".sample")) return false;
|
|
1548
1616
|
if (lower === ".env" || lower.startsWith(".env.")) return true;
|
|
1549
1617
|
if (lower.endsWith(".pem") || lower.endsWith(".key")) return true;
|
|
1550
1618
|
if (lower === "credentials.json" || lower === "secrets.json") return true;
|
|
@@ -1721,8 +1789,15 @@ var ANSI_RE = /\x1B\[[0-9;?]*[ -/]*[@-~]/g;
|
|
|
1721
1789
|
function stripAnsi(s) {
|
|
1722
1790
|
return s.replace(ANSI_RE, "");
|
|
1723
1791
|
}
|
|
1792
|
+
var cachedCli = null;
|
|
1793
|
+
function getVhkCli() {
|
|
1794
|
+
return cachedCli ??= resolveVhkCliInvocation();
|
|
1795
|
+
}
|
|
1724
1796
|
function runVhkCli(args, headline) {
|
|
1725
|
-
const
|
|
1797
|
+
const { bin, args: fullArgs } = composeInvocation(getVhkCli(), args);
|
|
1798
|
+
const result = safeExecFile(bin, fullArgs, {
|
|
1799
|
+
env: { FORCE_COLOR: "0", NO_COLOR: "1" }
|
|
1800
|
+
});
|
|
1726
1801
|
const body = stripAnsi(result.out || (result.ok ? "" : `(stdout \uC5C6\uC74C)
|
|
1727
1802
|
${result.err}`));
|
|
1728
1803
|
const prefix = result.ok ? `\u2705 ${headline}` : `\u274C ${headline} \uC2E4\uD328`;
|
|
@@ -1795,32 +1870,56 @@ ${preview}${more}
|
|
|
1795
1870
|
};
|
|
1796
1871
|
}
|
|
1797
1872
|
);
|
|
1798
|
-
server.registerTool(
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
}
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
{
|
|
1813
|
-
|
|
1814
|
-
|
|
1873
|
+
server.registerTool(
|
|
1874
|
+
"undo",
|
|
1875
|
+
{
|
|
1876
|
+
description: "\uCD5C\uADFC \uCEE4\uBC0B \uB418\uB3CC\uB9AC\uAE30 (soft reset). \uAE30\uBCF8\uC740 \uBBF8\uB9AC\uBCF4\uAE30 \u2014 confirm:true \uC77C \uB54C\uB9CC \uC2E4\uC81C \uC2E4\uD589.",
|
|
1877
|
+
inputSchema: {
|
|
1878
|
+
confirm: z.boolean().optional().describe("true \uC77C \uB54C\uB9CC \uC2E4\uC81C git reset \uC2E4\uD589 (\uAE30\uBCF8 false = \uBBF8\uB9AC\uBCF4\uAE30\uB9CC)")
|
|
1879
|
+
}
|
|
1880
|
+
},
|
|
1881
|
+
async ({ confirm }) => {
|
|
1882
|
+
if (!isGitRepo()) {
|
|
1883
|
+
return { content: [{ type: "text", text: "\u274C git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2D9\uB2C8\uB2E4." }] };
|
|
1884
|
+
}
|
|
1885
|
+
const last = safeExecFile("git", ["log", "--oneline", "-1"]);
|
|
1886
|
+
if (!last.ok || !last.out) {
|
|
1887
|
+
return { content: [{ type: "text", text: "\u{1F4ED} \uB418\uB3CC\uB9B4 \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4." }] };
|
|
1888
|
+
}
|
|
1889
|
+
if (!confirm) {
|
|
1890
|
+
return {
|
|
1891
|
+
content: [
|
|
1892
|
+
{
|
|
1893
|
+
type: "text",
|
|
1894
|
+
text: `\u{1F50E} \uBBF8\uB9AC\uBCF4\uAE30 \u2014 \uB418\uB3CC\uB9B4 \uCEE4\uBC0B:
|
|
1895
|
+
${last.out}
|
|
1896
|
+
|
|
1897
|
+
\uC2E4\uC81C\uB85C \uB418\uB3CC\uB9AC\uB824\uBA74 confirm: true \uB85C \uB2E4\uC2DC \uD638\uCD9C\uD558\uC138\uC694.
|
|
1898
|
+
(soft reset \u2014 \uBCC0\uACBD\uC0AC\uD56D\uC740 \uC2A4\uD14C\uC774\uC9D5 \uC601\uC5ED\uC5D0 \uBCF4\uC874\uB429\uB2C8\uB2E4.)
|
|
1899
|
+
\uB610\uB294 \uD130\uBBF8\uB110\uC5D0\uC11C \`vhk undo\` (\uB300\uD654\uD615 \uD655\uC778).`
|
|
1900
|
+
}
|
|
1901
|
+
]
|
|
1902
|
+
};
|
|
1903
|
+
}
|
|
1904
|
+
const reset = safeExecFile("git", ["reset", "--soft", "HEAD~1"]);
|
|
1905
|
+
if (!reset.ok) {
|
|
1906
|
+
return { content: [{ type: "text", text: `\u274C reset \uC2E4\uD328: ${reset.err}` }] };
|
|
1907
|
+
}
|
|
1908
|
+
return {
|
|
1909
|
+
content: [
|
|
1910
|
+
{
|
|
1911
|
+
type: "text",
|
|
1912
|
+
text: `\u2705 \uB418\uB3CC\uB9AC\uAE30 \uC644\uB8CC!
|
|
1815
1913
|
\uCDE8\uC18C\uB41C \uCEE4\uBC0B: ${last.out}
|
|
1816
1914
|
\u{1F4A1} \uBCC0\uACBD\uC0AC\uD56D\uC740 \uC2A4\uD14C\uC774\uC9D5 \uC601\uC5ED\uC5D0 \uB0A8\uC544\uC788\uC2B5\uB2C8\uB2E4.`
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1915
|
+
}
|
|
1916
|
+
]
|
|
1917
|
+
};
|
|
1918
|
+
}
|
|
1919
|
+
);
|
|
1821
1920
|
server.registerTool("status", { description: "\uD504\uB85C\uC81D\uD2B8 \uC0C1\uD0DC \uB300\uC2DC\uBCF4\uB4DC (\uBE0C\uB79C\uCE58/\uBCC0\uACBD\uC0AC\uD56D/\uCD5C\uADFC \uCEE4\uBC0B)" }, async () => {
|
|
1822
1921
|
const lines = [];
|
|
1823
|
-
if (
|
|
1922
|
+
if (existsSync7("package.json")) {
|
|
1824
1923
|
try {
|
|
1825
1924
|
const pkg = readJsonFile("package.json");
|
|
1826
1925
|
lines.push(`\u{1F4E6} \uD504\uB85C\uC81D\uD2B8: ${pkg.name ?? "(\uC774\uB984 \uC5C6\uC74C)"} v${pkg.version ?? "?"}`);
|
|
@@ -1905,7 +2004,7 @@ ${preview}${more}
|
|
|
1905
2004
|
checks.push(build.ok ? "\u2705 \uBE4C\uB4DC \uC131\uACF5" : "\u274C \uBE4C\uB4DC \uC2E4\uD328");
|
|
1906
2005
|
const test = safeExecFile("pnpm", ["test", "--run"]);
|
|
1907
2006
|
checks.push(test.ok ? "\u2705 \uD14C\uC2A4\uD2B8 \uD1B5\uACFC" : "\u274C \uD14C\uC2A4\uD2B8 \uC2E4\uD328");
|
|
1908
|
-
if (
|
|
2007
|
+
if (existsSync7("package.json")) {
|
|
1909
2008
|
try {
|
|
1910
2009
|
const pkg = readJsonFile("package.json");
|
|
1911
2010
|
checks.push(`\u{1F4E6} \uBC84\uC804: ${pkg.version}`);
|
|
@@ -1943,11 +2042,11 @@ ${preview}${more}
|
|
|
1943
2042
|
const recommended = ["CLAUDE.md", ".cursorrules", "docs/PRD.md", "docs/ARCHITECTURE.md"];
|
|
1944
2043
|
const lines = ["\u{1F50D} \uD504\uB85C\uC81D\uD2B8 \uC810\uAC80", "", "\uD544\uC218:"];
|
|
1945
2044
|
required.forEach((f) => {
|
|
1946
|
-
lines.push(` ${
|
|
2045
|
+
lines.push(` ${existsSync7(f) ? "\u2705" : "\u274C"} ${f}`);
|
|
1947
2046
|
});
|
|
1948
2047
|
lines.push("", "\uAD8C\uC7A5 (VHK \uD558\uB124\uC2A4):");
|
|
1949
2048
|
recommended.forEach((f) => {
|
|
1950
|
-
lines.push(` ${
|
|
2049
|
+
lines.push(` ${existsSync7(f) ? "\u2705" : "\u26A0\uFE0F"} ${f}`);
|
|
1951
2050
|
});
|
|
1952
2051
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
1953
2052
|
});
|
|
@@ -1981,7 +2080,7 @@ ${log.out}` }] };
|
|
|
1981
2080
|
description: ".env \u2192 .env.example \uB3D9\uAE30\uD654 + .gitignore\uC5D0 .env \uC790\uB3D9 \uCD94\uAC00"
|
|
1982
2081
|
},
|
|
1983
2082
|
async () => {
|
|
1984
|
-
if (!
|
|
2083
|
+
if (!existsSync7(".env")) {
|
|
1985
2084
|
return { content: [{ type: "text", text: "\u26A0\uFE0F .env \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 .env\uB97C \uB9CC\uB4E4\uC5B4\uC8FC\uC138\uC694." }] };
|
|
1986
2085
|
}
|
|
1987
2086
|
const keys = parseEnvKeys(readFileSync4(".env", "utf-8"));
|
|
@@ -1991,7 +2090,7 @@ ${log.out}` }] };
|
|
|
1991
2090
|
const exampleContent = keys.map((k) => `${k}=`).join("\n") + "\n";
|
|
1992
2091
|
writeFileSync3(".env.example", exampleContent, "utf-8");
|
|
1993
2092
|
const gitignoreLines = [];
|
|
1994
|
-
if (
|
|
2093
|
+
if (existsSync7(".gitignore")) {
|
|
1995
2094
|
const content = readFileSync4(".gitignore", "utf-8");
|
|
1996
2095
|
if (!content.split("\n").some((l) => l.trim() === ".env")) {
|
|
1997
2096
|
appendFileSync2(".gitignore", "\n.env\n");
|
|
@@ -2012,11 +2111,11 @@ ${log.out}` }] };
|
|
|
2012
2111
|
description: "\uD544\uC218 \uD658\uACBD\uBCC0\uC218 \uB204\uB77D \uAC80\uC0AC (.env.example \uAE30\uC900)"
|
|
2013
2112
|
},
|
|
2014
2113
|
async () => {
|
|
2015
|
-
if (!
|
|
2114
|
+
if (!existsSync7(".env.example")) {
|
|
2016
2115
|
return { content: [{ type: "text", text: "\u26A0\uFE0F .env.example\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 env \uB3C4\uAD6C\uB97C \uC2E4\uD589\uD558\uC138\uC694." }] };
|
|
2017
2116
|
}
|
|
2018
2117
|
const requiredKeys = parseEnvKeys(readFileSync4(".env.example", "utf-8"));
|
|
2019
|
-
const currentKeys =
|
|
2118
|
+
const currentKeys = existsSync7(".env") ? parseEnvKeys(readFileSync4(".env", "utf-8")) : [];
|
|
2020
2119
|
const missing = requiredKeys.filter((k) => !currentKeys.includes(k));
|
|
2021
2120
|
const extra = currentKeys.filter((k) => !requiredKeys.includes(k));
|
|
2022
2121
|
const lines = [`\u{1F4CB} \uD544\uC218 \uD658\uACBD\uBCC0\uC218: ${requiredKeys.length}\uAC1C`];
|
|
@@ -2035,7 +2134,9 @@ ${log.out}` }] };
|
|
|
2035
2134
|
);
|
|
2036
2135
|
server.registerTool(
|
|
2037
2136
|
"sync",
|
|
2038
|
-
{
|
|
2137
|
+
{
|
|
2138
|
+
description: "RULES.md \u2192 Cursor\xB7Claude\xB7Windsurf\xB7Copilot\xB7Antigravity\xB7AGENTS.md \uADDC\uCE59 \uB3D9\uAE30\uD654. \uB36E\uC5B4\uC4F0\uAE30 \uC804 \uAE30\uC874 \uD30C\uC77C\uC744 \uC790\uB3D9 \uBC31\uC5C5\uD558\uBBC0\uB85C \uC548\uC804\uD558\uBA70, \uB418\uB3CC\uB9AC\uB824\uBA74 \uC0AC\uC6A9\uC790\uC5D0\uAC8C `vhk restore` \uB97C \uC548\uB0B4\uD558\uC138\uC694."
|
|
2139
|
+
},
|
|
2039
2140
|
async () => runVhkCli(["sync"], "sync")
|
|
2040
2141
|
);
|
|
2041
2142
|
server.registerTool(
|
|
@@ -2130,7 +2231,7 @@ ${cliStatus}
|
|
|
2130
2231
|
description: "\uD604\uC7AC \uBC84\uC804 + bump \uD6C4\uBCF4 \uD45C\uC2DC (MCP \uBAA8\uB4DC: \uC2E4\uC81C npm publish \uBBF8\uC218\uD589 \u2014 `vhk publish` \uC548\uB0B4)"
|
|
2131
2232
|
},
|
|
2132
2233
|
async () => {
|
|
2133
|
-
if (!
|
|
2234
|
+
if (!existsSync7("package.json")) {
|
|
2134
2235
|
return { content: [{ type: "text", text: "\u274C package.json \uC5C6\uC74C." }] };
|
|
2135
2236
|
}
|
|
2136
2237
|
try {
|
|
@@ -2158,7 +2259,7 @@ ${cliStatus}
|
|
|
2158
2259
|
description: "\uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800 \uAC10\uC9C0 + \uC804\uD658 \uD6C4\uBCF4 \uAC00\uC6A9\uC131 (MCP \uBAA8\uB4DC: \uC2E4\uC81C \uC804\uD658 \uBBF8\uC218\uD589 \u2014 `vhk migrate <target>` \uC548\uB0B4)"
|
|
2159
2260
|
},
|
|
2160
2261
|
async () => {
|
|
2161
|
-
const current =
|
|
2262
|
+
const current = existsSync7("pnpm-lock.yaml") ? "pnpm" : existsSync7("yarn.lock") ? "yarn" : existsSync7("package-lock.json") ? "npm" : null;
|
|
2162
2263
|
const candidates = ["npm", "yarn", "pnpm"].filter((pm) => pm !== current);
|
|
2163
2264
|
const lines = [`\uD604\uC7AC PM: ${current ?? "\uAC10\uC9C0 \uBD88\uAC00 (lock \uD30C\uC77C \uC5C6\uC74C)"}`];
|
|
2164
2265
|
for (const pm of candidates) {
|
|
@@ -2241,5 +2342,6 @@ export {
|
|
|
2241
2342
|
publish,
|
|
2242
2343
|
audit,
|
|
2243
2344
|
getVhkVersion,
|
|
2345
|
+
resolveVhkCliInvocation,
|
|
2244
2346
|
startMcpServer
|
|
2245
2347
|
};
|
package/dist/index.d.ts
CHANGED