@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.
- package/dist/index.js +359 -208
- 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
|
|
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{
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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 \
|
|
22
|
-
fullHeader: "\u{1F50D} \
|
|
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} \
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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: (
|
|
30
|
-
statusPassChoice: "\u2705 \
|
|
31
|
-
statusHoldChoice: "\u{1F7E1} \uC544\uC9C1 \uBAA8\uB974\uACA0\
|
|
32
|
-
statusFailChoice: "\u{
|
|
33
|
-
statusPassLine: " \u2705 \
|
|
34
|
-
statusHoldLine: " \u{1F7E1} \uBCF4\uB958",
|
|
35
|
-
statusFailLine: " \u{
|
|
36
|
-
verdictTitle: "\u2550\u2550\u2550 \
|
|
37
|
-
ideaLabel: "\
|
|
38
|
-
painPointLabel: "
|
|
39
|
-
edgeLabel: "
|
|
40
|
-
countLine: (failCount, holdCount, total) => `\
|
|
41
|
-
go: "\
|
|
42
|
-
refine: "\u{
|
|
43
|
-
drop: "\u{
|
|
44
|
-
nextCommand: "\uB2E4\uC74C \
|
|
45
|
-
holdRemainHint: "\u{1F4A1} \uBCF4\uB958 \uD56D\uBAA9\uC740 \uAC1C\uBC1C\uD558\uBA74\uC11C \uCC44\uC6CC\
|
|
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
|
|
49
|
-
skipGate: "\u23ED\uFE0F
|
|
50
|
-
projectName: "\u{1F4E6} \uD504\uB85C\uC81D\uD2B8 \uC774\uB984
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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}
|
|
71
|
-
analyzing: "\u{1F4CA}
|
|
72
|
-
noRepo: "\u274C Git \
|
|
73
|
-
noChanges: "\u26A0\uFE0F \
|
|
74
|
-
summary: "\u{1F4DD} \uC774\uBC88
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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}
|
|
87
|
-
noRules: "\u26A0\uFE0F RULES.md\
|
|
88
|
-
noAutoRules: "\u26A0\uFE0F \uC790\uB3D9
|
|
89
|
-
allPassed: "\u{1F389} \
|
|
90
|
-
summary: "\u{1F4CA} \
|
|
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}
|
|
94
|
-
noGitignore: "\u26A0\uFE0F .gitignore\
|
|
95
|
-
noEnvInGitignore: "\u26A0\uFE0F .gitignore\uC5D0 .env\uAC00 \uC5C6\
|
|
96
|
-
scanning: "\u{1F50D} \uD30C\uC77C \
|
|
97
|
-
clean: "\u{1F389} \
|
|
98
|
-
summary: "\u{1F4CA} \
|
|
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}
|
|
102
|
-
noRules: "\u26A0\uFE0F RULES.md\
|
|
103
|
-
cursorrulesDone: "\u2705 .cursorrules \
|
|
104
|
-
claudeDone: "\u2705 CLAUDE.md \
|
|
105
|
-
done: "\u{1F504} \
|
|
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: "\
|
|
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(
|
|
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(
|
|
200
|
+
console.log(chalk2.green.bold(`
|
|
151
201
|
${ko.gate.skipGo}`));
|
|
152
|
-
console.log(
|
|
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(
|
|
208
|
+
console.log(chalk2.dim(`
|
|
159
209
|
${header} ${ko.gate.modeCountSuffix(total)}
|
|
160
210
|
`));
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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(
|
|
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(
|
|
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" ?
|
|
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(
|
|
256
|
+
console.log(chalk2.bold(`
|
|
197
257
|
${ko.gate.verdictTitle}
|
|
198
258
|
`));
|
|
199
|
-
console.log(`${ko.gate.ideaLabel} ${
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
471
|
+
import chalk3 from "chalk";
|
|
403
472
|
var log = {
|
|
404
|
-
success: (msg) => console.log(
|
|
405
|
-
error: (msg) => console.log(
|
|
406
|
-
warn: (msg) => console.log(
|
|
407
|
-
info: (msg) => console.log(
|
|
408
|
-
step: (msg) => console.log(
|
|
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(
|
|
682
|
+
console.log(chalk4.dim(`
|
|
614
683
|
${ko.init.skipGate}
|
|
615
684
|
`));
|
|
616
685
|
}
|
|
617
|
-
console.log(
|
|
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(
|
|
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(
|
|
745
|
+
console.log(chalk4.bold.green(`
|
|
677
746
|
${ko.init.done}`));
|
|
678
|
-
console.log(
|
|
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. ${
|
|
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. ${
|
|
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
|
|
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(
|
|
947
|
+
console.log(chalk5.bold(`
|
|
871
948
|
${ko.recap.title}
|
|
872
949
|
`));
|
|
873
950
|
if (!await isGitRepo()) {
|
|
874
|
-
console.log(
|
|
951
|
+
console.log(chalk5.red(ko.recap.noRepo));
|
|
875
952
|
return;
|
|
876
953
|
}
|
|
877
|
-
console.log(
|
|
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(
|
|
959
|
+
console.log(chalk5.yellow(ko.recap.noChanges));
|
|
883
960
|
return;
|
|
884
961
|
}
|
|
885
|
-
console.log(
|
|
886
|
-
console.log(` \uD30C\uC77C: ${
|
|
887
|
-
console.log(` \uCD94\uAC00: ${
|
|
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(
|
|
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" ?
|
|
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(
|
|
972
|
+
console.log(chalk5.dim(` ... \uC678 ${diff.files.length - 15}\uAC1C`));
|
|
896
973
|
}
|
|
897
974
|
}
|
|
898
975
|
if (commits.length > 0) {
|
|
899
|
-
console.log(
|
|
976
|
+
console.log(chalk5.dim("\n \uCD5C\uADFC \uCEE4\uBC0B:"));
|
|
900
977
|
commits.slice(0, 5).forEach((c) => {
|
|
901
|
-
console.log(
|
|
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(
|
|
1046
|
+
console.log(chalk5.cyan.bold(`
|
|
970
1047
|
${ko.recap.adrDetected} (${adrCandidates.length}\uAC74)`));
|
|
971
1048
|
for (const candidate of adrCandidates) {
|
|
972
|
-
console.log(
|
|
973
|
-
candidate.files.forEach((f) => console.log(
|
|
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(
|
|
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(
|
|
1087
|
+
console.log(chalk5.yellow.bold(`
|
|
1011
1088
|
${ko.recap.troubleDetected} (${troubleCommits.length}\uAC74)`));
|
|
1012
1089
|
troubleCommits.forEach((c) => {
|
|
1013
|
-
console.log(
|
|
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(
|
|
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(
|
|
1144
|
+
console.log(chalk5.green.bold(`
|
|
1068
1145
|
${ko.recap.done}`));
|
|
1069
|
-
console.log(
|
|
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(
|
|
1166
|
+
console.log(chalk5.green(" \u2705 CLAUDE.md \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC"));
|
|
1090
1167
|
}
|
|
1091
1168
|
}
|
|
1092
|
-
|
|
1093
|
-
\
|
|
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
|
|
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(
|
|
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(
|
|
1175
|
-
console.log(
|
|
1176
|
-
console.log(
|
|
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(
|
|
1179
|
-
console.log(
|
|
1180
|
-
console.log(
|
|
1181
|
-
console.log(
|
|
1182
|
-
console.log(
|
|
1183
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
1204
|
-
console.log(
|
|
1282
|
+
console.log(chalk6.green(` ${ko.sync.claudeDone}`));
|
|
1283
|
+
console.log(chalk6.bold.green(`
|
|
1205
1284
|
${ko.sync.done}`));
|
|
1206
|
-
console.log(
|
|
1207
|
-
console.log(
|
|
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
|
|
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(
|
|
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(
|
|
1386
|
-
console.log(
|
|
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(
|
|
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(
|
|
1394
|
-
console.log(
|
|
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(
|
|
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(
|
|
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 ?
|
|
1408
|
-
const icon = v.severity === "error" ?
|
|
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(
|
|
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(
|
|
1421
|
-
console.log(` \uADDC\uCE59: ${
|
|
1422
|
-
if (errors > 0) console.log(` ${
|
|
1423
|
-
if (warnings > 0) console.log(` ${
|
|
1424
|
-
|
|
1425
|
-
|
|
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
|
|
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(
|
|
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(
|
|
1529
|
-
console.log(
|
|
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(
|
|
1534
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
1568
|
-
console.log(
|
|
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(
|
|
1667
|
+
console.log(chalk8.red.bold(` \u{1F6A8} CRITICAL \u2014 ${critical.length}\uAC74`));
|
|
1576
1668
|
critical.forEach((f) => {
|
|
1577
|
-
console.log(
|
|
1578
|
-
console.log(
|
|
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(
|
|
1675
|
+
console.log(chalk8.yellow.bold(` \u26A0\uFE0F HIGH \u2014 ${high.length}\uAC74`));
|
|
1584
1676
|
high.forEach((f) => {
|
|
1585
|
-
console.log(
|
|
1586
|
-
console.log(
|
|
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(
|
|
1683
|
+
console.log(chalk8.blue.bold(` \u2139 MEDIUM \u2014 ${medium.length}\uAC74`));
|
|
1592
1684
|
medium.forEach((f) => {
|
|
1593
|
-
console.log(
|
|
1594
|
-
console.log(
|
|
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(
|
|
1599
|
-
console.log(` \uCD1D ${
|
|
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(
|
|
1602
|
-
console.log(
|
|
1603
|
-
console.log(
|
|
1604
|
-
console.log(
|
|
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
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
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.
|
|
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
|
-
"
|
|
13
|
+
"test:run": "vitest --run",
|
|
14
|
+
"prepublishOnly": "pnpm build && pnpm test:run"
|
|
14
15
|
},
|
|
15
16
|
"files": [
|
|
16
17
|
"dist",
|