@byh3071/vhk 1.4.0 โ†’ 1.5.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 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.0, ga]
4
+ tags: [vhk, cli, readme, v1.5.0, ga]
5
5
  ---
6
6
 
7
7
  # ๐Ÿ”ง VHK โ€” Vibe Harness Kit
8
8
 
9
- > ๐ŸŽ‰ **v1.4.0** โ€” ๋ฐ”์ด๋ธŒ์ฝ”๋”์˜ ์˜ฌ์ธ์› CLI. ์ปจํ…์ŠคํŠธ + ์ž์œจ ํ•˜๋„ค์Šค + ํฌํ„ฐ๋นŒ๋ฆฌํ‹ฐ.
9
+ > ๐ŸŽ‰ **v1.5.0** โ€” **๊ทœ์น™์€ ํ•œ ๋ฒŒ๋กœ 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. v1.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ํšŒ๋ฉด ์ž๋™ ๋ธ”๋กœ์ปค | `vhk goal next` |
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.3 ๊ธฐ์ค€ **24๊ฐœ** ๋กœ ํ™•์žฅ โ€” ์œ„ "Cursor์™€ MCP๋กœ ์—ฐ๋™ํ•˜๊ธฐ" ์„น์…˜ ์ฐธ์กฐ |
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 ํ˜ธ์ถœ์— `execFileSync` ์‚ฌ์šฉ |
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-3DV7AEN4.js";
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 toCursorrules(sections, projectName) {
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 Cursor Rules`,
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
- const codingSections = sections.filter(
1702
- (s) => CURSORRULES_KEYS.some((k) => s.title.includes(k))
1703
- );
1704
- const lines = [
1705
- `# ${projectName} \u2014 Windsurf Rules`,
1706
- "",
1707
- "> \uCF54\uB529/\uB514\uC790\uC778 \uC804\uC6A9. \uAE30\uB85D/\uC6B4\uC601 \u2192 CLAUDE.md \uCC38\uC870.",
1708
- "> \u26A1 \uC774 \uD30C\uC77C\uC740 RULES.md\uC5D0\uC11C \uC790\uB3D9 \uC0DD\uC131\uB428 (vhk sync). \uC9C1\uC811 \uC218\uC815 \uAE08\uC9C0.",
1709
- "",
1710
- "## \uD544\uC218 \uCC38\uC870",
1711
- "- docs/PRD.md \xB7 docs/ARCHITECTURE.md \xB7 CLAUDE.md \xB7 RULES.md",
1712
- ""
1713
- ];
1714
- for (const section of codingSections) {
1715
- lines.push(`## ${section.title}`);
1716
- lines.push(section.content);
1717
- lines.push("");
1718
- }
1719
- return lines.join("\n");
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 (\uC790\uB3D9 \uC0DD\uC131)"));
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 goal id ${id} \uD30C\uC77C \uC5C6\uC74C.`));
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
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startMcpServer
4
- } from "../chunk-3DV7AEN4.js";
4
+ } from "../chunk-4KWZANQG.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": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "Vibe Harness Kit โ€” ๋ฐ”์ด๋ธŒ์ฝ”๋”ฉ ํ’€์‚ฌ์ดํด CLI",
5
5
  "bin": {
6
6
  "vhk": "dist/index.js",