@byh3071/vhk 0.7.1 โ†’ 0.8.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,12 @@
1
1
  ---
2
2
  id: vhk-readme
3
3
  date: 2026-05-24
4
- tags: [vhk, cli, readme, v0.6.0]
4
+ tags: [vhk, cli, readme, v0.8.0]
5
5
  ---
6
6
 
7
7
  # ๐Ÿ”ง VHK โ€” Vibe Harness Kit
8
8
 
9
- > AI ์ฝ”๋”ฉ ์—์ด์ „ํŠธ๋ฅผ ๋ถ€๋ฆฌ๋Š” ์‚ฌ๋žŒ์„ ์œ„ํ•œ **ํ•œ๊ตญ์–ด ํ’€์‚ฌ์ดํด CLI** (v0.6.0)
9
+ > AI ์ฝ”๋”ฉ ์—์ด์ „ํŠธ๋ฅผ ๋ถ€๋ฆฌ๋Š” ์‚ฌ๋žŒ์„ ์œ„ํ•œ **ํ•œ๊ตญ์–ด ํ’€์‚ฌ์ดํด CLI** (v0.8.0)
10
10
  >
11
11
  > ๐Ÿฝ๏ธ **VHK๋Š” VHK๋กœ ๋ถ€ํŠธ์ŠคํŠธ๋žฉ๋จ** โ€” ์ด ๋ ˆํฌ์˜ `docs/`, `CLAUDE.md`, `.cursorrules`๋„ `vhk init`์ด ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.
12
12
 
@@ -95,6 +95,10 @@ vhk ๊ธฐํš ๋๋‚ฌ๊ณ  ๋ฐ”๋กœ ์‹œ์ž‘
95
95
  | `vhk env` | `ํ™˜๊ฒฝ๋ณ€์ˆ˜` | `.env` โ†’ `.env.example` ๋™๊ธฐํ™” + `.gitignore`์— `.env` ์ž๋™ ์ถ”๊ฐ€ |
96
96
  | `vhk env-check` | `ํ™˜๊ฒฝ๋ณ€์ˆ˜์ ๊ฒ€` | `.env.example` ๊ธฐ์ค€ ๋ˆ„๋ฝ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๊ฒ€์‚ฌ |
97
97
  | `vhk publish` | `์ถœ์‹œ` | npm ๋ฐฐํฌ ์ž๋™ํ™” (๋ฒ„์ „ ๋ฒ”ํ”„ โ†’ ๋นŒ๋“œ โ†’ ํ…Œ์ŠคํŠธ โ†’ publish โ†’ git tag) |
98
+ | `vhk design` | `๋””์ž์ธ` | ๋””์ž์ธ ํ† ํฐ ์ƒ์„ฑ (Tailwind config ๋˜๋Š” CSS ๋ณ€์ˆ˜) |
99
+ | `vhk design-palette` | `ํŒ”๋ ˆํŠธ` | ์ปฌ๋Ÿฌ ํŒ”๋ ˆํŠธ ํ”„๋ฆฌ์…‹ ์„ ํƒ + ์ ์šฉ |
100
+ | `vhk theme` | `ํ…Œ๋งˆ` | ๋‹คํฌ/๋ผ์ดํŠธ ๋ชจ๋“œ CSS + ํ† ๊ธ€ ์œ ํ‹ธ๋ฆฌํ‹ฐ ์ƒ์„ฑ |
101
+ | `vhk ref` | `๋ ˆํผ๋Ÿฐ์Šค` | ๋ ˆํผ๋Ÿฐ์Šค URL ๊ด€๋ฆฌ (`add` / `list` / `open`) |
98
102
 
99
103
  ### init ์˜ต์…˜
100
104
 
@@ -129,6 +133,31 @@ MCP ์„œ๋ฒ„๋ฅผ ์ˆ˜๋™์œผ๋กœ ๋„์šฐ๋ ค๋ฉด:
129
133
  vhk mcp # stdio ์„œ๋ฒ„ ์‹œ์ž‘ (Cursor๊ฐ€ ์ž๋™์œผ๋กœ ํ˜ธ์ถœ)
130
134
  ```
131
135
 
136
+ ## v0.8.0 ํ•˜์ด๋ผ์ดํŠธ
137
+
138
+ | ๊ธฐ๋Šฅ | ์„ค๋ช… |
139
+ |------|------|
140
+ | **design** | ํŒ”๋ ˆํŠธ ํ”„๋ฆฌ์…‹ 4์ข…(Minimal/Vibrant/Corporate/Pastel) ์„ ํƒ โ†’ `src/styles/tokens.css` ๋˜๋Š” `src/styles/vhk-colors.ts` (Tailwind config๊ฐ€ ์žˆ์œผ๋ฉด TS) ์ƒ์„ฑ |
141
+ | **theme** | `src/styles/theme.css` (๋‹คํฌ/๋ผ์ดํŠธ + `prefers-color-scheme` + `data-theme` ์…€๋ ‰ํ„ฐ) + `src/lib/theme-toggle.ts` (`getTheme`/`setTheme`/`toggleTheme`/`initTheme`) ์ƒ์„ฑ |
142
+ | **ref** | `.vhk/refs.json` ๊ธฐ๋ฐ˜ ๋ ˆํผ๋Ÿฐ์Šค URL ๊ด€๋ฆฌ. `ref add <url> --memo "..."` / `ref list` / `ref open <๋ฒˆํ˜ธ>` (Windows/macOS/Linux ๋ธŒ๋ผ์šฐ์ € ์ž๋™ ์˜คํ”ˆ) |
143
+ | **์ž์—ฐ์–ด ํ™•์žฅ** | `"๋””์ž์ธ ํ† ํฐ ๋งŒ๋“ค์–ด์ค˜"` / `"ํŒ”๋ ˆํŠธ ๊ณจ๋ผ์ค˜"` / `"๋‹คํฌ ๋ชจ๋“œ ์ ์šฉ"` / `"๋ ˆํผ๋Ÿฐ์Šค ๋ณด์—ฌ์ค˜"` ์ธ์‹. `ref add`/`open`์€ ์ธ์ž ์ถ”์ถœ ์ธํ”„๋ผ๊ฐ€ ์—†์–ด ์˜๋„์ ์œผ๋กœ NL ๋ฐฐ์ œ โ€” commander ์„œ๋ธŒ์ปค๋งจ๋“œ๋งŒ ์‚ฌ์šฉ |
144
+
145
+ ```powershell
146
+ vhk design # ํŒ”๋ ˆํŠธ ์„ ํƒ โ†’ src/styles/tokens.css ๋˜๋Š” vhk-colors.ts
147
+ vhk theme # src/styles/theme.css + src/lib/theme-toggle.ts
148
+ vhk ref add https://example.com --memo "์ฐธ๊ณ  ์‚ฌ์ดํŠธ"
149
+ vhk ref list # ์ €์žฅ๋œ ๋ ˆํผ๋Ÿฐ์Šค ๋ชฉ๋ก
150
+ vhk ref open 1 # 1๋ฒˆ ๋ ˆํผ๋Ÿฐ์Šค๋ฅผ ๋ธŒ๋ผ์šฐ์ €๋กœ ์—ด๊ธฐ
151
+ ```
152
+
153
+ ## v0.7.0 ํ•˜์ด๋ผ์ดํŠธ
154
+
155
+ | ๊ธฐ๋Šฅ | ์„ค๋ช… |
156
+ |------|------|
157
+ | **deploy** | Vercel / Netlify / Cloudflare Workers ์ž๋™ ๊ฐ์ง€ + ํ”„๋กœ๋•์…˜ ๋ฐฐํฌ |
158
+ | **env / env-check** | `.env` โ†’ `.env.example` ๋™๊ธฐํ™” + ๋ˆ„๋ฝ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๊ฒ€์‚ฌ. MCP ๋„๊ตฌ๋กœ๋„ ๋…ธ์ถœ (v0.7.1) |
159
+ | **publish** | semver ๋ฒ”ํ”„ + ๋นŒ๋“œ + ํ…Œ์ŠคํŠธ + `npm publish` + git tag ์ž๋™ํ™” |
160
+
132
161
  ## v0.6.0 ํ•˜์ด๋ผ์ดํŠธ
133
162
 
134
163
  | ๊ธฐ๋Šฅ | ์„ค๋ช… |
@@ -191,6 +220,10 @@ vhk mcp # stdio ์„œ๋ฒ„ ์‹œ์ž‘ (Cursor๊ฐ€ ์ž๋™์œผ๋กœ ํ˜ธ์ถœ)
191
220
  | ๋ณด์•ˆ ์Šค์บ” ๋Œ๋ ค | `vhk ๋ณด์•ˆ` |
192
221
  | ๋ฐฐํฌํ•˜๊ณ  ์‹ถ์–ด | `vhk ๋ฐฐํฌ` |
193
222
  | ๋ญ”๊ฐ€ ์•ˆ ๋ผ | `vhk doctor` |
223
+ | ๋””์ž์ธ ํ† ํฐ ๋งŒ๋“ค์–ด์ค˜ | `vhk design` |
224
+ | ํŒ”๋ ˆํŠธ ๊ณจ๋ผ์ค˜ | `vhk design-palette` |
225
+ | ๋‹คํฌ ๋ชจ๋“œ ์ ์šฉ | `vhk theme` |
226
+ | ๋ ˆํผ๋Ÿฐ์Šค ๋ณด์—ฌ์ค˜ | `vhk ref` (list) |
194
227
 
195
228
  ## ํŠน์ง•
196
229
 
@@ -272,6 +272,17 @@ var ko = {
272
272
  changelogNoUnreleased: "CHANGELOG.md\uC5D0 [Unreleased] \uC139\uC158\uC774 \uC5C6\uC5B4 \uC790\uB3D9 \uAC31\uC2E0\uC744 \uC2A4\uD0B5\uD588\uC5B4\uC694",
273
273
  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."
274
274
  },
275
+ design: {
276
+ title: "\uB514\uC790\uC778 \uD1A0\uD070 \uC0DD\uC131",
277
+ selectPalette: "\uCEEC\uB7EC \uD314\uB808\uD2B8\uB97C \uC120\uD0DD\uD558\uC138\uC694:"
278
+ },
279
+ theme: {
280
+ title: "\uD14C\uB9C8 \uC124\uC815 (\uB2E4\uD06C/\uB77C\uC774\uD2B8 \uBAA8\uB4DC)"
281
+ },
282
+ ref: {
283
+ addTitle: "\uB808\uD37C\uB7F0\uC2A4 \uCD94\uAC00",
284
+ listTitle: "\uB808\uD37C\uB7F0\uC2A4 \uBAA9\uB85D"
285
+ },
275
286
  mcp: {
276
287
  initTitle: "Cursor MCP \uC5F0\uB3D9 \uC124\uC815",
277
288
  serverStarted: "VHK MCP \uC11C\uBC84 \uC2DC\uC791\uB428"
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  safeExecFileStream,
11
11
  startMcpServer,
12
12
  t
13
- } from "./chunk-3HHU7V77.js";
13
+ } from "./chunk-NQ4V3VN4.js";
14
14
 
15
15
  // node_modules/.pnpm/ignore@7.0.5/node_modules/ignore/index.js
16
16
  var require_ignore = __commonJS({
@@ -472,7 +472,7 @@ var require_ignore = __commonJS({
472
472
 
473
473
  // src/index.ts
474
474
  import { Command, Help } from "commander";
475
- import inquirer10 from "inquirer";
475
+ import inquirer12 from "inquirer";
476
476
 
477
477
  // src/lib/nlp-router.ts
478
478
  function normalize(input) {
@@ -516,6 +516,30 @@ var RULES = [
516
516
  confidence: "high",
517
517
  test: (t2) => /mcp.*(์„ค์ •|์—ฐ๋™|์ดˆ๊ธฐ|init)|์ปค์„œ.*(์—ฐ๋™|์„ค์ •|mcp)|cursor.*mcp/.test(t2)
518
518
  },
519
+ {
520
+ command: "design-palette",
521
+ explanation: "\uCEEC\uB7EC \uD314\uB808\uD2B8 \uC120\uD0DD (vhk design-palette)",
522
+ confidence: "high",
523
+ test: (t2) => /ํŒ”๋ ˆํŠธ|palette|์ปฌ๋Ÿฌ\s*(๊ณ |์„ ํƒ|๋ฐ”๊ฟ”|๋ณ€๊ฒฝ)|์ƒ‰์ƒ\s*(๊ณ |์„ ํƒ|๋ณ€๊ฒฝ)|์ƒ‰๊น”\s*์„ ํƒ/.test(t2)
524
+ },
525
+ {
526
+ command: "design",
527
+ explanation: "\uB514\uC790\uC778 \uD1A0\uD070 \uC0DD\uC131 (vhk design)",
528
+ confidence: "high",
529
+ test: (t2) => /๋””์ž์ธ\s*(ํ† ํฐ|์‹œ์Šคํ…œ|๋งŒ๋“ค|์ƒ์„ฑ|์…‹์—…|์„ค์ •)|design\s*(token|system|setup)|ํ† ํฐ\s*๋งŒ๋“ค|css\s*๋ณ€์ˆ˜.*๋งŒ๋“ค|tailwind\s*(์ปฌ๋Ÿฌ|์„ค์ •)/.test(t2)
530
+ },
531
+ {
532
+ command: "theme",
533
+ explanation: "\uB2E4\uD06C/\uB77C\uC774\uD2B8 \uD14C\uB9C8 \uC801\uC6A9 (vhk theme)",
534
+ confidence: "high",
535
+ test: (t2) => /ํ…Œ๋งˆ(?!\s*(ํŒŒ์ผ|์ด๋ฆ„))|theme|๋‹คํฌ\s*๋ชจ๋“œ|๋ผ์ดํŠธ\s*๋ชจ๋“œ|dark\s*mode|light\s*mode|์ƒ‰์ƒ\s*๋ชจ๋“œ|๋ชจ๋“œ\s*์ „ํ™˜/.test(t2)
536
+ },
537
+ {
538
+ command: "ref",
539
+ explanation: "\uB808\uD37C\uB7F0\uC2A4 \uBAA9\uB85D (vhk ref list)",
540
+ confidence: "high",
541
+ test: (t2) => /^๋ ˆํผ๋Ÿฐ์Šค$|^ref$|๋ ˆํผ๋Ÿฐ์Šค.*(๋ณด|๋ชฉ๋ก|ํ™•์ธ|์žˆ|๋ญ)|์ฐธ๊ณ \s*(์‚ฌ์ดํŠธ|๋ชฉ๋ก|๋งํฌ)|reference.*list/.test(t2) && !/(add|์ถ”๊ฐ€|open|์—ด|https?:\/\/)/.test(t2)
542
+ },
519
543
  {
520
544
  command: "secure",
521
545
  explanation: "\uBCF4\uC548 \uC2A4\uCE94 (vhk \uBCF4\uC548)",
@@ -674,6 +698,14 @@ var KNOWN_COMMAND_TOKENS = /* @__PURE__ */ new Set([
674
698
  "publish",
675
699
  "\uCD9C\uC2DC",
676
700
  "\uCD9C\uD558",
701
+ "design",
702
+ "\uB514\uC790\uC778",
703
+ "design-palette",
704
+ "\uD314\uB808\uD2B8",
705
+ "theme",
706
+ "\uD14C\uB9C8",
707
+ "ref",
708
+ "\uB808\uD37C\uB7F0\uC2A4",
677
709
  "help"
678
710
  ]);
679
711
  function isOptionToken(token) {
@@ -697,8 +729,8 @@ function detectNaturalLanguageInput(argv) {
697
729
  }
698
730
 
699
731
  // src/lib/nlp-run.ts
700
- import chalk18 from "chalk";
701
- import inquirer9 from "inquirer";
732
+ import chalk21 from "chalk";
733
+ import inquirer11 from "inquirer";
702
734
 
703
735
  // src/commands/gate.ts
704
736
  import inquirer from "inquirer";
@@ -3571,6 +3603,329 @@ async function publish() {
3571
3603
  });
3572
3604
  }
3573
3605
 
3606
+ // src/commands/design.ts
3607
+ import { existsSync as existsSync4, mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "fs";
3608
+ import chalk18 from "chalk";
3609
+ import inquirer9 from "inquirer";
3610
+ var PALETTES = [
3611
+ {
3612
+ name: "Minimal",
3613
+ colors: {
3614
+ primary: "#1a1a1a",
3615
+ secondary: "#6b7280",
3616
+ accent: "#3b82f6",
3617
+ background: "#ffffff",
3618
+ surface: "#f9fafb",
3619
+ text: "#111827",
3620
+ muted: "#9ca3af"
3621
+ }
3622
+ },
3623
+ {
3624
+ name: "Vibrant",
3625
+ colors: {
3626
+ primary: "#7c3aed",
3627
+ secondary: "#ec4899",
3628
+ accent: "#f59e0b",
3629
+ background: "#ffffff",
3630
+ surface: "#faf5ff",
3631
+ text: "#1e1b4b",
3632
+ muted: "#8b5cf6"
3633
+ }
3634
+ },
3635
+ {
3636
+ name: "Corporate",
3637
+ colors: {
3638
+ primary: "#1e40af",
3639
+ secondary: "#0f766e",
3640
+ accent: "#ca8a04",
3641
+ background: "#ffffff",
3642
+ surface: "#f0f9ff",
3643
+ text: "#0f172a",
3644
+ muted: "#64748b"
3645
+ }
3646
+ },
3647
+ {
3648
+ name: "Pastel",
3649
+ colors: {
3650
+ primary: "#a78bfa",
3651
+ secondary: "#f9a8d4",
3652
+ accent: "#fcd34d",
3653
+ background: "#fffbeb",
3654
+ surface: "#fef3c7",
3655
+ text: "#44403c",
3656
+ muted: "#a8a29e"
3657
+ }
3658
+ }
3659
+ ];
3660
+ function hasTailwind() {
3661
+ return existsSync4("tailwind.config.js") || existsSync4("tailwind.config.ts") || existsSync4("tailwind.config.mjs") || existsSync4("tailwind.config.cjs");
3662
+ }
3663
+ function generateCSSTokens(palette) {
3664
+ const lines = Object.entries(palette.colors).map(([key, value]) => ` --color-${key}: ${value};`).join("\n");
3665
+ return `:root {
3666
+ ${lines}
3667
+ }
3668
+ `;
3669
+ }
3670
+ function generateTailwindExtend(palette) {
3671
+ const entries = Object.entries(palette.colors).map(([key, value]) => ` '${key}': '${value}',`).join("\n");
3672
+ return `// vhk design \u2014 Tailwind config \uD655\uC7A5\uC6A9 \uCEEC\uB7EC \uD1A0\uD070
3673
+ // tailwind.config\uC758 theme.extend.colors\uC5D0 spread \uD558\uC138\uC694.
3674
+ const vhkColors = {
3675
+ ${entries}
3676
+ }
3677
+
3678
+ export default vhkColors
3679
+ `;
3680
+ }
3681
+ async function design() {
3682
+ console.log(chalk18.bold("\n\u{1F3A8} " + t("design.title")));
3683
+ console.log(chalk18.gray("\u2500".repeat(40)));
3684
+ const { paletteIndex } = await inquirer9.prompt([
3685
+ {
3686
+ type: "list",
3687
+ name: "paletteIndex",
3688
+ message: t("design.selectPalette"),
3689
+ choices: PALETTES.map((p, i) => ({
3690
+ name: `${p.name} \u2014 primary ${p.colors.primary}`,
3691
+ value: i
3692
+ }))
3693
+ }
3694
+ ]);
3695
+ const palette = PALETTES[paletteIndex];
3696
+ console.log(chalk18.cyan(`
3697
+ \u{1F3A8} \uC120\uD0DD\uB41C \uD314\uB808\uD2B8: ${palette.name}`));
3698
+ const targetPath = hasTailwind() ? "src/styles/vhk-colors.ts" : "src/styles/tokens.css";
3699
+ const content = hasTailwind() ? generateTailwindExtend(palette) : generateCSSTokens(palette);
3700
+ if (existsSync4(targetPath)) {
3701
+ const { overwrite } = await inquirer9.prompt([{
3702
+ type: "confirm",
3703
+ name: "overwrite",
3704
+ message: `${targetPath} \uC774\uBBF8 \uC788\uC5B4\uC694. \uB36E\uC5B4\uC4F8\uAE4C\uC694?`,
3705
+ default: false
3706
+ }]);
3707
+ if (!overwrite) {
3708
+ console.log(chalk18.yellow("\n\u23ED\uFE0F \uC0DD\uC131 \uCDE8\uC18C \u2014 \uAE30\uC874 \uD30C\uC77C \uC720\uC9C0."));
3709
+ return;
3710
+ }
3711
+ }
3712
+ mkdirSync2("src/styles", { recursive: true });
3713
+ writeFileSync3(targetPath, content, "utf-8");
3714
+ if (hasTailwind()) {
3715
+ console.log(chalk18.green("\n\u2705 src/styles/vhk-colors.ts \uC0DD\uC131"));
3716
+ console.log(chalk18.gray(" tailwind.config\uC758 extend.colors\uC5D0 import \uD574\uC11C \uC0AC\uC6A9\uD558\uC138\uC694."));
3717
+ } else {
3718
+ console.log(chalk18.green("\n\u2705 src/styles/tokens.css \uC0DD\uC131"));
3719
+ console.log(chalk18.gray(" HTML\uC5D0 <link>\uB85C \uCD94\uAC00\uD558\uAC70\uB098 CSS\uC5D0\uC11C @import \uD558\uC138\uC694."));
3720
+ }
3721
+ console.log(chalk18.bold("\n\u{1F308} \uCEEC\uB7EC \uBBF8\uB9AC\uBCF4\uAE30:"));
3722
+ for (const [key, value] of Object.entries(palette.colors)) {
3723
+ console.log(` ${key.padEnd(12)} ${value}`);
3724
+ }
3725
+ printNextStep({
3726
+ message: "\uB514\uC790\uC778 \uD1A0\uD070 \uC0DD\uC131 \uC644\uB8CC!",
3727
+ command: "vhk theme",
3728
+ cursorHint: "\uD14C\uB9C8 \uC124\uC815\uD574\uC918"
3729
+ });
3730
+ }
3731
+ async function designPalette() {
3732
+ await design();
3733
+ }
3734
+
3735
+ // src/commands/theme.ts
3736
+ import { existsSync as existsSync5, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
3737
+ import chalk19 from "chalk";
3738
+ import inquirer10 from "inquirer";
3739
+ function generateDarkCSS() {
3740
+ return `/* vhk theme \u2014 \uB2E4\uD06C/\uB77C\uC774\uD2B8 \uBAA8\uB4DC CSS \uBCC0\uC218 */
3741
+
3742
+ @media (prefers-color-scheme: dark) {
3743
+ :root {
3744
+ --color-background: #0f172a;
3745
+ --color-surface: #1e293b;
3746
+ --color-text: #f1f5f9;
3747
+ --color-muted: #64748b;
3748
+ }
3749
+ }
3750
+
3751
+ [data-theme="dark"] {
3752
+ --color-background: #0f172a;
3753
+ --color-surface: #1e293b;
3754
+ --color-text: #f1f5f9;
3755
+ --color-muted: #64748b;
3756
+ }
3757
+
3758
+ [data-theme="light"] {
3759
+ --color-background: #ffffff;
3760
+ --color-surface: #f9fafb;
3761
+ --color-text: #111827;
3762
+ --color-muted: #9ca3af;
3763
+ }
3764
+ `;
3765
+ }
3766
+ function generateToggleUtil() {
3767
+ return `// vhk theme \u2014 \uB2E4\uD06C/\uB77C\uC774\uD2B8 \uBAA8\uB4DC \uD1A0\uAE00 \uC720\uD2F8\uB9AC\uD2F0
3768
+
3769
+ export function getTheme(): 'light' | 'dark' {
3770
+ if (typeof window === 'undefined') return 'light'
3771
+ const stored = localStorage.getItem('vhk-theme') as 'light' | 'dark' | null
3772
+ if (stored === 'light' || stored === 'dark') return stored
3773
+ return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
3774
+ }
3775
+
3776
+ export function setTheme(theme: 'light' | 'dark'): void {
3777
+ if (typeof document === 'undefined') return
3778
+ document.documentElement.setAttribute('data-theme', theme)
3779
+ localStorage.setItem('vhk-theme', theme)
3780
+ }
3781
+
3782
+ export function toggleTheme(): 'light' | 'dark' {
3783
+ const next = getTheme() === 'light' ? 'dark' : 'light'
3784
+ setTheme(next)
3785
+ return next
3786
+ }
3787
+
3788
+ export function initTheme(): void {
3789
+ setTheme(getTheme())
3790
+ }
3791
+ `;
3792
+ }
3793
+ async function theme() {
3794
+ console.log(chalk19.bold("\n\u{1F319} " + t("theme.title")));
3795
+ console.log(chalk19.gray("\u2500".repeat(40)));
3796
+ const cssPath = "src/styles/theme.css";
3797
+ const togglePath = "src/lib/theme-toggle.ts";
3798
+ const conflicts = [cssPath, togglePath].filter((p) => existsSync5(p));
3799
+ if (conflicts.length > 0) {
3800
+ const { overwrite } = await inquirer10.prompt([{
3801
+ type: "confirm",
3802
+ name: "overwrite",
3803
+ message: `\uB2E4\uC74C \uD30C\uC77C\uC774 \uC774\uBBF8 \uC788\uC5B4\uC694. \uB36E\uC5B4\uC4F8\uAE4C\uC694?
3804
+ ${conflicts.join("\n ")}`,
3805
+ default: false
3806
+ }]);
3807
+ if (!overwrite) {
3808
+ console.log(chalk19.yellow("\n\u23ED\uFE0F \uC0DD\uC131 \uCDE8\uC18C \u2014 \uAE30\uC874 \uD30C\uC77C \uC720\uC9C0."));
3809
+ return;
3810
+ }
3811
+ }
3812
+ mkdirSync3("src/styles", { recursive: true });
3813
+ mkdirSync3("src/lib", { recursive: true });
3814
+ writeFileSync4(cssPath, generateDarkCSS(), "utf-8");
3815
+ console.log(chalk19.green("\n\u2705 src/styles/theme.css \uC0DD\uC131 (\uB2E4\uD06C/\uB77C\uC774\uD2B8 \uBAA8\uB4DC)"));
3816
+ writeFileSync4(togglePath, generateToggleUtil(), "utf-8");
3817
+ console.log(chalk19.green("\u2705 src/lib/theme-toggle.ts \uC0DD\uC131 (\uD1A0\uAE00 \uC720\uD2F8\uB9AC\uD2F0)"));
3818
+ console.log(chalk19.bold("\n\u{1F4D6} \uC0AC\uC6A9\uBC95:"));
3819
+ console.log(chalk19.gray(" 1. theme.css\uB97C \uAE00\uB85C\uBC8C \uC2A4\uD0C0\uC77C\uC5D0 \uCD94\uAC00"));
3820
+ console.log(chalk19.gray(' 2. import { initTheme, toggleTheme } from "./lib/theme-toggle"'));
3821
+ console.log(chalk19.gray(" 3. \uC571 \uC9C4\uC785\uC810\uC5D0\uC11C initTheme() \uD638\uCD9C"));
3822
+ console.log(chalk19.gray(" 4. \uD1A0\uAE00 \uBC84\uD2BC\uC5D0\uC11C toggleTheme() \uD638\uCD9C"));
3823
+ printNextStep({
3824
+ message: "\uD14C\uB9C8 \uC124\uC815 \uC644\uB8CC!",
3825
+ command: "vhk ref list",
3826
+ cursorHint: "\uB808\uD37C\uB7F0\uC2A4 \uD655\uC778\uD574\uC918"
3827
+ });
3828
+ }
3829
+
3830
+ // src/commands/ref.ts
3831
+ import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync5 } from "fs";
3832
+ import chalk20 from "chalk";
3833
+ var REFS_PATH = ".vhk/refs.json";
3834
+ function loadRefs() {
3835
+ if (!existsSync6(REFS_PATH)) return [];
3836
+ try {
3837
+ const raw = readFileSync3(REFS_PATH, "utf-8");
3838
+ const parsed = JSON.parse(raw);
3839
+ return Array.isArray(parsed) ? parsed : [];
3840
+ } catch {
3841
+ return [];
3842
+ }
3843
+ }
3844
+ function saveRefs(refs) {
3845
+ mkdirSync4(".vhk", { recursive: true });
3846
+ writeFileSync5(REFS_PATH, JSON.stringify(refs, null, 2) + "\n", "utf-8");
3847
+ }
3848
+ async function refAdd(url, memo = "") {
3849
+ console.log(chalk20.bold("\n\u{1F517} " + t("ref.addTitle")));
3850
+ console.log(chalk20.gray("\u2500".repeat(40)));
3851
+ if (!url) {
3852
+ console.log(chalk20.red("\u274C URL\uC744 \uC785\uB825\uD574\uC8FC\uC138\uC694."));
3853
+ console.log(chalk20.gray(' \uC608: vhk ref add https://example.com --memo "\uCC38\uACE0 \uC0AC\uC774\uD2B8"'));
3854
+ return;
3855
+ }
3856
+ const refs = loadRefs();
3857
+ if (refs.some((r) => r.url === url)) {
3858
+ console.log(chalk20.yellow("\u26A0\uFE0F \uC774\uBBF8 \uC800\uC7A5\uB41C URL\uC785\uB2C8\uB2E4."));
3859
+ return;
3860
+ }
3861
+ refs.push({ url, memo, addedAt: (/* @__PURE__ */ new Date()).toISOString() });
3862
+ saveRefs(refs);
3863
+ console.log(chalk20.green(`
3864
+ \u2705 \uB808\uD37C\uB7F0\uC2A4 \uCD94\uAC00\uB428 (#${refs.length})`));
3865
+ console.log(chalk20.cyan(` ${url}`));
3866
+ if (memo) console.log(chalk20.gray(` \u{1F4DD} ${memo}`));
3867
+ printNextStep({
3868
+ message: "\uB808\uD37C\uB7F0\uC2A4 \uC800\uC7A5 \uC644\uB8CC!",
3869
+ command: "vhk ref list",
3870
+ cursorHint: "\uB808\uD37C\uB7F0\uC2A4 \uBAA9\uB85D \uBCF4\uC5EC\uC918"
3871
+ });
3872
+ }
3873
+ async function refList() {
3874
+ console.log(chalk20.bold("\n\u{1F4DA} " + t("ref.listTitle")));
3875
+ console.log(chalk20.gray("\u2500".repeat(40)));
3876
+ const refs = loadRefs();
3877
+ if (refs.length === 0) {
3878
+ console.log(chalk20.yellow("\n\u{1F4ED} \uC800\uC7A5\uB41C \uB808\uD37C\uB7F0\uC2A4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4."));
3879
+ console.log(chalk20.gray(' vhk ref add <url> --memo "\uBA54\uBAA8"\uB85C \uCD94\uAC00\uD558\uC138\uC694.'));
3880
+ return;
3881
+ }
3882
+ console.log(chalk20.cyan(`
3883
+ \uCD1D ${refs.length}\uAC1C\uC758 \uB808\uD37C\uB7F0\uC2A4:
3884
+ `));
3885
+ refs.forEach((ref, index) => {
3886
+ const date = new Date(ref.addedAt).toLocaleDateString("ko-KR");
3887
+ console.log(chalk20.white(` [${index + 1}] ${ref.url}`));
3888
+ if (ref.memo) console.log(chalk20.gray(` \u{1F4DD} ${ref.memo}`));
3889
+ console.log(chalk20.gray(` \u{1F4C5} ${date}`));
3890
+ console.log("");
3891
+ });
3892
+ }
3893
+ async function refOpen(indexStr) {
3894
+ const refs = loadRefs();
3895
+ const idx = parseInt(indexStr, 10) - 1;
3896
+ if (Number.isNaN(idx) || idx < 0 || idx >= refs.length) {
3897
+ console.log(chalk20.red(`\u274C \uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uBC88\uD638\uC785\uB2C8\uB2E4. (1~${refs.length || 0})`));
3898
+ return;
3899
+ }
3900
+ const ref = refs[idx];
3901
+ let parsed;
3902
+ try {
3903
+ parsed = new URL(ref.url);
3904
+ } catch {
3905
+ console.log(chalk20.red(`\u274C \uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 URL: ${ref.url}`));
3906
+ return;
3907
+ }
3908
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
3909
+ console.log(chalk20.red(`\u274C http(s) URL\uB9CC \uC5F4 \uC218 \uC788\uC2B5\uB2C8\uB2E4 (${parsed.protocol})`));
3910
+ return;
3911
+ }
3912
+ console.log(chalk20.cyan(`
3913
+ \u{1F310} \uC5F4\uAE30: ${ref.url}`));
3914
+ let result;
3915
+ if (process.platform === "darwin") {
3916
+ result = safeExecFile("open", [ref.url]);
3917
+ } else if (process.platform === "win32") {
3918
+ result = safeExecFile("cmd.exe", ["/c", "start", "", ref.url]);
3919
+ } else {
3920
+ result = safeExecFile("xdg-open", [ref.url]);
3921
+ }
3922
+ if (result.ok) {
3923
+ console.log(chalk20.green("\u2705 \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uC5F4\uC5C8\uC2B5\uB2C8\uB2E4."));
3924
+ } else {
3925
+ console.log(chalk20.yellow("\u26A0\uFE0F \uBE0C\uB77C\uC6B0\uC800\uB97C \uC5F4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. URL\uC744 \uC9C1\uC811 \uBC29\uBB38\uD574\uC8FC\uC138\uC694."));
3926
+ }
3927
+ }
3928
+
3574
3929
  // src/lib/nlp-run.ts
3575
3930
  async function dispatchNlpRoute(route, input) {
3576
3931
  switch (route.command) {
@@ -3611,28 +3966,36 @@ async function dispatchNlpRoute(route, input) {
3611
3966
  return envCheck();
3612
3967
  case "publish":
3613
3968
  return publish();
3969
+ case "design":
3970
+ return design();
3971
+ case "design-palette":
3972
+ return designPalette();
3973
+ case "theme":
3974
+ return theme();
3975
+ case "ref":
3976
+ return refList();
3614
3977
  }
3615
3978
  }
3616
3979
  async function runNaturalLanguageRoute(input) {
3617
3980
  const route = routeNaturalLanguage(input);
3618
3981
  if (!route) {
3619
- console.log(chalk18.yellow(`
3982
+ console.log(chalk21.yellow(`
3620
3983
  \u2753 "${input}" \u2014 ${ko.nlp.notMatched}
3621
3984
  `));
3622
3985
  return;
3623
3986
  }
3624
3987
  console.log("");
3625
- console.log(chalk18.cyan(` \u{1F4AC} "${input}"`));
3626
- console.log(chalk18.cyan(` \u2192 ${route.explanation}`));
3988
+ console.log(chalk21.cyan(` \u{1F4AC} "${input}"`));
3989
+ console.log(chalk21.cyan(` \u2192 ${route.explanation}`));
3627
3990
  if (route.confidence === "low") {
3628
- const { confirm } = await inquirer9.prompt([{
3991
+ const { confirm } = await inquirer11.prompt([{
3629
3992
  type: "confirm",
3630
3993
  name: "confirm",
3631
3994
  message: `${route.explanation} \u2014 ${ko.nlp.matched}`,
3632
3995
  default: true
3633
3996
  }]);
3634
3997
  if (!confirm) {
3635
- console.log(chalk18.dim(` ${ko.nlp.menuHint}`));
3998
+ console.log(chalk21.dim(` ${ko.nlp.menuHint}`));
3636
3999
  return;
3637
4000
  }
3638
4001
  }
@@ -3659,9 +4022,13 @@ var KO_ALIASES = {
3659
4022
  deploy: "\uBC30\uD3EC",
3660
4023
  env: "\uD658\uACBD\uBCC0\uC218",
3661
4024
  "env-check": "\uD658\uACBD\uBCC0\uC218\uC810\uAC80",
3662
- publish: "\uCD9C\uC2DC"
4025
+ publish: "\uCD9C\uC2DC",
4026
+ design: "\uB514\uC790\uC778",
4027
+ "design-palette": "\uD314\uB808\uD2B8",
4028
+ theme: "\uD14C\uB9C8",
4029
+ ref: "\uB808\uD37C\uB7F0\uC2A4"
3663
4030
  };
3664
- 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.7.1");
4031
+ 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.8.0");
3665
4032
  program.configureHelp({
3666
4033
  formatHelp(cmd, helper) {
3667
4034
  if (cmd.parent) {
@@ -3722,6 +4089,27 @@ program.command("env-check").alias("\uD658\uACBD\uBCC0\uC218\uC810\uAC80").descr
3722
4089
  program.command("publish").alias("\uCD9C\uC2DC").description("npm \uBC30\uD3EC (\uBC84\uC804 \uBC94\uD504 \u2192 \uBE4C\uB4DC \u2192 \uD14C\uC2A4\uD2B8 \u2192 publish)").action(async () => {
3723
4090
  await publish();
3724
4091
  });
4092
+ program.command("design").alias("\uB514\uC790\uC778").description("\uB514\uC790\uC778 \uD1A0\uD070 \uC0DD\uC131 (Tailwind config \uB610\uB294 CSS \uBCC0\uC218)").action(async () => {
4093
+ await design();
4094
+ });
4095
+ program.command("design-palette").alias("\uD314\uB808\uD2B8").description("\uCEEC\uB7EC \uD314\uB808\uD2B8 \uD504\uB9AC\uC14B \uC120\uD0DD + \uC801\uC6A9").action(async () => {
4096
+ await designPalette();
4097
+ });
4098
+ program.command("theme").alias("\uD14C\uB9C8").description("\uB2E4\uD06C/\uB77C\uC774\uD2B8 \uBAA8\uB4DC CSS + \uD1A0\uAE00 \uC720\uD2F8\uB9AC\uD2F0 \uC0DD\uC131").action(async () => {
4099
+ await theme();
4100
+ });
4101
+ var refCmd = program.command("ref").alias("\uB808\uD37C\uB7F0\uC2A4").description("\uB808\uD37C\uB7F0\uC2A4 URL \uAD00\uB9AC (add / list / open)").action(async () => {
4102
+ await refList();
4103
+ });
4104
+ refCmd.command("add <url>").option("--memo <memo>", "\uBA54\uBAA8 \uCD94\uAC00").description("\uB808\uD37C\uB7F0\uC2A4 URL \uCD94\uAC00").action(async (url, opts) => {
4105
+ await refAdd(url, opts.memo);
4106
+ });
4107
+ refCmd.command("list").alias("\uBAA9\uB85D").description("\uC800\uC7A5\uB41C \uB808\uD37C\uB7F0\uC2A4 \uBAA9\uB85D").action(async () => {
4108
+ await refList();
4109
+ });
4110
+ refCmd.command("open <index>").alias("\uC5F4\uAE30").description("\uB808\uD37C\uB7F0\uC2A4\uB97C \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uC5F4\uAE30").action(async (index) => {
4111
+ await refOpen(index);
4112
+ });
3725
4113
  program.on("command:*", async (operands) => {
3726
4114
  const unknown = operands[0] ?? "";
3727
4115
  const rest = operands.slice(1);
@@ -3730,7 +4118,7 @@ program.on("command:*", async (operands) => {
3730
4118
  });
3731
4119
  program.action(async () => {
3732
4120
  console.log("\n\u{1F3AF} VHK \u2014 \uBC14\uC774\uBE0C\uCF54\uB529 \uD504\uB85C\uC81D\uD2B8 \uCF54\uCE58\n");
3733
- const { choice } = await inquirer10.prompt([{
4121
+ const { choice } = await inquirer12.prompt([{
3734
4122
  type: "list",
3735
4123
  name: "choice",
3736
4124
  message: "\uBB58 \uB3C4\uC640\uB4DC\uB9B4\uAE4C\uC694?",
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-3HHU7V77.js";
4
+ } from "../chunk-NQ4V3VN4.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.1",
3
+ "version": "0.8.0",
4
4
  "description": "Vibe Harness Kit โ€” ๋ฐ”์ด๋ธŒ์ฝ”๋”ฉ ํ’€์‚ฌ์ดํด CLI",
5
5
  "bin": {
6
6
  "vhk": "dist/index.js",