@byh3071/vhk 0.3.0 → 0.3.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.
Files changed (2) hide show
  1. package/dist/index.js +359 -208
  2. package/package.json +3 -2
package/dist/index.js CHANGED
@@ -1,118 +1,168 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { Command } from "commander";
4
+ import { Command, Help } from "commander";
5
+ import inquirer4 from "inquirer";
5
6
 
6
7
  // src/commands/gate.ts
7
8
  import inquirer from "inquirer";
8
- import chalk from "chalk";
9
+ import chalk2 from "chalk";
9
10
 
10
11
  // src/i18n/ko.ts
11
12
  var ko = {
13
+ start: {
14
+ title: "\u{1F527} VHK \u2014 \uBB34\uC5C7\uC744 \uB3C4\uC640\uB4DC\uB9B4\uAE4C\uC694?",
15
+ subtitle: "\uBC88\uD638\uB9CC \uACE0\uB974\uBA74 \uB429\uB2C8\uB2E4. \uBA85\uB839\uC5B4\uB97C \uC678\uC6B8 \uD544\uC694 \uC5C6\uC5B4\uC694.",
16
+ menuPrompt: "\uC5B4\uB5A4 \uC791\uC5C5\uC744 \uD560\uAE4C\uC694?",
17
+ choiceGate: "1) \uC0C8 \uC544\uC774\uB514\uC5B4 \uAC80\uC99D\uD558\uAE30",
18
+ choiceInit: "2) \uD504\uB85C\uC81D\uD2B8 \uC2DC\uC791\uD558\uAE30 \u2014 \uD3F4\uB354\xB7\uD30C\uC77C \uB9CC\uB4E4\uAE30",
19
+ choiceInitSkipGate: "3) \uAE30\uD68D\uC740 \uB05D\uB0AC\uC5B4\uC694 \u2014 \uBC14\uB85C \uD504\uB85C\uC81D\uD2B8 \uB9CC\uB4E4\uAE30",
20
+ choiceRecap: "4) \uC624\uB298 \uD55C \uC77C \uC815\uB9AC\uD558\uAE30",
21
+ choiceSync: "5) \uADDC\uCE59 \uD30C\uC77C \uB9DE\uCD94\uAE30",
22
+ choiceCheck: "6) \uD504\uB85C\uC81D\uD2B8 \uADDC\uCE59 \uC810\uAC80\uD558\uAE30",
23
+ choiceSecure: "7) \uBE44\uBC00\uBC88\uD638\xB7\uD0A4 \uC720\uCD9C \uAC80\uC0AC\uD558\uAE30",
24
+ choiceExit: "\uB098\uAC00\uAE30",
25
+ goodbye: "\uB2E4\uC74C\uC5D0 \uB610 \uBD88\uB7EC\uC8FC\uC138\uC694."
26
+ },
12
27
  gate: {
13
- title: "\u{1F50D} VHK IDEA GATE \u2014 \uC544\uC774\uB514\uC5B4 \uAC80\uC99D",
14
- modePrompt: "\uC5B4\uB5A4 \uAC80\uC99D\uC744 \uD560\uAE4C\uC694?",
15
- modeQuickLabel: "\u26A1 \uD035 \uCCB4\uD06C (\uD575\uC2EC 5\uBB38\uD56D) \u2014 \uC544\uC774\uB514\uC5B4 \uB9C9 \uB5A0\uC62C\uB790\uC744 \uB54C",
16
- modeFullLabel: "\u{1F50D} \uD480 \uAC80\uC99D (13\uB2E8\uACC4) \u2014 \uAE30\uD68D\uC774 \uC5B4\uB290 \uC815\uB3C4 \uC7A1\uD614\uC744 \uB54C",
17
- modeSkipLabel: "\u23ED\uFE0F \uC2A4\uD0B5 \u2014 \uC774\uBBF8 \uB178\uC158/\uBB38\uC11C\uC5D0\uC11C \uAE30\uD68D \uC644\uB8CC",
18
- skipSourcePrompt: "\u{1F4C4} \uAE30\uD68D \uBB38\uC11C \uC704\uCE58 (\uB178\uC158 URL, \uD30C\uC77C \uACBD\uB85C \uB4F1):",
19
- skipGo: "\u{1F7E2} GO \u2014 \uAE30\uD68D \uC644\uB8CC \uD655\uC778. vhk init\uC73C\uB85C \uC9C4\uD589\uD558\uC138\uC694!",
28
+ title: "\u{1F4A1} \uC544\uC774\uB514\uC5B4 \uAC80\uC99D",
29
+ welcome: "\uC0C8 \uC544\uC774\uB514\uC5B4\uB97C \uAC80\uC99D\uD569\uB2C8\uB2E4. \uC9C8\uBB38\uC5D0 \uB2F5\uD574\uC8FC\uC138\uC694.",
30
+ modePrompt: "\uC5B4\uB5BB\uAC8C \uAC80\uC99D\uD560\uAE4C\uC694?",
31
+ modeQuickLabel: "\u26A1 \uC9E7\uAC8C (\uD575\uC2EC 5\uBB38\uD56D) \u2014 \uB9C9 \uB5A0\uC62C\uB790\uC744 \uB54C",
32
+ modeFullLabel: "\u{1F50D} \uC790\uC138\uD788 (13\uBB38\uD56D) \u2014 \uAE30\uD68D\uC774 \uC5B4\uB290 \uC815\uB3C4 \uC7A1\uD614\uC744 \uB54C",
33
+ modeSkipLabel: "\u23ED\uFE0F \uAC74\uB108\uB6F0\uAE30 \u2014 \uB178\uC158\xB7\uBB38\uC11C\uC5D0 \uC774\uBBF8 \uAE30\uD68D\uD574 \uB460",
34
+ skipSourcePrompt: "\u{1F4C4} \uAE30\uD68D \uBB38\uC11C \uC704\uCE58 (\uB178\uC158 \uC8FC\uC18C, \uD30C\uC77C \uACBD\uB85C \uB4F1):",
35
+ skipGo: "\u2705 \uC2DC\uC791\uD574\uB3C4 \uB3FC\uC694! \uC774\uC81C \uD504\uB85C\uC81D\uD2B8\uB97C \uB9CC\uB4E4\uC5B4 \uBCF4\uC138\uC694 (vhk init)",
20
36
  skipSourceLabel: (source) => `\uAE30\uD68D \uBB38\uC11C: ${source}`,
21
- quickHeader: "\u26A1 \uD035 \uCCB4\uD06C",
22
- fullHeader: "\u{1F50D} \uD480 \uAC80\uC99D",
37
+ quickHeader: "\u26A1 \uC9E7\uC740 \uAC80\uC99D",
38
+ fullHeader: "\u{1F50D} \uC790\uC138\uD55C \uAC80\uC99D",
23
39
  modeCountSuffix: (total) => `\u2014 ${total}\uBB38\uD56D`,
24
- idea: "\u{1F4A1} \uBB50 \uB9CC\uB4E4 \uAC70\uC57C? (\uD55C \uC904)",
25
- painPoint: "\u{1F624} Pain Point\uB294?",
26
- edge: "\u{1F4AA} \uB098\uC758 Edge\uB294?",
27
- checklistStart: "\u2500\u2500\u2500 \uCCB4\uD06C\uB9AC\uC2A4\uD2B8 \uC2DC\uC791 \u2500\u2500\u2500",
40
+ idea: "\u{1F4A1} \uC5B4\uB5A4 \uAC78 \uB9CC\uB4E4 \uAC74\uAC00\uC694? (\uD55C \uC904)",
41
+ ideaHint: '\uC608: "\uD300 \uD560 \uC77C\uC744 3\uCD08\uC5D0 \uCD94\uAC00\uD558\uB294 \uC571"',
42
+ painPoint: "\u{1F624} \uC774 \uBB38\uC81C, \uB204\uAC00 \uC5BC\uB9C8\uB098 \uC544\uD30C\uD574\uC694?",
43
+ painPointHint: '\uC608: "\uB9E4\uC77C \uC5D1\uC140\uC5D0 \uBCF5\uBD99\uD558\uB290\uB77C 30\uBD84\uC529 \uB0A0\uB9BC"',
44
+ edge: "\u{1F4AA} \uB098\uB9CC\uC758 \uAC15\uC810\uC740? (\uBE44\uC2B7\uD55C \uAC8C \uC788\uB294\uB370 \uC65C \uC774\uAC78?)",
45
+ edgeHint: '\uC608: "\uD55C\uAD6D\uC5B4\uB85C \uB41C \uAC00\uC774\uB4DC + \uBC14\uB85C \uC4F0\uB294 \uD15C\uD50C\uB9BF"',
46
+ checklistStart: "\u2500\u2500\u2500 \uC774\uC5B4\uC11C \uC9C8\uBB38\uD569\uB2C8\uB2E4 \u2500\u2500\u2500",
28
47
  hintPrefix: " \u{1F4A1}",
29
- verdictPrompt: (failIf) => ` \u2192 \uD310\uC815? (\u{1F534} \uC2E4\uD328 \uAE30\uC900: ${failIf})`,
30
- statusPassChoice: "\u2705 \uD1B5\uACFC",
31
- statusHoldChoice: "\u{1F7E1} \uC544\uC9C1 \uBAA8\uB974\uACA0\uC74C / \uBCF4\uB958",
32
- statusFailChoice: "\u{1F534} \uC2E4\uD328",
33
- statusPassLine: " \u2705 \uD1B5\uACFC",
34
- statusHoldLine: " \u{1F7E1} \uBCF4\uB958",
35
- statusFailLine: " \u{1F534} \uC2E4\uD328",
36
- verdictTitle: "\u2550\u2550\u2550 \uD310\uC815 \uACB0\uACFC \u2550\u2550\u2550",
37
- ideaLabel: "\uC544\uC774\uB514\uC5B4:",
38
- painPointLabel: "Pain Point:",
39
- edgeLabel: "Edge:",
40
- countLine: (failCount, holdCount, total) => `\u{1F534} \uC2E4\uD328: ${failCount}\uAC1C \xB7 \u{1F7E1} \uBCF4\uB958: ${holdCount}\uAC1C / ${total}\uAC1C`,
41
- go: "\u{1F7E2} GO \u2014 Phase 2\uB85C \uC989\uC2DC \uC9C4\uD589!",
42
- refine: "\u{1F7E1} REFINE \u2014 \u{1F534} \uD56D\uBAA9 \uC218\uC815 \uD6C4 \uC7AC\uAC80\uC99D (1\uD68C \uD55C\uC815)",
43
- drop: "\u{1F534} DROP \u2014 \uC544\uC774\uB514\uC5B4 \uD5C8\uBE0C\uB85C \uBCF5\uADC0. \uB2E4\uB978 \uC544\uC774\uB514\uC5B4\uB97C \uC2DC\uB3C4\uD558\uC138\uC694.",
44
- nextCommand: "\uB2E4\uC74C \uBA85\uB839\uC5B4: vhk init",
45
- holdRemainHint: "\u{1F4A1} \uBCF4\uB958 \uD56D\uBAA9\uC740 \uAC1C\uBC1C\uD558\uBA74\uC11C \uCC44\uC6CC\uB098\uAC00\uC138\uC694."
48
+ verdictPrompt: (_failIf) => " \u2192 \uC9C0\uAE08 \uC0C1\uD0DC\uB294?",
49
+ statusPassChoice: "\u2705 \uAD1C\uCC2E\uC544\uC694",
50
+ statusHoldChoice: "\u{1F7E1} \uC544\uC9C1 \uBAA8\uB974\uACA0\uC5B4\uC694 (\uB098\uC911\uC5D0 \uCC44\uC6CC\uB3C4 \uB429\uB2C8\uB2E4)",
51
+ statusFailChoice: "\u{1F504} \uBC94\uC704\uB97C \uC904\uC5EC\uBCFC\uAC8C\uC694",
52
+ statusPassLine: " \u2705 \uAD1C\uCC2E\uC544\uC694",
53
+ statusHoldLine: " \u{1F7E1} \uBCF4\uB958 \u2014 \uAC1C\uBC1C\uD558\uBA74\uC11C \uCC44\uC6CC\uB3C4 \uB429\uB2C8\uB2E4",
54
+ statusFailLine: " \u{1F504} \uBC94\uC704 \uC870\uC815\uC774 \uD544\uC694\uD574 \uBCF4\uC5EC\uC694",
55
+ verdictTitle: "\u2550\u2550\u2550 \uACB0\uACFC \u2550\u2550\u2550",
56
+ ideaLabel: "\uB9CC\uB4E4 \uAC83:",
57
+ painPointLabel: "\uC544\uD508 \uC810:",
58
+ edgeLabel: "\uB098\uB9CC\uC758 \uAC15\uC810:",
59
+ countLine: (failCount, holdCount, total) => `\uBC94\uC704 \uC870\uC815 ${failCount}\uAC1C \xB7 \uBCF4\uB958 ${holdCount}\uAC1C / ${total}\uBB38\uD56D`,
60
+ go: "\u2705 \uC2DC\uC791\uD574\uB3C4 \uB3FC\uC694! \uB2E4\uC74C \uB2E8\uACC4(\uD504\uB85C\uC81D\uD2B8 \uB9CC\uB4E4\uAE30)\uB85C \uB118\uC5B4\uAC00\uC138\uC694.",
61
+ refine: "\u{1F504} \uC870\uAE08 \uB354 \uB2E4\uB4EC\uC73C\uBA74 \uC88B\uACA0\uC5B4\uC694. \uC704 \uD56D\uBAA9\uC744 \uBCF4\uC644\uD574 \uBCF4\uC138\uC694.",
62
+ drop: "\u{1F4A1} \uB2E4\uB978 \uC544\uC774\uB514\uC5B4\uB97C \uAC80\uD1A0\uD574 \uBCF4\uB294 \uAC74 \uC5B4\uB5A8\uAE4C\uC694?",
63
+ nextCommand: "\uB2E4\uC74C: vhk init (\uD504\uB85C\uC81D\uD2B8 \uC2DC\uC791\uD558\uAE30)",
64
+ holdRemainHint: "\u{1F4A1} \uBCF4\uB958\uD55C \uD56D\uBAA9\uC740 \uAC1C\uBC1C\uD558\uBA74\uC11C \uCC44\uC6CC\uB3C4 \uAD1C\uCC2E\uC544\uC694.",
65
+ failMessage: "\uC544\uC9C1 \uBAA8\uB974\uACA0\uC5B4\uC694 \u2192 \uAD1C\uCC2E\uC544\uC694, \uAC1C\uBC1C\uD558\uBA74\uC11C \uCC44\uC6CC\uB3C4 \uB429\uB2C8\uB2E4."
46
66
  },
47
67
  init: {
48
- title: "\u{1F6E0}\uFE0F VHK INIT \u2014 \uD504\uB85C\uC81D\uD2B8 \uCD08\uAE30\uD654 + \uD558\uB124\uC2A4 \uC0DD\uC131",
49
- skipGate: "\u23ED\uFE0F Phase 0 (gate) \uC2A4\uD0B5 \u2014 \uAE30\uC874 \uAE30\uD68D/\uC124\uACC4\uB85C \uC9C4\uD589",
50
- projectName: "\u{1F4E6} \uD504\uB85C\uC81D\uD2B8 \uC774\uB984:",
51
- description: "\u{1F4DD} \uD55C \uC904 \uC124\uBA85:",
52
- projectType: "\u{1F3D7}\uFE0F \uD504\uB85C\uC81D\uD2B8 \uC720\uD615:",
53
- confirmStack: "\uC774 \uC2A4\uD0DD\uC73C\uB85C \uC9C4\uD589\uD560\uAE4C\uC694?",
54
- canceled: "\uCDE8\uC18C\uB428. \uC2A4\uD0DD\uC744 \uC218\uC815\uD558\uB824\uBA74 \uB2E4\uC2DC vhk init\uC744 \uC2E4\uD589\uD558\uC138\uC694.",
55
- recommendedStack: "\uCD94\uCC9C \uC2A4\uD0DD:",
56
- filesGenerating: "\u{1F4C2} \uD30C\uC77C \uC0DD\uC131 \uC911...",
57
- overwrite: (filePath) => ` \u26A0\uFE0F ${filePath} \uC774\uBBF8 \uC874\uC7AC\uD569\uB2C8\uB2E4. \uB36E\uC5B4\uC4F8\uAE4C\uC694?`,
58
- skipped: (filePath) => `${filePath} \uAC74\uB108\uB700`,
59
- done: "\u{1F389} \uD558\uB124\uC2A4 \uC124\uCE58 \uC644\uB8CC!",
60
- nextSteps: "\uB2E4\uC74C \uB2E8\uACC4:",
61
- fillHint: "CLAUDE.md \xB7 .cursorrules\uC758 __FILL__ \uC601\uC5ED\uC744 \uCC44\uC6B0\uC138\uC694",
62
- prdHint: "docs/PRD.md\uC5D0 v1 IN/OUT \uAE30\uB2A5\uC744 \uC815\uC758\uD558\uC138\uC694",
63
- notionFetching: "\u{1F4E1} Notion PRD \uD398\uC774\uC9C0 \uBD88\uB7EC\uC624\uB294 \uC911...",
64
- notionDone: (name) => `Notion PRD import \uC644\uB8CC: ${name}`,
65
- notionReviewHint: "docs/PRD.md \uB0B4\uC6A9\uC744 \uAC80\uD1A0\uD558\uACE0 __FILL__ \uC794\uC5EC \uD56D\uBAA9\uC744 \uCC44\uC6B0\uC138\uC694",
66
- gitHint: 'git init && git add . && git commit -m "feat: vhk init"',
67
- startDev: "\uAC1C\uBC1C \uC2DC\uC791! \u{1F680}"
68
+ title: "\u{1F6E0}\uFE0F \uD504\uB85C\uC81D\uD2B8 \uC2DC\uC791\uD558\uAE30",
69
+ skipGate: "\u23ED\uFE0F 1\uB2E8\uACC4(\uC544\uC774\uB514\uC5B4 \uAC80\uC99D) \uAC74\uB108\uB6F0\uAE30 \u2014 \uAE30\uD68D\xB7\uC124\uACC4\uAC00 \uC774\uBBF8 \uC788\uC5B4\uC694",
70
+ projectName: "\u{1F4E6} \uD504\uB85C\uC81D\uD2B8 \uC774\uB984\uC740?",
71
+ projectNameHint: '\uC608: "\uD300 \uD560 \uC77C \uC571"',
72
+ description: "\u{1F4DD} \uD55C \uC904\uB85C \uC124\uBA85\uD558\uBA74?",
73
+ descriptionHint: '\uC608: "3\uCD08 \uB9CC\uC5D0 \uD560 \uC77C \uCD94\uAC00"',
74
+ projectType: "\u{1F3D7}\uFE0F \uC5B4\uB5A4 \uC885\uB958\uC778\uAC00\uC694?",
75
+ confirmStack: "\uC774 \uAE30\uC220 \uBB36\uC74C\uC73C\uB85C \uC9C4\uD589\uD560\uAE4C\uC694?",
76
+ canceled: "\uCDE8\uC18C\uD588\uC5B4\uC694. \uAE30\uC220 \uBB36\uC74C\uC744 \uBC14\uAFB8\uB824\uBA74 \uB2E4\uC2DC vhk init\uC744 \uC2E4\uD589\uD558\uC138\uC694.",
77
+ recommendedStack: "\uCD94\uCC9C \uAE30\uC220 \uBB36\uC74C:",
78
+ filesGenerating: "\u{1F4C2} \uD544\uC694\uD55C \uD30C\uC77C \uB9CC\uB4DC\uB294 \uC911...",
79
+ overwrite: (filePath) => ` \u26A0\uFE0F ${filePath} \uD30C\uC77C\uC774 \uC788\uC5B4\uC694. \uB36E\uC5B4\uC4F8\uAE4C\uC694?`,
80
+ skipped: (filePath) => `${filePath} \u2014 \uAC74\uB108\uB700`,
81
+ done: "\u{1F389} \uD504\uB85C\uC81D\uD2B8 \uBF08\uB300\uAC00 \uC900\uBE44\uB410\uC5B4\uC694!",
82
+ nextSteps: "\uB2E4\uC74C\uC5D0 \uD560 \uC77C:",
83
+ fillHint: "CLAUDE.md \xB7 .cursorrules\uC5D0\uC11C \u{1F449} \uC5EC\uAE30\uB97C \uCC44\uC6CC\uC8FC\uC138\uC694 \uD45C\uC2DC\uB97C \uCC3E\uC544 \uCC44\uC6B0\uC138\uC694",
84
+ prdHint: "docs/PRD.md\uC5D0 1\uCC28 \uBC84\uC804\uC5D0 \uB123\uC744 \uAE30\uB2A5\xB7\uBE7C\uB294 \uAE30\uB2A5\uC744 \uC801\uC5B4 \uBCF4\uC138\uC694",
85
+ notionFetching: "\u{1F4E1} \uB178\uC158 \uAE30\uD68D \uD398\uC774\uC9C0 \uBD88\uB7EC\uC624\uB294 \uC911...",
86
+ notionDone: (name) => `\uB178\uC158\uC5D0\uC11C \uAC00\uC838\uC624\uAE30 \uC644\uB8CC: ${name}`,
87
+ notionReviewHint: "docs/PRD.md\uB97C \uC77D\uACE0 \u{1F449} \uC5EC\uAE30\uB97C \uCC44\uC6CC\uC8FC\uC138\uC694 \uD56D\uBAA9\uC744 \uCC44\uC6B0\uC138\uC694",
88
+ gitHintLabel: "\uD130\uBBF8\uB110\uC5D0 \uBCF5\uC0AC\uD560 \uBA85\uB839 (\uC544\uB798 \uBC15\uC2A4 \uBCF5\uBD99):",
89
+ gitHintCommand: 'git init && git add . && git commit -m "feat: \uD504\uB85C\uC81D\uD2B8 \uC2DC\uC791"',
90
+ startDev: "\uC774\uC81C \uAC1C\uBC1C\uD574 \uBCF4\uC138\uC694! \u{1F680}"
68
91
  },
69
92
  recap: {
70
- title: "\u{1F4DD} VHK RECAP \u2014 \uC138\uC158 \uAE30\uB85D \uC790\uB3D9 \uC0DD\uC131",
71
- analyzing: "\u{1F4CA} Git \uBCC0\uACBD\uC0AC\uD56D \uBD84\uC11D \uC911...",
72
- noRepo: "\u274C Git \uB808\uD3EC\uAC00 \uC544\uB2D9\uB2C8\uB2E4. git init \uBA3C\uC800 \uC2E4\uD589\uD558\uC138\uC694.",
73
- noChanges: "\u26A0\uFE0F \uBCC0\uACBD\uC0AC\uD56D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
74
- summary: "\u{1F4DD} \uC774\uBC88 \uC138\uC158\uC5D0\uC11C \uBB58 \uD588\uB098\uC694? (1~3\uC904)",
75
- decisions: "\u{1F9ED} \uB0B4\uB9B0 \uACB0\uC815\uC774 \uC788\uB098\uC694? (\uC5C6\uC73C\uBA74 Enter)",
76
- nextTodo: "\u23ED\uFE0F \uB2E4\uC74C \uC138\uC158\uC5D0\uC11C \uD560 \uC77C\uC740?",
77
- blockers: "\u{1F6A7} \uBE14\uB85C\uCEE4\uAC00 \uC788\uB098\uC694? (\uC5C6\uC73C\uBA74 Enter)",
78
- done: "\u2705 \uC138\uC158 \uB85C\uADF8 \uC0DD\uC131 \uC644\uB8CC!",
79
- updateClaude: 'CLAUDE.md "\uD604\uC7AC \uC0C1\uD0DC" \uC139\uC158\uB3C4 \uC5C5\uB370\uC774\uD2B8\uD560\uAE4C\uC694?',
80
- adrDetected: "\u{1F4D0} \uAE30\uC220 \uC2A4\uD0DD \uBCC0\uACBD \uAC10\uC9C0!",
81
- createAdr: "ADR\uC744 \uC0DD\uC131\uD560\uAE4C\uC694?",
82
- troubleDetected: "\u{1F527} \uD2B8\uB7EC\uBE14\uC288\uD305 \uCEE4\uBC0B \uAC10\uC9C0!",
83
- createTroubleshoot: "\uD2B8\uB7EC\uBE14\uC288\uD305 \uBB38\uC11C\uB97C \uC0DD\uC131\uD560\uAE4C\uC694?"
93
+ title: "\u{1F4DD} \uC624\uB298 \uD55C \uC77C \uC815\uB9AC",
94
+ analyzing: "\u{1F4CA} \uC624\uB298 \uBC14\uB010 \uD30C\uC77C\xB7\uCEE4\uBC0B\uC744 \uC0B4\uD3B4\uBCF4\uB294 \uC911...",
95
+ noRepo: "\u274C Git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2C8\uC5D0\uC694. \uBA3C\uC800 git init\uC744 \uC2E4\uD589\uD558\uC138\uC694.",
96
+ noChanges: "\u26A0\uFE0F \uC624\uB298 \uBC14\uB010 \uB0B4\uC6A9\uC774 \uC5C6\uC5B4\uC694.",
97
+ summary: "\u{1F4DD} \uC774\uBC88\uC5D0 \uBB58 \uD588\uB098\uC694? (1~3\uC904)",
98
+ summaryHint: '\uC608: "\uB85C\uADF8\uC778 \uD654\uBA74 \uB9CC\uB4E4\uACE0 \uBC84\uD2BC \uC0C9 \uACE0\uCE68"',
99
+ decisions: "\u{1F9ED} \uC815\uD55C \uACB0\uC815\uC774 \uC788\uB098\uC694? (\uC5C6\uC73C\uBA74 Enter)",
100
+ nextTodo: "\u23ED\uFE0F \uB2E4\uC74C\uC5D0 \uD560 \uC77C\uC740?",
101
+ blockers: "\u{1F6A7} \uB9C9\uD78C \uAC8C \uC788\uB098\uC694? (\uC5C6\uC73C\uBA74 Enter)",
102
+ done: "\u2705 \uC624\uB298 \uAE30\uB85D\uC744 \uC800\uC7A5\uD588\uC5B4\uC694!",
103
+ updateClaude: 'CLAUDE.md "\uC9C0\uAE08 \uC0C1\uD0DC"\uB3C4 \uAC19\uC774 \uACE0\uCE60\uAE4C\uC694?',
104
+ adrDetected: "\u{1F4D0} \uC4F0\uB294 \uAE30\uC220\xB7\uC124\uC815\uC774 \uBC14\uB010 \uAC83 \uAC19\uC544\uC694!",
105
+ createAdr: "\uC65C \uADF8\uB807\uAC8C \uD588\uB294\uC9C0 \uAE30\uB85D \uBB38\uC11C\uB97C \uB9CC\uB4E4\uAE4C\uC694?",
106
+ troubleDetected: "\u{1F527} \uBC84\uADF8\xB7\uC624\uB958\uB97C \uACE0\uCE5C \uCEE4\uBC0B\uC774 \uBCF4\uC5EC\uC694!",
107
+ createTroubleshoot: "\uC5B4\uB5BB\uAC8C \uACE0\uCCE4\uB294\uC9C0 \uBA54\uBAA8\uB97C \uB0A8\uAE38\uAE4C\uC694?"
84
108
  },
85
109
  check: {
86
- title: "\u{1F50D} VHK CHECK \u2014 RULES.md \uADDC\uCE59 \uB9B0\uD2B8",
87
- noRules: "\u26A0\uFE0F RULES.md\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
88
- noAutoRules: "\u26A0\uFE0F \uC790\uB3D9 \uAC80\uC99D \uAC00\uB2A5\uD55C \uADDC\uCE59\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
89
- allPassed: "\u{1F389} \uBAA8\uB4E0 \uADDC\uCE59 \uD1B5\uACFC!",
90
- summary: "\u{1F4CA} \uB9B0\uD2B8 \uACB0\uACFC:"
110
+ title: "\u{1F50D} \uD504\uB85C\uC81D\uD2B8 \uADDC\uCE59 \uC810\uAC80",
111
+ noRules: "\u26A0\uFE0F RULES.md \uD30C\uC77C\uC774 \uC5C6\uC5B4\uC694.",
112
+ noAutoRules: "\u26A0\uFE0F \uC790\uB3D9\uC73C\uB85C \uAC80\uC0AC\uD560 \uADDC\uCE59\uC774 \uC5C6\uC5B4\uC694.",
113
+ allPassed: "\u{1F389} \uADDC\uCE59\uC744 \uBAA8\uB450 \uC9C0\uCF30\uC5B4\uC694!",
114
+ summary: "\u{1F4CA} \uC810\uAC80 \uACB0\uACFC:"
91
115
  },
92
116
  secure: {
93
- title: "\u{1F512} VHK SECURE SCAN \u2014 \uC2DC\uD06C\uB9BF/\uD0A4 \uC720\uCD9C \uAC10\uC9C0",
94
- noGitignore: "\u26A0\uFE0F .gitignore\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4!",
95
- noEnvInGitignore: "\u26A0\uFE0F .gitignore\uC5D0 .env\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4!",
96
- scanning: "\u{1F50D} \uD30C\uC77C \uC2A4\uCE94 \uC911...",
97
- clean: "\u{1F389} \uC2DC\uD06C\uB9BF\uC774 \uAC10\uC9C0\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4!",
98
- summary: "\u{1F4CA} \uC2A4\uCE94 \uC694\uC57D:"
117
+ title: "\u{1F512} \uBE44\uBC00\uBC88\uD638\xB7\uD0A4 \uC720\uCD9C \uAC80\uC0AC",
118
+ noGitignore: "\u26A0\uFE0F .gitignore \uD30C\uC77C\uC774 \uC5C6\uC5B4\uC694!",
119
+ noEnvInGitignore: "\u26A0\uFE0F .gitignore\uC5D0 .env\uAC00 \uC5C6\uC5B4\uC694!",
120
+ scanning: "\u{1F50D} \uD30C\uC77C\uC744 \uC0B4\uD3B4\uBCF4\uB294 \uC911...",
121
+ clean: "\u{1F389} \uBE44\uBC00\uBC88\uD638\xB7\uD0A4\uAC00 \uCF54\uB4DC\uC5D0 \uBCF4\uC774\uC9C0 \uC54A\uC544\uC694!",
122
+ summary: "\u{1F4CA} \uAC80\uC0AC \uC694\uC57D:"
99
123
  },
100
124
  sync: {
101
- title: "\u{1F504} VHK SYNC \u2014 RULES.md \u2192 \uADDC\uCE59 \uD30C\uC77C \uB3D9\uAE30\uD654",
102
- noRules: "\u26A0\uFE0F RULES.md\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
103
- cursorrulesDone: "\u2705 .cursorrules \uB3D9\uAE30\uD654 \uC644\uB8CC",
104
- claudeDone: "\u2705 CLAUDE.md \uB3D9\uAE30\uD654 \uC644\uB8CC",
105
- done: "\u{1F504} \uB3D9\uAE30\uD654 \uC644\uB8CC!"
125
+ title: "\u{1F504} \uADDC\uCE59 \uD30C\uC77C \uB9DE\uCD94\uAE30",
126
+ noRules: "\u26A0\uFE0F RULES.md \uD30C\uC77C\uC774 \uC5C6\uC5B4\uC694.",
127
+ cursorrulesDone: "\u2705 .cursorrules \uB9DE\uCDA4 \uC644\uB8CC",
128
+ claudeDone: "\u2705 CLAUDE.md \uB9DE\uCDA4 \uC644\uB8CC",
129
+ done: "\u{1F504} \uB9DE\uCD94\uAE30 \uC644\uB8CC!"
106
130
  }
107
131
  };
108
132
 
133
+ // src/lib/next-step.ts
134
+ import chalk from "chalk";
135
+ function printNextStep(step) {
136
+ console.log("");
137
+ console.log(chalk.cyan.bold("\u2501\u2501\u2501 \uB2E4\uC74C\uC5D0 \uC774\uAC83\uB9CC \uD558\uC138\uC694 \u2501\u2501\u2501"));
138
+ console.log("");
139
+ console.log(` ${step.message}`);
140
+ if (step.command) {
141
+ console.log("");
142
+ console.log(chalk.white.bgGray(" \uD130\uBBF8\uB110\uC5D0 \uBCF5\uBD99 "));
143
+ console.log(chalk.green(` ${step.command}`));
144
+ }
145
+ if (step.cursorHint) {
146
+ console.log("");
147
+ console.log(chalk.white.bgBlue(" Cursor\uC5D0\uAC8C \uB9D0\uD558\uAE30 "));
148
+ console.log(chalk.blue(` "${step.cursorHint}"`));
149
+ }
150
+ if (step.alternative) {
151
+ console.log("");
152
+ console.log(chalk.dim(` \uB610\uB294: ${step.alternative}`));
153
+ }
154
+ console.log("");
155
+ console.log(chalk.cyan.bold("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
156
+ console.log("");
157
+ }
158
+
109
159
  // src/commands/gate.ts
110
160
  var GATE_QUESTIONS = [
111
161
  { id: 1, stage: "\uBB38\uC81C \uC815\uC758", question: "\uC774 \uC544\uC774\uB514\uC5B4\uAC00 \uD574\uACB0\uD558\uB294 \uBB38\uC81C\uB97C \uD55C \uBB38\uC7A5\uC73C\uB85C \uB9D0\uD574\uBCF4\uC138\uC694.", failIf: "\uD55C \uBB38\uC7A5 \uBD88\uAC00 \u2192 \uBBF8\uC131\uC219", quick: true },
112
162
  { id: 2, stage: "\uD575\uC2EC \uAE30\uB2A5", question: "\uB531 1\uAC1C \uAE30\uB2A5\uB9CC \uACE0\uB974\uBA74?", failIf: "2\uAC1C \uC774\uC0C1 \u2192 \uBC94\uC704 \uCD08\uACFC", quick: true },
113
163
  { id: 3, stage: "\uC218\uC694 \uAC80\uC99D", question: "\uD0C0\uAC9F\uC774 \uBAA8\uC774\uB294 \uCEE4\uBBA4\uB2C8\uD2F0 3\uACF3\uC740?", failIf: "0\uACF3 \u2192 \uC2DC\uC7A5 \uC811\uC810 \uBD80\uC7AC", quick: true, hint: "\uC608: \uC778\uB514\uD574\uCEE4\uC2A4, \uD2B8\uC704\uD130 #buildinpublic, \uB514\uC2A4\uCF54\uB4DC" },
114
164
  { id: 4, stage: "\uAE30\uD68D", question: "v1 \uAE30\uB2A5 \uBAA9\uB85D 5\uAC1C \uC774\uB0B4\uB85C \uC815\uB9AC\uD574\uBCF4\uC138\uC694.", failIf: "5\uAC1C \uCD08\uACFC \u2192 \uC624\uBC84\uC5D4\uC9C0\uB2C8\uC5B4\uB9C1" },
115
- { id: 5, stage: "\uC124\uACC4", question: "\uD575\uC2EC API/DB \uC2A4\uD0A4\uB9C8 3\uAC1C \uC774\uB0B4\uB85C?", failIf: "\uBCF5\uC7A1\uB3C4 \uCD08\uACFC" },
165
+ { id: 5, stage: "\uC124\uACC4", question: "\uB370\uC774\uD130\uB97C \uC800\uC7A5\uD560 \uACF3\uC774 \uB5A0\uC624\uB974\uC2DC\uB098\uC694?", failIf: "\uBAA8\uB974\uBA74 \uBCF4\uB958\uD574\uB3C4 \uB429\uB2C8\uB2E4", hint: '\uC608: \uC0AC\uC6A9\uC790 \uBAA9\uB85D\uC740 DB, \uB85C\uADF8\uC778\uC740 Supabase \u2014 \uBAA8\uB974\uBA74 "\uBCF4\uB958"' },
116
166
  { id: 6, stage: "\uAC1C\uBC1C", question: "3\uC77C \uC548\uC5D0 \uCF54\uC5B4 \uC644\uC131 \uAC00\uB2A5\uD55C\uAC00?", failIf: "\uBD88\uAC00\uB2A5 \u2192 \uBC94\uC704 \uCD95\uC18C \uD544\uC694", quick: true },
117
167
  { id: 7, stage: "\uB514\uC790\uC778", question: "\uB808\uD37C\uB7F0\uC2A4 UI 1\uAC1C \uC9C0\uC815\uD560 \uC218 \uC788\uB098?", failIf: "\uC5C6\uC73C\uBA74 \u2192 \uBC29\uD5A5 \uBBF8\uC815" },
118
168
  { id: 8, stage: "\uBC30\uD3EC", question: "\uBC30\uD3EC \uD50C\uB7AB\uD3FC + \uB3C4\uBA54\uC778 \uD655\uC815\uD588\uB098?", failIf: "\uBBF8\uC815 \u2192 \uBC30\uD3EC \uC9C0\uC5F0 \uC608\uACE0" },
@@ -128,7 +178,7 @@ function judgeGate(failCount, holdCount) {
128
178
  return "DROP";
129
179
  }
130
180
  async function gate() {
131
- console.log(chalk.bold(`
181
+ console.log(chalk2.bold(`
132
182
  ${ko.gate.title}
133
183
  `));
134
184
  const { mode } = await inquirer.prompt([{
@@ -147,23 +197,33 @@ ${ko.gate.title}
147
197
  name: "source",
148
198
  message: ko.gate.skipSourcePrompt
149
199
  }]);
150
- console.log(chalk.green.bold(`
200
+ console.log(chalk2.green.bold(`
151
201
  ${ko.gate.skipGo}`));
152
- console.log(chalk.dim(ko.gate.skipSourceLabel(source)));
202
+ console.log(chalk2.dim(ko.gate.skipSourceLabel(source)));
153
203
  return;
154
204
  }
155
205
  const questions = mode === "quick" ? GATE_QUESTIONS.filter((q) => q.quick) : GATE_QUESTIONS;
156
206
  const total = questions.length;
157
207
  const header = mode === "quick" ? ko.gate.quickHeader : ko.gate.fullHeader;
158
- console.log(chalk.dim(`
208
+ console.log(chalk2.dim(`
159
209
  ${header} ${ko.gate.modeCountSuffix(total)}
160
210
  `));
161
- const { idea, painPoint, edge } = await inquirer.prompt([
162
- { type: "input", name: "idea", message: ko.gate.idea },
163
- { type: "input", name: "painPoint", message: ko.gate.painPoint },
211
+ console.log(chalk2.dim(`
212
+ ${ko.gate.welcome}
213
+ `));
214
+ console.log(chalk2.dim(` ${ko.gate.ideaHint}`));
215
+ const { idea } = await inquirer.prompt([
216
+ { type: "input", name: "idea", message: ko.gate.idea }
217
+ ]);
218
+ console.log(chalk2.dim(` ${ko.gate.painPointHint}`));
219
+ const { painPoint } = await inquirer.prompt([
220
+ { type: "input", name: "painPoint", message: ko.gate.painPoint }
221
+ ]);
222
+ console.log(chalk2.dim(` ${ko.gate.edgeHint}`));
223
+ const { edge } = await inquirer.prompt([
164
224
  { type: "input", name: "edge", message: ko.gate.edge }
165
225
  ]);
166
- console.log(chalk.dim(`
226
+ console.log(chalk2.dim(`
167
227
  ${ko.gate.checklistStart}
168
228
  `));
169
229
  let failCount = 0;
@@ -171,7 +231,7 @@ ${ko.gate.checklistStart}
171
231
  const results = [];
172
232
  for (let i = 0; i < questions.length; i++) {
173
233
  const q = questions[i];
174
- if (q.hint) console.log(chalk.dim(`${ko.gate.hintPrefix} ${q.hint}`));
234
+ if (q.hint) console.log(chalk2.dim(`${ko.gate.hintPrefix} ${q.hint}`));
175
235
  const { answer } = await inquirer.prompt([{
176
236
  type: "input",
177
237
  name: "answer",
@@ -190,34 +250,43 @@ ${ko.gate.checklistStart}
190
250
  if (status === "fail") failCount++;
191
251
  if (status === "hold") holdCount++;
192
252
  results.push({ id: q.id, stage: q.stage, status, answer });
193
- const icon = status === "pass" ? chalk.green(ko.gate.statusPassLine) : status === "hold" ? chalk.yellow(ko.gate.statusHoldLine) : chalk.red(ko.gate.statusFailLine);
253
+ const icon = status === "pass" ? chalk2.green(ko.gate.statusPassLine) : status === "hold" ? chalk2.yellow(ko.gate.statusHoldLine) : chalk2.red(ko.gate.statusFailLine);
194
254
  console.log(icon);
195
255
  }
196
- console.log(chalk.bold(`
256
+ console.log(chalk2.bold(`
197
257
  ${ko.gate.verdictTitle}
198
258
  `));
199
- console.log(`${ko.gate.ideaLabel} ${chalk.cyan(idea)}`);
259
+ console.log(`${ko.gate.ideaLabel} ${chalk2.cyan(idea)}`);
200
260
  console.log(`${ko.gate.painPointLabel} ${painPoint}`);
201
261
  console.log(`${ko.gate.edgeLabel} ${edge}`);
202
262
  console.log(`${ko.gate.countLine(failCount, holdCount, total)}
203
263
  `);
204
264
  const verdict = judgeGate(failCount, holdCount);
205
265
  if (verdict === "GO") {
206
- console.log(chalk.green.bold(ko.gate.go));
207
- console.log(chalk.green(ko.gate.nextCommand));
266
+ console.log(chalk2.green.bold(ko.gate.go));
208
267
  if (holdCount > 0) {
209
- console.log(chalk.yellow(ko.gate.holdRemainHint));
268
+ console.log(chalk2.yellow(ko.gate.holdRemainHint));
210
269
  }
270
+ printNextStep({
271
+ message: "\uC544\uC774\uB514\uC5B4 \uD1B5\uACFC! \uC774\uC81C \uD504\uB85C\uC81D\uD2B8\uB97C \uB9CC\uB4E4\uC5B4\uBCF4\uC138\uC694.",
272
+ command: "vhk \uC2DC\uC791",
273
+ cursorHint: "\uD504\uB85C\uC81D\uD2B8 \uB9CC\uB4E4\uC5B4\uC918"
274
+ });
211
275
  } else if (verdict === "REFINE") {
212
- console.log(chalk.yellow.bold(ko.gate.refine));
276
+ console.log(chalk2.yellow.bold(ko.gate.refine));
277
+ printNextStep({
278
+ message: "\uC870\uAE08 \uB354 \uB2E4\uB4EC\uC740 \uD6C4 \uB2E4\uC2DC \uAC80\uC99D\uD574\uBCF4\uC138\uC694.",
279
+ command: "vhk \uAC80\uC99D",
280
+ cursorHint: "\uC544\uC774\uB514\uC5B4 \uB2E4\uC2DC \uAC80\uC99D\uD574\uC918"
281
+ });
213
282
  } else {
214
- console.log(chalk.red.bold(ko.gate.drop));
283
+ console.log(chalk2.red.bold(ko.gate.drop));
215
284
  }
216
285
  }
217
286
 
218
287
  // src/commands/init.ts
219
288
  import inquirer2 from "inquirer";
220
- import chalk3 from "chalk";
289
+ import chalk4 from "chalk";
221
290
  import path2 from "path";
222
291
 
223
292
  // src/templates/claude-md.ts
@@ -399,13 +468,13 @@ function ADR_TEMPLATE() {
399
468
  }
400
469
 
401
470
  // src/utils/logger.ts
402
- import chalk2 from "chalk";
471
+ import chalk3 from "chalk";
403
472
  var log = {
404
- success: (msg) => console.log(chalk2.green(`\u2705 ${msg}`)),
405
- error: (msg) => console.log(chalk2.red(`\u274C ${msg}`)),
406
- warn: (msg) => console.log(chalk2.yellow(`\u26A0\uFE0F ${msg}`)),
407
- info: (msg) => console.log(chalk2.blue(`\u2139\uFE0F ${msg}`)),
408
- step: (msg) => console.log(chalk2.bold(`
473
+ success: (msg) => console.log(chalk3.green(`\u2705 ${msg}`)),
474
+ error: (msg) => console.log(chalk3.red(`\u274C ${msg}`)),
475
+ warn: (msg) => console.log(chalk3.yellow(`\u26A0\uFE0F ${msg}`)),
476
+ info: (msg) => console.log(chalk3.blue(`\u2139\uFE0F ${msg}`)),
477
+ step: (msg) => console.log(chalk3.bold(`
409
478
  \u25B8 ${msg}`))
410
479
  };
411
480
 
@@ -610,11 +679,11 @@ async function collectAnswers(options, defaults = {}) {
610
679
  async function init(options = {}) {
611
680
  const skipGate = Boolean(options.skipGate || options.fromNotion);
612
681
  if (skipGate) {
613
- console.log(chalk3.dim(`
682
+ console.log(chalk4.dim(`
614
683
  ${ko.init.skipGate}
615
684
  `));
616
685
  }
617
- console.log(chalk3.bold(`
686
+ console.log(chalk4.bold(`
618
687
  ${ko.init.title}
619
688
  `));
620
689
  let prdContent = {};
@@ -638,7 +707,7 @@ ${ko.init.title}
638
707
  process.exit(1);
639
708
  }
640
709
  const stack = STACK_PRESETS[answers.type];
641
- console.log(chalk3.dim(`
710
+ console.log(chalk4.dim(`
642
711
  ${ko.init.recommendedStack} ${stack.join(" + ")}
643
712
  `));
644
713
  if (!options.yes) {
@@ -673,22 +742,30 @@ ${ko.init.recommendedStack} ${stack.join(" + ")}
673
742
  writeFile(fullPath, content);
674
743
  log.success(filePath);
675
744
  }
676
- console.log(chalk3.bold.green(`
745
+ console.log(chalk4.bold.green(`
677
746
  ${ko.init.done}`));
678
- console.log(chalk3.dim(`
747
+ console.log(chalk4.dim(`
679
748
  ${ko.init.nextSteps}`));
680
749
  if (options.fromNotion) {
681
750
  console.log(` 1. ${ko.init.notionReviewHint}`);
682
- console.log(` 2. ${chalk3.cyan(ko.init.gitHint)}`);
751
+ console.log(` 2. ${ko.init.gitHintLabel}`);
752
+ console.log(` ${chalk4.cyan(ko.init.gitHintCommand)}`);
683
753
  console.log(` 3. ${ko.init.startDev}
684
754
  `);
685
755
  } else {
686
756
  console.log(` 1. ${ko.init.fillHint}`);
687
757
  console.log(` 2. ${ko.init.prdHint}`);
688
- console.log(` 3. ${chalk3.cyan(ko.init.gitHint)}`);
758
+ console.log(` 3. ${ko.init.gitHintLabel}`);
759
+ console.log(` ${chalk4.cyan(ko.init.gitHintCommand)}`);
689
760
  console.log(` 4. ${ko.init.startDev}
690
761
  `);
691
762
  }
763
+ printNextStep({
764
+ message: "\uD504\uB85C\uC81D\uD2B8 \uBF08\uB300 \uC644\uC131! \uC774\uC81C \uAC1C\uBC1C\uC744 \uC2DC\uC791\uD558\uC138\uC694.",
765
+ command: `cd ${cwd}`,
766
+ cursorHint: "docs/PRD.md \uC5F4\uACE0 \uAC1C\uBC1C \uC2DC\uC791\uD574\uC918",
767
+ alternative: "VS Code/Cursor\uC5D0\uC11C \uD3F4\uB354\uB97C \uC5F4\uC5B4\uB3C4 \uB429\uB2C8\uB2E4"
768
+ });
692
769
  }
693
770
  function generateFiles(name, description, stack, prdContent = {}) {
694
771
  const stackStr = stack.join(" + ");
@@ -721,7 +798,7 @@ function generateFiles(name, description, stack, prdContent = {}) {
721
798
 
722
799
  // src/commands/recap.ts
723
800
  import inquirer3 from "inquirer";
724
- import chalk4 from "chalk";
801
+ import chalk5 from "chalk";
725
802
  import fs3 from "fs";
726
803
  import path4 from "path";
727
804
 
@@ -867,38 +944,38 @@ function createAdrFile(cwd, title, context, decision, consequences) {
867
944
 
868
945
  // src/commands/recap.ts
869
946
  async function recap(options = {}) {
870
- console.log(chalk4.bold(`
947
+ console.log(chalk5.bold(`
871
948
  ${ko.recap.title}
872
949
  `));
873
950
  if (!await isGitRepo()) {
874
- console.log(chalk4.red(ko.recap.noRepo));
951
+ console.log(chalk5.red(ko.recap.noRepo));
875
952
  return;
876
953
  }
877
- console.log(chalk4.dim(`${ko.recap.analyzing}
954
+ console.log(chalk5.dim(`${ko.recap.analyzing}
878
955
  `));
879
956
  const diff = await getSessionDiff(options.since);
880
957
  const commits = await getRecentCommits(10, options.since);
881
958
  if (diff.filesChanged === 0 && commits.length === 0) {
882
- console.log(chalk4.yellow(ko.recap.noChanges));
959
+ console.log(chalk5.yellow(ko.recap.noChanges));
883
960
  return;
884
961
  }
885
- console.log(chalk4.bold("\u{1F4CA} \uBCC0\uACBD \uC694\uC57D:"));
886
- console.log(` \uD30C\uC77C: ${chalk4.cyan(String(diff.filesChanged))}\uAC1C \uBCC0\uACBD`);
887
- console.log(` \uCD94\uAC00: ${chalk4.green("+" + diff.insertions)} / \uC0AD\uC81C: ${chalk4.red("-" + diff.deletions)}`);
962
+ console.log(chalk5.bold("\u{1F4CA} \uBCC0\uACBD \uC694\uC57D:"));
963
+ console.log(` \uD30C\uC77C: ${chalk5.cyan(String(diff.filesChanged))}\uAC1C \uBCC0\uACBD`);
964
+ console.log(` \uCD94\uAC00: ${chalk5.green("+" + diff.insertions)} / \uC0AD\uC81C: ${chalk5.red("-" + diff.deletions)}`);
888
965
  if (diff.files.length > 0) {
889
- console.log(chalk4.dim("\n \uBCC0\uACBD \uD30C\uC77C:"));
966
+ console.log(chalk5.dim("\n \uBCC0\uACBD \uD30C\uC77C:"));
890
967
  diff.files.slice(0, 15).forEach((f) => {
891
- const icon = f.status === "new" ? chalk4.green("\u{1F195}") : f.status === "deleted" ? chalk4.red("\u{1F5D1}\uFE0F") : chalk4.yellow("\u270F\uFE0F");
968
+ const icon = f.status === "new" ? chalk5.green("\u{1F195}") : f.status === "deleted" ? chalk5.red("\u{1F5D1}\uFE0F") : chalk5.yellow("\u270F\uFE0F");
892
969
  console.log(` ${icon} ${f.file}`);
893
970
  });
894
971
  if (diff.files.length > 15) {
895
- console.log(chalk4.dim(` ... \uC678 ${diff.files.length - 15}\uAC1C`));
972
+ console.log(chalk5.dim(` ... \uC678 ${diff.files.length - 15}\uAC1C`));
896
973
  }
897
974
  }
898
975
  if (commits.length > 0) {
899
- console.log(chalk4.dim("\n \uCD5C\uADFC \uCEE4\uBC0B:"));
976
+ console.log(chalk5.dim("\n \uCD5C\uADFC \uCEE4\uBC0B:"));
900
977
  commits.slice(0, 5).forEach((c) => {
901
- console.log(chalk4.dim(` \u2022 ${c.message}`));
978
+ console.log(chalk5.dim(` \u2022 ${c.message}`));
902
979
  });
903
980
  }
904
981
  console.log("");
@@ -966,11 +1043,11 @@ ${ko.recap.title}
966
1043
  fs3.writeFileSync(filePath, content, "utf-8");
967
1044
  const adrCandidates = detectAdrCandidates(diff);
968
1045
  if (adrCandidates.length > 0) {
969
- console.log(chalk4.cyan.bold(`
1046
+ console.log(chalk5.cyan.bold(`
970
1047
  ${ko.recap.adrDetected} (${adrCandidates.length}\uAC74)`));
971
1048
  for (const candidate of adrCandidates) {
972
- console.log(chalk4.cyan(` \u2022 ${candidate.title}: ${candidate.context}`));
973
- candidate.files.forEach((f) => console.log(chalk4.dim(` ${f}`)));
1049
+ console.log(chalk5.cyan(` \u2022 ${candidate.title}: ${candidate.context}`));
1050
+ candidate.files.forEach((f) => console.log(chalk5.dim(` ${f}`)));
974
1051
  }
975
1052
  const { createAdr } = await inquirer3.prompt([{
976
1053
  type: "confirm",
@@ -1000,17 +1077,17 @@ ${ko.recap.adrDetected} (${adrCandidates.length}\uAC74)`));
1000
1077
  adrAnswers.decision,
1001
1078
  adrAnswers.consequences
1002
1079
  );
1003
- console.log(chalk4.green(` \u2705 ADR \uC0DD\uC131: ${path4.relative(process.cwd(), adrPath)}`));
1080
+ console.log(chalk5.green(` \u2705 ADR \uC0DD\uC131: ${path4.relative(process.cwd(), adrPath)}`));
1004
1081
  }
1005
1082
  }
1006
1083
  }
1007
1084
  const troubleshootingKeywords = /fix|bug|error|crash|hotfix|patch|revert|트러블|에러|버그|수정|핫픽스/i;
1008
1085
  const troubleCommits = commits.filter((c) => troubleshootingKeywords.test(c.message));
1009
1086
  if (troubleCommits.length > 0) {
1010
- console.log(chalk4.yellow.bold(`
1087
+ console.log(chalk5.yellow.bold(`
1011
1088
  ${ko.recap.troubleDetected} (${troubleCommits.length}\uAC74)`));
1012
1089
  troubleCommits.forEach((c) => {
1013
- console.log(chalk4.dim(` \u2022 ${c.message}`));
1090
+ console.log(chalk5.dim(` \u2022 ${c.message}`));
1014
1091
  });
1015
1092
  const { createTroubleshoot } = await inquirer3.prompt([{
1016
1093
  type: "confirm",
@@ -1061,12 +1138,12 @@ ${ko.recap.troubleDetected} (${troubleCommits.length}\uAC74)`));
1061
1138
  `*Generated by \`vhk recap\` at ${(/* @__PURE__ */ new Date()).toISOString()}*`
1062
1139
  ].join("\n");
1063
1140
  fs3.writeFileSync(tsFilePath, tsContent, "utf-8");
1064
- console.log(chalk4.green(` \u2705 \uD2B8\uB7EC\uBE14\uC288\uD305 \uBB38\uC11C \uC0DD\uC131: ${path4.relative(process.cwd(), tsFilePath)}`));
1141
+ console.log(chalk5.green(` \u2705 \uD2B8\uB7EC\uBE14\uC288\uD305 \uBB38\uC11C \uC0DD\uC131: ${path4.relative(process.cwd(), tsFilePath)}`));
1065
1142
  }
1066
1143
  }
1067
- console.log(chalk4.green.bold(`
1144
+ console.log(chalk5.green.bold(`
1068
1145
  ${ko.recap.done}`));
1069
- console.log(chalk4.dim(` \u{1F4C4} ${path4.relative(process.cwd(), filePath)}`));
1146
+ console.log(chalk5.dim(` \u{1F4C4} ${path4.relative(process.cwd(), filePath)}`));
1070
1147
  const claudeMdPath = path4.join(process.cwd(), "CLAUDE.md");
1071
1148
  if (fs3.existsSync(claudeMdPath)) {
1072
1149
  const { updateClaude } = await inquirer3.prompt([{
@@ -1086,16 +1163,18 @@ ${ko.recap.done}`));
1086
1163
  `- **\uB2E4\uC74C \uC561\uC158:** ${answers.nextTodo}`
1087
1164
  );
1088
1165
  fs3.writeFileSync(claudeMdPath, claudeContent, "utf-8");
1089
- console.log(chalk4.green(" \u2705 CLAUDE.md \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC"));
1166
+ console.log(chalk5.green(" \u2705 CLAUDE.md \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC"));
1090
1167
  }
1091
1168
  }
1092
- console.log(chalk4.dim(`
1093
- \u{1F4A1} \uD301: git add docs/log/ && git commit -m "docs: session recap ${today}"
1094
- `));
1169
+ printNextStep({
1170
+ message: "\uC624\uB298 \uAE30\uB85D \uC644\uB8CC! \uC800\uC7A5\uD558\uACE0 \uC2F6\uC73C\uBA74:",
1171
+ command: 'git add . && git commit -m "recap: \uC138\uC158 \uAE30\uB85D"',
1172
+ cursorHint: "\uC800\uC7A5\uD574\uC918"
1173
+ });
1095
1174
  }
1096
1175
 
1097
1176
  // src/commands/sync.ts
1098
- import chalk5 from "chalk";
1177
+ import chalk6 from "chalk";
1099
1178
  import fs4 from "fs";
1100
1179
  import path5 from "path";
1101
1180
  var CURSORRULES_KEYS = ["\uCF54\uB529 \uADDC\uCE59", "\uAE30\uC220 \uC2A4\uD0DD", "\uC544\uD0A4\uD14D\uCC98", "\uB514\uC790\uC778", "Anti-patterns", "\uCEE4\uBC0B"];
@@ -1165,32 +1244,32 @@ function toClaudeMd(sections, existing) {
1165
1244
  return lines.join("\n");
1166
1245
  }
1167
1246
  async function sync() {
1168
- console.log(chalk5.bold(`
1247
+ console.log(chalk6.bold(`
1169
1248
  ${ko.sync.title}
1170
1249
  `));
1171
1250
  const cwd = process.cwd();
1172
1251
  const rulesPath = path5.join(cwd, "RULES.md");
1173
1252
  if (!fs4.existsSync(rulesPath)) {
1174
- console.log(chalk5.yellow(ko.sync.noRules));
1175
- console.log(chalk5.dim(" RULES.md\uB294 \uD504\uB85C\uC81D\uD2B8 \uADDC\uCE59\uC758 Single Source of Truth\uC785\uB2C8\uB2E4."));
1176
- console.log(chalk5.dim(" \uC0DD\uC131\uD558\uB824\uBA74: vhk init \uC2E4\uD589 \uD6C4 RULES.md\uB97C \uC791\uC131\uD558\uC138\uC694."));
1253
+ console.log(chalk6.yellow(ko.sync.noRules));
1254
+ console.log(chalk6.dim(" RULES.md\uB294 \uD504\uB85C\uC81D\uD2B8 \uADDC\uCE59\uC758 Single Source of Truth\uC785\uB2C8\uB2E4."));
1255
+ console.log(chalk6.dim(" \uC0DD\uC131\uD558\uB824\uBA74: vhk init \uC2E4\uD589 \uD6C4 RULES.md\uB97C \uC791\uC131\uD558\uC138\uC694."));
1177
1256
  console.log("");
1178
- console.log(chalk5.dim(" RULES.md \uAE30\uBCF8 \uAD6C\uC870:"));
1179
- console.log(chalk5.dim(" ## \uD504\uB85C\uC81D\uD2B8 \uC815\uCCB4\uC131"));
1180
- console.log(chalk5.dim(" ## \uAE30\uC220 \uC2A4\uD0DD"));
1181
- console.log(chalk5.dim(" ## \uCF54\uB529 \uADDC\uCE59"));
1182
- console.log(chalk5.dim(" ## \uAE30\uB85D \uADDC\uCE59"));
1183
- console.log(chalk5.dim(" ## \uCEE4\uBC0B \uCEE8\uBCA4\uC158"));
1257
+ console.log(chalk6.dim(" RULES.md \uAE30\uBCF8 \uAD6C\uC870:"));
1258
+ console.log(chalk6.dim(" ## \uD504\uB85C\uC81D\uD2B8 \uC815\uCCB4\uC131"));
1259
+ console.log(chalk6.dim(" ## \uAE30\uC220 \uC2A4\uD0DD"));
1260
+ console.log(chalk6.dim(" ## \uCF54\uB529 \uADDC\uCE59"));
1261
+ console.log(chalk6.dim(" ## \uAE30\uB85D \uADDC\uCE59"));
1262
+ console.log(chalk6.dim(" ## \uCEE4\uBC0B \uCEE8\uBCA4\uC158"));
1184
1263
  return;
1185
1264
  }
1186
1265
  const rulesContent = fs4.readFileSync(rulesPath, "utf-8");
1187
1266
  const sections = parseRulesMd(rulesContent);
1188
- console.log(chalk5.dim(` \u{1F4C4} RULES.md \uD30C\uC2F1 \uC644\uB8CC \u2014 ${sections.length}\uAC1C \uC139\uC158`));
1267
+ console.log(chalk6.dim(` \u{1F4C4} RULES.md \uD30C\uC2F1 \uC644\uB8CC \u2014 ${sections.length}\uAC1C \uC139\uC158`));
1189
1268
  const firstLine = rulesContent.split("\n")[0];
1190
1269
  const projectName = firstLine.replace(/^#\s*/, "").replace(/\s*—.*/, "").trim() || "Project";
1191
1270
  const cursorrulesPath = path5.join(cwd, ".cursorrules");
1192
1271
  fs4.writeFileSync(cursorrulesPath, toCursorrules(sections, projectName), "utf-8");
1193
- console.log(chalk5.green(` ${ko.sync.cursorrulesDone}`));
1272
+ console.log(chalk6.green(` ${ko.sync.cursorrulesDone}`));
1194
1273
  const claudePath = path5.join(cwd, "CLAUDE.md");
1195
1274
  const existingClaude = fs4.existsSync(claudePath) ? fs4.readFileSync(claudePath, "utf-8") : `# \uAE30\uB85D \uADDC\uCE59 (${projectName})
1196
1275
 
@@ -1200,15 +1279,20 @@ ${ko.sync.title}
1200
1279
  - **\uB2E4\uC74C \uC561\uC158:** __FILL__
1201
1280
  - **\uB9C8\uC9C0\uB9C9 \uC5C5\uB370\uC774\uD2B8:** ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}`;
1202
1281
  fs4.writeFileSync(claudePath, toClaudeMd(sections, existingClaude), "utf-8");
1203
- console.log(chalk5.green(` ${ko.sync.claudeDone}`));
1204
- console.log(chalk5.bold.green(`
1282
+ console.log(chalk6.green(` ${ko.sync.claudeDone}`));
1283
+ console.log(chalk6.bold.green(`
1205
1284
  ${ko.sync.done}`));
1206
- console.log(chalk5.dim(" RULES.md (\uC6D0\uBCF8) \u2192 .cursorrules + CLAUDE.md (\uC790\uB3D9 \uC0DD\uC131)"));
1207
- console.log(chalk5.dim(" \uADDC\uCE59 \uBCC0\uACBD\uC740 \uD56D\uC0C1 RULES.md\uC5D0\uC11C\uB9CC \uD558\uC138\uC694.\n"));
1285
+ console.log(chalk6.dim(" RULES.md (\uC6D0\uBCF8) \u2192 .cursorrules + CLAUDE.md (\uC790\uB3D9 \uC0DD\uC131)"));
1286
+ console.log(chalk6.dim(" \uADDC\uCE59 \uBCC0\uACBD\uC740 \uD56D\uC0C1 RULES.md\uC5D0\uC11C\uB9CC \uD558\uC138\uC694."));
1287
+ printNextStep({
1288
+ message: "\uADDC\uCE59 \uB3D9\uAE30\uD654 \uC644\uB8CC! \uC774\uC81C Cursor\uAC00 \uC0C8 \uADDC\uCE59\uC744 \uB530\uB985\uB2C8\uB2E4.",
1289
+ command: "vhk \uC810\uAC80",
1290
+ cursorHint: "\uADDC\uCE59 \uC810\uAC80\uD574\uC918"
1291
+ });
1208
1292
  }
1209
1293
 
1210
1294
  // src/commands/check.ts
1211
- import chalk6 from "chalk";
1295
+ import chalk7 from "chalk";
1212
1296
  import path7 from "path";
1213
1297
  import fs6 from "fs";
1214
1298
 
@@ -1376,22 +1460,22 @@ function escapeRegex(str) {
1376
1460
 
1377
1461
  // src/commands/check.ts
1378
1462
  async function check() {
1379
- console.log(chalk6.bold(`
1463
+ console.log(chalk7.bold(`
1380
1464
  ${ko.check.title}
1381
1465
  `));
1382
1466
  const cwd = process.cwd();
1383
1467
  const rulesPath = path7.join(cwd, "RULES.md");
1384
1468
  if (!fs6.existsSync(rulesPath)) {
1385
- console.log(chalk6.yellow(ko.check.noRules));
1386
- console.log(chalk6.dim(" vhk init\uC73C\uB85C \uD504\uB85C\uC81D\uD2B8\uB97C \uCD08\uAE30\uD654\uD558\uAC70\uB098 RULES.md\uB97C \uC791\uC131\uD558\uC138\uC694."));
1469
+ console.log(chalk7.yellow(ko.check.noRules));
1470
+ console.log(chalk7.dim(" vhk init\uC73C\uB85C \uC2DC\uC791\uD558\uAC70\uB098 RULES.md\uB97C \uB9CC\uB4E4\uC5B4 \uBCF4\uC138\uC694."));
1387
1471
  return;
1388
1472
  }
1389
1473
  const rules = parseRules(rulesPath);
1390
- console.log(chalk6.dim(` \u{1F4CF} ${rules.length}\uAC1C \uAC80\uC99D \uAC00\uB2A5\uD55C \uADDC\uCE59 \uAC10\uC9C0
1474
+ console.log(chalk7.dim(` \u{1F4CF} ${rules.length}\uAC1C \uAC80\uC99D \uAC00\uB2A5\uD55C \uADDC\uCE59 \uAC10\uC9C0
1391
1475
  `));
1392
1476
  if (rules.length === 0) {
1393
- console.log(chalk6.yellow(ko.check.noAutoRules));
1394
- console.log(chalk6.dim(" RULES.md\uC5D0 \uCF54\uB529 \uADDC\uCE59/\uB124\uC774\uBC0D/\uAD6C\uC870 \uADDC\uCE59\uC744 \uCD94\uAC00\uD558\uBA74 \uC790\uB3D9 \uB9B0\uD2B8\uB429\uB2C8\uB2E4."));
1477
+ console.log(chalk7.yellow(ko.check.noAutoRules));
1478
+ console.log(chalk7.dim(" RULES.md\uC5D0 \uD30C\uC77C \uC774\uB984\xB7\uD3F4\uB354 \uADDC\uCE59\uC744 \uC801\uC73C\uBA74 \uC790\uB3D9\uC73C\uB85C \uC810\uAC80\uD574\uC694."));
1395
1479
  return;
1396
1480
  }
1397
1481
  const allViolations = [];
@@ -1399,13 +1483,13 @@ ${ko.check.title}
1399
1483
  for (const rule of rules) {
1400
1484
  const violations = rule.check(cwd);
1401
1485
  if (violations.length === 0) {
1402
- console.log(chalk6.green(` \u2705 ${rule.id}`) + chalk6.dim(` \u2014 ${rule.description.slice(0, 60)}`));
1486
+ console.log(chalk7.green(` \u2705 ${rule.id}`) + chalk7.dim(` \u2014 ${rule.description.slice(0, 60)}`));
1403
1487
  passCount++;
1404
1488
  } else {
1405
- console.log(chalk6.red(` \u274C ${rule.id}`) + chalk6.dim(` \u2014 ${violations.length}\uAC74 \uC704\uBC18`));
1489
+ console.log(chalk7.red(` \u274C ${rule.id}`) + chalk7.dim(` \u2014 ${violations.length}\uAC74 \uC704\uBC18`));
1406
1490
  violations.forEach((v) => {
1407
- const loc = v.file ? chalk6.dim(` (${v.file}${v.line ? ":" + v.line : ""})`) : "";
1408
- const icon = v.severity === "error" ? chalk6.red("\u2716") : v.severity === "warning" ? chalk6.yellow("\u26A0") : chalk6.blue("\u2139");
1491
+ const loc = v.file ? chalk7.dim(` (${v.file}${v.line ? ":" + v.line : ""})`) : "";
1492
+ const icon = v.severity === "error" ? chalk7.red("\u2716") : v.severity === "warning" ? chalk7.yellow("\u26A0") : chalk7.blue("\u2139");
1409
1493
  console.log(` ${icon} ${v.message}${loc}`);
1410
1494
  });
1411
1495
  allViolations.push(...violations);
@@ -1415,14 +1499,22 @@ ${ko.check.title}
1415
1499
  const errors = allViolations.filter((v) => v.severity === "error").length;
1416
1500
  const warnings = allViolations.filter((v) => v.severity === "warning").length;
1417
1501
  if (allViolations.length === 0) {
1418
- console.log(chalk6.green.bold(`${ko.check.allPassed} (${passCount}/${rules.length})`));
1502
+ console.log(chalk7.green.bold(`${ko.check.allPassed} (${passCount}/${rules.length})`));
1503
+ printNextStep({
1504
+ message: "\uBAA8\uB4E0 \uADDC\uCE59 \uD1B5\uACFC! \uBCF4\uC548 \uC2A4\uCE94\uB3C4 \uD574\uBCFC\uAE4C\uC694?",
1505
+ command: "vhk \uBCF4\uC548 scan",
1506
+ cursorHint: "\uBCF4\uC548 \uC2A4\uCE94 \uB3CC\uB824\uC918"
1507
+ });
1419
1508
  } else {
1420
- console.log(chalk6.bold(ko.check.summary));
1421
- console.log(` \uADDC\uCE59: ${chalk6.cyan(String(rules.length))}\uAC1C | \uD1B5\uACFC: ${chalk6.green(String(passCount))}\uAC1C | \uC704\uBC18: ${chalk6.red(String(allViolations.length))}\uAC74`);
1422
- if (errors > 0) console.log(` ${chalk6.red(`\u2716 ${errors}\uAC1C \uC5D0\uB7EC`)}`);
1423
- if (warnings > 0) console.log(` ${chalk6.yellow(`\u26A0 ${warnings}\uAC1C \uACBD\uACE0`)}`);
1424
- console.log("");
1425
- console.log(chalk6.dim(" \u{1F4A1} \uC704\uBC18 \uD56D\uBAA9\uC744 \uC218\uC815\uD55C \uD6C4 vhk check\uB97C \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694."));
1509
+ console.log(chalk7.bold(ko.check.summary));
1510
+ console.log(` \uADDC\uCE59: ${chalk7.cyan(String(rules.length))}\uAC1C | \uD1B5\uACFC: ${chalk7.green(String(passCount))}\uAC1C | \uC704\uBC18: ${chalk7.red(String(allViolations.length))}\uAC74`);
1511
+ if (errors > 0) console.log(` ${chalk7.red(`\u2716 ${errors}\uAC1C \uC5D0\uB7EC`)}`);
1512
+ if (warnings > 0) console.log(` ${chalk7.yellow(`\u26A0 ${warnings}\uAC1C \uACBD\uACE0`)}`);
1513
+ printNextStep({
1514
+ message: "\uC704\uBC18 \uD56D\uBAA9\uC744 \uC218\uC815\uD55C \uD6C4 \uB2E4\uC2DC \uC810\uAC80\uD558\uC138\uC694.",
1515
+ command: "vhk \uC810\uAC80",
1516
+ cursorHint: "\uC704\uBC18 \uD56D\uBAA9 \uC218\uC815\uD574\uC918"
1517
+ });
1426
1518
  }
1427
1519
  if (errors > 0) {
1428
1520
  process.exitCode = 1;
@@ -1430,7 +1522,7 @@ ${ko.check.title}
1430
1522
  }
1431
1523
 
1432
1524
  // src/commands/secure.ts
1433
- import chalk7 from "chalk";
1525
+ import chalk8 from "chalk";
1434
1526
  import fs7 from "fs";
1435
1527
  import path8 from "path";
1436
1528
 
@@ -1516,7 +1608,7 @@ var SCAN_EXTENSIONS = [
1516
1608
  ".development"
1517
1609
  ];
1518
1610
  async function secure() {
1519
- console.log(chalk7.bold(`
1611
+ console.log(chalk8.bold(`
1520
1612
  ${ko.secure.title}
1521
1613
  `));
1522
1614
  const cwd = process.cwd();
@@ -1525,16 +1617,16 @@ ${ko.secure.title}
1525
1617
  const gitignorePath = path8.join(cwd, ".gitignore");
1526
1618
  const hasGitignore = fs7.existsSync(gitignorePath);
1527
1619
  if (!hasGitignore) {
1528
- console.log(chalk7.yellow(` ${ko.secure.noGitignore}`));
1529
- console.log(chalk7.dim(" .env \uD30C\uC77C\uC774 \uCEE4\uBC0B\uB420 \uC218 \uC788\uC2B5\uB2C8\uB2E4.\n"));
1620
+ console.log(chalk8.yellow(` ${ko.secure.noGitignore}`));
1621
+ console.log(chalk8.dim(" .env \uD30C\uC77C\uC774 \uCEE4\uBC0B\uB420 \uC218 \uC788\uC2B5\uB2C8\uB2E4.\n"));
1530
1622
  } else {
1531
1623
  const gitignoreContent = fs7.readFileSync(gitignorePath, "utf-8");
1532
1624
  if (!gitignoreContent.includes(".env")) {
1533
- console.log(chalk7.yellow(` ${ko.secure.noEnvInGitignore}`));
1534
- console.log(chalk7.dim(" \uCD94\uAC00\uB97C \uAD8C\uC7A5\uD569\uB2C8\uB2E4.\n"));
1625
+ console.log(chalk8.yellow(` ${ko.secure.noEnvInGitignore}`));
1626
+ console.log(chalk8.dim(" \uCD94\uAC00\uB97C \uAD8C\uC7A5\uD569\uB2C8\uB2E4.\n"));
1535
1627
  }
1536
1628
  }
1537
- console.log(chalk7.dim(` ${ko.secure.scanning}
1629
+ console.log(chalk8.dim(` ${ko.secure.scanning}
1538
1630
  `));
1539
1631
  walkFiles2(cwd, (filePath) => {
1540
1632
  scannedFiles++;
@@ -1561,47 +1653,47 @@ ${ko.secure.title}
1561
1653
  });
1562
1654
  }
1563
1655
  });
1564
- console.log(chalk7.dim(` \u{1F4C2} ${scannedFiles}\uAC1C \uD30C\uC77C \uC2A4\uCE94 \uC644\uB8CC
1656
+ console.log(chalk8.dim(` \u{1F4C2} ${scannedFiles}\uAC1C \uD30C\uC77C \uC2A4\uCE94 \uC644\uB8CC
1565
1657
  `));
1566
1658
  if (findings.length === 0) {
1567
- console.log(chalk7.green.bold(` ${ko.secure.clean}`));
1568
- console.log(chalk7.dim(" \uC815\uAE30\uC801\uC73C\uB85C vhk secure scan\uC744 \uC2E4\uD589\uD558\uC138\uC694.\n"));
1659
+ console.log(chalk8.green.bold(` ${ko.secure.clean}`));
1660
+ console.log(chalk8.dim(" \uAC00\uB054 vhk secure scan\uC73C\uB85C \uB2E4\uC2DC \uD655\uC778\uD574 \uBCF4\uC138\uC694.\n"));
1569
1661
  return;
1570
1662
  }
1571
1663
  const critical = findings.filter((f) => f.severity === "critical");
1572
1664
  const high = findings.filter((f) => f.severity === "high");
1573
1665
  const medium = findings.filter((f) => f.severity === "medium");
1574
1666
  if (critical.length > 0) {
1575
- console.log(chalk7.red.bold(` \u{1F6A8} CRITICAL \u2014 ${critical.length}\uAC74`));
1667
+ console.log(chalk8.red.bold(` \u{1F6A8} CRITICAL \u2014 ${critical.length}\uAC74`));
1576
1668
  critical.forEach((f) => {
1577
- console.log(chalk7.red(` \u2716 ${f.patternName}`));
1578
- console.log(chalk7.dim(` ${f.file}:${f.line} \u2192 ${f.match}`));
1669
+ console.log(chalk8.red(` \u2716 ${f.patternName}`));
1670
+ console.log(chalk8.dim(` ${f.file}:${f.line} \u2192 ${f.match}`));
1579
1671
  });
1580
1672
  console.log("");
1581
1673
  }
1582
1674
  if (high.length > 0) {
1583
- console.log(chalk7.yellow.bold(` \u26A0\uFE0F HIGH \u2014 ${high.length}\uAC74`));
1675
+ console.log(chalk8.yellow.bold(` \u26A0\uFE0F HIGH \u2014 ${high.length}\uAC74`));
1584
1676
  high.forEach((f) => {
1585
- console.log(chalk7.yellow(` \u26A0 ${f.patternName}`));
1586
- console.log(chalk7.dim(` ${f.file}:${f.line} \u2192 ${f.match}`));
1677
+ console.log(chalk8.yellow(` \u26A0 ${f.patternName}`));
1678
+ console.log(chalk8.dim(` ${f.file}:${f.line} \u2192 ${f.match}`));
1587
1679
  });
1588
1680
  console.log("");
1589
1681
  }
1590
1682
  if (medium.length > 0) {
1591
- console.log(chalk7.blue.bold(` \u2139 MEDIUM \u2014 ${medium.length}\uAC74`));
1683
+ console.log(chalk8.blue.bold(` \u2139 MEDIUM \u2014 ${medium.length}\uAC74`));
1592
1684
  medium.forEach((f) => {
1593
- console.log(chalk7.blue(` \u2139 ${f.patternName}`));
1594
- console.log(chalk7.dim(` ${f.file}:${f.line} \u2192 ${f.match}`));
1685
+ console.log(chalk8.blue(` \u2139 ${f.patternName}`));
1686
+ console.log(chalk8.dim(` ${f.file}:${f.line} \u2192 ${f.match}`));
1595
1687
  });
1596
1688
  console.log("");
1597
1689
  }
1598
- console.log(chalk7.bold(` ${ko.secure.summary}`));
1599
- console.log(` \uCD1D ${chalk7.red(String(findings.length))}\uAC74 \uAC10\uC9C0 | CRITICAL: ${critical.length} | HIGH: ${high.length} | MEDIUM: ${medium.length}`);
1690
+ console.log(chalk8.bold(` ${ko.secure.summary}`));
1691
+ console.log(` \uCD1D ${chalk8.red(String(findings.length))}\uAC74 \uAC10\uC9C0 | CRITICAL: ${critical.length} | HIGH: ${high.length} | MEDIUM: ${medium.length}`);
1600
1692
  console.log("");
1601
- console.log(chalk7.dim(" \u{1F4A1} \uC870\uCE58 \uBC29\uBC95:"));
1602
- console.log(chalk7.dim(" 1. \uD574\uB2F9 \uD30C\uC77C\uC5D0\uC11C \uC2DC\uD06C\uB9BF\uC744 \uC81C\uAC70\uD558\uACE0 \uD658\uACBD\uBCC0\uC218\uB85C \uC774\uB3D9"));
1603
- console.log(chalk7.dim(" 2. git history\uC5D0\uC11C\uB3C4 \uC81C\uAC70: git filter-branch \uB610\uB294 BFG Repo-Cleaner"));
1604
- console.log(chalk7.dim(" 3. \uC720\uCD9C\uB41C \uD0A4\uB294 \uC989\uC2DC \uD3D0\uAE30\uD558\uACE0 \uC7AC\uBC1C\uAE09\n"));
1693
+ console.log(chalk8.dim(" \u{1F4A1} \uC870\uCE58 \uBC29\uBC95:"));
1694
+ console.log(chalk8.dim(" 1. \uD574\uB2F9 \uD30C\uC77C\uC5D0\uC11C \uC2DC\uD06C\uB9BF\uC744 \uC81C\uAC70\uD558\uACE0 \uD658\uACBD\uBCC0\uC218\uB85C \uC774\uB3D9"));
1695
+ console.log(chalk8.dim(" 2. git history\uC5D0\uC11C\uB3C4 \uC81C\uAC70: git filter-branch \uB610\uB294 BFG Repo-Cleaner"));
1696
+ console.log(chalk8.dim(" 3. \uC720\uCD9C\uB41C \uD0A4\uB294 \uC989\uC2DC \uD3D0\uAE30\uD558\uACE0 \uC7AC\uBC1C\uAE09\n"));
1605
1697
  if (critical.length > 0) {
1606
1698
  process.exitCode = 1;
1607
1699
  }
@@ -1625,12 +1717,71 @@ function walkFiles2(dir, callback) {
1625
1717
 
1626
1718
  // src/index.ts
1627
1719
  var program = new Command();
1628
- program.name("vhk").description("Vibe Harness Kit \u2014 \uBC14\uC774\uBE0C\uCF54\uB529 \uD480\uC0AC\uC774\uD074 CLI").version("0.3.0");
1629
- program.command("gate").description("Phase 0: \uC544\uC774\uB514\uC5B4 \uAC80\uC99D \u2192 GO/REFINE/DROP").action(gate);
1630
- program.command("init").description("Phase 1-2: \uD504\uB85C\uC81D\uD2B8 \uCD08\uAE30\uD654 + \uD558\uB124\uC2A4 \uD30C\uC77C \uC790\uB3D9 \uC0DD\uC131").option("--skip-gate", "gate \uAC80\uC99D \uC2A4\uD0B5").option("--from-notion <url>", "Notion PRD \uD398\uC774\uC9C0\uC5D0\uC11C import").option("--name <name>", "\uD504\uB85C\uC81D\uD2B8 \uC774\uB984").option("--description <desc>", "\uD55C \uC904 \uC124\uBA85").option("--type <type>", "\uD504\uB85C\uC81D\uD2B8 \uC720\uD615 (webapp|extension|cli|notion|mobile)").option("-y, --yes", "\uC2A4\uD0DD \uD655\uC778 \uC2A4\uD0B5").action(init);
1631
- program.command("recap").description("Phase 3: \uC138\uC158 \uAE30\uB85D + ADR/\uD2B8\uB7EC\uBE14\uC288\uD305 \uC790\uB3D9 \uBD84\uB9AC").option("--since <date>", "\uBD84\uC11D \uC2DC\uC791\uC77C (YYYY-MM-DD)").action(recap);
1632
- program.command("sync").description("RULES.md \u2192 .cursorrules + CLAUDE.md \uB3D9\uAE30\uD654").action(sync);
1633
- program.command("check").description("RULES.md \uADDC\uCE59 \uB9B0\uD2B8 \u2014 \uCF54\uB4DC\uBCA0\uC774\uC2A4 \uC704\uBC18 \uAC80\uC0AC").action(check);
1634
- var secureCmd = program.command("secure").description("\uBCF4\uC548 \uB3C4\uAD6C \uBAA8\uC74C");
1635
- secureCmd.command("scan").description("\uC2DC\uD06C\uB9BF/\uD0A4 \uC720\uCD9C \uC2A4\uCE94").action(secure);
1720
+ var defaultHelp = new Help();
1721
+ var KO_ALIASES = {
1722
+ gate: "\uAC80\uC99D",
1723
+ init: "\uC2DC\uC791",
1724
+ recap: "\uC815\uB9AC",
1725
+ sync: "\uADDC\uCE59",
1726
+ check: "\uC810\uAC80",
1727
+ secure: "\uBCF4\uC548"
1728
+ };
1729
+ 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.3.1");
1730
+ program.configureHelp({
1731
+ formatHelp(cmd, helper) {
1732
+ if (cmd.parent) {
1733
+ return defaultHelp.formatHelp(cmd, helper);
1734
+ }
1735
+ const subs = helper.visibleCommands(cmd).filter((c) => c.name() !== "help");
1736
+ const terms = subs.map((c) => `${c.name()} (${KO_ALIASES[c.name()]})`);
1737
+ const termWidth = Math.max(...terms.map((t) => t.length), 0);
1738
+ const lines = [
1739
+ helper.commandDescription(cmd),
1740
+ "",
1741
+ "\uBA85\uB839\uC5B4:",
1742
+ ...subs.map((sub, i) => {
1743
+ const term = terms[i].padEnd(termWidth + 2);
1744
+ return ` ${term}${sub.description()}`;
1745
+ })
1746
+ ];
1747
+ return lines.join("\n") + "\n";
1748
+ }
1749
+ });
1750
+ program.command("gate").alias("\uAC80\uC99D").alias("\uC544\uC774\uB514\uC5B4").description("\uC544\uC774\uB514\uC5B4 \uAC80\uC99D \u2192 \uC2DC\uC791\uD574\uB3C4 \uB3FC\uC694 / \uB2E4\uB4EC\uAE30 / \uB2E4\uB978 \uC544\uC774\uB514\uC5B4").action(gate);
1751
+ program.command("init").alias("\uC2DC\uC791").alias("\uB9CC\uB4E4\uAE30").description("\uD504\uB85C\uC81D\uD2B8 \uC2DC\uC791\uD558\uAE30 \u2014 \uD3F4\uB354 + \uD558\uB124\uC2A4 \uD30C\uC77C \uC790\uB3D9 \uC0DD\uC131").option("--skip-gate", "gate \uAC80\uC99D \uC2A4\uD0B5").option("--from-notion <url>", "Notion PRD \uD398\uC774\uC9C0\uC5D0\uC11C import").option("--name <name>", "\uD504\uB85C\uC81D\uD2B8 \uC774\uB984").option("--description <desc>", "\uD55C \uC904 \uC124\uBA85").option("--type <type>", "\uD504\uB85C\uC81D\uD2B8 \uC720\uD615 (webapp|extension|cli|notion|mobile)").option("-y, --yes", "\uC2A4\uD0DD \uD655\uC778 \uC2A4\uD0B5").action(init);
1752
+ program.command("recap").alias("\uC815\uB9AC").alias("\uC624\uB298").description("\uC624\uB298 \uD55C \uC77C \uC815\uB9AC + ADR/\uD2B8\uB7EC\uBE14\uC288\uD305 \uC790\uB3D9 \uBD84\uB9AC").option("--since <date>", "\uBD84\uC11D \uC2DC\uC791\uC77C (YYYY-MM-DD)").action(recap);
1753
+ program.command("sync").alias("\uB9DE\uCD94\uAE30").alias("\uADDC\uCE59").description("RULES.md \u2192 .cursorrules + CLAUDE.md \uB3D9\uAE30\uD654").action(sync);
1754
+ program.command("check").alias("\uC810\uAC80").alias("\uB9B0\uD2B8").description("RULES.md \uADDC\uCE59 \uC810\uAC80 \u2014 \uCF54\uB4DC \uC704\uBC18 \uAC80\uC0AC").action(check);
1755
+ var secureCmd = program.command("secure").alias("\uBCF4\uC548").description("\uBCF4\uC548 \uB3C4\uAD6C \uBAA8\uC74C");
1756
+ secureCmd.command("scan").alias("\uC2A4\uCE94").description("\uC2DC\uD06C\uB9BF/\uD0A4 \uC720\uCD9C \uC2A4\uCE94").action(secure);
1757
+ program.action(async () => {
1758
+ console.log("\n\u{1F3AF} VHK \u2014 \uBC14\uC774\uBE0C\uCF54\uB529 \uD504\uB85C\uC81D\uD2B8 \uCF54\uCE58\n");
1759
+ const { choice } = await inquirer4.prompt([{
1760
+ type: "list",
1761
+ name: "choice",
1762
+ message: "\uBB58 \uB3C4\uC640\uB4DC\uB9B4\uAE4C\uC694?",
1763
+ choices: [
1764
+ { name: "\u{1F4A1} \uC0C8 \uC544\uC774\uB514\uC5B4 \uAC80\uC99D\uD558\uAE30", value: "gate" },
1765
+ { name: "\u{1F4E6} \uD504\uB85C\uC81D\uD2B8 \uC2DC\uC791\uD558\uAE30 (\uD30C\uC77C \uB9CC\uB4E4\uAE30)", value: "init" },
1766
+ { name: "\u{1F4DD} \uC624\uB298 \uD55C \uC77C \uC815\uB9AC\uD558\uAE30", value: "recap" },
1767
+ { name: "\u{1F50D} \uADDC\uCE59 \uD30C\uC77C \uC810\uAC80\uD558\uAE30", value: "check" },
1768
+ { name: "\u{1F512} \uBCF4\uC548 \uC2A4\uCE94 \uB3CC\uB9AC\uAE30", value: "secure" },
1769
+ { name: "\u{1F504} \uADDC\uCE59 \uD30C\uC77C \uB3D9\uAE30\uD654", value: "sync" }
1770
+ ]
1771
+ }]);
1772
+ switch (choice) {
1773
+ case "gate":
1774
+ return gate();
1775
+ case "init":
1776
+ return init({ skipGate: false });
1777
+ case "recap":
1778
+ return recap({});
1779
+ case "check":
1780
+ return check();
1781
+ case "secure":
1782
+ return secure();
1783
+ case "sync":
1784
+ return sync();
1785
+ }
1786
+ });
1636
1787
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@byh3071/vhk",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Vibe Harness Kit — 바이브코딩 풀사이클 CLI",
5
5
  "bin": {
6
6
  "vhk": "./dist/index.js"
@@ -10,7 +10,8 @@
10
10
  "dev": "tsx src/index.ts",
11
11
  "build": "tsup",
12
12
  "test": "vitest",
13
- "prepublishOnly": "pnpm build && pnpm test"
13
+ "test:run": "vitest --run",
14
+ "prepublishOnly": "pnpm build && pnpm test:run"
14
15
  },
15
16
  "files": [
16
17
  "dist",