@byh3071/vhk 1.4.0 → 1.5.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/README.md +22 -7
- package/dist/{chunk-3DV7AEN4.js → chunk-4KWZANQG.js} +6 -1
- package/dist/index.js +72 -24
- package/dist/mcp/index.js +1 -1
- package/package.json +10 -3
package/README.md
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: vhk-readme
|
|
3
3
|
date: 2026-05-28
|
|
4
|
-
tags: [vhk, cli, readme, v1.
|
|
4
|
+
tags: [vhk, cli, readme, v1.5.1, ga]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# 🔧 VHK — Vibe Harness Kit
|
|
8
8
|
|
|
9
|
-
> 🎉 **v1.
|
|
9
|
+
> 🎉 **v1.5.1** — **규칙은 한 벌로 Cursor·Claude·Windsurf·Copilot·Antigravity에, 맥락은 클라우드로.**
|
|
10
|
+
> 도구·기기를 옮겨도 `vhk` 명령으로 그대로 불러옵니다. (포터빌리티)
|
|
10
11
|
>
|
|
11
12
|
> AI 코딩 에이전트를 부리는 사람을 위한 **한국어 풀사이클 CLI**.
|
|
12
13
|
>
|
|
@@ -14,6 +15,20 @@ tags: [vhk, cli, readme, v1.4.0, ga]
|
|
|
14
15
|
|
|
15
16
|
명령어를 외우지 않아도 됩니다. `vhk`만 치면 메뉴가 나오고, 한국어로 말해도 알아듣습니다.
|
|
16
17
|
|
|
18
|
+
## 왜 VHK? — 포터빌리티
|
|
19
|
+
|
|
20
|
+
AI 코딩 도구는 저마다 규칙 파일이 다르고(`.cursorrules`·`CLAUDE.md`·`.windsurfrules`·`.github/copilot-instructions.md`·`.agents/rules/`…), 컴퓨터를 바꾸면 프로젝트 맥락을 처음부터 다시 모읍니다. VHK는 이 둘을 한 곳에서 관리합니다.
|
|
21
|
+
|
|
22
|
+
| 문제 | VHK 해결 | 명령 |
|
|
23
|
+
|------|----------|------|
|
|
24
|
+
| 도구마다 규칙 파일이 따로 논다 | `RULES.md` 한 벌 → Cursor·Claude·Windsurf·Copilot·Antigravity 규칙 동시 생성 | `vhk sync` |
|
|
25
|
+
| 컴퓨터·환경 바뀌면 맥락 유실 | `.vhk/` 맥락을 GitHub gist로 백업·복원 | `vhk cloud push` / `pull` |
|
|
26
|
+
| 새 프로젝트 세팅 반복 | 유형별 문서·규칙·맥락 뼈대를 한 번에 | `vhk init` |
|
|
27
|
+
|
|
28
|
+
> 규칙·맥락은 **자동이 아니라 명령으로** 동기화됩니다(한 줄이면 충분). 개인 메모(`memory.json`)·참고링크(`refs.json`)는 프라이버시 위해 기본 제외, 새 PC의 코드 자체는 `git clone` 으로 받습니다.
|
|
29
|
+
>
|
|
30
|
+
> ℹ️ Windsurf·Copilot·Antigravity 출력 경로·포맷은 각 도구의 **공식 문서 기준**으로 생성합니다(`.windsurfrules` · `.github/copilot-instructions.md` · `.agents/rules/`). Antigravity 는 파일당 12,000자 제한이 있어 초과 시 안전하게 절삭하고 전체는 `RULES.md` 에 남습니다.
|
|
31
|
+
|
|
17
32
|
## 3분 안에 시작하기 (Getting Started)
|
|
18
33
|
|
|
19
34
|
### 1. 설치
|
|
@@ -40,12 +55,12 @@ vhk start
|
|
|
40
55
|
vhk gate # 퀵 5문항 — GO / 다듬기 / 다른 아이디어
|
|
41
56
|
```
|
|
42
57
|
|
|
43
|
-
### 3.
|
|
58
|
+
### 3. 그 외 기능 한눈에 (v1.5)
|
|
44
59
|
|
|
45
60
|
| 기능 | 한 줄 요약 | 진입 명령 |
|
|
46
61
|
|------|-----------|-----------|
|
|
47
62
|
| 🎯 **Goals 체계** | 단계별 미션 + 게이트 스크립트로 AI가 목표를 스스로 추적 | `vhk goal init` |
|
|
48
|
-
| ▶️ **자율 루프** | `goal next → 작업 → goal check → goal done`. FAIL 3
|
|
63
|
+
| ▶️ **자율 루프** | `goal next → 작업 → goal check → goal done`. FAIL 시 `vhk blocker` 수동 기록 → 블로커 3건 누적 시 HARD_STOP 자동 | `vhk goal next` |
|
|
49
64
|
| 🚧 **HARD_STOP 안전장치** | 블로커 3건 누적 → `.vhk/HARD_STOP` 트립와이어. `vhk resume --confirm` 만 해제 | `vhk blocker "<증상>"` |
|
|
50
65
|
| 🔌 **MCP 24 tool** | Cursor·Claude Desktop 등에서 vhk를 채팅으로 호출 | `vhk mcp-init` |
|
|
51
66
|
| 📋 **컨텍스트 영속화** | `.vhk/context.md` + `memory.json` + `brief.md` 로 세션 간 맥락 유지 | `vhk context` |
|
|
@@ -115,7 +130,7 @@ vhk 기획 끝났고 바로 시작
|
|
|
115
130
|
| `vhk gate` | `검증`, `아이디어` | 아이디어 검증 (퀵 5문항 / 풀 13문항 / 스킵) |
|
|
116
131
|
| `vhk init` | `시작`, `만들기` | 프로젝트 초기화 + 하네스 생성 |
|
|
117
132
|
| `vhk recap` | `정리`, `오늘` | Git 변경 → `docs/log/` 세션 로그 |
|
|
118
|
-
| `vhk sync` | `규칙`, `맞추기` | RULES.md → `.cursorrules` + CLAUDE.md + `.windsurfrules` |
|
|
133
|
+
| `vhk sync` | `규칙`, `맞추기` | RULES.md → `.cursorrules` + CLAUDE.md + `.windsurfrules` + `.github/copilot-instructions.md` + `.agents/rules/vhk-rules.md` (5개) |
|
|
119
134
|
| `vhk check` | `점검`, `린트` | RULES.md 규칙 위반 검사 |
|
|
120
135
|
| `vhk secure` | `보안` | 시크릿·키 유출 스캔 (`scan` / `스캔` 동일). **CRITICAL/HIGH 발견 시 exit code 1** (CI용) |
|
|
121
136
|
| `vhk ship` | `출하` | 배포 체크리스트 + 회고 + 빌드 로그 |
|
|
@@ -300,10 +315,10 @@ vhk ref open 1 # 1번 레퍼런스를 브라우저로 열기
|
|
|
300
315
|
|
|
301
316
|
| 기능 | 설명 |
|
|
302
317
|
|------|------|
|
|
303
|
-
| **MCP 서버** | `vhk mcp` — stdio MCP 서버 첫 도입 (v0.6.0 당시 8개 도구 — save/undo/status/diff/ship/doctor/check/recap). 현재 v1.
|
|
318
|
+
| **MCP 서버** | `vhk mcp` — stdio MCP 서버 첫 도입 (v0.6.0 당시 8개 도구 — save/undo/status/diff/ship/doctor/check/recap). 현재 v1.5 기준 **24개** 로 확장 — 위 "Cursor와 MCP로 연동하기" 섹션 참조 |
|
|
304
319
|
| **mcp-init** | `vhk mcp-init` — Cursor `.cursor/mcp.json` 자동 생성. 재시작 한 번으로 연동 완료 |
|
|
305
320
|
| **자연어 라우팅 확장** | `vhk mcp설정` → `vhk mcp-init` 별칭 |
|
|
306
|
-
| **보안** | MCP save 도구의 shell injection 차단 — 모든 git 호출에 `
|
|
321
|
+
| **보안** | MCP save 도구의 shell injection 차단 — 모든 git 호출에 shell 미경유 `safeExecFile` 사용 |
|
|
307
322
|
|
|
308
323
|
## v0.5.3 하이라이트
|
|
309
324
|
|
|
@@ -755,6 +755,9 @@ var ko = {
|
|
|
755
755
|
cursorrulesDone: "\u2705 .cursorrules \uB9DE\uCDA4 \uC644\uB8CC",
|
|
756
756
|
claudeDone: "\u2705 CLAUDE.md \uB9DE\uCDA4 \uC644\uB8CC",
|
|
757
757
|
windsurfDone: "\u2705 .windsurfrules \uB9DE\uCDA4 \uC644\uB8CC",
|
|
758
|
+
copilotDone: "\u2705 .github/copilot-instructions.md \uB9DE\uCDA4 \uC644\uB8CC",
|
|
759
|
+
antigravityDone: "\u2705 .agents/rules/vhk-rules.md \uB9DE\uCDA4 \uC644\uB8CC",
|
|
760
|
+
antigravityTruncated: "Antigravity 12,000\uC790 \uC81C\uD55C\uC73C\uB85C \uC77C\uBD80 \uC808\uC0AD\uB428 \u2014 \uC804\uCCB4\uB294 RULES.md \uCC38\uC870",
|
|
758
761
|
done: "\u{1F504} \uB9DE\uCD94\uAE30 \uC644\uB8CC!"
|
|
759
762
|
},
|
|
760
763
|
cloud: {
|
|
@@ -882,7 +885,9 @@ var ko = {
|
|
|
882
885
|
nextTitle: "\u27A1\uFE0F \uB2E4\uC74C Goal",
|
|
883
886
|
initTitle: "\u{1F3D7}\uFE0F goals/ \uAD6C\uC870 \uC2A4\uCE90\uD3F4\uB529",
|
|
884
887
|
checkTitle: "\u2705 Goal \uAC8C\uC774\uD2B8 \uAC80\uC99D",
|
|
885
|
-
doneTitle: "\u{1F3C1} Goal \uC644\uB8CC \uCC98\uB9AC"
|
|
888
|
+
doneTitle: "\u{1F3C1} Goal \uC644\uB8CC \uCC98\uB9AC",
|
|
889
|
+
duplicateId: (ids) => `\u26A0 \uC911\uBCF5\uB41C goal id: ${ids} \u2014 \uAC19\uC740 id \uD30C\uC77C\uC774 \uC5EC\uB7EC \uAC1C\uBA74 \uCCAB \uB9E4\uCE58\uB9CC \uC0AC\uC6A9\uB429\uB2C8\uB2E4. id \uB97C \uC720\uC77C\uD558\uAC8C \uACE0\uCE58\uC138\uC694.`,
|
|
890
|
+
notFound: (id) => `goal id ${id} \uC5C6\uC74C \u2014 vhk goal list \uB85C \uD655\uC778\uD558\uC138\uC694.`
|
|
886
891
|
},
|
|
887
892
|
agent: {
|
|
888
893
|
blockerTitle: "\u{1F6D1} Blocker \uAE30\uB85D",
|
package/dist/index.js
CHANGED
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
scanProjectForSecrets,
|
|
21
21
|
startMcpServer,
|
|
22
22
|
t
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-4KWZANQG.js";
|
|
24
24
|
|
|
25
25
|
// src/index.ts
|
|
26
26
|
import { Command, Help } from "commander";
|
|
@@ -1676,12 +1676,12 @@ function parseRulesMd(content) {
|
|
|
1676
1676
|
}
|
|
1677
1677
|
return sections;
|
|
1678
1678
|
}
|
|
1679
|
-
function
|
|
1679
|
+
function buildCodingDoc(headerTitle, sections, projectName) {
|
|
1680
1680
|
const codingSections = sections.filter(
|
|
1681
1681
|
(s) => CURSORRULES_KEYS.some((k) => s.title.includes(k))
|
|
1682
1682
|
);
|
|
1683
1683
|
const lines = [
|
|
1684
|
-
`# ${projectName} \u2014
|
|
1684
|
+
`# ${projectName} \u2014 ${headerTitle}`,
|
|
1685
1685
|
"",
|
|
1686
1686
|
"> \uCF54\uB529/\uB514\uC790\uC778 \uC804\uC6A9. \uAE30\uB85D/\uC6B4\uC601 \u2192 CLAUDE.md \uCC38\uC870.",
|
|
1687
1687
|
"> \u26A1 \uC774 \uD30C\uC77C\uC740 RULES.md\uC5D0\uC11C \uC790\uB3D9 \uC0DD\uC131\uB428 (vhk sync). \uC9C1\uC811 \uC218\uC815 \uAE08\uC9C0.",
|
|
@@ -1697,26 +1697,38 @@ function toCursorrules(sections, projectName) {
|
|
|
1697
1697
|
}
|
|
1698
1698
|
return lines.join("\n");
|
|
1699
1699
|
}
|
|
1700
|
+
function toCursorrules(sections, projectName) {
|
|
1701
|
+
return buildCodingDoc("Cursor Rules", sections, projectName);
|
|
1702
|
+
}
|
|
1700
1703
|
function toWindsurfrules(sections, projectName) {
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
}
|
|
1719
|
-
|
|
1704
|
+
return buildCodingDoc("Windsurf Rules", sections, projectName);
|
|
1705
|
+
}
|
|
1706
|
+
function toCopilotInstructions(sections, projectName) {
|
|
1707
|
+
return buildCodingDoc("GitHub Copilot Instructions", sections, projectName);
|
|
1708
|
+
}
|
|
1709
|
+
var ANTIGRAVITY_CHAR_LIMIT = 12e3;
|
|
1710
|
+
var ANTIGRAVITY_TRUNCATE_MARKER = "\n\n<!-- \u26A0\uFE0F Antigravity 12,000\uC790 \uC81C\uD55C\uC73C\uB85C \uC808\uC0AD\uB428 \u2014 \uC804\uCCB4 \uADDC\uCE59\uC740 RULES.md \uCC38\uC870 -->\n";
|
|
1711
|
+
function truncateForAntigravity(content, limit = ANTIGRAVITY_CHAR_LIMIT) {
|
|
1712
|
+
if (Buffer.byteLength(content, "utf8") <= limit) return content;
|
|
1713
|
+
const SAFETY = 200;
|
|
1714
|
+
const budget = limit - Buffer.byteLength(ANTIGRAVITY_TRUNCATE_MARKER, "utf8") - SAFETY;
|
|
1715
|
+
let lo = 0;
|
|
1716
|
+
let hi = content.length;
|
|
1717
|
+
while (lo < hi) {
|
|
1718
|
+
const mid = lo + hi + 1 >> 1;
|
|
1719
|
+
if (Buffer.byteLength(content.slice(0, mid), "utf8") <= budget) lo = mid;
|
|
1720
|
+
else hi = mid - 1;
|
|
1721
|
+
}
|
|
1722
|
+
const charCut = lo;
|
|
1723
|
+
let cut = content.lastIndexOf("\n## ", charCut);
|
|
1724
|
+
if (cut < charCut * 0.5) {
|
|
1725
|
+
const nl = content.lastIndexOf("\n", charCut);
|
|
1726
|
+
cut = nl > 0 ? nl : charCut;
|
|
1727
|
+
}
|
|
1728
|
+
return content.slice(0, cut).trimEnd() + ANTIGRAVITY_TRUNCATE_MARKER;
|
|
1729
|
+
}
|
|
1730
|
+
function toAntigravityRules(sections, projectName) {
|
|
1731
|
+
return truncateForAntigravity(buildCodingDoc("Antigravity Rules", sections, projectName));
|
|
1720
1732
|
}
|
|
1721
1733
|
function toClaudeMd(sections, existing) {
|
|
1722
1734
|
const recordSections = sections.filter(
|
|
@@ -1780,9 +1792,22 @@ ${ko.sync.title}
|
|
|
1780
1792
|
const windsurfPath = path6.join(cwd, ".windsurfrules");
|
|
1781
1793
|
fs5.writeFileSync(windsurfPath, toWindsurfrules(sections, projectName), "utf-8");
|
|
1782
1794
|
console.log(chalk5.green(` ${ko.sync.windsurfDone}`));
|
|
1795
|
+
const copilotPath = path6.join(cwd, ".github", "copilot-instructions.md");
|
|
1796
|
+
fs5.mkdirSync(path6.dirname(copilotPath), { recursive: true });
|
|
1797
|
+
fs5.writeFileSync(copilotPath, toCopilotInstructions(sections, projectName), "utf-8");
|
|
1798
|
+
console.log(chalk5.green(` ${ko.sync.copilotDone}`));
|
|
1799
|
+
const antigravityPath = path6.join(cwd, ".agents", "rules", "vhk-rules.md");
|
|
1800
|
+
fs5.mkdirSync(path6.dirname(antigravityPath), { recursive: true });
|
|
1801
|
+
const antigravityDoc = toAntigravityRules(sections, projectName);
|
|
1802
|
+
fs5.writeFileSync(antigravityPath, antigravityDoc, "utf-8");
|
|
1803
|
+
console.log(chalk5.green(` ${ko.sync.antigravityDone}`));
|
|
1804
|
+
if (antigravityDoc.includes("\uC808\uC0AD\uB428")) {
|
|
1805
|
+
console.log(chalk5.yellow(` \u26A0\uFE0F ${ko.sync.antigravityTruncated}`));
|
|
1806
|
+
}
|
|
1783
1807
|
console.log(chalk5.bold.green(`
|
|
1784
1808
|
${ko.sync.done}`));
|
|
1785
|
-
console.log(chalk5.dim(" RULES.md (\uC6D0\uBCF8) \u2192 .cursorrules + CLAUDE.md + .windsurfrules
|
|
1809
|
+
console.log(chalk5.dim(" RULES.md (\uC6D0\uBCF8) \u2192 .cursorrules + CLAUDE.md + .windsurfrules"));
|
|
1810
|
+
console.log(chalk5.dim(" + .github/copilot-instructions.md + .agents/rules/vhk-rules.md (\uC790\uB3D9 \uC0DD\uC131)"));
|
|
1786
1811
|
console.log(chalk5.dim(" \uADDC\uCE59 \uBCC0\uACBD\uC740 \uD56D\uC0C1 RULES.md\uC5D0\uC11C\uB9CC \uD558\uC138\uC694."));
|
|
1787
1812
|
printNextStep({
|
|
1788
1813
|
message: "\uADDC\uCE59 \uB3D9\uAE30\uD654 \uC644\uB8CC! \uC774\uC81C Cursor\uAC00 \uC0C8 \uADDC\uCE59\uC744 \uB530\uB985\uB2C8\uB2E4.",
|
|
@@ -2024,6 +2049,19 @@ function listGoals(goalsDir) {
|
|
|
2024
2049
|
parsed.sort((a, b) => a.frontmatter.id - b.frontmatter.id);
|
|
2025
2050
|
return parsed;
|
|
2026
2051
|
}
|
|
2052
|
+
function findDuplicateIds(goals) {
|
|
2053
|
+
const counts = /* @__PURE__ */ new Map();
|
|
2054
|
+
for (const g of goals) {
|
|
2055
|
+
const id = g.frontmatter.id;
|
|
2056
|
+
if (typeof id !== "number") continue;
|
|
2057
|
+
counts.set(id, (counts.get(id) ?? 0) + 1);
|
|
2058
|
+
}
|
|
2059
|
+
const dups = [];
|
|
2060
|
+
for (const [id, n] of counts) {
|
|
2061
|
+
if (n > 1) dups.push(id);
|
|
2062
|
+
}
|
|
2063
|
+
return dups.sort((a, b) => a - b);
|
|
2064
|
+
}
|
|
2027
2065
|
function updateFrontmatterStatus(content, newStatus, extraFields) {
|
|
2028
2066
|
const m = content.match(FRONTMATTER_RE);
|
|
2029
2067
|
if (!m) return content;
|
|
@@ -2108,6 +2146,11 @@ ${ko.goal.listTitle}
|
|
|
2108
2146
|
` [${id}] ${icon} ${status2.padEnd(11)} ${pri} ${ver} ${fm.title ?? "(untitled)"}`
|
|
2109
2147
|
);
|
|
2110
2148
|
}
|
|
2149
|
+
const dups = findDuplicateIds(goals);
|
|
2150
|
+
if (dups.length > 0) {
|
|
2151
|
+
console.log("");
|
|
2152
|
+
console.log(chalk6.yellow(` ${ko.goal.duplicateId(dups.join(", "))}`));
|
|
2153
|
+
}
|
|
2111
2154
|
}
|
|
2112
2155
|
async function goalNext() {
|
|
2113
2156
|
console.log(chalk6.bold(`
|
|
@@ -2221,6 +2264,11 @@ ${ko.goal.checkTitle}
|
|
|
2221
2264
|
process.exitCode = 1;
|
|
2222
2265
|
return;
|
|
2223
2266
|
}
|
|
2267
|
+
if (!goals.some((g) => g.frontmatter.id === id)) {
|
|
2268
|
+
console.log(chalk6.red(` \u274C ${ko.goal.notFound(id)}`));
|
|
2269
|
+
process.exitCode = 1;
|
|
2270
|
+
return;
|
|
2271
|
+
}
|
|
2224
2272
|
const scriptPath = findGateScript(id);
|
|
2225
2273
|
if (!scriptPath) {
|
|
2226
2274
|
console.log(
|
|
@@ -2258,7 +2306,7 @@ ${ko.goal.doneTitle}
|
|
|
2258
2306
|
}
|
|
2259
2307
|
const target = goals.find((g) => g.frontmatter.id === id);
|
|
2260
2308
|
if (!target) {
|
|
2261
|
-
console.log(chalk6.red(` \u274C
|
|
2309
|
+
console.log(chalk6.red(` \u274C ${ko.goal.notFound(id)}`));
|
|
2262
2310
|
process.exitCode = 1;
|
|
2263
2311
|
return;
|
|
2264
2312
|
}
|
package/dist/mcp/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@byh3071/vhk",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Vibe Harness Kit —
|
|
3
|
+
"version": "1.5.1",
|
|
4
|
+
"description": "Vibe Harness Kit — AI 코딩 도구·기기를 바꿔도 규칙·맥락이 따라가는 포터빌리티 CLI (sync: Cursor·Claude·Windsurf·Copilot·Antigravity / cloud 백업)",
|
|
5
5
|
"bin": {
|
|
6
6
|
"vhk": "dist/index.js",
|
|
7
7
|
"vhk-mcp": "dist/mcp/index.js"
|
|
@@ -18,7 +18,14 @@
|
|
|
18
18
|
"cli",
|
|
19
19
|
"scaffold",
|
|
20
20
|
"session-log",
|
|
21
|
-
"rules-sync"
|
|
21
|
+
"rules-sync",
|
|
22
|
+
"portability",
|
|
23
|
+
"ai-coding",
|
|
24
|
+
"cursor",
|
|
25
|
+
"claude",
|
|
26
|
+
"windsurf",
|
|
27
|
+
"copilot",
|
|
28
|
+
"context-sync"
|
|
22
29
|
],
|
|
23
30
|
"author": "byh3071 <byh3071@gmail.com>",
|
|
24
31
|
"license": "MIT",
|