@byh3071/vhk 1.6.5 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-EJTVXWUZ.js → chunk-GXFZ7JXX.js} +147 -116
- package/dist/index.js +681 -519
- package/dist/mcp/index.js +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -12,6 +12,8 @@ import {
|
|
|
12
12
|
countLocalCommits,
|
|
13
13
|
deploy,
|
|
14
14
|
detectExistingRuleFiles,
|
|
15
|
+
ensureInteractive,
|
|
16
|
+
ensureVhkIgnored,
|
|
15
17
|
env,
|
|
16
18
|
envCheck,
|
|
17
19
|
filterSevereFindings,
|
|
@@ -22,12 +24,15 @@ import {
|
|
|
22
24
|
gitOut,
|
|
23
25
|
gitRun,
|
|
24
26
|
hasGitRemote,
|
|
27
|
+
isInteractive,
|
|
28
|
+
isPromptAbortError,
|
|
25
29
|
ko,
|
|
26
30
|
listBackups,
|
|
27
31
|
localDate,
|
|
28
32
|
printContextResumeHint,
|
|
29
33
|
printNextStep,
|
|
30
34
|
printSecurityWarnings,
|
|
35
|
+
promptOrDefault,
|
|
31
36
|
publish,
|
|
32
37
|
readJsonFile,
|
|
33
38
|
require_ignore,
|
|
@@ -38,12 +43,12 @@ import {
|
|
|
38
43
|
stripBom,
|
|
39
44
|
sync,
|
|
40
45
|
t
|
|
41
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-GXFZ7JXX.js";
|
|
42
47
|
|
|
43
48
|
// src/index.ts
|
|
44
49
|
import { Command, Help } from "commander";
|
|
45
50
|
import { pathToFileURL } from "url";
|
|
46
|
-
import
|
|
51
|
+
import chalk33 from "chalk";
|
|
47
52
|
import inquirer13 from "inquirer";
|
|
48
53
|
|
|
49
54
|
// src/lib/nlp-router.ts
|
|
@@ -500,37 +505,12 @@ function detectNaturalLanguageInput(argv) {
|
|
|
500
505
|
}
|
|
501
506
|
|
|
502
507
|
// src/lib/nlp-run.ts
|
|
503
|
-
import
|
|
508
|
+
import chalk31 from "chalk";
|
|
504
509
|
import inquirer12 from "inquirer";
|
|
505
510
|
|
|
506
511
|
// src/commands/gate.ts
|
|
507
512
|
import inquirer from "inquirer";
|
|
508
|
-
import chalk2 from "chalk";
|
|
509
|
-
|
|
510
|
-
// src/lib/interactive.ts
|
|
511
513
|
import chalk from "chalk";
|
|
512
|
-
function isInteractive(opts) {
|
|
513
|
-
if (opts?.yes) return false;
|
|
514
|
-
if (process.env.VHK_FORCE_INTERACTIVE === "1") return true;
|
|
515
|
-
return !!process.stdin.isTTY;
|
|
516
|
-
}
|
|
517
|
-
async function promptOrDefault(ask, fallback, opts) {
|
|
518
|
-
if (!isInteractive(opts)) return fallback;
|
|
519
|
-
return await ask();
|
|
520
|
-
}
|
|
521
|
-
function ensureInteractive(hint = "") {
|
|
522
|
-
if (isInteractive()) return true;
|
|
523
|
-
console.error(chalk.yellow(" \u26A0\uFE0F \uC774 \uBA85\uB839\uC740 \uB300\uD654\uD615 \uC785\uB825\uC774 \uD544\uC694\uD569\uB2C8\uB2E4 \u2014 \uBE44-TTY/\uD30C\uC774\uD504 \uD658\uACBD\uC5D0\uC11C\uB294 \uC2E4\uD589\uD560 \uC218 \uC5C6\uC5B4\uC694."));
|
|
524
|
-
if (hint) console.error(chalk.dim(` ${hint}`));
|
|
525
|
-
process.exitCode = 1;
|
|
526
|
-
return false;
|
|
527
|
-
}
|
|
528
|
-
function isPromptAbortError(err) {
|
|
529
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
530
|
-
return /ERR_USE_AFTER_CLOSE|force closed|ExitPromptError|readline was closed|User force closed/i.test(msg);
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
// src/commands/gate.ts
|
|
534
514
|
var GATE_QUESTIONS = [
|
|
535
515
|
{ 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 },
|
|
536
516
|
{ 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 },
|
|
@@ -553,7 +533,7 @@ function judgeGate(failCount, holdCount) {
|
|
|
553
533
|
}
|
|
554
534
|
async function gate() {
|
|
555
535
|
if (!ensureInteractive("\uC544\uC774\uB514\uC5B4 \uAC80\uC99D\uC740 \uB300\uD654\uD615 \uC9C8\uBB38\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. \uD130\uBBF8\uB110(PowerShell \uB4F1)\uC5D0\uC11C \uC9C1\uC811 \uC2E4\uD589\uD558\uC138\uC694. Git Bash \uBA74 VHK_FORCE_INTERACTIVE=1.")) return;
|
|
556
|
-
console.log(
|
|
536
|
+
console.log(chalk.bold(`
|
|
557
537
|
${ko.gate.title}
|
|
558
538
|
`));
|
|
559
539
|
const { mode: mode2 } = await inquirer.prompt([{
|
|
@@ -572,33 +552,33 @@ ${ko.gate.title}
|
|
|
572
552
|
name: "source",
|
|
573
553
|
message: ko.gate.skipSourcePrompt
|
|
574
554
|
}]);
|
|
575
|
-
console.log(
|
|
555
|
+
console.log(chalk.green.bold(`
|
|
576
556
|
${ko.gate.skipGo}`));
|
|
577
|
-
console.log(
|
|
557
|
+
console.log(chalk.dim(ko.gate.skipSourceLabel(source)));
|
|
578
558
|
return;
|
|
579
559
|
}
|
|
580
560
|
const questions = mode2 === "quick" ? GATE_QUESTIONS.filter((q) => q.quick) : GATE_QUESTIONS;
|
|
581
561
|
const total = questions.length;
|
|
582
562
|
const header = mode2 === "quick" ? ko.gate.quickHeader : ko.gate.fullHeader;
|
|
583
|
-
console.log(
|
|
563
|
+
console.log(chalk.dim(`
|
|
584
564
|
${header} ${ko.gate.modeCountSuffix(total)}
|
|
585
565
|
`));
|
|
586
|
-
console.log(
|
|
566
|
+
console.log(chalk.dim(`
|
|
587
567
|
${ko.gate.welcome}
|
|
588
568
|
`));
|
|
589
|
-
console.log(
|
|
569
|
+
console.log(chalk.dim(` ${ko.gate.ideaHint}`));
|
|
590
570
|
const { idea } = await inquirer.prompt([
|
|
591
571
|
{ type: "input", name: "idea", message: ko.gate.idea }
|
|
592
572
|
]);
|
|
593
|
-
console.log(
|
|
573
|
+
console.log(chalk.dim(` ${ko.gate.painPointHint}`));
|
|
594
574
|
const { painPoint } = await inquirer.prompt([
|
|
595
575
|
{ type: "input", name: "painPoint", message: ko.gate.painPoint }
|
|
596
576
|
]);
|
|
597
|
-
console.log(
|
|
577
|
+
console.log(chalk.dim(` ${ko.gate.edgeHint}`));
|
|
598
578
|
const { edge } = await inquirer.prompt([
|
|
599
579
|
{ type: "input", name: "edge", message: ko.gate.edge }
|
|
600
580
|
]);
|
|
601
|
-
console.log(
|
|
581
|
+
console.log(chalk.dim(`
|
|
602
582
|
${ko.gate.checklistStart}
|
|
603
583
|
`));
|
|
604
584
|
let failCount = 0;
|
|
@@ -606,7 +586,7 @@ ${ko.gate.checklistStart}
|
|
|
606
586
|
const results = [];
|
|
607
587
|
for (let i = 0; i < questions.length; i++) {
|
|
608
588
|
const q = questions[i];
|
|
609
|
-
if (q.hint) console.log(
|
|
589
|
+
if (q.hint) console.log(chalk.dim(`${ko.gate.hintPrefix} ${q.hint}`));
|
|
610
590
|
const { answer } = await inquirer.prompt([{
|
|
611
591
|
type: "input",
|
|
612
592
|
name: "answer",
|
|
@@ -625,22 +605,22 @@ ${ko.gate.checklistStart}
|
|
|
625
605
|
if (status2 === "fail") failCount++;
|
|
626
606
|
if (status2 === "hold") holdCount++;
|
|
627
607
|
results.push({ id: q.id, stage: q.stage, status: status2, answer });
|
|
628
|
-
const icon = status2 === "pass" ?
|
|
608
|
+
const icon = status2 === "pass" ? chalk.green(ko.gate.statusPassLine) : status2 === "hold" ? chalk.yellow(ko.gate.statusHoldLine) : chalk.red(ko.gate.statusFailLine);
|
|
629
609
|
console.log(icon);
|
|
630
610
|
}
|
|
631
|
-
console.log(
|
|
611
|
+
console.log(chalk.bold(`
|
|
632
612
|
${ko.gate.verdictTitle}
|
|
633
613
|
`));
|
|
634
|
-
console.log(`${ko.gate.ideaLabel} ${
|
|
614
|
+
console.log(`${ko.gate.ideaLabel} ${chalk.cyan(idea)}`);
|
|
635
615
|
console.log(`${ko.gate.painPointLabel} ${painPoint}`);
|
|
636
616
|
console.log(`${ko.gate.edgeLabel} ${edge}`);
|
|
637
617
|
console.log(`${ko.gate.countLine(failCount, holdCount, total)}
|
|
638
618
|
`);
|
|
639
619
|
const verdict = judgeGate(failCount, holdCount);
|
|
640
620
|
if (verdict === "GO") {
|
|
641
|
-
console.log(
|
|
621
|
+
console.log(chalk.green.bold(ko.gate.go));
|
|
642
622
|
if (holdCount > 0) {
|
|
643
|
-
console.log(
|
|
623
|
+
console.log(chalk.yellow(ko.gate.holdRemainHint));
|
|
644
624
|
}
|
|
645
625
|
printNextStep({
|
|
646
626
|
message: "\uC544\uC774\uB514\uC5B4 \uD1B5\uACFC! \uC774\uC81C \uD504\uB85C\uC81D\uD2B8\uB97C \uB9CC\uB4E4\uC5B4\uBCF4\uC138\uC694.",
|
|
@@ -648,20 +628,20 @@ ${ko.gate.verdictTitle}
|
|
|
648
628
|
cursorHint: "\uD504\uB85C\uC81D\uD2B8 \uB9CC\uB4E4\uC5B4\uC918"
|
|
649
629
|
});
|
|
650
630
|
} else if (verdict === "REFINE") {
|
|
651
|
-
console.log(
|
|
631
|
+
console.log(chalk.yellow.bold(ko.gate.refine));
|
|
652
632
|
printNextStep({
|
|
653
633
|
message: "\uC870\uAE08 \uB354 \uB2E4\uB4EC\uC740 \uD6C4 \uB2E4\uC2DC \uAC80\uC99D\uD574\uBCF4\uC138\uC694.",
|
|
654
634
|
command: "vhk \uAC80\uC99D",
|
|
655
635
|
cursorHint: "\uC544\uC774\uB514\uC5B4 \uB2E4\uC2DC \uAC80\uC99D\uD574\uC918"
|
|
656
636
|
});
|
|
657
637
|
} else {
|
|
658
|
-
console.log(
|
|
638
|
+
console.log(chalk.red.bold(ko.gate.drop));
|
|
659
639
|
}
|
|
660
640
|
}
|
|
661
641
|
|
|
662
642
|
// src/commands/init.ts
|
|
663
643
|
import inquirer2 from "inquirer";
|
|
664
|
-
import
|
|
644
|
+
import chalk3 from "chalk";
|
|
665
645
|
import fs2 from "fs";
|
|
666
646
|
import path2 from "path";
|
|
667
647
|
|
|
@@ -983,13 +963,13 @@ function VHK_CONTEXT_SEED(name, type, stack) {
|
|
|
983
963
|
}
|
|
984
964
|
|
|
985
965
|
// src/utils/logger.ts
|
|
986
|
-
import
|
|
966
|
+
import chalk2 from "chalk";
|
|
987
967
|
var log = {
|
|
988
|
-
success: (msg) => console.log(
|
|
989
|
-
error: (msg) => console.log(
|
|
990
|
-
warn: (msg) => console.log(
|
|
991
|
-
info: (msg) => console.log(
|
|
992
|
-
step: (msg) => console.log(
|
|
968
|
+
success: (msg) => console.log(chalk2.green(`\u2705 ${msg}`)),
|
|
969
|
+
error: (msg) => console.log(chalk2.red(`\u274C ${msg}`)),
|
|
970
|
+
warn: (msg) => console.log(chalk2.yellow(`\u26A0\uFE0F ${msg}`)),
|
|
971
|
+
info: (msg) => console.log(chalk2.blue(`\u2139\uFE0F ${msg}`)),
|
|
972
|
+
step: (msg) => console.log(chalk2.bold(`
|
|
993
973
|
\u25B8 ${msg}`))
|
|
994
974
|
};
|
|
995
975
|
|
|
@@ -1232,11 +1212,11 @@ async function collectAnswers(options, defaults = {}) {
|
|
|
1232
1212
|
async function init(options = {}) {
|
|
1233
1213
|
const skipGate = Boolean(options.skipGate || options.fromNotion);
|
|
1234
1214
|
if (skipGate) {
|
|
1235
|
-
console.log(
|
|
1215
|
+
console.log(chalk3.dim(`
|
|
1236
1216
|
${ko.init.skipGate}
|
|
1237
1217
|
`));
|
|
1238
1218
|
}
|
|
1239
|
-
console.log(
|
|
1219
|
+
console.log(chalk3.bold(`
|
|
1240
1220
|
${ko.init.title}
|
|
1241
1221
|
`));
|
|
1242
1222
|
printSecurityWarnings();
|
|
@@ -1262,8 +1242,8 @@ ${ko.init.title}
|
|
|
1262
1242
|
}
|
|
1263
1243
|
const detected = detectProjectStack(process.cwd());
|
|
1264
1244
|
const stack = detected ?? STACK_PRESETS[answers.type];
|
|
1265
|
-
if (detected) console.log(
|
|
1266
|
-
console.log(
|
|
1245
|
+
if (detected) console.log(chalk3.dim(" \u{1F50E} package.json \uC758\uC874\uC131\uC5D0\uC11C \uC2E4\uC81C \uC2A4\uD0DD \uAC10\uC9C0"));
|
|
1246
|
+
console.log(chalk3.dim(`
|
|
1267
1247
|
${ko.init.recommendedStack} ${stack.join(" + ")}
|
|
1268
1248
|
`));
|
|
1269
1249
|
if (isInteractive(options)) {
|
|
@@ -1294,7 +1274,7 @@ ${ko.init.recommendedStack} ${stack.join(" + ")}
|
|
|
1294
1274
|
}]);
|
|
1295
1275
|
if (adopt) {
|
|
1296
1276
|
adoptedRules = buildAdoptedRules(existingRules, answers.name);
|
|
1297
|
-
console.log(
|
|
1277
|
+
console.log(chalk3.dim(` ${ko.init.adoptPreview(existingRules.length)}`));
|
|
1298
1278
|
}
|
|
1299
1279
|
}
|
|
1300
1280
|
}
|
|
@@ -1319,21 +1299,21 @@ ${ko.init.recommendedStack} ${stack.join(" + ")}
|
|
|
1319
1299
|
log.success(filePath);
|
|
1320
1300
|
}
|
|
1321
1301
|
await writeInitExtras(cwd, !isInteractive(options));
|
|
1322
|
-
console.log(
|
|
1302
|
+
console.log(chalk3.bold.green(`
|
|
1323
1303
|
${ko.init.done}`));
|
|
1324
|
-
console.log(
|
|
1304
|
+
console.log(chalk3.dim(`
|
|
1325
1305
|
${ko.init.nextSteps}`));
|
|
1326
1306
|
if (options.fromNotion) {
|
|
1327
1307
|
console.log(` 1. ${ko.init.notionReviewHint}`);
|
|
1328
1308
|
console.log(` 2. ${ko.init.gitHintLabel}`);
|
|
1329
|
-
console.log(` ${
|
|
1309
|
+
console.log(` ${chalk3.cyan(ko.init.gitHintCommand)}`);
|
|
1330
1310
|
console.log(` 3. ${ko.init.startDev}
|
|
1331
1311
|
`);
|
|
1332
1312
|
} else {
|
|
1333
1313
|
console.log(` 1. ${ko.init.fillHint}`);
|
|
1334
1314
|
console.log(` 2. ${ko.init.prdHint}`);
|
|
1335
1315
|
console.log(` 3. ${ko.init.gitHintLabel}`);
|
|
1336
|
-
console.log(` ${
|
|
1316
|
+
console.log(` ${chalk3.cyan(ko.init.gitHintCommand)}`);
|
|
1337
1317
|
console.log(` 4. ${ko.init.startDev}
|
|
1338
1318
|
`);
|
|
1339
1319
|
}
|
|
@@ -1465,7 +1445,7 @@ async function writeInitExtras(projectDir, noninteractive = false) {
|
|
|
1465
1445
|
|
|
1466
1446
|
// src/commands/recap.ts
|
|
1467
1447
|
import inquirer3 from "inquirer";
|
|
1468
|
-
import
|
|
1448
|
+
import chalk5 from "chalk";
|
|
1469
1449
|
import fs4 from "fs";
|
|
1470
1450
|
import path5 from "path";
|
|
1471
1451
|
|
|
@@ -1651,7 +1631,7 @@ function createAdrFile(cwd, title, context2, decision, consequences) {
|
|
|
1651
1631
|
}
|
|
1652
1632
|
|
|
1653
1633
|
// src/lib/hard-stop-guard.ts
|
|
1654
|
-
import
|
|
1634
|
+
import chalk4 from "chalk";
|
|
1655
1635
|
|
|
1656
1636
|
// src/lib/state-files.ts
|
|
1657
1637
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, appendFileSync, rmSync } from "fs";
|
|
@@ -1766,11 +1746,11 @@ function clearHardStop() {
|
|
|
1766
1746
|
// src/lib/hard-stop-guard.ts
|
|
1767
1747
|
function ensureNotHardStopped(action) {
|
|
1768
1748
|
if (!isHardStopActive()) return true;
|
|
1769
|
-
console.error(
|
|
1749
|
+
console.error(chalk4.red.bold(`
|
|
1770
1750
|
\u{1F6D1} HARD STOP \uD65C\uC131 \u2014 '${action}' \uC744(\uB97C) \uC2E4\uD589\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.`));
|
|
1771
1751
|
const reason = readHardStopReason();
|
|
1772
|
-
if (reason) console.error(
|
|
1773
|
-
console.error(
|
|
1752
|
+
if (reason) console.error(chalk4.dim(` \uC0AC\uC720: ${reason.replace(/\s*\n\s*/g, " ")}`));
|
|
1753
|
+
console.error(chalk4.dim(" \uD574\uC81C: vhk resume --confirm (\uC0AC\uB78C\uC774 \uC9C1\uC811 \uC2E4\uD589)"));
|
|
1774
1754
|
process.exitCode = 1;
|
|
1775
1755
|
return false;
|
|
1776
1756
|
}
|
|
@@ -1778,45 +1758,45 @@ function ensureNotHardStopped(action) {
|
|
|
1778
1758
|
// src/commands/recap.ts
|
|
1779
1759
|
async function recap(options = {}) {
|
|
1780
1760
|
if (!ensureNotHardStopped("recap")) return;
|
|
1781
|
-
console.log(
|
|
1761
|
+
console.log(chalk5.bold(`
|
|
1782
1762
|
${ko.recap.title}
|
|
1783
1763
|
`));
|
|
1784
1764
|
if (!await isGitRepo()) {
|
|
1785
|
-
console.log(
|
|
1765
|
+
console.log(chalk5.red(ko.recap.noRepo));
|
|
1786
1766
|
return;
|
|
1787
1767
|
}
|
|
1788
1768
|
if (!await hasAnyCommits()) {
|
|
1789
|
-
console.log(
|
|
1790
|
-
console.log(
|
|
1769
|
+
console.log(chalk5.yellow("\u26A0\uFE0F \uC544\uC9C1 \uCEE4\uBC0B\uC774 \uC5C6\uC5B4\uC694."));
|
|
1770
|
+
console.log(chalk5.gray(" \uD30C\uC77C\uC744 \uCD94\uAC00\uD558\uACE0 `vhk save` \uB610\uB294 `git commit`\uC73C\uB85C \uCCAB \uCEE4\uBC0B\uC744 \uB9CC\uB4E4\uC5B4 \uBCF4\uC138\uC694."));
|
|
1791
1771
|
return;
|
|
1792
1772
|
}
|
|
1793
1773
|
printSecurityWarnings();
|
|
1794
|
-
console.log(
|
|
1774
|
+
console.log(chalk5.dim(`${ko.recap.analyzing}
|
|
1795
1775
|
`));
|
|
1796
1776
|
const since = options.since || localDate();
|
|
1797
1777
|
const diff2 = await getSessionDiff(since);
|
|
1798
1778
|
const commits = await getRecentCommits(10, since);
|
|
1799
1779
|
if (diff2.filesChanged === 0 && commits.length === 0) {
|
|
1800
|
-
console.log(
|
|
1780
|
+
console.log(chalk5.yellow(ko.recap.noChanges));
|
|
1801
1781
|
return;
|
|
1802
1782
|
}
|
|
1803
|
-
console.log(
|
|
1804
|
-
console.log(` \uD30C\uC77C: ${
|
|
1805
|
-
console.log(` \uCD94\uAC00: ${
|
|
1783
|
+
console.log(chalk5.bold("\u{1F4CA} \uBCC0\uACBD \uC694\uC57D:"));
|
|
1784
|
+
console.log(` \uD30C\uC77C: ${chalk5.cyan(String(diff2.filesChanged))}\uAC1C \uBCC0\uACBD`);
|
|
1785
|
+
console.log(` \uCD94\uAC00: ${chalk5.green("+" + diff2.insertions)} / \uC0AD\uC81C: ${chalk5.red("-" + diff2.deletions)}`);
|
|
1806
1786
|
if (diff2.files.length > 0) {
|
|
1807
|
-
console.log(
|
|
1787
|
+
console.log(chalk5.dim("\n \uBCC0\uACBD \uD30C\uC77C:"));
|
|
1808
1788
|
diff2.files.slice(0, 15).forEach((f) => {
|
|
1809
|
-
const icon = f.status === "new" ?
|
|
1789
|
+
const icon = f.status === "new" ? chalk5.green("\u{1F195}") : f.status === "deleted" ? chalk5.red("\u{1F5D1}\uFE0F") : chalk5.yellow("\u270F\uFE0F");
|
|
1810
1790
|
console.log(` ${icon} ${f.file}`);
|
|
1811
1791
|
});
|
|
1812
1792
|
if (diff2.files.length > 15) {
|
|
1813
|
-
console.log(
|
|
1793
|
+
console.log(chalk5.dim(` ... \uC678 ${diff2.files.length - 15}\uAC1C`));
|
|
1814
1794
|
}
|
|
1815
1795
|
}
|
|
1816
1796
|
if (commits.length > 0) {
|
|
1817
|
-
console.log(
|
|
1797
|
+
console.log(chalk5.dim("\n \uCD5C\uADFC \uCEE4\uBC0B:"));
|
|
1818
1798
|
commits.slice(0, 5).forEach((c) => {
|
|
1819
|
-
console.log(
|
|
1799
|
+
console.log(chalk5.dim(` \u2022 ${c.message}`));
|
|
1820
1800
|
});
|
|
1821
1801
|
}
|
|
1822
1802
|
console.log("");
|
|
@@ -1885,11 +1865,11 @@ ${ko.recap.title}
|
|
|
1885
1865
|
fs4.writeFileSync(filePath, content, "utf-8");
|
|
1886
1866
|
const adrCandidates = detectAdrCandidates(diff2);
|
|
1887
1867
|
if (adrCandidates.length > 0) {
|
|
1888
|
-
console.log(
|
|
1868
|
+
console.log(chalk5.cyan.bold(`
|
|
1889
1869
|
${ko.recap.adrDetected} (${adrCandidates.length}\uAC74)`));
|
|
1890
1870
|
for (const candidate of adrCandidates) {
|
|
1891
|
-
console.log(
|
|
1892
|
-
candidate.files.forEach((f) => console.log(
|
|
1871
|
+
console.log(chalk5.cyan(` \u2022 ${candidate.title}: ${candidate.context}`));
|
|
1872
|
+
candidate.files.forEach((f) => console.log(chalk5.dim(` ${f}`)));
|
|
1893
1873
|
}
|
|
1894
1874
|
const { createAdr } = await inquirer3.prompt([{
|
|
1895
1875
|
type: "confirm",
|
|
@@ -1919,17 +1899,17 @@ ${ko.recap.adrDetected} (${adrCandidates.length}\uAC74)`));
|
|
|
1919
1899
|
adrAnswers.decision,
|
|
1920
1900
|
adrAnswers.consequences
|
|
1921
1901
|
);
|
|
1922
|
-
console.log(
|
|
1902
|
+
console.log(chalk5.green(` \u2705 ADR \uC0DD\uC131: ${path5.relative(process.cwd(), adrPath)}`));
|
|
1923
1903
|
}
|
|
1924
1904
|
}
|
|
1925
1905
|
}
|
|
1926
1906
|
const troubleshootingKeywords = /fix|bug|error|crash|hotfix|patch|revert|트러블|에러|버그|수정|핫픽스/i;
|
|
1927
1907
|
const troubleCommits = commits.filter((c) => troubleshootingKeywords.test(c.message));
|
|
1928
1908
|
if (troubleCommits.length > 0) {
|
|
1929
|
-
console.log(
|
|
1909
|
+
console.log(chalk5.yellow.bold(`
|
|
1930
1910
|
${ko.recap.troubleDetected} (${troubleCommits.length}\uAC74)`));
|
|
1931
1911
|
troubleCommits.forEach((c) => {
|
|
1932
|
-
console.log(
|
|
1912
|
+
console.log(chalk5.dim(` \u2022 ${c.message}`));
|
|
1933
1913
|
});
|
|
1934
1914
|
const { createTroubleshoot } = await inquirer3.prompt([{
|
|
1935
1915
|
type: "confirm",
|
|
@@ -1980,12 +1960,12 @@ ${ko.recap.troubleDetected} (${troubleCommits.length}\uAC74)`));
|
|
|
1980
1960
|
`*Generated by \`vhk recap\` at ${(/* @__PURE__ */ new Date()).toISOString()}*`
|
|
1981
1961
|
].join("\n");
|
|
1982
1962
|
fs4.writeFileSync(tsFilePath, tsContent, "utf-8");
|
|
1983
|
-
console.log(
|
|
1963
|
+
console.log(chalk5.green(` \u2705 \uD2B8\uB7EC\uBE14\uC288\uD305 \uBB38\uC11C \uC0DD\uC131: ${path5.relative(process.cwd(), tsFilePath)}`));
|
|
1984
1964
|
}
|
|
1985
1965
|
}
|
|
1986
|
-
console.log(
|
|
1966
|
+
console.log(chalk5.green.bold(`
|
|
1987
1967
|
${ko.recap.done}`));
|
|
1988
|
-
console.log(
|
|
1968
|
+
console.log(chalk5.dim(` \u{1F4C4} ${path5.relative(process.cwd(), filePath)}`));
|
|
1989
1969
|
const claudeMdPath = path5.join(process.cwd(), "CLAUDE.md");
|
|
1990
1970
|
if (fs4.existsSync(claudeMdPath)) {
|
|
1991
1971
|
const { updateClaude } = await inquirer3.prompt([{
|
|
@@ -2005,7 +1985,7 @@ ${ko.recap.done}`));
|
|
|
2005
1985
|
`- **\uB2E4\uC74C \uC561\uC158:** ${answers.nextTodo}`
|
|
2006
1986
|
);
|
|
2007
1987
|
fs4.writeFileSync(claudeMdPath, claudeContent, "utf-8");
|
|
2008
|
-
console.log(
|
|
1988
|
+
console.log(chalk5.green(" \u2705 CLAUDE.md \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC"));
|
|
2009
1989
|
}
|
|
2010
1990
|
}
|
|
2011
1991
|
const gitSaveCmd = process.platform === "win32" ? 'git add .; git commit -m "recap: \uC138\uC158 \uAE30\uB85D"' : 'git add . && git commit -m "recap: \uC138\uC158 \uAE30\uB85D"';
|
|
@@ -2017,7 +1997,7 @@ ${ko.recap.done}`));
|
|
|
2017
1997
|
}
|
|
2018
1998
|
|
|
2019
1999
|
// src/commands/check.ts
|
|
2020
|
-
import
|
|
2000
|
+
import chalk7 from "chalk";
|
|
2021
2001
|
import path7 from "path";
|
|
2022
2002
|
import fs6 from "fs";
|
|
2023
2003
|
|
|
@@ -2181,7 +2161,7 @@ function escapeRegex(str) {
|
|
|
2181
2161
|
// src/commands/goal.ts
|
|
2182
2162
|
import { existsSync as existsSync3, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, readFileSync as readFileSync3 } from "fs";
|
|
2183
2163
|
import { join as join4 } from "path";
|
|
2184
|
-
import
|
|
2164
|
+
import chalk6 from "chalk";
|
|
2185
2165
|
|
|
2186
2166
|
// src/lib/goal-frontmatter.ts
|
|
2187
2167
|
import { existsSync as existsSync2, readFileSync as readFileSync2, readdirSync, statSync } from "fs";
|
|
@@ -2361,14 +2341,14 @@ function resolveGoalId(optId, goals) {
|
|
|
2361
2341
|
return selectActiveId(goals);
|
|
2362
2342
|
}
|
|
2363
2343
|
async function goalList() {
|
|
2364
|
-
console.log(
|
|
2344
|
+
console.log(chalk6.bold(`
|
|
2365
2345
|
${ko.goal.listTitle}
|
|
2366
2346
|
`));
|
|
2367
2347
|
const goals = listGoals(GOALS_DIR);
|
|
2368
2348
|
const skipped = findSkippedGoalFiles(GOALS_DIR);
|
|
2369
2349
|
if (goals.length === 0) {
|
|
2370
|
-
console.log(
|
|
2371
|
-
console.log(
|
|
2350
|
+
console.log(chalk6.yellow(" \u{1F4ED} goals/ \uB514\uB809\uD1A0\uB9AC\uC5D0 goal \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
|
|
2351
|
+
console.log(chalk6.dim(" vhk goal init \uC73C\uB85C \uC2DC\uC791\uD558\uC138\uC694."));
|
|
2372
2352
|
printSkippedGoalWarnings(skipped);
|
|
2373
2353
|
return;
|
|
2374
2354
|
}
|
|
@@ -2386,33 +2366,33 @@ ${ko.goal.listTitle}
|
|
|
2386
2366
|
const dups = findDuplicateIds(goals);
|
|
2387
2367
|
if (dups.length > 0) {
|
|
2388
2368
|
console.log("");
|
|
2389
|
-
console.log(
|
|
2369
|
+
console.log(chalk6.yellow(` ${ko.goal.duplicateId(dups.join(", "))}`));
|
|
2390
2370
|
}
|
|
2391
2371
|
printSkippedGoalWarnings(skipped);
|
|
2392
2372
|
}
|
|
2393
2373
|
function printSkippedGoalWarnings(skipped) {
|
|
2394
2374
|
if (skipped.length > 0) {
|
|
2395
2375
|
console.log("");
|
|
2396
|
-
console.log(
|
|
2376
|
+
console.log(chalk6.yellow(` ${ko.goal.skippedFiles(skipped.length)}`));
|
|
2397
2377
|
for (const s of skipped) {
|
|
2398
|
-
console.log(
|
|
2378
|
+
console.log(chalk6.yellow(` - goals/${s.file}: ${s.reason}`));
|
|
2399
2379
|
}
|
|
2400
|
-
console.log(
|
|
2380
|
+
console.log(chalk6.dim(" \uD544\uC218: type: goal + \uC22B\uC790 id. \uC2A4\uD0A4\uB9C8 \uC804\uCCB4: goals/_meta.md"));
|
|
2401
2381
|
}
|
|
2402
2382
|
}
|
|
2403
2383
|
async function goalNext() {
|
|
2404
|
-
console.log(
|
|
2384
|
+
console.log(chalk6.bold(`
|
|
2405
2385
|
${ko.goal.nextTitle}
|
|
2406
2386
|
`));
|
|
2407
2387
|
const goals = listGoals(GOALS_DIR);
|
|
2408
2388
|
if (goals.length === 0) {
|
|
2409
|
-
console.log(
|
|
2410
|
-
console.log(
|
|
2389
|
+
console.log(chalk6.yellow(" \u{1F4ED} \uC815\uC758\uB41C goal \uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
|
|
2390
|
+
console.log(chalk6.dim(" vhk goal init \uC73C\uB85C \uC2DC\uC791\uD558\uC138\uC694."));
|
|
2411
2391
|
return;
|
|
2412
2392
|
}
|
|
2413
2393
|
const activeId = selectActiveId(goals);
|
|
2414
2394
|
if (activeId === null) {
|
|
2415
|
-
console.log(
|
|
2395
|
+
console.log(chalk6.green(" \u{1F389} \uBAA8\uB4E0 goal \uC774 \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4!"));
|
|
2416
2396
|
return;
|
|
2417
2397
|
}
|
|
2418
2398
|
const active = goals.find((g) => g.frontmatter.id === activeId);
|
|
@@ -2434,7 +2414,7 @@ ${ko.goal.nextTitle}
|
|
|
2434
2414
|
mkdirSync2(STATE_DIR2, { recursive: true });
|
|
2435
2415
|
writeFileSync2(join4(STATE_DIR2, "next-task.md"), text, "utf-8");
|
|
2436
2416
|
console.log(
|
|
2437
|
-
|
|
2417
|
+
chalk6.green(
|
|
2438
2418
|
` \u2705 next-task.md \uAC31\uC2E0 \u2014 Goal ${activeId}: ${active.frontmatter.title ?? ""}`
|
|
2439
2419
|
)
|
|
2440
2420
|
);
|
|
@@ -2493,7 +2473,7 @@ var STATE_NEXT_TASK_TEMPLATE = "# Next Task\n\n```\nTASK: (vhk goal next \uB85C
|
|
|
2493
2473
|
var STATE_BLOCKERS_TEMPLATE = "# Blockers\n\n_Append-only. \uD574\uACB0 \uD56D\uBAA9\uC740 ~~\uCDE8\uC18C\uC120~~\uC73C\uB85C \uD45C\uAE30._\n";
|
|
2494
2474
|
var STATE_LEARNINGS_TEMPLATE = "# Learnings\n\n_Append-only. \uD55C \uC904 = \uD55C \uAD50\uD6C8._\n";
|
|
2495
2475
|
async function goalInit() {
|
|
2496
|
-
console.log(
|
|
2476
|
+
console.log(chalk6.bold(`
|
|
2497
2477
|
${ko.goal.initTitle}
|
|
2498
2478
|
`));
|
|
2499
2479
|
const targets = [
|
|
@@ -2508,15 +2488,15 @@ ${ko.goal.initTitle}
|
|
|
2508
2488
|
let skipped = 0;
|
|
2509
2489
|
for (const t2 of targets) {
|
|
2510
2490
|
if (existsSync3(t2.path)) {
|
|
2511
|
-
console.log(
|
|
2491
|
+
console.log(chalk6.gray(` \u2298 skip (\uC774\uBBF8 \uC874\uC7AC): ${t2.path}`));
|
|
2512
2492
|
skipped++;
|
|
2513
2493
|
} else {
|
|
2514
2494
|
writeFileSync2(t2.path, t2.content, "utf-8");
|
|
2515
|
-
console.log(
|
|
2495
|
+
console.log(chalk6.green(` \u2713 created: ${t2.path}`));
|
|
2516
2496
|
created++;
|
|
2517
2497
|
}
|
|
2518
2498
|
}
|
|
2519
|
-
console.log(
|
|
2499
|
+
console.log(chalk6.bold(`
|
|
2520
2500
|
\u{1F4CA} created=${created} skipped=${skipped}`));
|
|
2521
2501
|
if (created > 0) {
|
|
2522
2502
|
printNextStep({
|
|
@@ -2542,76 +2522,76 @@ function runGate(scriptPath) {
|
|
|
2542
2522
|
function warnIfBashOnWindows(scriptPath) {
|
|
2543
2523
|
if (process.platform === "win32" && scriptPath.endsWith(".sh")) {
|
|
2544
2524
|
console.log(
|
|
2545
|
-
|
|
2525
|
+
chalk6.yellow(
|
|
2546
2526
|
" \u26A0 Windows: .sh \uAC8C\uC774\uD2B8\uB294 bash \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. cross-platform .mjs \uB85C \uBC31\uD544\uD558\uC138\uC694 \u2192 vhk goal sync"
|
|
2547
2527
|
)
|
|
2548
2528
|
);
|
|
2549
2529
|
}
|
|
2550
2530
|
}
|
|
2551
2531
|
async function goalCheck(opts) {
|
|
2552
|
-
console.log(
|
|
2532
|
+
console.log(chalk6.bold(`
|
|
2553
2533
|
${ko.goal.checkTitle}
|
|
2554
2534
|
`));
|
|
2555
2535
|
const goals = listGoals(GOALS_DIR);
|
|
2556
2536
|
const id = resolveGoalId(opts.id, goals);
|
|
2557
2537
|
if (id === null) {
|
|
2558
2538
|
console.log(
|
|
2559
|
-
|
|
2539
|
+
chalk6.yellow(" \u26A0 \uB300\uC0C1 goal \uC744 \uACB0\uC815\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (--id \uBA85\uC2DC \uB610\uB294 active goal \uD544\uC694).")
|
|
2560
2540
|
);
|
|
2561
2541
|
process.exitCode = 1;
|
|
2562
2542
|
return;
|
|
2563
2543
|
}
|
|
2564
2544
|
if (!goals.some((g) => g.frontmatter.id === id)) {
|
|
2565
|
-
console.log(
|
|
2545
|
+
console.log(chalk6.red(` \u274C ${ko.goal.notFound(id)}`));
|
|
2566
2546
|
process.exitCode = 1;
|
|
2567
2547
|
return;
|
|
2568
2548
|
}
|
|
2569
2549
|
const scriptPath = findGateScript(id);
|
|
2570
2550
|
if (!scriptPath) {
|
|
2571
2551
|
console.log(
|
|
2572
|
-
|
|
2552
|
+
chalk6.red(` \u274C \uAC8C\uC774\uD2B8 \uC2A4\uD06C\uB9BD\uD2B8 \uC5C6\uC74C: scripts/check-goal-${id}.{mjs,sh}`)
|
|
2573
2553
|
);
|
|
2574
2554
|
process.exitCode = 1;
|
|
2575
2555
|
return;
|
|
2576
2556
|
}
|
|
2577
2557
|
warnIfBashOnWindows(scriptPath);
|
|
2578
2558
|
const gate2 = runGate(scriptPath);
|
|
2579
|
-
console.log(
|
|
2559
|
+
console.log(chalk6.dim(` \u25B6 ${gate2.runner} ${scriptPath}
|
|
2580
2560
|
`));
|
|
2581
2561
|
if (gate2.out) console.log(gate2.out);
|
|
2582
2562
|
if (gate2.ok) {
|
|
2583
|
-
console.log(
|
|
2563
|
+
console.log(chalk6.green(`
|
|
2584
2564
|
\u2705 Goal ${id} \uAC8C\uC774\uD2B8 \uD1B5\uACFC`));
|
|
2585
2565
|
} else {
|
|
2586
|
-
console.log(
|
|
2566
|
+
console.log(chalk6.red(`
|
|
2587
2567
|
\u274C Goal ${id} \uAC8C\uC774\uD2B8 \uC2E4\uD328`));
|
|
2588
|
-
if (gate2.err && !gate2.out) console.log(
|
|
2568
|
+
if (gate2.err && !gate2.out) console.log(chalk6.dim(gate2.err.slice(0, 500)));
|
|
2589
2569
|
process.exitCode = 1;
|
|
2590
2570
|
}
|
|
2591
2571
|
}
|
|
2592
2572
|
async function goalDone(opts) {
|
|
2593
|
-
console.log(
|
|
2573
|
+
console.log(chalk6.bold(`
|
|
2594
2574
|
${ko.goal.doneTitle}
|
|
2595
2575
|
`));
|
|
2596
2576
|
const goals = listGoals(GOALS_DIR);
|
|
2597
2577
|
const id = resolveGoalId(opts.id, goals);
|
|
2598
2578
|
if (id === null) {
|
|
2599
2579
|
console.log(
|
|
2600
|
-
|
|
2580
|
+
chalk6.yellow(" \u26A0 \uB300\uC0C1 goal \uC744 \uACB0\uC815\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (--id \uBA85\uC2DC \uB610\uB294 active goal \uD544\uC694).")
|
|
2601
2581
|
);
|
|
2602
2582
|
process.exitCode = 1;
|
|
2603
2583
|
return;
|
|
2604
2584
|
}
|
|
2605
2585
|
const target = goals.find((g) => g.frontmatter.id === id);
|
|
2606
2586
|
if (!target) {
|
|
2607
|
-
console.log(
|
|
2587
|
+
console.log(chalk6.red(` \u274C ${ko.goal.notFound(id)}`));
|
|
2608
2588
|
process.exitCode = 1;
|
|
2609
2589
|
return;
|
|
2610
2590
|
}
|
|
2611
2591
|
const scriptPath = findGateScript(id);
|
|
2612
2592
|
if (!scriptPath) {
|
|
2613
2593
|
console.log(
|
|
2614
|
-
|
|
2594
|
+
chalk6.red(
|
|
2615
2595
|
` \u274C \uAC8C\uC774\uD2B8 \uC2A4\uD06C\uB9BD\uD2B8 \uC5C6\uC74C \u2014 done \uCC98\uB9AC \uAC70\uBD80: scripts/check-goal-${id}.{mjs,sh}`
|
|
2616
2596
|
)
|
|
2617
2597
|
);
|
|
@@ -2620,12 +2600,12 @@ ${ko.goal.doneTitle}
|
|
|
2620
2600
|
}
|
|
2621
2601
|
warnIfBashOnWindows(scriptPath);
|
|
2622
2602
|
const gate2 = runGate(scriptPath);
|
|
2623
|
-
console.log(
|
|
2603
|
+
console.log(chalk6.dim(` \u25B6 \uAC8C\uC774\uD2B8 \uAC80\uC99D: ${gate2.runner} ${scriptPath}
|
|
2624
2604
|
`));
|
|
2625
2605
|
if (gate2.out) console.log(gate2.out);
|
|
2626
2606
|
if (!gate2.ok) {
|
|
2627
2607
|
console.log(
|
|
2628
|
-
|
|
2608
|
+
chalk6.red(
|
|
2629
2609
|
`
|
|
2630
2610
|
\u274C \uAC8C\uC774\uD2B8 \uC2E4\uD328 \u2014 frontmatter \uBCC0\uACBD \uC5C6\uC774 \uC885\uB8CC. (Forbidden: \uC2E4\uD328 = \uBCF4\uC874)`
|
|
2631
2611
|
)
|
|
@@ -2637,7 +2617,7 @@ ${ko.goal.doneTitle}
|
|
|
2637
2617
|
const today = localDate();
|
|
2638
2618
|
const updated = updateFrontmatterStatus(content, "DONE", { completed: today });
|
|
2639
2619
|
writeFileSync2(target.filePath, updated, "utf-8");
|
|
2640
|
-
console.log(
|
|
2620
|
+
console.log(chalk6.green(`
|
|
2641
2621
|
\u2705 Goal ${id} \u2192 DONE (completed: ${today})`));
|
|
2642
2622
|
printNextStep({
|
|
2643
2623
|
message: `Goal ${id} \uC644\uB8CC! \uB2E4\uC74C goal \uB85C:`,
|
|
@@ -2710,14 +2690,14 @@ function generateGateScript(id) {
|
|
|
2710
2690
|
].join("\n");
|
|
2711
2691
|
}
|
|
2712
2692
|
async function goalSync() {
|
|
2713
|
-
console.log(
|
|
2693
|
+
console.log(chalk6.bold(`
|
|
2714
2694
|
${ko.goal.syncTitle}
|
|
2715
2695
|
`));
|
|
2716
2696
|
const goals = listGoals(GOALS_DIR);
|
|
2717
2697
|
const result = { created: [], skipped: [] };
|
|
2718
2698
|
if (goals.length === 0) {
|
|
2719
2699
|
console.log(
|
|
2720
|
-
|
|
2700
|
+
chalk6.yellow(" \u{1F4ED} goals/ \uC5D0 goal \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. vhk goal init \uC73C\uB85C \uC2DC\uC791\uD558\uC138\uC694.")
|
|
2721
2701
|
);
|
|
2722
2702
|
return result;
|
|
2723
2703
|
}
|
|
@@ -2727,19 +2707,19 @@ ${ko.goal.syncTitle}
|
|
|
2727
2707
|
if (typeof id !== "number") continue;
|
|
2728
2708
|
const target = join4(SCRIPTS_DIR, `check-goal-${id}.mjs`);
|
|
2729
2709
|
if (existsSync3(target)) {
|
|
2730
|
-
console.log(
|
|
2710
|
+
console.log(chalk6.gray(` \u2298 skip (\uC774\uBBF8 \uC874\uC7AC): ${target}`));
|
|
2731
2711
|
result.skipped.push(id);
|
|
2732
2712
|
continue;
|
|
2733
2713
|
}
|
|
2734
2714
|
const shOnly = existsSync3(join4(SCRIPTS_DIR, `check-goal-${id}.sh`));
|
|
2735
2715
|
writeFileSync2(target, generateGateScript(id), "utf-8");
|
|
2736
2716
|
console.log(
|
|
2737
|
-
|
|
2717
|
+
chalk6.green(` \u2713 created: ${target}${shOnly ? " (.sh \u2192 .mjs \uBC31\uD544, Windows 1\uAE09)" : ""}`)
|
|
2738
2718
|
);
|
|
2739
2719
|
result.created.push(id);
|
|
2740
2720
|
}
|
|
2741
2721
|
console.log(
|
|
2742
|
-
|
|
2722
|
+
chalk6.bold(`
|
|
2743
2723
|
\u{1F4CA} created=${result.created.length} skipped=${result.skipped.length}`)
|
|
2744
2724
|
);
|
|
2745
2725
|
if (result.created.length > 0) {
|
|
@@ -2760,22 +2740,22 @@ async function check(opts = {}) {
|
|
|
2760
2740
|
return checkRules();
|
|
2761
2741
|
}
|
|
2762
2742
|
async function checkRules() {
|
|
2763
|
-
console.log(
|
|
2743
|
+
console.log(chalk7.bold(`
|
|
2764
2744
|
${ko.check.title}
|
|
2765
2745
|
`));
|
|
2766
2746
|
const cwd = process.cwd();
|
|
2767
2747
|
const rulesPath = path7.join(cwd, "RULES.md");
|
|
2768
2748
|
if (!fs6.existsSync(rulesPath)) {
|
|
2769
|
-
console.log(
|
|
2770
|
-
console.log(
|
|
2749
|
+
console.log(chalk7.yellow(ko.check.noRules));
|
|
2750
|
+
console.log(chalk7.dim(" vhk init\uC73C\uB85C \uC2DC\uC791\uD558\uAC70\uB098 RULES.md\uB97C \uB9CC\uB4E4\uC5B4 \uBCF4\uC138\uC694."));
|
|
2771
2751
|
return;
|
|
2772
2752
|
}
|
|
2773
2753
|
const rules = parseRules(rulesPath);
|
|
2774
|
-
console.log(
|
|
2754
|
+
console.log(chalk7.dim(` \u{1F4CF} \uC790\uB3D9 \uAC80\uC99D \uAC00\uB2A5\uD55C \uADDC\uCE59 ${rules.length}\uAC1C \uAC10\uC9C0 (\uB098\uBA38\uC9C0 \uADDC\uCE59\uC740 \uC218\uB3D9/\uB3C4\uAD6C \uD655\uC778)
|
|
2775
2755
|
`));
|
|
2776
2756
|
if (rules.length === 0) {
|
|
2777
|
-
console.log(
|
|
2778
|
-
console.log(
|
|
2757
|
+
console.log(chalk7.yellow(ko.check.noAutoRules));
|
|
2758
|
+
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."));
|
|
2779
2759
|
return;
|
|
2780
2760
|
}
|
|
2781
2761
|
const allViolations = [];
|
|
@@ -2783,14 +2763,14 @@ ${ko.check.title}
|
|
|
2783
2763
|
for (const rule of rules) {
|
|
2784
2764
|
const violations = rule.check(cwd);
|
|
2785
2765
|
if (violations.length === 0) {
|
|
2786
|
-
const patternHint = rule.type === "content" && rule.pattern ?
|
|
2787
|
-
console.log(
|
|
2766
|
+
const patternHint = rule.type === "content" && rule.pattern ? chalk7.dim(` [\uAC80\uC0AC: ${rule.pattern.source}]`) : "";
|
|
2767
|
+
console.log(chalk7.green(` \u2705 ${rule.id}`) + chalk7.dim(` \u2014 ${rule.description.slice(0, 60)}`) + patternHint);
|
|
2788
2768
|
passCount++;
|
|
2789
2769
|
} else {
|
|
2790
|
-
console.log(
|
|
2770
|
+
console.log(chalk7.red(` \u274C ${rule.id}`) + chalk7.dim(` \u2014 ${violations.length}\uAC74 \uC704\uBC18`));
|
|
2791
2771
|
violations.forEach((v) => {
|
|
2792
|
-
const loc = v.file ?
|
|
2793
|
-
const icon = v.severity === "error" ?
|
|
2772
|
+
const loc = v.file ? chalk7.dim(` (${v.file}${v.line ? ":" + v.line : ""})`) : "";
|
|
2773
|
+
const icon = v.severity === "error" ? chalk7.red("\u2716") : v.severity === "warning" ? chalk7.yellow("\u26A0") : chalk7.blue("\u2139");
|
|
2794
2774
|
console.log(` ${icon} ${v.message}${loc}`);
|
|
2795
2775
|
});
|
|
2796
2776
|
allViolations.push(...violations);
|
|
@@ -2800,18 +2780,18 @@ ${ko.check.title}
|
|
|
2800
2780
|
const errors = allViolations.filter((v) => v.severity === "error").length;
|
|
2801
2781
|
const warnings = allViolations.filter((v) => v.severity === "warning").length;
|
|
2802
2782
|
if (allViolations.length === 0) {
|
|
2803
|
-
console.log(
|
|
2804
|
-
console.log(
|
|
2783
|
+
console.log(chalk7.green.bold(`\u2705 \uC790\uB3D9 \uAC80\uC99D \uAC00\uB2A5\uD55C \uADDC\uCE59 ${passCount}\uAC1C \uD1B5\uACFC`));
|
|
2784
|
+
console.log(chalk7.dim(" (RULES.md \uC758 \uB098\uBA38\uC9C0 \uADDC\uCE59\uC740 \uCF54\uB4DC \uC790\uB3D9 \uAC80\uC0AC \uBD88\uAC00 \u2014 \uC9C1\uC811/\uB3C4\uAD6C\uB85C \uD655\uC778\uD558\uC138\uC694.)"));
|
|
2805
2785
|
printNextStep({
|
|
2806
2786
|
message: "\uBAA8\uB4E0 \uADDC\uCE59 \uD1B5\uACFC! \uBCF4\uC548 \uC2A4\uCE94\uB3C4 \uD574\uBCFC\uAE4C\uC694?",
|
|
2807
2787
|
command: "vhk \uBCF4\uC548 scan",
|
|
2808
2788
|
cursorHint: "\uBCF4\uC548 \uC2A4\uCE94 \uB3CC\uB824\uC918"
|
|
2809
2789
|
});
|
|
2810
2790
|
} else {
|
|
2811
|
-
console.log(
|
|
2812
|
-
console.log(` \uADDC\uCE59: ${
|
|
2813
|
-
if (errors > 0) console.log(` ${
|
|
2814
|
-
if (warnings > 0) console.log(` ${
|
|
2791
|
+
console.log(chalk7.bold(ko.check.summary));
|
|
2792
|
+
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`);
|
|
2793
|
+
if (errors > 0) console.log(` ${chalk7.red(`\u2716 ${errors}\uAC1C \uC5D0\uB7EC`)}`);
|
|
2794
|
+
if (warnings > 0) console.log(` ${chalk7.yellow(`\u26A0 ${warnings}\uAC1C \uACBD\uACE0`)}`);
|
|
2815
2795
|
printNextStep({
|
|
2816
2796
|
message: "\uC704\uBC18 \uD56D\uBAA9\uC744 \uC218\uC815\uD55C \uD6C4 \uB2E4\uC2DC \uC810\uAC80\uD558\uC138\uC694.",
|
|
2817
2797
|
command: "vhk \uC810\uAC80",
|
|
@@ -2824,36 +2804,36 @@ ${ko.check.title}
|
|
|
2824
2804
|
}
|
|
2825
2805
|
|
|
2826
2806
|
// src/commands/secure.ts
|
|
2827
|
-
import
|
|
2807
|
+
import chalk8 from "chalk";
|
|
2828
2808
|
import fs7 from "fs";
|
|
2829
2809
|
import path8 from "path";
|
|
2830
2810
|
async function secure() {
|
|
2831
|
-
console.log(
|
|
2811
|
+
console.log(chalk8.bold(`
|
|
2832
2812
|
${ko.secure.title}
|
|
2833
2813
|
`));
|
|
2834
2814
|
const cwd = process.cwd();
|
|
2835
2815
|
const gitignorePath = path8.join(cwd, ".gitignore");
|
|
2836
2816
|
const hasGitignore = fs7.existsSync(gitignorePath);
|
|
2837
2817
|
if (!hasGitignore) {
|
|
2838
|
-
console.log(
|
|
2839
|
-
console.log(
|
|
2818
|
+
console.log(chalk8.yellow(` ${ko.secure.noGitignore}`));
|
|
2819
|
+
console.log(chalk8.dim(" .env \uD30C\uC77C\uC774 \uCEE4\uBC0B\uB420 \uC218 \uC788\uC2B5\uB2C8\uB2E4.\n"));
|
|
2840
2820
|
} else {
|
|
2841
2821
|
const gitignoreContent = fs7.readFileSync(gitignorePath, "utf-8");
|
|
2842
2822
|
if (!gitignoreContent.includes(".env")) {
|
|
2843
|
-
console.log(
|
|
2844
|
-
console.log(
|
|
2823
|
+
console.log(chalk8.yellow(` ${ko.secure.noEnvInGitignore}`));
|
|
2824
|
+
console.log(chalk8.dim(" \uCD94\uAC00\uB97C \uAD8C\uC7A5\uD569\uB2C8\uB2E4.\n"));
|
|
2845
2825
|
}
|
|
2846
2826
|
}
|
|
2847
|
-
console.log(
|
|
2827
|
+
console.log(chalk8.dim(` ${ko.secure.scanning}
|
|
2848
2828
|
`));
|
|
2849
2829
|
const { findings, scannedFiles, truncated } = scanProjectForSecrets(cwd);
|
|
2850
|
-
console.log(
|
|
2830
|
+
console.log(chalk8.dim(` \u{1F4C2} ${scannedFiles}\uAC1C \uD30C\uC77C \uC2A4\uCE94 \uC644\uB8CC (lock\xB7node_modules\xB7>${MAX_SCAN_FILE_BYTES / 1024}KB \uC81C\uC678)`));
|
|
2851
2831
|
if (truncated) {
|
|
2852
|
-
console.log(
|
|
2832
|
+
console.log(chalk8.yellow(` \u26A0\uFE0F \uACB0\uACFC ${MAX_SECRET_FINDINGS}\uAC74\uC5D0\uC11C \uCD9C\uB825\uC744 \uC81C\uD55C\uD588\uC2B5\uB2C8\uB2E4. lock \uD30C\uC77C \uB4F1\uC740 \uC790\uB3D9 \uC81C\uC678\uB429\uB2C8\uB2E4.`));
|
|
2853
2833
|
}
|
|
2854
2834
|
console.log("");
|
|
2855
2835
|
if (findings.length === 0) {
|
|
2856
|
-
console.log(
|
|
2836
|
+
console.log(chalk8.green.bold(` ${ko.secure.clean}`));
|
|
2857
2837
|
printNextStep({
|
|
2858
2838
|
message: "\uBCF4\uC548 \uC774\uC0C1 \uC5C6\uC74C! \uAE68\uB057\uD569\uB2C8\uB2E4.",
|
|
2859
2839
|
command: "vhk \uC815\uB9AC",
|
|
@@ -2865,43 +2845,43 @@ ${ko.secure.title}
|
|
|
2865
2845
|
const high = findings.filter((f) => f.severity === "high");
|
|
2866
2846
|
const medium = findings.filter((f) => f.severity === "medium");
|
|
2867
2847
|
if (critical.length > 0) {
|
|
2868
|
-
console.log(
|
|
2848
|
+
console.log(chalk8.red.bold(` \u{1F6A8} CRITICAL \u2014 ${critical.length}\uAC74`));
|
|
2869
2849
|
critical.forEach((f) => {
|
|
2870
|
-
console.log(
|
|
2871
|
-
console.log(
|
|
2850
|
+
console.log(chalk8.red(` \u2716 ${f.patternName}`));
|
|
2851
|
+
console.log(chalk8.dim(` ${f.file}:${f.line} \u2192 ${f.match}`));
|
|
2872
2852
|
});
|
|
2873
2853
|
console.log("");
|
|
2874
2854
|
}
|
|
2875
2855
|
if (high.length > 0) {
|
|
2876
|
-
console.log(
|
|
2856
|
+
console.log(chalk8.yellow.bold(` \u26A0\uFE0F HIGH \u2014 ${high.length}\uAC74`));
|
|
2877
2857
|
high.forEach((f) => {
|
|
2878
|
-
console.log(
|
|
2879
|
-
console.log(
|
|
2858
|
+
console.log(chalk8.yellow(` \u26A0 ${f.patternName}`));
|
|
2859
|
+
console.log(chalk8.dim(` ${f.file}:${f.line} \u2192 ${f.match}`));
|
|
2880
2860
|
});
|
|
2881
2861
|
console.log("");
|
|
2882
2862
|
}
|
|
2883
2863
|
if (medium.length > 0) {
|
|
2884
|
-
console.log(
|
|
2864
|
+
console.log(chalk8.blue.bold(` \u2139 MEDIUM \u2014 ${medium.length}\uAC74`));
|
|
2885
2865
|
medium.forEach((f) => {
|
|
2886
|
-
console.log(
|
|
2887
|
-
console.log(
|
|
2866
|
+
console.log(chalk8.blue(` \u2139 ${f.patternName}`));
|
|
2867
|
+
console.log(chalk8.dim(` ${f.file}:${f.line} \u2192 ${f.match}`));
|
|
2888
2868
|
});
|
|
2889
2869
|
console.log("");
|
|
2890
2870
|
}
|
|
2891
|
-
console.log(
|
|
2892
|
-
console.log(` \uCD1D ${
|
|
2871
|
+
console.log(chalk8.bold(` ${ko.secure.summary}`));
|
|
2872
|
+
console.log(` \uCD1D ${chalk8.red(String(findings.length))}\uAC74 \uAC10\uC9C0 | CRITICAL: ${critical.length} | HIGH: ${high.length} | MEDIUM: ${medium.length}`);
|
|
2893
2873
|
console.log("");
|
|
2894
|
-
console.log(
|
|
2895
|
-
console.log(
|
|
2896
|
-
console.log(
|
|
2897
|
-
console.log(
|
|
2874
|
+
console.log(chalk8.dim(" \u{1F4A1} \uC870\uCE58 \uBC29\uBC95:"));
|
|
2875
|
+
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"));
|
|
2876
|
+
console.log(chalk8.dim(" 2. git history\uC5D0\uC11C\uB3C4 \uC81C\uAC70: git filter-branch \uB610\uB294 BFG Repo-Cleaner"));
|
|
2877
|
+
console.log(chalk8.dim(" 3. \uC720\uCD9C\uB41C \uD0A4\uB294 \uC989\uC2DC \uD3D0\uAE30\uD558\uACE0 \uC7AC\uBC1C\uAE09\n"));
|
|
2898
2878
|
if (critical.length > 0 || high.length > 0) {
|
|
2899
2879
|
process.exitCode = 1;
|
|
2900
2880
|
}
|
|
2901
2881
|
}
|
|
2902
2882
|
|
|
2903
2883
|
// src/commands/doctor.ts
|
|
2904
|
-
import
|
|
2884
|
+
import chalk9 from "chalk";
|
|
2905
2885
|
import fs8 from "fs";
|
|
2906
2886
|
import path9 from "path";
|
|
2907
2887
|
import { fileURLToPath } from "url";
|
|
@@ -2947,7 +2927,7 @@ function compareSemver(a, b) {
|
|
|
2947
2927
|
return a3 - b3;
|
|
2948
2928
|
}
|
|
2949
2929
|
async function doctor() {
|
|
2950
|
-
console.log(
|
|
2930
|
+
console.log(chalk9.bold(`
|
|
2951
2931
|
${ko.doctor.title}
|
|
2952
2932
|
`));
|
|
2953
2933
|
const checks = [
|
|
@@ -2959,30 +2939,30 @@ ${ko.doctor.title}
|
|
|
2959
2939
|
let allOk = true;
|
|
2960
2940
|
for (const check2 of checks) {
|
|
2961
2941
|
if (check2.ok) {
|
|
2962
|
-
console.log(
|
|
2942
|
+
console.log(chalk9.green(` \u2705 ${check2.name}`) + chalk9.dim(` \u2014 ${check2.version}`));
|
|
2963
2943
|
} else {
|
|
2964
|
-
console.log(
|
|
2965
|
-
console.log(
|
|
2944
|
+
console.log(chalk9.red(` \u274C ${check2.name} \uC5C6\uC74C`));
|
|
2945
|
+
console.log(chalk9.dim(` \u2192 ${check2.hint}`));
|
|
2966
2946
|
allOk = false;
|
|
2967
2947
|
}
|
|
2968
2948
|
}
|
|
2969
2949
|
console.log("");
|
|
2970
2950
|
const vhkVersion = getVhkVersion2();
|
|
2971
2951
|
if (vhkVersion) {
|
|
2972
|
-
console.log(
|
|
2952
|
+
console.log(chalk9.green(" \u2705 VHK") + chalk9.dim(` \u2014 v${vhkVersion}`));
|
|
2973
2953
|
} else {
|
|
2974
|
-
console.log(
|
|
2954
|
+
console.log(chalk9.green(" \u2705 VHK") + chalk9.dim(" \u2014 \uC124\uCE58\uB428"));
|
|
2975
2955
|
}
|
|
2976
2956
|
if (vhkVersion) {
|
|
2977
2957
|
const latest = fetchLatestNpmVersion("@byh3071/vhk");
|
|
2978
2958
|
if (latest && compareSemver(latest, vhkVersion) > 0) {
|
|
2979
|
-
console.log(
|
|
2959
|
+
console.log(chalk9.yellow(` ${ko.doctor.updateAvailable(latest)}`));
|
|
2980
2960
|
} else if (latest) {
|
|
2981
|
-
console.log(
|
|
2961
|
+
console.log(chalk9.dim(` ${ko.doctor.updateCurrent}`));
|
|
2982
2962
|
}
|
|
2983
2963
|
}
|
|
2984
2964
|
console.log("");
|
|
2985
|
-
console.log(
|
|
2965
|
+
console.log(chalk9.bold(` ${ko.doctor.projectFiles}`));
|
|
2986
2966
|
const cwd = process.cwd();
|
|
2987
2967
|
const projectFiles = [
|
|
2988
2968
|
{ name: "RULES.md", hint: "vhk init\uC73C\uB85C \uC0DD\uC131 \uAC00\uB2A5" },
|
|
@@ -2994,49 +2974,49 @@ ${ko.doctor.title}
|
|
|
2994
2974
|
for (const file of projectFiles) {
|
|
2995
2975
|
const exists = fs8.existsSync(path9.join(cwd, file.name));
|
|
2996
2976
|
if (exists) {
|
|
2997
|
-
console.log(
|
|
2977
|
+
console.log(chalk9.green(` \u2705 ${file.name}`));
|
|
2998
2978
|
if (file.name === ".env") {
|
|
2999
2979
|
const gitignorePath = path9.join(cwd, ".gitignore");
|
|
3000
2980
|
if (fs8.existsSync(gitignorePath)) {
|
|
3001
2981
|
const gitignore = fs8.readFileSync(gitignorePath, "utf-8");
|
|
3002
2982
|
if (!gitignore.includes(".env")) {
|
|
3003
|
-
console.log(
|
|
2983
|
+
console.log(chalk9.yellow(` ${ko.doctor.envNotIgnored}`));
|
|
3004
2984
|
}
|
|
3005
2985
|
}
|
|
3006
2986
|
}
|
|
3007
2987
|
} else if (file.name === ".env" && fs8.existsSync(path9.join(cwd, ".env.local"))) {
|
|
3008
|
-
console.log(
|
|
2988
|
+
console.log(chalk9.green(" \u2705 .env.local") + chalk9.dim(" \u2014 \uB85C\uCEEC env \uC0AC\uC6A9 \uC911 (.env \uC5C6\uC5B4\uB3C4 \uC815\uC0C1)"));
|
|
3009
2989
|
} else {
|
|
3010
|
-
console.log(
|
|
2990
|
+
console.log(chalk9.dim(` \u2B1A ${file.name}`) + chalk9.dim(` \u2014 ${file.hint}`));
|
|
3011
2991
|
}
|
|
3012
2992
|
}
|
|
3013
2993
|
console.log("");
|
|
3014
|
-
console.log(
|
|
2994
|
+
console.log(chalk9.bold(` ${ko.doctor.driftTitle}`));
|
|
3015
2995
|
const ruleDrift = checkRuleDrift(cwd);
|
|
3016
2996
|
if (!ruleDrift.checked) {
|
|
3017
|
-
console.log(
|
|
2997
|
+
console.log(chalk9.dim(` ${ko.doctor.driftNoRules}`));
|
|
3018
2998
|
} else {
|
|
3019
2999
|
const drifted = ruleDrift.results.filter((r) => r.status === "drifted");
|
|
3020
3000
|
if (drifted.length === 0) {
|
|
3021
|
-
console.log(
|
|
3001
|
+
console.log(chalk9.green(` ${ko.doctor.driftRuleClean}`));
|
|
3022
3002
|
} else {
|
|
3023
|
-
console.log(
|
|
3003
|
+
console.log(chalk9.yellow(` ${ko.doctor.driftRuleWarn(drifted.map((d) => d.path).join(", "))}`));
|
|
3024
3004
|
}
|
|
3025
3005
|
}
|
|
3026
3006
|
const ctxDrift = checkContextDrift(cwd);
|
|
3027
3007
|
if (ctxDrift.checked && ctxDrift.stale) {
|
|
3028
|
-
console.log(
|
|
3008
|
+
console.log(chalk9.yellow(` ${ko.doctor.driftContextWarn}`));
|
|
3029
3009
|
}
|
|
3030
3010
|
console.log("");
|
|
3031
3011
|
if (allOk) {
|
|
3032
|
-
console.log(
|
|
3012
|
+
console.log(chalk9.green.bold(` ${ko.doctor.allOk}`));
|
|
3033
3013
|
printNextStep({
|
|
3034
3014
|
message: ko.doctor.nextOkMessage,
|
|
3035
3015
|
command: "vhk \uC2DC\uC791",
|
|
3036
3016
|
cursorHint: "\uD504\uB85C\uC81D\uD2B8 \uB9CC\uB4E4\uC5B4\uC918"
|
|
3037
3017
|
});
|
|
3038
3018
|
} else {
|
|
3039
|
-
console.log(
|
|
3019
|
+
console.log(chalk9.yellow.bold(` ${ko.doctor.missing} ${ko.doctor.missingHint}`));
|
|
3040
3020
|
printNextStep({
|
|
3041
3021
|
message: ko.doctor.nextRetryMessage,
|
|
3042
3022
|
command: "vhk doctor",
|
|
@@ -3047,7 +3027,7 @@ ${ko.doctor.title}
|
|
|
3047
3027
|
}
|
|
3048
3028
|
|
|
3049
3029
|
// src/commands/ship.ts
|
|
3050
|
-
import
|
|
3030
|
+
import chalk10 from "chalk";
|
|
3051
3031
|
import inquirer4 from "inquirer";
|
|
3052
3032
|
import fs9 from "fs";
|
|
3053
3033
|
import path10 from "path";
|
|
@@ -3087,29 +3067,30 @@ function updateChangelogUnreleased(cwd, version, date) {
|
|
|
3087
3067
|
}
|
|
3088
3068
|
async function ship() {
|
|
3089
3069
|
if (!ensureNotHardStopped("ship")) return;
|
|
3090
|
-
|
|
3070
|
+
if (!ensureInteractive("\uBC30\uD3EC \uCCB4\uD06C\uB9AC\uC2A4\uD2B8\xB7\uD68C\uACE0\uB294 \uB300\uD654\uD615 \uC785\uB825\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. PowerShell \uB4F1 TTY \uC5D0\uC11C \uC2E4\uD589\uD558\uC138\uC694.")) return;
|
|
3071
|
+
console.log(chalk10.bold(`
|
|
3091
3072
|
${ko.ship.title}
|
|
3092
3073
|
`));
|
|
3093
3074
|
const cwd = process.cwd();
|
|
3094
|
-
console.log(
|
|
3075
|
+
console.log(chalk10.cyan.bold(` ${ko.ship.checklist}
|
|
3095
3076
|
`));
|
|
3096
3077
|
const { passed } = await inquirer4.prompt([{
|
|
3097
3078
|
type: "checkbox",
|
|
3098
3079
|
name: "passed",
|
|
3099
3080
|
message: ko.ship.checkboxPrompt,
|
|
3100
3081
|
choices: CHECKLIST.map((c) => ({
|
|
3101
|
-
name: `${ko.ship[c.questionKey]} ${
|
|
3082
|
+
name: `${ko.ship[c.questionKey]} ${chalk10.dim(`(${ko.ship[c.hintKey]})`)}`,
|
|
3102
3083
|
value: c.id
|
|
3103
3084
|
}))
|
|
3104
3085
|
}]);
|
|
3105
3086
|
const allPassed = passed.length === CHECKLIST.length;
|
|
3106
3087
|
const skipped = CHECKLIST.filter((c) => !passed.includes(c.id));
|
|
3107
3088
|
if (!allPassed) {
|
|
3108
|
-
console.log(
|
|
3089
|
+
console.log(chalk10.yellow(`
|
|
3109
3090
|
${ko.ship.incompleteHeader}`));
|
|
3110
3091
|
skipped.forEach((s) => {
|
|
3111
|
-
console.log(
|
|
3112
|
-
console.log(
|
|
3092
|
+
console.log(chalk10.yellow(` \u2022 ${ko.ship[s.questionKey]}`));
|
|
3093
|
+
console.log(chalk10.dim(` \u2192 ${ko.ship[s.hintKey]}`));
|
|
3113
3094
|
});
|
|
3114
3095
|
const { proceed } = await inquirer4.prompt([{
|
|
3115
3096
|
type: "confirm",
|
|
@@ -3126,13 +3107,13 @@ ${ko.ship.title}
|
|
|
3126
3107
|
return;
|
|
3127
3108
|
}
|
|
3128
3109
|
} else {
|
|
3129
|
-
console.log(
|
|
3110
|
+
console.log(chalk10.green(`
|
|
3130
3111
|
${ko.ship.allPassed}
|
|
3131
3112
|
`));
|
|
3132
3113
|
}
|
|
3133
|
-
console.log(
|
|
3114
|
+
console.log(chalk10.cyan.bold(` ${ko.ship.retro}
|
|
3134
3115
|
`));
|
|
3135
|
-
console.log(
|
|
3116
|
+
console.log(chalk10.dim(` ${ko.ship.versionHint}`));
|
|
3136
3117
|
const retro = await inquirer4.prompt([
|
|
3137
3118
|
{ type: "input", name: "version", message: ko.ship.versionPrompt },
|
|
3138
3119
|
{ type: "input", name: "whatWentWell", message: ko.ship.questionWell },
|
|
@@ -3175,7 +3156,7 @@ ${ko.ship.title}
|
|
|
3175
3156
|
`*Generated by \`vhk ship\` at ${(/* @__PURE__ */ new Date()).toISOString()}*`
|
|
3176
3157
|
].join("\n");
|
|
3177
3158
|
fs9.writeFileSync(filePath, content, "utf-8");
|
|
3178
|
-
console.log(
|
|
3159
|
+
console.log(chalk10.green(`
|
|
3179
3160
|
${ko.ship.buildLogDone(path10.relative(cwd, filePath))}`));
|
|
3180
3161
|
const changelogResult = updateChangelogUnreleased(cwd, versionSlug, today);
|
|
3181
3162
|
if (changelogResult.status === "updated") {
|
|
@@ -3195,7 +3176,7 @@ ${ko.ship.title}
|
|
|
3195
3176
|
|
|
3196
3177
|
// src/commands/save.ts
|
|
3197
3178
|
import { execFileSync } from "child_process";
|
|
3198
|
-
import
|
|
3179
|
+
import chalk11 from "chalk";
|
|
3199
3180
|
import ora from "ora";
|
|
3200
3181
|
import inquirer5 from "inquirer";
|
|
3201
3182
|
|
|
@@ -3223,29 +3204,29 @@ function statusIcon(code) {
|
|
|
3223
3204
|
return "\u{1F4C4}";
|
|
3224
3205
|
}
|
|
3225
3206
|
async function save() {
|
|
3226
|
-
console.log(
|
|
3207
|
+
console.log(chalk11.bold(`
|
|
3227
3208
|
\u{1F4BE} ${t("save.title")}`));
|
|
3228
|
-
console.log(
|
|
3209
|
+
console.log(chalk11.gray("\u2500".repeat(40)));
|
|
3229
3210
|
let gitRoot;
|
|
3230
3211
|
try {
|
|
3231
3212
|
execFileSync("git", ["rev-parse", "--is-inside-work-tree"], { stdio: "pipe" });
|
|
3232
3213
|
gitRoot = getGitRoot();
|
|
3233
3214
|
} catch {
|
|
3234
|
-
console.log(
|
|
3215
|
+
console.log(chalk11.red(`\u274C ${t("save.notGitRepo")}`));
|
|
3235
3216
|
return;
|
|
3236
3217
|
}
|
|
3237
|
-
console.log(
|
|
3218
|
+
console.log(chalk11.cyan(`
|
|
3238
3219
|
\u{1F512} ${t("save.securityWarnHeader")}`));
|
|
3239
3220
|
printSecurityWarnings(gitRoot);
|
|
3240
3221
|
const severe = filterSevereFindings(scanProjectForSecrets(gitRoot).findings);
|
|
3241
3222
|
if (severe.length > 0) {
|
|
3242
|
-
console.log(
|
|
3223
|
+
console.log(chalk11.red(`
|
|
3243
3224
|
\u26A0\uFE0F ${t("save.secretsFound", severe.length)}`));
|
|
3244
3225
|
severe.slice(0, 5).forEach((f) => {
|
|
3245
|
-
console.log(
|
|
3226
|
+
console.log(chalk11.dim(` ${f.file}:${f.line} \u2014 ${f.patternName}`));
|
|
3246
3227
|
});
|
|
3247
3228
|
if (severe.length > 5) {
|
|
3248
|
-
console.log(
|
|
3229
|
+
console.log(chalk11.dim(` ... \uC678 ${severe.length - 5}\uAC74 (vhk \uBCF4\uC548 scan)`));
|
|
3249
3230
|
}
|
|
3250
3231
|
const proceed = await promptOrDefault(
|
|
3251
3232
|
async () => (await inquirer5.prompt([{
|
|
@@ -3258,16 +3239,16 @@ async function save() {
|
|
|
3258
3239
|
// 비대화형 = 시크릿 커밋 안 함 (안전)
|
|
3259
3240
|
);
|
|
3260
3241
|
if (!proceed) {
|
|
3261
|
-
console.log(
|
|
3242
|
+
console.log(chalk11.gray(t("save.cancelled")));
|
|
3262
3243
|
return;
|
|
3263
3244
|
}
|
|
3264
3245
|
}
|
|
3265
3246
|
const lines = parsePorcelainLines(gitOut(["status", "--porcelain"], gitRoot));
|
|
3266
3247
|
if (lines.length === 0) {
|
|
3267
|
-
console.log(
|
|
3248
|
+
console.log(chalk11.yellow(`\u{1F4ED} ${t("save.noChanges")}`));
|
|
3268
3249
|
return;
|
|
3269
3250
|
}
|
|
3270
|
-
console.log(
|
|
3251
|
+
console.log(chalk11.cyan(`
|
|
3271
3252
|
\u{1F4CB} ${t("save.filesHeader", lines.length)}`));
|
|
3272
3253
|
lines.forEach((line) => {
|
|
3273
3254
|
const code = line.substring(0, 2);
|
|
@@ -3292,21 +3273,21 @@ async function save() {
|
|
|
3292
3273
|
spinner.text = t("save.pushing");
|
|
3293
3274
|
if (!hasGitRemote(gitRoot)) {
|
|
3294
3275
|
spinner.succeed(t("save.successLocal"));
|
|
3295
|
-
console.log(
|
|
3276
|
+
console.log(chalk11.yellow(` \u{1F4A1} ${t("save.noRemote")}`));
|
|
3296
3277
|
} else {
|
|
3297
3278
|
try {
|
|
3298
3279
|
gitRun(["push"], gitRoot);
|
|
3299
3280
|
spinner.succeed(t("save.successWithPush"));
|
|
3300
3281
|
} catch (pushErr) {
|
|
3301
3282
|
spinner.fail(t("save.pushFailed"));
|
|
3302
|
-
console.log(
|
|
3303
|
-
console.log(
|
|
3283
|
+
console.log(chalk11.red(getExecErrorMessage(pushErr)));
|
|
3284
|
+
console.log(chalk11.yellow(`
|
|
3304
3285
|
\u{1F4A1} ${t("save.commitOkPushFailed")}`));
|
|
3305
3286
|
process.exitCode = 1;
|
|
3306
3287
|
}
|
|
3307
3288
|
}
|
|
3308
3289
|
if (process.exitCode !== 1) {
|
|
3309
|
-
console.log(
|
|
3290
|
+
console.log(chalk11.green(`
|
|
3310
3291
|
\u2705 ${t("save.done", lines.length)}`));
|
|
3311
3292
|
printNextStep({
|
|
3312
3293
|
message: t("save.nextOkMessage"),
|
|
@@ -3314,7 +3295,7 @@ async function save() {
|
|
|
3314
3295
|
cursorHint: t("save.nextOkCursor")
|
|
3315
3296
|
});
|
|
3316
3297
|
} else {
|
|
3317
|
-
console.log(
|
|
3298
|
+
console.log(chalk11.green(`
|
|
3318
3299
|
\u2705 ${t("save.doneLocalOnly", lines.length)}`));
|
|
3319
3300
|
printNextStep({
|
|
3320
3301
|
message: t("save.nextPushFailMessage"),
|
|
@@ -3324,12 +3305,12 @@ async function save() {
|
|
|
3324
3305
|
}
|
|
3325
3306
|
} catch (err) {
|
|
3326
3307
|
spinner.fail(t("save.failed"));
|
|
3327
|
-
console.log(
|
|
3308
|
+
console.log(chalk11.red(getExecErrorMessage(err)));
|
|
3328
3309
|
if (didAdd) {
|
|
3329
3310
|
try {
|
|
3330
3311
|
const staged = gitOut(["diff", "--cached", "--stat"], gitRoot).trim();
|
|
3331
3312
|
if (staged) {
|
|
3332
|
-
console.log(
|
|
3313
|
+
console.log(chalk11.yellow(`
|
|
3333
3314
|
\u{1F4A1} ${t("save.stagedAfterFail")}`));
|
|
3334
3315
|
}
|
|
3335
3316
|
} catch {
|
|
@@ -3341,7 +3322,7 @@ async function save() {
|
|
|
3341
3322
|
|
|
3342
3323
|
// src/commands/undo.ts
|
|
3343
3324
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
3344
|
-
import
|
|
3325
|
+
import chalk12 from "chalk";
|
|
3345
3326
|
import inquirer6 from "inquirer";
|
|
3346
3327
|
function parseRecentCommits(logOutput) {
|
|
3347
3328
|
return logOutput.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
@@ -3365,30 +3346,30 @@ function isUndoRisky(undoCount, unpushedCount, hasRemote) {
|
|
|
3365
3346
|
return false;
|
|
3366
3347
|
}
|
|
3367
3348
|
async function undo() {
|
|
3368
|
-
console.log(
|
|
3349
|
+
console.log(chalk12.bold(`
|
|
3369
3350
|
\u23EA ${t("undo.title")}`));
|
|
3370
|
-
console.log(
|
|
3351
|
+
console.log(chalk12.gray("\u2500".repeat(40)));
|
|
3371
3352
|
let gitRoot;
|
|
3372
3353
|
try {
|
|
3373
3354
|
execFileSync2("git", ["rev-parse", "--is-inside-work-tree"], { stdio: "pipe" });
|
|
3374
3355
|
gitRoot = getGitRoot();
|
|
3375
3356
|
} catch {
|
|
3376
|
-
console.log(
|
|
3357
|
+
console.log(chalk12.red(`\u274C ${t("undo.notGitRepo")}`));
|
|
3377
3358
|
return;
|
|
3378
3359
|
}
|
|
3379
3360
|
let logOutput;
|
|
3380
3361
|
try {
|
|
3381
3362
|
logOutput = gitOut(["log", "--oneline", "-5"], gitRoot).trim();
|
|
3382
3363
|
} catch {
|
|
3383
|
-
console.log(
|
|
3364
|
+
console.log(chalk12.yellow(`\u{1F4ED} ${t("undo.noCommits")}`));
|
|
3384
3365
|
return;
|
|
3385
3366
|
}
|
|
3386
3367
|
const commits = parseRecentCommits(logOutput);
|
|
3387
3368
|
if (commits.length === 0) {
|
|
3388
|
-
console.log(
|
|
3369
|
+
console.log(chalk12.yellow(`\u{1F4ED} ${t("undo.noCommits")}`));
|
|
3389
3370
|
return;
|
|
3390
3371
|
}
|
|
3391
|
-
console.log(
|
|
3372
|
+
console.log(chalk12.cyan(`
|
|
3392
3373
|
${t("undo.recentHeader")}`));
|
|
3393
3374
|
commits.forEach((c, i) => {
|
|
3394
3375
|
console.log(` ${i === 0 ? "\u{1F449}" : " "} ${c}`);
|
|
@@ -3405,7 +3386,7 @@ ${t("undo.recentHeader")}`));
|
|
|
3405
3386
|
const undoCount = Math.min(Math.max(1, count || 1), maxUndo);
|
|
3406
3387
|
const headCount = countLocalCommits(gitRoot);
|
|
3407
3388
|
if (undoCount >= headCount) {
|
|
3408
|
-
console.log(
|
|
3389
|
+
console.log(chalk12.yellow(`
|
|
3409
3390
|
\u{1F4ED} ${t("undo.rootCommit")}`));
|
|
3410
3391
|
return;
|
|
3411
3392
|
}
|
|
@@ -3414,10 +3395,10 @@ ${t("undo.recentHeader")}`));
|
|
|
3414
3395
|
const risky = isUndoRisky(undoCount, unpushed, remote);
|
|
3415
3396
|
if (risky) {
|
|
3416
3397
|
if (unpushed < 0) {
|
|
3417
|
-
console.log(
|
|
3398
|
+
console.log(chalk12.red(`
|
|
3418
3399
|
\u26A0\uFE0F ${t("undo.noUpstreamWarning")}`));
|
|
3419
3400
|
} else {
|
|
3420
|
-
console.log(
|
|
3401
|
+
console.log(chalk12.red(`
|
|
3421
3402
|
\u26A0\uFE0F ${t("undo.alreadyPushed")}`));
|
|
3422
3403
|
}
|
|
3423
3404
|
}
|
|
@@ -3428,16 +3409,16 @@ ${t("undo.recentHeader")}`));
|
|
|
3428
3409
|
default: false
|
|
3429
3410
|
}]);
|
|
3430
3411
|
if (!confirm) {
|
|
3431
|
-
console.log(
|
|
3412
|
+
console.log(chalk12.gray(t("undo.cancelled")));
|
|
3432
3413
|
return;
|
|
3433
3414
|
}
|
|
3434
3415
|
try {
|
|
3435
3416
|
gitRun(["reset", "--soft", `HEAD~${undoCount}`], gitRoot);
|
|
3436
|
-
console.log(
|
|
3417
|
+
console.log(chalk12.green(`
|
|
3437
3418
|
\u2705 ${t("undo.success")}`));
|
|
3438
|
-
console.log(
|
|
3419
|
+
console.log(chalk12.gray(` \u{1F4A1} ${t("undo.stagedHint")}`));
|
|
3439
3420
|
if (risky) {
|
|
3440
|
-
console.log(
|
|
3421
|
+
console.log(chalk12.yellow(`
|
|
3441
3422
|
\u{1F4A1} ${t("undo.forcePushHint")}`));
|
|
3442
3423
|
}
|
|
3443
3424
|
printNextStep({
|
|
@@ -3446,35 +3427,35 @@ ${t("undo.recentHeader")}`));
|
|
|
3446
3427
|
cursorHint: t("undo.nextCursor")
|
|
3447
3428
|
});
|
|
3448
3429
|
} catch (err) {
|
|
3449
|
-
console.log(
|
|
3430
|
+
console.log(chalk12.red(`\u274C ${t("undo.failed")}`));
|
|
3450
3431
|
const msg = err instanceof Error ? err.message : String(err);
|
|
3451
|
-
console.log(
|
|
3432
|
+
console.log(chalk12.red(msg));
|
|
3452
3433
|
process.exitCode = 1;
|
|
3453
3434
|
}
|
|
3454
3435
|
}
|
|
3455
3436
|
|
|
3456
3437
|
// src/commands/restore.ts
|
|
3457
|
-
import
|
|
3438
|
+
import chalk13 from "chalk";
|
|
3458
3439
|
import inquirer7 from "inquirer";
|
|
3459
3440
|
async function restore(id) {
|
|
3460
|
-
console.log(
|
|
3441
|
+
console.log(chalk13.bold(`
|
|
3461
3442
|
${ko.restore.title}`));
|
|
3462
|
-
console.log(
|
|
3463
|
-
console.log(
|
|
3443
|
+
console.log(chalk13.gray("\u2500".repeat(40)));
|
|
3444
|
+
console.log(chalk13.dim(` ${ko.restore.notGitNote}`));
|
|
3464
3445
|
const cwd = process.cwd();
|
|
3465
3446
|
const backups = listBackups(cwd);
|
|
3466
3447
|
if (backups.length === 0) {
|
|
3467
|
-
console.log(
|
|
3448
|
+
console.log(chalk13.yellow(`
|
|
3468
3449
|
${ko.restore.noBackups}`));
|
|
3469
3450
|
return;
|
|
3470
3451
|
}
|
|
3471
3452
|
let targetId = id;
|
|
3472
3453
|
if (!targetId) {
|
|
3473
3454
|
if (!process.stdout.isTTY) {
|
|
3474
|
-
console.log(
|
|
3455
|
+
console.log(chalk13.cyan(`
|
|
3475
3456
|
${ko.restore.listHeader}`));
|
|
3476
3457
|
for (const b of backups) console.log(` ${b.id} (${b.files.length}\uAC1C \uD30C\uC77C)`);
|
|
3477
|
-
console.log(
|
|
3458
|
+
console.log(chalk13.yellow(`
|
|
3478
3459
|
${ko.restore.nonTtyHint}`));
|
|
3479
3460
|
return;
|
|
3480
3461
|
}
|
|
@@ -3493,16 +3474,16 @@ ${ko.restore.nonTtyHint}`));
|
|
|
3493
3474
|
}
|
|
3494
3475
|
try {
|
|
3495
3476
|
const restored = restoreBackup(targetId, cwd);
|
|
3496
|
-
console.log(
|
|
3477
|
+
console.log(chalk13.green(`
|
|
3497
3478
|
${ko.restore.restored(restored.length, targetId)}`));
|
|
3498
|
-
for (const r of restored) console.log(
|
|
3479
|
+
for (const r of restored) console.log(chalk13.gray(` ${r}`));
|
|
3499
3480
|
printNextStep({
|
|
3500
3481
|
message: "\uBC31\uC5C5 \uBCF5\uC6D0 \uC644\uB8CC! \uBCC0\uACBD \uB0B4\uC6A9\uC744 \uD655\uC778\uD558\uC138\uC694.",
|
|
3501
3482
|
command: "vhk diff",
|
|
3502
3483
|
cursorHint: "\uBCC0\uACBD\uC0AC\uD56D \uBCF4\uC5EC\uC918"
|
|
3503
3484
|
});
|
|
3504
3485
|
} catch {
|
|
3505
|
-
console.log(
|
|
3486
|
+
console.log(chalk13.red(`
|
|
3506
3487
|
${ko.restore.notFound(targetId)}`));
|
|
3507
3488
|
process.exitCode = 1;
|
|
3508
3489
|
}
|
|
@@ -3512,7 +3493,7 @@ ${ko.restore.notFound(targetId)}`));
|
|
|
3512
3493
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
3513
3494
|
import fs10 from "fs";
|
|
3514
3495
|
import path11 from "path";
|
|
3515
|
-
import
|
|
3496
|
+
import chalk14 from "chalk";
|
|
3516
3497
|
function countFileChanges(porcelain) {
|
|
3517
3498
|
const lines = porcelain.split("\n").filter(Boolean);
|
|
3518
3499
|
let staged = 0;
|
|
@@ -3587,15 +3568,15 @@ function getSyncCounts(gitRoot) {
|
|
|
3587
3568
|
}
|
|
3588
3569
|
}
|
|
3589
3570
|
async function status() {
|
|
3590
|
-
console.log(
|
|
3571
|
+
console.log(chalk14.bold(`
|
|
3591
3572
|
\u{1F4CA} ${t("status.title")}`));
|
|
3592
|
-
console.log(
|
|
3573
|
+
console.log(chalk14.gray("\u2500".repeat(40)));
|
|
3593
3574
|
let gitRoot;
|
|
3594
3575
|
try {
|
|
3595
3576
|
execFileSync3("git", ["rev-parse", "--is-inside-work-tree"], { stdio: "pipe" });
|
|
3596
3577
|
gitRoot = getGitRoot();
|
|
3597
3578
|
} catch {
|
|
3598
|
-
console.log(
|
|
3579
|
+
console.log(chalk14.red(`\u274C ${t("status.notGitRepo")}`));
|
|
3599
3580
|
return;
|
|
3600
3581
|
}
|
|
3601
3582
|
let branch;
|
|
@@ -3614,29 +3595,29 @@ async function status() {
|
|
|
3614
3595
|
commits = [];
|
|
3615
3596
|
}
|
|
3616
3597
|
const pkg = readProjectPackage();
|
|
3617
|
-
console.log(
|
|
3618
|
-
\u{1F33F} ${t("status.branch")}`) +
|
|
3598
|
+
console.log(chalk14.cyan(`
|
|
3599
|
+
\u{1F33F} ${t("status.branch")}`) + chalk14.white(` ${branch}`));
|
|
3619
3600
|
console.log(
|
|
3620
|
-
|
|
3601
|
+
chalk14.cyan(`\u{1F4C1} ${t("status.changes")}`) + chalk14.white(
|
|
3621
3602
|
` staged ${counts.staged} \xB7 unstaged ${counts.unstaged} \xB7 untracked ${counts.untracked}`
|
|
3622
3603
|
)
|
|
3623
3604
|
);
|
|
3624
|
-
console.log(
|
|
3605
|
+
console.log(chalk14.cyan(`
|
|
3625
3606
|
\u{1F4CB} ${t("status.recentCommits", commits.length)}`));
|
|
3626
3607
|
if (commits.length === 0) {
|
|
3627
|
-
console.log(
|
|
3608
|
+
console.log(chalk14.dim(` ${t("status.noCommits")}`));
|
|
3628
3609
|
} else {
|
|
3629
|
-
commits.forEach((c) => console.log(` ${
|
|
3610
|
+
commits.forEach((c) => console.log(` ${chalk14.dim("\u2022")} ${c}`));
|
|
3630
3611
|
}
|
|
3631
3612
|
console.log(
|
|
3632
|
-
|
|
3633
|
-
\u{1F504} ${t("status.remote")}`) +
|
|
3613
|
+
chalk14.cyan(`
|
|
3614
|
+
\u{1F504} ${t("status.remote")}`) + chalk14.white(` ${formatSyncLabel(sync2)}`)
|
|
3634
3615
|
);
|
|
3635
|
-
console.log(
|
|
3616
|
+
console.log(chalk14.gray("\n" + "\u2500".repeat(40)));
|
|
3636
3617
|
if (pkg) {
|
|
3637
|
-
console.log(
|
|
3618
|
+
console.log(chalk14.cyan(`\u{1F4E6} ${t("status.package")}`) + chalk14.white(` ${pkg.name} v${pkg.version}`));
|
|
3638
3619
|
} else {
|
|
3639
|
-
console.log(
|
|
3620
|
+
console.log(chalk14.dim(`\u{1F4E6} ${t("status.noPackage")}`));
|
|
3640
3621
|
}
|
|
3641
3622
|
const hasChanges = counts.staged + counts.unstaged + counts.untracked > 0;
|
|
3642
3623
|
printNextStep(selectStatusNextStep(hasChanges));
|
|
@@ -3644,7 +3625,7 @@ async function status() {
|
|
|
3644
3625
|
}
|
|
3645
3626
|
|
|
3646
3627
|
// src/commands/diff.ts
|
|
3647
|
-
import
|
|
3628
|
+
import chalk15 from "chalk";
|
|
3648
3629
|
function gitOut2(args) {
|
|
3649
3630
|
const r = safeExecFile("git", args);
|
|
3650
3631
|
return r.ok ? r.out : "";
|
|
@@ -3681,51 +3662,51 @@ function summarizeNumstat(numstat) {
|
|
|
3681
3662
|
return { fileCount, totalAdd, totalDel };
|
|
3682
3663
|
}
|
|
3683
3664
|
function printFile(f) {
|
|
3684
|
-
const adds = f.additions > 0 ?
|
|
3685
|
-
const dels = f.deletions > 0 ?
|
|
3665
|
+
const adds = f.additions > 0 ? chalk15.green(`+${f.additions}`) : "";
|
|
3666
|
+
const dels = f.deletions > 0 ? chalk15.red(`-${f.deletions}`) : "";
|
|
3686
3667
|
const change = [adds, dels].filter(Boolean).join(" ");
|
|
3687
3668
|
console.log(` ${f.name} ${change}`);
|
|
3688
3669
|
}
|
|
3689
3670
|
async function diff() {
|
|
3690
|
-
console.log(
|
|
3671
|
+
console.log(chalk15.bold(`
|
|
3691
3672
|
\u{1F50D} ${t("diff.title")}`));
|
|
3692
|
-
console.log(
|
|
3673
|
+
console.log(chalk15.gray("\u2500".repeat(40)));
|
|
3693
3674
|
if (!safeExecFile("git", ["rev-parse", "--is-inside-work-tree"]).ok) {
|
|
3694
|
-
console.log(
|
|
3675
|
+
console.log(chalk15.red(`\u274C ${t("diff.notGitRepo")}`));
|
|
3695
3676
|
return;
|
|
3696
3677
|
}
|
|
3697
3678
|
const unstaged = gitOut2(["diff", "--stat"]);
|
|
3698
3679
|
const staged = gitOut2(["diff", "--cached", "--stat"]);
|
|
3699
3680
|
const untracked = gitOut2(["ls-files", "--others", "--exclude-standard"]);
|
|
3700
3681
|
if (!unstaged && !staged && !untracked) {
|
|
3701
|
-
console.log(
|
|
3682
|
+
console.log(chalk15.green(`
|
|
3702
3683
|
\u2705 ${t("diff.noChanges")}`));
|
|
3703
3684
|
return;
|
|
3704
3685
|
}
|
|
3705
3686
|
if (staged) {
|
|
3706
|
-
console.log(
|
|
3687
|
+
console.log(chalk15.cyan(`
|
|
3707
3688
|
${t("diff.stagedHeader")}`));
|
|
3708
3689
|
parseDiffStat(staged).forEach((f) => printFile(f));
|
|
3709
3690
|
}
|
|
3710
3691
|
if (unstaged) {
|
|
3711
|
-
console.log(
|
|
3692
|
+
console.log(chalk15.cyan(`
|
|
3712
3693
|
${t("diff.unstagedHeader")}`));
|
|
3713
3694
|
parseDiffStat(unstaged).forEach((f) => printFile(f));
|
|
3714
3695
|
}
|
|
3715
3696
|
if (untracked) {
|
|
3716
3697
|
const files = untracked.split("\n").filter(Boolean);
|
|
3717
|
-
console.log(
|
|
3698
|
+
console.log(chalk15.cyan(`
|
|
3718
3699
|
${t("diff.untrackedHeader", files.length)}`));
|
|
3719
|
-
files.forEach((f) => console.log(` ${
|
|
3700
|
+
files.forEach((f) => console.log(` ${chalk15.green("+")} ${f}`));
|
|
3720
3701
|
}
|
|
3721
3702
|
const numstat = gitOut2(["diff", "--numstat", "HEAD"]);
|
|
3722
3703
|
if (numstat) {
|
|
3723
3704
|
const { fileCount, totalAdd, totalDel } = summarizeNumstat(numstat);
|
|
3724
|
-
console.log(
|
|
3705
|
+
console.log(chalk15.cyan(`
|
|
3725
3706
|
${t("diff.summaryHeader")}`));
|
|
3726
3707
|
console.log(` ${t("diff.filesLine", fileCount)}`);
|
|
3727
|
-
console.log(` \uCD94\uAC00: ${
|
|
3728
|
-
console.log(` \uC0AD\uC81C: ${
|
|
3708
|
+
console.log(` \uCD94\uAC00: ${chalk15.green(`+${totalAdd}`)}\uC904`);
|
|
3709
|
+
console.log(` \uC0AD\uC81C: ${chalk15.red(`-${totalDel}`)}\uC904`);
|
|
3729
3710
|
}
|
|
3730
3711
|
console.log("");
|
|
3731
3712
|
}
|
|
@@ -3734,7 +3715,7 @@ ${t("diff.summaryHeader")}`));
|
|
|
3734
3715
|
import { existsSync as existsSync4, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
3735
3716
|
import { join as join5, dirname } from "path";
|
|
3736
3717
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3737
|
-
import
|
|
3718
|
+
import chalk16 from "chalk";
|
|
3738
3719
|
function resolveMcpEntryPoint() {
|
|
3739
3720
|
try {
|
|
3740
3721
|
const here = fileURLToPath2(import.meta.url);
|
|
@@ -3774,8 +3755,8 @@ function resolveVhkMcpEntry() {
|
|
|
3774
3755
|
return { command: "vhk-mcp", args: [] };
|
|
3775
3756
|
}
|
|
3776
3757
|
async function mcpInit() {
|
|
3777
|
-
console.log(
|
|
3778
|
-
console.log(
|
|
3758
|
+
console.log(chalk16.bold("\n\u{1F50C} " + t("mcp.initTitle")));
|
|
3759
|
+
console.log(chalk16.gray("\u2500".repeat(40)));
|
|
3779
3760
|
const cursorDir = join5(process.cwd(), ".cursor");
|
|
3780
3761
|
if (!existsSync4(cursorDir)) {
|
|
3781
3762
|
mkdirSync3(cursorDir, { recursive: true });
|
|
@@ -3790,15 +3771,15 @@ async function mcpInit() {
|
|
|
3790
3771
|
mcpServers: { ...parsed.mcpServers ?? {}, vhk: vhkEntry }
|
|
3791
3772
|
};
|
|
3792
3773
|
} catch {
|
|
3793
|
-
console.log(
|
|
3774
|
+
console.log(chalk16.yellow("\u26A0\uFE0F \uAE30\uC874 .cursor/mcp.json \uD30C\uC2F1 \uC2E4\uD328 \u2014 \uC0C8 \uD30C\uC77C\uB85C \uB36E\uC5B4\uC501\uB2C8\uB2E4."));
|
|
3794
3775
|
config = { mcpServers: { vhk: vhkEntry } };
|
|
3795
3776
|
}
|
|
3796
3777
|
} else {
|
|
3797
3778
|
config = { mcpServers: { vhk: vhkEntry } };
|
|
3798
3779
|
}
|
|
3799
3780
|
writeFileSync3(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
3800
|
-
console.log(
|
|
3801
|
-
console.log(
|
|
3781
|
+
console.log(chalk16.green("\n\u2705 Cursor MCP \uC124\uC815 \uC644\uB8CC!"));
|
|
3782
|
+
console.log(chalk16.cyan("\u{1F4C1} \uC0DD\uC131\uB41C \uD30C\uC77C:"));
|
|
3802
3783
|
console.log(` ${configPath}`);
|
|
3803
3784
|
printNextStep({
|
|
3804
3785
|
message: t("mcp.nextMessage"),
|
|
@@ -3809,7 +3790,7 @@ async function mcpInit() {
|
|
|
3809
3790
|
|
|
3810
3791
|
// src/commands/design.ts
|
|
3811
3792
|
import { existsSync as existsSync5, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
3812
|
-
import
|
|
3793
|
+
import chalk17 from "chalk";
|
|
3813
3794
|
import inquirer8 from "inquirer";
|
|
3814
3795
|
var PALETTES = [
|
|
3815
3796
|
{
|
|
@@ -3912,8 +3893,8 @@ export default vhkColors
|
|
|
3912
3893
|
`;
|
|
3913
3894
|
}
|
|
3914
3895
|
async function design() {
|
|
3915
|
-
console.log(
|
|
3916
|
-
console.log(
|
|
3896
|
+
console.log(chalk17.bold("\n\u{1F3A8} " + t("design.title")));
|
|
3897
|
+
console.log(chalk17.gray("\u2500".repeat(40)));
|
|
3917
3898
|
if (!ensureInteractive("\uCEEC\uB7EC \uD314\uB808\uD2B8 \uC120\uD0DD\uC740 \uB300\uD654\uD615\uC73C\uB85C\uB9CC \uAC00\uB2A5\uD569\uB2C8\uB2E4.")) return;
|
|
3918
3899
|
const { paletteIndex } = await inquirer8.prompt([
|
|
3919
3900
|
{
|
|
@@ -3927,7 +3908,7 @@ async function design() {
|
|
|
3927
3908
|
}
|
|
3928
3909
|
]);
|
|
3929
3910
|
const palette = PALETTES[paletteIndex];
|
|
3930
|
-
console.log(
|
|
3911
|
+
console.log(chalk17.cyan(`
|
|
3931
3912
|
\u{1F3A8} \uC120\uD0DD\uB41C \uD314\uB808\uD2B8: ${palette.name}`));
|
|
3932
3913
|
const v4 = hasTailwindV4();
|
|
3933
3914
|
const targetPath = v4 ? "src/styles/theme.css" : hasTailwind() ? "src/styles/vhk-colors.ts" : "src/styles/tokens.css";
|
|
@@ -3940,24 +3921,24 @@ async function design() {
|
|
|
3940
3921
|
default: false
|
|
3941
3922
|
}]);
|
|
3942
3923
|
if (!overwrite) {
|
|
3943
|
-
console.log(
|
|
3924
|
+
console.log(chalk17.yellow("\n\u23ED\uFE0F \uC0DD\uC131 \uCDE8\uC18C \u2014 \uAE30\uC874 \uD30C\uC77C \uC720\uC9C0."));
|
|
3944
3925
|
return;
|
|
3945
3926
|
}
|
|
3946
3927
|
}
|
|
3947
3928
|
mkdirSync4("src/styles", { recursive: true });
|
|
3948
3929
|
writeFileSync4(targetPath, content, "utf-8");
|
|
3949
3930
|
if (v4) {
|
|
3950
|
-
console.log(
|
|
3951
|
-
console.log(
|
|
3952
|
-
console.log(
|
|
3931
|
+
console.log(chalk17.green("\n\u2705 src/styles/theme.css \uC0DD\uC131 (Tailwind v4 @theme)"));
|
|
3932
|
+
console.log(chalk17.gray(' \uC9C4\uC785 CSS(\uC608: src/index.css)\uC5D0 `@import "./styles/theme.css";` \uCD94\uAC00 \u2192 bg-primary \uB4F1 \uC720\uD2F8 \uC0AC\uC6A9.'));
|
|
3933
|
+
console.log(chalk17.gray(" \uB2E4\uD06C \uD1A0\uAE00: \uB8E8\uD2B8 <html>/<body> \uC5D0 `.dark` \uD074\uB798\uC2A4 on/off."));
|
|
3953
3934
|
} else if (hasTailwind()) {
|
|
3954
|
-
console.log(
|
|
3955
|
-
console.log(
|
|
3935
|
+
console.log(chalk17.green("\n\u2705 src/styles/vhk-colors.ts \uC0DD\uC131"));
|
|
3936
|
+
console.log(chalk17.gray(" tailwind.config\uC758 extend.colors\uC5D0 import \uD574\uC11C \uC0AC\uC6A9\uD558\uC138\uC694."));
|
|
3956
3937
|
} else {
|
|
3957
|
-
console.log(
|
|
3958
|
-
console.log(
|
|
3938
|
+
console.log(chalk17.green("\n\u2705 src/styles/tokens.css \uC0DD\uC131"));
|
|
3939
|
+
console.log(chalk17.gray(" HTML\uC5D0 <link>\uB85C \uCD94\uAC00\uD558\uAC70\uB098 CSS\uC5D0\uC11C @import \uD558\uC138\uC694."));
|
|
3959
3940
|
}
|
|
3960
|
-
console.log(
|
|
3941
|
+
console.log(chalk17.bold("\n\u{1F308} \uCEEC\uB7EC \uBBF8\uB9AC\uBCF4\uAE30:"));
|
|
3961
3942
|
for (const [key, value] of Object.entries(palette.colors)) {
|
|
3962
3943
|
console.log(` ${key.padEnd(12)} ${value}`);
|
|
3963
3944
|
}
|
|
@@ -3973,7 +3954,7 @@ async function designPalette() {
|
|
|
3973
3954
|
|
|
3974
3955
|
// src/commands/theme.ts
|
|
3975
3956
|
import { existsSync as existsSync6, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
3976
|
-
import
|
|
3957
|
+
import chalk18 from "chalk";
|
|
3977
3958
|
import inquirer9 from "inquirer";
|
|
3978
3959
|
function generateDarkCSS() {
|
|
3979
3960
|
return `/* vhk theme \u2014 \uB2E4\uD06C/\uB77C\uC774\uD2B8 \uBAA8\uB4DC CSS \uBCC0\uC218 */
|
|
@@ -4029,36 +4010,39 @@ export function initTheme(): void {
|
|
|
4029
4010
|
}
|
|
4030
4011
|
`;
|
|
4031
4012
|
}
|
|
4032
|
-
async function theme() {
|
|
4033
|
-
console.log(
|
|
4034
|
-
console.log(
|
|
4013
|
+
async function theme(options) {
|
|
4014
|
+
console.log(chalk18.bold("\n\u{1F319} " + t("theme.title")));
|
|
4015
|
+
console.log(chalk18.gray("\u2500".repeat(40)));
|
|
4035
4016
|
const cssPath = "src/styles/theme.css";
|
|
4036
4017
|
const togglePath = "src/lib/theme-toggle.ts";
|
|
4037
4018
|
const conflicts = [cssPath, togglePath].filter((p) => existsSync6(p));
|
|
4038
4019
|
if (conflicts.length > 0) {
|
|
4039
|
-
const
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4020
|
+
const overwrite = options?.yes === true ? true : await promptOrDefault(
|
|
4021
|
+
async () => (await inquirer9.prompt([{
|
|
4022
|
+
type: "confirm",
|
|
4023
|
+
name: "overwrite",
|
|
4024
|
+
message: `\uB2E4\uC74C \uD30C\uC77C\uC774 \uC774\uBBF8 \uC788\uC5B4\uC694. \uB36E\uC5B4\uC4F8\uAE4C\uC694?
|
|
4043
4025
|
${conflicts.join("\n ")}`,
|
|
4044
|
-
|
|
4045
|
-
|
|
4026
|
+
default: false
|
|
4027
|
+
}])).overwrite,
|
|
4028
|
+
false
|
|
4029
|
+
);
|
|
4046
4030
|
if (!overwrite) {
|
|
4047
|
-
console.log(
|
|
4031
|
+
console.log(chalk18.yellow("\n\u23ED\uFE0F \uC0DD\uC131 \uCDE8\uC18C \u2014 \uAE30\uC874 \uD30C\uC77C \uC720\uC9C0. (\uBE44\uB300\uD654\uD615\uC774\uBA74 --yes \uB85C \uB36E\uC5B4\uC4F0\uAE30)"));
|
|
4048
4032
|
return;
|
|
4049
4033
|
}
|
|
4050
4034
|
}
|
|
4051
4035
|
mkdirSync5("src/styles", { recursive: true });
|
|
4052
4036
|
mkdirSync5("src/lib", { recursive: true });
|
|
4053
4037
|
writeFileSync5(cssPath, generateDarkCSS(), "utf-8");
|
|
4054
|
-
console.log(
|
|
4038
|
+
console.log(chalk18.green("\n\u2705 src/styles/theme.css \uC0DD\uC131 (\uB2E4\uD06C/\uB77C\uC774\uD2B8 \uBAA8\uB4DC)"));
|
|
4055
4039
|
writeFileSync5(togglePath, generateToggleUtil(), "utf-8");
|
|
4056
|
-
console.log(
|
|
4057
|
-
console.log(
|
|
4058
|
-
console.log(
|
|
4059
|
-
console.log(
|
|
4060
|
-
console.log(
|
|
4061
|
-
console.log(
|
|
4040
|
+
console.log(chalk18.green("\u2705 src/lib/theme-toggle.ts \uC0DD\uC131 (\uD1A0\uAE00 \uC720\uD2F8\uB9AC\uD2F0)"));
|
|
4041
|
+
console.log(chalk18.bold("\n\u{1F4D6} \uC0AC\uC6A9\uBC95:"));
|
|
4042
|
+
console.log(chalk18.gray(" 1. theme.css\uB97C \uAE00\uB85C\uBC8C \uC2A4\uD0C0\uC77C\uC5D0 \uCD94\uAC00"));
|
|
4043
|
+
console.log(chalk18.gray(' 2. import { initTheme, toggleTheme } from "./lib/theme-toggle"'));
|
|
4044
|
+
console.log(chalk18.gray(" 3. \uC571 \uC9C4\uC785\uC810\uC5D0\uC11C initTheme() \uD638\uCD9C"));
|
|
4045
|
+
console.log(chalk18.gray(" 4. \uD1A0\uAE00 \uBC84\uD2BC\uC5D0\uC11C toggleTheme() \uD638\uCD9C"));
|
|
4062
4046
|
printNextStep({
|
|
4063
4047
|
message: "\uD14C\uB9C8 \uC124\uC815 \uC644\uB8CC!",
|
|
4064
4048
|
command: "vhk ref list",
|
|
@@ -4068,7 +4052,7 @@ async function theme() {
|
|
|
4068
4052
|
|
|
4069
4053
|
// src/commands/ref.ts
|
|
4070
4054
|
import { existsSync as existsSync7, mkdirSync as mkdirSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
4071
|
-
import
|
|
4055
|
+
import chalk19 from "chalk";
|
|
4072
4056
|
var REFS_PATH = ".vhk/refs.json";
|
|
4073
4057
|
function loadRefs() {
|
|
4074
4058
|
if (!existsSync7(REFS_PATH)) return [];
|
|
@@ -4084,24 +4068,24 @@ function saveRefs(refs) {
|
|
|
4084
4068
|
writeFileSync6(REFS_PATH, JSON.stringify(refs, null, 2) + "\n", "utf-8");
|
|
4085
4069
|
}
|
|
4086
4070
|
async function refAdd(url, memo = "") {
|
|
4087
|
-
console.log(
|
|
4088
|
-
console.log(
|
|
4071
|
+
console.log(chalk19.bold("\n\u{1F517} " + t("ref.addTitle")));
|
|
4072
|
+
console.log(chalk19.gray("\u2500".repeat(40)));
|
|
4089
4073
|
if (!url) {
|
|
4090
|
-
console.log(
|
|
4091
|
-
console.log(
|
|
4074
|
+
console.log(chalk19.red("\u274C URL\uC744 \uC785\uB825\uD574\uC8FC\uC138\uC694."));
|
|
4075
|
+
console.log(chalk19.gray(' \uC608: vhk ref add https://example.com --memo "\uCC38\uACE0 \uC0AC\uC774\uD2B8"'));
|
|
4092
4076
|
return;
|
|
4093
4077
|
}
|
|
4094
4078
|
const refs = loadRefs();
|
|
4095
4079
|
if (refs.some((r) => r.url === url)) {
|
|
4096
|
-
console.log(
|
|
4080
|
+
console.log(chalk19.yellow("\u26A0\uFE0F \uC774\uBBF8 \uC800\uC7A5\uB41C URL\uC785\uB2C8\uB2E4."));
|
|
4097
4081
|
return;
|
|
4098
4082
|
}
|
|
4099
4083
|
refs.push({ url, memo, addedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
4100
4084
|
saveRefs(refs);
|
|
4101
|
-
console.log(
|
|
4085
|
+
console.log(chalk19.green(`
|
|
4102
4086
|
\u2705 \uB808\uD37C\uB7F0\uC2A4 \uCD94\uAC00\uB428 (#${refs.length})`));
|
|
4103
|
-
console.log(
|
|
4104
|
-
if (memo) console.log(
|
|
4087
|
+
console.log(chalk19.cyan(` ${url}`));
|
|
4088
|
+
if (memo) console.log(chalk19.gray(` \u{1F4DD} ${memo}`));
|
|
4105
4089
|
printNextStep({
|
|
4106
4090
|
message: "\uB808\uD37C\uB7F0\uC2A4 \uC800\uC7A5 \uC644\uB8CC!",
|
|
4107
4091
|
command: "vhk ref list",
|
|
@@ -4109,22 +4093,22 @@ async function refAdd(url, memo = "") {
|
|
|
4109
4093
|
});
|
|
4110
4094
|
}
|
|
4111
4095
|
async function refList() {
|
|
4112
|
-
console.log(
|
|
4113
|
-
console.log(
|
|
4096
|
+
console.log(chalk19.bold("\n\u{1F4DA} " + t("ref.listTitle")));
|
|
4097
|
+
console.log(chalk19.gray("\u2500".repeat(40)));
|
|
4114
4098
|
const refs = loadRefs();
|
|
4115
4099
|
if (refs.length === 0) {
|
|
4116
|
-
console.log(
|
|
4117
|
-
console.log(
|
|
4100
|
+
console.log(chalk19.yellow("\n\u{1F4ED} \uC800\uC7A5\uB41C \uB808\uD37C\uB7F0\uC2A4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4."));
|
|
4101
|
+
console.log(chalk19.gray(' vhk ref add <url> --memo "\uBA54\uBAA8"\uB85C \uCD94\uAC00\uD558\uC138\uC694.'));
|
|
4118
4102
|
return;
|
|
4119
4103
|
}
|
|
4120
|
-
console.log(
|
|
4104
|
+
console.log(chalk19.cyan(`
|
|
4121
4105
|
\uCD1D ${refs.length}\uAC1C\uC758 \uB808\uD37C\uB7F0\uC2A4:
|
|
4122
4106
|
`));
|
|
4123
4107
|
refs.forEach((ref, index) => {
|
|
4124
4108
|
const date = new Date(ref.addedAt).toLocaleDateString("ko-KR");
|
|
4125
|
-
console.log(
|
|
4126
|
-
if (ref.memo) console.log(
|
|
4127
|
-
console.log(
|
|
4109
|
+
console.log(chalk19.white(` [${index + 1}] ${ref.url}`));
|
|
4110
|
+
if (ref.memo) console.log(chalk19.gray(` \u{1F4DD} ${ref.memo}`));
|
|
4111
|
+
console.log(chalk19.gray(` \u{1F4C5} ${date}`));
|
|
4128
4112
|
console.log("");
|
|
4129
4113
|
});
|
|
4130
4114
|
}
|
|
@@ -4132,7 +4116,7 @@ async function refOpen(indexStr) {
|
|
|
4132
4116
|
const refs = loadRefs();
|
|
4133
4117
|
const idx = parseInt(indexStr, 10) - 1;
|
|
4134
4118
|
if (Number.isNaN(idx) || idx < 0 || idx >= refs.length) {
|
|
4135
|
-
console.log(
|
|
4119
|
+
console.log(chalk19.red(`\u274C \uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uBC88\uD638\uC785\uB2C8\uB2E4. (1~${refs.length || 0})`));
|
|
4136
4120
|
return;
|
|
4137
4121
|
}
|
|
4138
4122
|
const ref = refs[idx];
|
|
@@ -4140,14 +4124,14 @@ async function refOpen(indexStr) {
|
|
|
4140
4124
|
try {
|
|
4141
4125
|
parsed = new URL(ref.url);
|
|
4142
4126
|
} catch {
|
|
4143
|
-
console.log(
|
|
4127
|
+
console.log(chalk19.red(`\u274C \uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 URL: ${ref.url}`));
|
|
4144
4128
|
return;
|
|
4145
4129
|
}
|
|
4146
4130
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
4147
|
-
console.log(
|
|
4131
|
+
console.log(chalk19.red(`\u274C http(s) URL\uB9CC \uC5F4 \uC218 \uC788\uC2B5\uB2C8\uB2E4 (${parsed.protocol})`));
|
|
4148
4132
|
return;
|
|
4149
4133
|
}
|
|
4150
|
-
console.log(
|
|
4134
|
+
console.log(chalk19.cyan(`
|
|
4151
4135
|
\u{1F310} \uC5F4\uAE30: ${ref.url}`));
|
|
4152
4136
|
let result;
|
|
4153
4137
|
if (process.platform === "darwin") {
|
|
@@ -4158,15 +4142,15 @@ async function refOpen(indexStr) {
|
|
|
4158
4142
|
result = safeExecFile("xdg-open", [ref.url]);
|
|
4159
4143
|
}
|
|
4160
4144
|
if (result.ok) {
|
|
4161
|
-
console.log(
|
|
4145
|
+
console.log(chalk19.green("\u2705 \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uC5F4\uC5C8\uC2B5\uB2C8\uB2E4."));
|
|
4162
4146
|
} else {
|
|
4163
|
-
console.log(
|
|
4147
|
+
console.log(chalk19.yellow("\u26A0\uFE0F \uBE0C\uB77C\uC6B0\uC800\uB97C \uC5F4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. URL\uC744 \uC9C1\uC811 \uBC29\uBB38\uD574\uC8FC\uC138\uC694."));
|
|
4164
4148
|
}
|
|
4165
4149
|
}
|
|
4166
4150
|
|
|
4167
4151
|
// src/commands/harness.ts
|
|
4168
4152
|
import { existsSync as existsSync8 } from "fs";
|
|
4169
|
-
import
|
|
4153
|
+
import chalk20 from "chalk";
|
|
4170
4154
|
import ora2 from "ora";
|
|
4171
4155
|
function detectPM() {
|
|
4172
4156
|
if (existsSync8("pnpm-lock.yaml")) return "pnpm";
|
|
@@ -4208,15 +4192,15 @@ function detectChecks() {
|
|
|
4208
4192
|
}
|
|
4209
4193
|
async function harness() {
|
|
4210
4194
|
if (!ensureNotHardStopped("harness")) return;
|
|
4211
|
-
console.log(
|
|
4212
|
-
console.log(
|
|
4195
|
+
console.log(chalk20.bold("\n\u{1F527} " + t("harness.title")));
|
|
4196
|
+
console.log(chalk20.gray("\u2500".repeat(40)));
|
|
4213
4197
|
const checks = detectChecks();
|
|
4214
4198
|
if (checks.length === 0) {
|
|
4215
|
-
console.log(
|
|
4216
|
-
console.log(
|
|
4199
|
+
console.log(chalk20.yellow("\n\u26A0\uFE0F \uC2E4\uD589\uD560 \uC218 \uC788\uB294 \uC2A4\uD06C\uB9BD\uD2B8\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4."));
|
|
4200
|
+
console.log(chalk20.gray(" package.json\uC5D0 lint, test, build \uC2A4\uD06C\uB9BD\uD2B8\uB97C \uCD94\uAC00\uD574\uC8FC\uC138\uC694."));
|
|
4217
4201
|
return;
|
|
4218
4202
|
}
|
|
4219
|
-
console.log(
|
|
4203
|
+
console.log(chalk20.cyan(`
|
|
4220
4204
|
\u{1F3C3} ${checks.length}\uAC1C \uC810\uAC80 \uC2DC\uC791:
|
|
4221
4205
|
`));
|
|
4222
4206
|
const results = [];
|
|
@@ -4228,10 +4212,10 @@ async function harness() {
|
|
|
4228
4212
|
const duration = Date.now() - start2;
|
|
4229
4213
|
const sec = (duration / 1e3).toFixed(1);
|
|
4230
4214
|
if (result.ok) {
|
|
4231
|
-
spinner.succeed(`${check2.name} ${
|
|
4215
|
+
spinner.succeed(`${check2.name} ${chalk20.gray(`(${sec}s)`)}`);
|
|
4232
4216
|
results.push({ name: check2.name, command: display, passed: true, duration });
|
|
4233
4217
|
} else {
|
|
4234
|
-
spinner.fail(`${check2.name} ${
|
|
4218
|
+
spinner.fail(`${check2.name} ${chalk20.gray(`(${sec}s)`)}`);
|
|
4235
4219
|
results.push({
|
|
4236
4220
|
name: check2.name,
|
|
4237
4221
|
command: display,
|
|
@@ -4241,22 +4225,22 @@ async function harness() {
|
|
|
4241
4225
|
});
|
|
4242
4226
|
}
|
|
4243
4227
|
}
|
|
4244
|
-
console.log(
|
|
4245
|
-
console.log(
|
|
4228
|
+
console.log(chalk20.bold("\n\u{1F4CA} \uD1B5\uD569 \uB9AC\uD3EC\uD2B8:"));
|
|
4229
|
+
console.log(chalk20.gray("\u2500".repeat(40)));
|
|
4246
4230
|
for (const r of results) {
|
|
4247
|
-
const icon = r.passed ?
|
|
4231
|
+
const icon = r.passed ? chalk20.green("\u2705") : chalk20.red("\u274C");
|
|
4248
4232
|
const sec = (r.duration / 1e3).toFixed(1);
|
|
4249
|
-
console.log(` ${icon} ${r.name.padEnd(15)} ${
|
|
4233
|
+
console.log(` ${icon} ${r.name.padEnd(15)} ${chalk20.gray(`${sec}s`)}`);
|
|
4250
4234
|
}
|
|
4251
4235
|
const passed = results.filter((r) => r.passed).length;
|
|
4252
4236
|
const all = passed === results.length;
|
|
4253
|
-
console.log(
|
|
4237
|
+
console.log(chalk20.gray("\u2500".repeat(40)));
|
|
4254
4238
|
if (all) {
|
|
4255
|
-
console.log(
|
|
4239
|
+
console.log(chalk20.green.bold(`
|
|
4256
4240
|
\u{1F389} \uC804\uCCB4 \uD1B5\uACFC! (${passed}/${results.length})`));
|
|
4257
4241
|
} else {
|
|
4258
4242
|
console.log(
|
|
4259
|
-
|
|
4243
|
+
chalk20.red.bold(`
|
|
4260
4244
|
\u26A0\uFE0F ${results.length - passed}\uAC1C \uC2E4\uD328 (${passed}/${results.length} \uD1B5\uACFC)`)
|
|
4261
4245
|
);
|
|
4262
4246
|
process.exitCode = 1;
|
|
@@ -4270,7 +4254,7 @@ async function harness() {
|
|
|
4270
4254
|
|
|
4271
4255
|
// src/commands/migrate.ts
|
|
4272
4256
|
import { existsSync as existsSync9, unlinkSync, rmSync as rmSync2 } from "fs";
|
|
4273
|
-
import
|
|
4257
|
+
import chalk21 from "chalk";
|
|
4274
4258
|
import inquirer10 from "inquirer";
|
|
4275
4259
|
import ora3 from "ora";
|
|
4276
4260
|
var LOCK_FILES = {
|
|
@@ -4288,10 +4272,10 @@ function isCLIAvailable(pm) {
|
|
|
4288
4272
|
return safeExecFile(pm, ["--version"]).ok;
|
|
4289
4273
|
}
|
|
4290
4274
|
async function migrate(target) {
|
|
4291
|
-
console.log(
|
|
4292
|
-
console.log(
|
|
4275
|
+
console.log(chalk21.bold("\n\u{1F504} " + t("migrate.title")));
|
|
4276
|
+
console.log(chalk21.gray("\u2500".repeat(40)));
|
|
4293
4277
|
const current = detectCurrentPM();
|
|
4294
|
-
console.log(
|
|
4278
|
+
console.log(chalk21.cyan(`
|
|
4295
4279
|
\uD604\uC7AC \uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800: ${current ?? "\uAC10\uC9C0 \uBD88\uAC00"}`));
|
|
4296
4280
|
let targetPM;
|
|
4297
4281
|
if (target && ["npm", "yarn", "pnpm"].includes(target)) {
|
|
@@ -4309,14 +4293,14 @@ async function migrate(target) {
|
|
|
4309
4293
|
targetPM = selected;
|
|
4310
4294
|
}
|
|
4311
4295
|
if (targetPM === current) {
|
|
4312
|
-
console.log(
|
|
4296
|
+
console.log(chalk21.yellow(`
|
|
4313
4297
|
\u26A0\uFE0F \uC774\uBBF8 ${targetPM}\uC744 \uC0AC\uC6A9 \uC911\uC785\uB2C8\uB2E4.`));
|
|
4314
4298
|
return;
|
|
4315
4299
|
}
|
|
4316
4300
|
if (!isCLIAvailable(targetPM)) {
|
|
4317
|
-
console.log(
|
|
4301
|
+
console.log(chalk21.red(`
|
|
4318
4302
|
\u274C ${targetPM}\uC774 \uC124\uCE58\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.`));
|
|
4319
|
-
console.log(
|
|
4303
|
+
console.log(chalk21.yellow(` npm i -g ${targetPM}`));
|
|
4320
4304
|
return;
|
|
4321
4305
|
}
|
|
4322
4306
|
const { confirm } = await inquirer10.prompt([
|
|
@@ -4328,7 +4312,7 @@ async function migrate(target) {
|
|
|
4328
4312
|
}
|
|
4329
4313
|
]);
|
|
4330
4314
|
if (!confirm) {
|
|
4331
|
-
console.log(
|
|
4315
|
+
console.log(chalk21.gray("\uCDE8\uC18C\uB428"));
|
|
4332
4316
|
return;
|
|
4333
4317
|
}
|
|
4334
4318
|
const cleanup = ora3("\uAE30\uC874 lock \uD30C\uC77C \uC815\uB9AC \uC911...").start();
|
|
@@ -4348,10 +4332,10 @@ async function migrate(target) {
|
|
|
4348
4332
|
install.succeed(`${targetPM} install \uC644\uB8CC!`);
|
|
4349
4333
|
} else {
|
|
4350
4334
|
install.fail(`${targetPM} install \uC2E4\uD328`);
|
|
4351
|
-
console.log(
|
|
4335
|
+
console.log(chalk21.red(installResult.err.slice(0, 300)));
|
|
4352
4336
|
return;
|
|
4353
4337
|
}
|
|
4354
|
-
console.log(
|
|
4338
|
+
console.log(chalk21.green.bold(`
|
|
4355
4339
|
\u{1F389} ${current ?? "\uC774\uC804"} \u2192 ${targetPM} \uC804\uD658 \uC644\uB8CC!`));
|
|
4356
4340
|
printNextStep({
|
|
4357
4341
|
message: "\uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800 \uC804\uD658 \uC644\uB8CC!",
|
|
@@ -4364,7 +4348,7 @@ async function migrate(target) {
|
|
|
4364
4348
|
import { existsSync as existsSync10 } from "fs";
|
|
4365
4349
|
import { dirname as dirname2, join as join6 } from "path";
|
|
4366
4350
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
4367
|
-
import
|
|
4351
|
+
import chalk22 from "chalk";
|
|
4368
4352
|
import ora4 from "ora";
|
|
4369
4353
|
var PACKAGE = "@byh3071/vhk";
|
|
4370
4354
|
function getCurrentVersion() {
|
|
@@ -4394,32 +4378,32 @@ function isUpToDate(current, latest) {
|
|
|
4394
4378
|
return cc >= lc;
|
|
4395
4379
|
}
|
|
4396
4380
|
async function update() {
|
|
4397
|
-
console.log(
|
|
4398
|
-
console.log(
|
|
4381
|
+
console.log(chalk22.bold("\n\u2B06\uFE0F " + t("update.title")));
|
|
4382
|
+
console.log(chalk22.gray("\u2500".repeat(40)));
|
|
4399
4383
|
const current = getCurrentVersion();
|
|
4400
|
-
console.log(
|
|
4384
|
+
console.log(chalk22.cyan(`
|
|
4401
4385
|
\u{1F4CC} \uD604\uC7AC \uBC84\uC804: v${current}`));
|
|
4402
4386
|
const spinner = ora4("\uCD5C\uC2E0 \uBC84\uC804 \uD655\uC778 \uC911...").start();
|
|
4403
4387
|
const latest = getLatestVersion();
|
|
4404
4388
|
if (!latest) {
|
|
4405
4389
|
spinner.fail("\uCD5C\uC2E0 \uBC84\uC804\uC744 \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
4406
|
-
console.log(
|
|
4407
|
-
console.log(
|
|
4390
|
+
console.log(chalk22.yellow(" \uB124\uD2B8\uC6CC\uD06C\uB97C \uD655\uC778\uD558\uAC70\uB098 \uC218\uB3D9\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694:"));
|
|
4391
|
+
console.log(chalk22.gray(` npm update -g ${PACKAGE}`));
|
|
4408
4392
|
return;
|
|
4409
4393
|
}
|
|
4410
4394
|
spinner.stop();
|
|
4411
|
-
console.log(
|
|
4395
|
+
console.log(chalk22.cyan(`\u{1F195} \uCD5C\uC2E0 \uBC84\uC804: v${latest}`));
|
|
4412
4396
|
if (isUpToDate(current, latest)) {
|
|
4413
|
-
console.log(
|
|
4397
|
+
console.log(chalk22.green("\n\u2705 \uC774\uBBF8 \uCD5C\uC2E0 \uBC84\uC804\uC785\uB2C8\uB2E4!"));
|
|
4414
4398
|
return;
|
|
4415
4399
|
}
|
|
4416
4400
|
const updateSpinner = ora4(`v${latest}\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8 \uC911...`).start();
|
|
4417
4401
|
const upd = safeExecFile("npm", ["update", "-g", PACKAGE]);
|
|
4418
4402
|
if (upd.ok) {
|
|
4419
4403
|
updateSpinner.succeed(`v${latest}\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC!`);
|
|
4420
|
-
console.log(
|
|
4404
|
+
console.log(chalk22.green.bold(`
|
|
4421
4405
|
\u{1F389} VHK CLI v${latest} \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC!`));
|
|
4422
|
-
console.log(
|
|
4406
|
+
console.log(chalk22.gray(" \uBCC0\uACBD \uC0AC\uD56D\uC740 GitHub Releases\uB97C \uD655\uC778\uD558\uC138\uC694."));
|
|
4423
4407
|
printNextStep({
|
|
4424
4408
|
message: t("update.nextOkMessage"),
|
|
4425
4409
|
command: "vhk --version",
|
|
@@ -4427,9 +4411,9 @@ async function update() {
|
|
|
4427
4411
|
});
|
|
4428
4412
|
} else {
|
|
4429
4413
|
updateSpinner.fail("\uC5C5\uB370\uC774\uD2B8 \uC2E4\uD328");
|
|
4430
|
-
console.log(
|
|
4431
|
-
console.log(
|
|
4432
|
-
console.log(
|
|
4414
|
+
console.log(chalk22.red(upd.err.slice(0, 300)));
|
|
4415
|
+
console.log(chalk22.yellow("\n\uC218\uB3D9\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694:"));
|
|
4416
|
+
console.log(chalk22.gray(` npm update -g ${PACKAGE}`));
|
|
4433
4417
|
printNextStep({
|
|
4434
4418
|
message: t("update.nextFailMessage"),
|
|
4435
4419
|
command: "vhk doctor",
|
|
@@ -4448,7 +4432,7 @@ import {
|
|
|
4448
4432
|
writeFileSync as writeFileSync7
|
|
4449
4433
|
} from "fs";
|
|
4450
4434
|
import { join as join7 } from "path";
|
|
4451
|
-
import
|
|
4435
|
+
import chalk23 from "chalk";
|
|
4452
4436
|
var CONTEXT_PATH = ".vhk/context.md";
|
|
4453
4437
|
var IGNORE_DIRS = /* @__PURE__ */ new Set([
|
|
4454
4438
|
"node_modules",
|
|
@@ -4549,8 +4533,8 @@ function getVhkCommands() {
|
|
|
4549
4533
|
}
|
|
4550
4534
|
async function context(opts = {}) {
|
|
4551
4535
|
const compact = opts.compact === true;
|
|
4552
|
-
console.log(
|
|
4553
|
-
console.log(
|
|
4536
|
+
console.log(chalk23.bold("\n\u{1F9E0} " + t("context.title")));
|
|
4537
|
+
console.log(chalk23.gray("\u2500".repeat(40)));
|
|
4554
4538
|
const stack = extractTechStack();
|
|
4555
4539
|
const tree = buildTree(".", "", compact ? 2 : 3).join("\n");
|
|
4556
4540
|
const commands = getVhkCommands();
|
|
@@ -4665,10 +4649,10 @@ async function context(opts = {}) {
|
|
|
4665
4649
|
lines.push("");
|
|
4666
4650
|
mkdirSync7(".vhk", { recursive: true });
|
|
4667
4651
|
writeFileSync7(CONTEXT_PATH, lines.join("\n"), "utf-8");
|
|
4668
|
-
console.log(
|
|
4652
|
+
console.log(chalk23.green(`
|
|
4669
4653
|
\u2705 ${CONTEXT_PATH} \uC0DD\uC131 \uC644\uB8CC!`));
|
|
4670
|
-
console.log(
|
|
4671
|
-
console.log(
|
|
4654
|
+
console.log(chalk23.gray(` \uAE30\uC220 \uC2A4\uD0DD ${Object.keys(stack).length}\uAC1C \uAC10\uC9C0`));
|
|
4655
|
+
console.log(chalk23.gray(" AI \uC5B4\uC2DC\uC2A4\uD134\uD2B8\uC5D0\uAC8C \uC774 \uD30C\uC77C\uC744 \uCC38\uC870\uD558\uAC8C \uD558\uC138\uC694."));
|
|
4672
4656
|
printNextStep({
|
|
4673
4657
|
message: "\uCEE8\uD14D\uC2A4\uD2B8 \uD30C\uC77C \uC0DD\uC131 \uC644\uB8CC!",
|
|
4674
4658
|
command: "vhk context-show",
|
|
@@ -4676,11 +4660,11 @@ async function context(opts = {}) {
|
|
|
4676
4660
|
});
|
|
4677
4661
|
}
|
|
4678
4662
|
async function contextShow() {
|
|
4679
|
-
console.log(
|
|
4680
|
-
console.log(
|
|
4663
|
+
console.log(chalk23.bold("\n\u{1F4C4} " + t("context.showTitle")));
|
|
4664
|
+
console.log(chalk23.gray("\u2500".repeat(40)));
|
|
4681
4665
|
if (!existsSync11(CONTEXT_PATH)) {
|
|
4682
|
-
console.log(
|
|
4683
|
-
console.log(
|
|
4666
|
+
console.log(chalk23.yellow("\n\u26A0\uFE0F \uCEE8\uD14D\uC2A4\uD2B8 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
|
|
4667
|
+
console.log(chalk23.gray(" vhk context\uB97C \uBA3C\uC800 \uC2E4\uD589\uD558\uC138\uC694."));
|
|
4684
4668
|
return;
|
|
4685
4669
|
}
|
|
4686
4670
|
const content = readFileSync4(CONTEXT_PATH, "utf-8");
|
|
@@ -4689,7 +4673,7 @@ async function contextShow() {
|
|
|
4689
4673
|
|
|
4690
4674
|
// src/commands/memory.ts
|
|
4691
4675
|
import { existsSync as existsSync12, mkdirSync as mkdirSync8, writeFileSync as writeFileSync8 } from "fs";
|
|
4692
|
-
import
|
|
4676
|
+
import chalk24 from "chalk";
|
|
4693
4677
|
var MEMORY_PATH = ".vhk/memory.json";
|
|
4694
4678
|
function loadMemories() {
|
|
4695
4679
|
if (!existsSync12(MEMORY_PATH)) return [];
|
|
@@ -4705,11 +4689,11 @@ function saveMemories(memories) {
|
|
|
4705
4689
|
writeFileSync8(MEMORY_PATH, JSON.stringify(memories, null, 2) + "\n", "utf-8");
|
|
4706
4690
|
}
|
|
4707
4691
|
async function memoryAdd(content, tags) {
|
|
4708
|
-
console.log(
|
|
4709
|
-
console.log(
|
|
4692
|
+
console.log(chalk24.bold("\n\u{1F9E0} " + t("memory.addTitle")));
|
|
4693
|
+
console.log(chalk24.gray("\u2500".repeat(40)));
|
|
4710
4694
|
if (!content) {
|
|
4711
|
-
console.log(
|
|
4712
|
-
console.log(
|
|
4695
|
+
console.log(chalk24.red("\u274C \uAE30\uC5B5\uD560 \uB0B4\uC6A9\uC744 \uC785\uB825\uD574\uC8FC\uC138\uC694."));
|
|
4696
|
+
console.log(chalk24.gray(' \uC608: vhk memory add "API\uB294 tRPC \uC0AC\uC6A9\uD558\uAE30\uB85C \uACB0\uC815"'));
|
|
4713
4697
|
process.exitCode = 1;
|
|
4714
4698
|
return;
|
|
4715
4699
|
}
|
|
@@ -4720,9 +4704,9 @@ async function memoryAdd(content, tags) {
|
|
|
4720
4704
|
tags: tags && tags.length > 0 ? tags : []
|
|
4721
4705
|
});
|
|
4722
4706
|
saveMemories(memories);
|
|
4723
|
-
console.log(
|
|
4707
|
+
console.log(chalk24.green(`
|
|
4724
4708
|
\u2705 \uAE30\uC5B5 \uC800\uC7A5\uB428 (#${memories.length})`));
|
|
4725
|
-
console.log(
|
|
4709
|
+
console.log(chalk24.cyan(` \u{1F4DD} ${content}`));
|
|
4726
4710
|
printNextStep({
|
|
4727
4711
|
message: "\uAE30\uC5B5 \uC800\uC7A5 \uC644\uB8CC!",
|
|
4728
4712
|
command: "vhk memory list",
|
|
@@ -4730,24 +4714,24 @@ async function memoryAdd(content, tags) {
|
|
|
4730
4714
|
});
|
|
4731
4715
|
}
|
|
4732
4716
|
async function memoryList() {
|
|
4733
|
-
console.log(
|
|
4734
|
-
console.log(
|
|
4717
|
+
console.log(chalk24.bold("\n\u{1F9E0} " + t("memory.listTitle")));
|
|
4718
|
+
console.log(chalk24.gray("\u2500".repeat(40)));
|
|
4735
4719
|
const memories = loadMemories();
|
|
4736
4720
|
if (memories.length === 0) {
|
|
4737
|
-
console.log(
|
|
4738
|
-
console.log(
|
|
4721
|
+
console.log(chalk24.yellow("\n\u{1F4ED} \uC800\uC7A5\uB41C \uAE30\uC5B5\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
|
|
4722
|
+
console.log(chalk24.gray(' vhk memory add "\uB0B4\uC6A9"\uC73C\uB85C \uCD94\uAC00\uD558\uC138\uC694.'));
|
|
4739
4723
|
return;
|
|
4740
4724
|
}
|
|
4741
|
-
console.log(
|
|
4725
|
+
console.log(chalk24.cyan(`
|
|
4742
4726
|
\uCD1D ${memories.length}\uAC1C\uC758 \uAE30\uC5B5:
|
|
4743
4727
|
`));
|
|
4744
4728
|
memories.forEach((m, index) => {
|
|
4745
4729
|
const date = new Date(m.addedAt).toLocaleDateString("ko-KR");
|
|
4746
|
-
console.log(
|
|
4730
|
+
console.log(chalk24.white(` [${index + 1}] ${m.content}`));
|
|
4747
4731
|
if (m.tags && m.tags.length > 0) {
|
|
4748
|
-
console.log(
|
|
4732
|
+
console.log(chalk24.blue(` \u{1F3F7}\uFE0F ${m.tags.join(", ")}`));
|
|
4749
4733
|
}
|
|
4750
|
-
console.log(
|
|
4734
|
+
console.log(chalk24.gray(` \u{1F4C5} ${date}`));
|
|
4751
4735
|
console.log("");
|
|
4752
4736
|
});
|
|
4753
4737
|
}
|
|
@@ -4755,18 +4739,18 @@ async function memoryRemove(indexStr) {
|
|
|
4755
4739
|
const memories = loadMemories();
|
|
4756
4740
|
const idx = parseInt(indexStr, 10) - 1;
|
|
4757
4741
|
if (Number.isNaN(idx) || idx < 0 || idx >= memories.length) {
|
|
4758
|
-
console.log(
|
|
4742
|
+
console.log(chalk24.red(`\u274C \uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uBC88\uD638\uC785\uB2C8\uB2E4. (1~${memories.length || 0})`));
|
|
4759
4743
|
return;
|
|
4760
4744
|
}
|
|
4761
4745
|
const removed = memories.splice(idx, 1)[0];
|
|
4762
4746
|
saveMemories(memories);
|
|
4763
|
-
console.log(
|
|
4764
|
-
console.log(
|
|
4747
|
+
console.log(chalk24.green("\n\u2705 \uAE30\uC5B5 \uC0AD\uC81C\uB428:"));
|
|
4748
|
+
console.log(chalk24.gray(` ${removed.content}`));
|
|
4765
4749
|
}
|
|
4766
4750
|
|
|
4767
4751
|
// src/commands/brief.ts
|
|
4768
4752
|
import { existsSync as existsSync13, mkdirSync as mkdirSync9, writeFileSync as writeFileSync9, readFileSync as readFileSync5 } from "fs";
|
|
4769
|
-
import
|
|
4753
|
+
import chalk25 from "chalk";
|
|
4770
4754
|
var BRIEF_PATH = ".vhk/brief.md";
|
|
4771
4755
|
function readProjectIdentity() {
|
|
4772
4756
|
const out = {};
|
|
@@ -4791,8 +4775,8 @@ function git2(args) {
|
|
|
4791
4775
|
return result.ok ? result.out : "";
|
|
4792
4776
|
}
|
|
4793
4777
|
async function brief() {
|
|
4794
|
-
console.log(
|
|
4795
|
-
console.log(
|
|
4778
|
+
console.log(chalk25.bold("\n\u{1F4CB} " + t("brief.title")));
|
|
4779
|
+
console.log(chalk25.gray("\u2500".repeat(40)));
|
|
4796
4780
|
const lines = [];
|
|
4797
4781
|
lines.push("# \uD504\uB85C\uC81D\uD2B8 \uBE0C\uB9AC\uD551");
|
|
4798
4782
|
lines.push("");
|
|
@@ -4877,7 +4861,7 @@ async function brief() {
|
|
|
4877
4861
|
mkdirSync9(".vhk", { recursive: true });
|
|
4878
4862
|
writeFileSync9(BRIEF_PATH, lines.join("\n"), "utf-8");
|
|
4879
4863
|
console.log("\n" + lines.join("\n"));
|
|
4880
|
-
console.log(
|
|
4864
|
+
console.log(chalk25.green(`
|
|
4881
4865
|
\u2705 ${BRIEF_PATH} \uC800\uC7A5 \uC644\uB8CC`));
|
|
4882
4866
|
printNextStep({
|
|
4883
4867
|
message: "\uBE0C\uB9AC\uD551 \uC0DD\uC131 \uC644\uB8CC!",
|
|
@@ -4887,7 +4871,7 @@ async function brief() {
|
|
|
4887
4871
|
}
|
|
4888
4872
|
|
|
4889
4873
|
// src/commands/start.ts
|
|
4890
|
-
import
|
|
4874
|
+
import chalk26 from "chalk";
|
|
4891
4875
|
import inquirer11 from "inquirer";
|
|
4892
4876
|
import { simpleGit as simpleGit2 } from "simple-git";
|
|
4893
4877
|
import { existsSync as existsSync14 } from "fs";
|
|
@@ -4929,21 +4913,21 @@ async function runStep(label, fn) {
|
|
|
4929
4913
|
}
|
|
4930
4914
|
}
|
|
4931
4915
|
async function start(options = {}) {
|
|
4932
|
-
console.log(
|
|
4916
|
+
console.log(chalk26.bold(`
|
|
4933
4917
|
${ko.start.title}
|
|
4934
4918
|
`));
|
|
4935
|
-
console.log(
|
|
4936
|
-
console.log(
|
|
4937
|
-
console.log(
|
|
4938
|
-
console.log(
|
|
4939
|
-
console.log(
|
|
4919
|
+
console.log(chalk26.dim(ko.start.intro));
|
|
4920
|
+
console.log(chalk26.dim(` ${ko.start.step1}`));
|
|
4921
|
+
console.log(chalk26.dim(` ${ko.start.step2}`));
|
|
4922
|
+
console.log(chalk26.dim(` ${ko.start.step3}`));
|
|
4923
|
+
console.log(chalk26.dim(` ${ko.start.step4}`));
|
|
4940
4924
|
console.log();
|
|
4941
4925
|
const cwd = process.cwd();
|
|
4942
4926
|
const footprint = detectExistingFootprint(cwd);
|
|
4943
4927
|
if (footprint.length > 0 && !options.yes) {
|
|
4944
|
-
console.log(
|
|
4945
|
-
for (const f of footprint) console.log(
|
|
4946
|
-
console.log(
|
|
4928
|
+
console.log(chalk26.yellow("\u26A0\uFE0F \uC774\uBBF8 VHK \uC124\uCE58 \uD754\uC801\uC774 \uAC10\uC9C0\uB410\uC5B4\uC694:"));
|
|
4929
|
+
for (const f of footprint) console.log(chalk26.dim(` - ${f}`));
|
|
4930
|
+
console.log(chalk26.dim(" \uACC4\uC18D \uC9C4\uD589\uD558\uBA74 \uC77C\uBD80 \uD30C\uC77C(`.cursor/mcp.json`, `.vhk/context.md`)\uC740 \uAC31\uC2E0\xB7\uB36E\uC5B4\uC4F0\uAE30\uB429\uB2C8\uB2E4."));
|
|
4947
4931
|
const { proceedExisting } = await inquirer11.prompt([{
|
|
4948
4932
|
type: "confirm",
|
|
4949
4933
|
name: "proceedExisting",
|
|
@@ -4981,7 +4965,7 @@ ${ko.start.title}
|
|
|
4981
4965
|
await runStep("[3/4] vhk mcp-init", () => mcpInit());
|
|
4982
4966
|
log.step(ko.start.step4Header);
|
|
4983
4967
|
await runStep("[4/4] vhk context", () => context());
|
|
4984
|
-
console.log(
|
|
4968
|
+
console.log(chalk26.bold.green(`
|
|
4985
4969
|
${ko.start.allDone}
|
|
4986
4970
|
`));
|
|
4987
4971
|
printNextStep({
|
|
@@ -4994,7 +4978,7 @@ ${ko.start.allDone}
|
|
|
4994
4978
|
import fs12 from "fs";
|
|
4995
4979
|
import os from "os";
|
|
4996
4980
|
import path13 from "path";
|
|
4997
|
-
import
|
|
4981
|
+
import chalk27 from "chalk";
|
|
4998
4982
|
|
|
4999
4983
|
// src/lib/vhk-cloud.ts
|
|
5000
4984
|
var import_ignore = __toESM(require_ignore(), 1);
|
|
@@ -5086,14 +5070,14 @@ ${CLOUD_CONFIG_FILE}
|
|
|
5086
5070
|
function ensureGhReady() {
|
|
5087
5071
|
const ver = safeExecFile("gh", ["--version"]);
|
|
5088
5072
|
if (!ver.ok) {
|
|
5089
|
-
console.log(
|
|
5090
|
-
console.log(
|
|
5073
|
+
console.log(chalk27.red(` ${ko.cloud.noGh}`));
|
|
5074
|
+
console.log(chalk27.dim(" \uC124\uCE58: https://cli.github.com/ (\uC124\uCE58 \uD6C4 `gh auth login`)"));
|
|
5091
5075
|
return false;
|
|
5092
5076
|
}
|
|
5093
5077
|
const auth = safeExecFile("gh", ["auth", "status"]);
|
|
5094
5078
|
if (!auth.ok) {
|
|
5095
|
-
console.log(
|
|
5096
|
-
console.log(
|
|
5079
|
+
console.log(chalk27.red(` ${ko.cloud.noAuth}`));
|
|
5080
|
+
console.log(chalk27.dim(" \uC2E4\uD589: gh auth login (gist \uAD8C\uD55C \uD544\uC694)"));
|
|
5097
5081
|
return false;
|
|
5098
5082
|
}
|
|
5099
5083
|
return true;
|
|
@@ -5106,18 +5090,18 @@ function parseGistId(output) {
|
|
|
5106
5090
|
return null;
|
|
5107
5091
|
}
|
|
5108
5092
|
async function cloudPush() {
|
|
5109
|
-
console.log(
|
|
5093
|
+
console.log(chalk27.bold(`
|
|
5110
5094
|
${ko.cloud.pushTitle}
|
|
5111
5095
|
`));
|
|
5112
5096
|
const cwd = process.cwd();
|
|
5113
5097
|
if (!fs12.existsSync(path13.join(cwd, VHK_DIR2))) {
|
|
5114
|
-
console.log(
|
|
5098
|
+
console.log(chalk27.yellow(` ${ko.cloud.noVhkDir}`));
|
|
5115
5099
|
return;
|
|
5116
5100
|
}
|
|
5117
5101
|
const ig = loadVhkignore(cwd);
|
|
5118
5102
|
const files = collectVhkFiles(cwd, ig);
|
|
5119
5103
|
if (files.length === 0) {
|
|
5120
|
-
console.log(
|
|
5104
|
+
console.log(chalk27.yellow(` ${ko.cloud.nothingToSync}`));
|
|
5121
5105
|
return;
|
|
5122
5106
|
}
|
|
5123
5107
|
if (!ensureGhReady()) {
|
|
@@ -5125,7 +5109,7 @@ ${ko.cloud.pushTitle}
|
|
|
5125
5109
|
return;
|
|
5126
5110
|
}
|
|
5127
5111
|
const filePaths = files.map((f) => path13.join(cwd, VHK_DIR2, f));
|
|
5128
|
-
console.log(
|
|
5112
|
+
console.log(chalk27.dim(` \u{1F4E6} \uBC31\uC5C5 \uB300\uC0C1 ${files.length}\uAC1C: ${files.join(", ")}
|
|
5129
5113
|
`));
|
|
5130
5114
|
const existing = readCloudConfig(cwd);
|
|
5131
5115
|
const desc = `vhk .vhk backup \u2014 ${path13.basename(cwd)}`;
|
|
@@ -5137,8 +5121,8 @@ ${ko.cloud.pushTitle}
|
|
|
5137
5121
|
const args = gistFiles.includes(name) ? ["gist", "edit", existing.gistId, "-f", name, src] : ["gist", "edit", existing.gistId, "-a", src];
|
|
5138
5122
|
const res2 = safeExecFile("gh", args);
|
|
5139
5123
|
if (!res2.ok) {
|
|
5140
|
-
console.log(
|
|
5141
|
-
console.log(
|
|
5124
|
+
console.log(chalk27.red(` ${ko.cloud.pushFail}: ${name}`));
|
|
5125
|
+
console.log(chalk27.dim(` ${res2.err}`));
|
|
5142
5126
|
process.exitCode = 1;
|
|
5143
5127
|
return;
|
|
5144
5128
|
}
|
|
@@ -5153,15 +5137,15 @@ ${ko.cloud.pushTitle}
|
|
|
5153
5137
|
if (!purgeFailed.includes(name)) purgeFailed.push(name);
|
|
5154
5138
|
}
|
|
5155
5139
|
}
|
|
5156
|
-
console.log(
|
|
5157
|
-
console.log(
|
|
5140
|
+
console.log(chalk27.green.bold(` ${ko.cloud.pushDone}`));
|
|
5141
|
+
console.log(chalk27.dim(` gist: ${existing.gistId} (\uAC31\uC2E0)`));
|
|
5158
5142
|
if (excluded.length > 0) {
|
|
5159
5143
|
const purged = excluded.filter((n) => !purgeFailed.includes(n));
|
|
5160
5144
|
if (purged.length > 0) {
|
|
5161
|
-
console.log(
|
|
5145
|
+
console.log(chalk27.dim(` \u{1F512} \uC81C\uC678 \uB300\uC0C1 ${purged.length}\uAC1C gist \uC5D0\uC11C \uC81C\uAC70: ${purged.join(", ")}`));
|
|
5162
5146
|
}
|
|
5163
5147
|
if (purgeFailed.length > 0) {
|
|
5164
|
-
console.log(
|
|
5148
|
+
console.log(chalk27.yellow(` \u26A0\uFE0F \uC81C\uC678 \uB300\uC0C1 \uC81C\uAC70 \uC2E4\uD328: ${purgeFailed.join(", ")} (\uC218\uB3D9 \uC81C\uAC70 \uAD8C\uC7A5 \u2014 pull \uC2DC\uC5D4 \uBCF5\uC6D0 \uC548 \uB428)`));
|
|
5165
5149
|
}
|
|
5166
5150
|
}
|
|
5167
5151
|
printPushNext();
|
|
@@ -5169,32 +5153,32 @@ ${ko.cloud.pushTitle}
|
|
|
5169
5153
|
}
|
|
5170
5154
|
const res = safeExecFile("gh", ["gist", "create", "--desc", desc, ...filePaths]);
|
|
5171
5155
|
if (!res.ok) {
|
|
5172
|
-
console.log(
|
|
5173
|
-
console.log(
|
|
5156
|
+
console.log(chalk27.red(` ${ko.cloud.pushFail}`));
|
|
5157
|
+
console.log(chalk27.dim(` ${res.err || res.out}`));
|
|
5174
5158
|
process.exitCode = 1;
|
|
5175
5159
|
return;
|
|
5176
5160
|
}
|
|
5177
5161
|
const gistId = parseGistId(res.out);
|
|
5178
5162
|
if (!gistId) {
|
|
5179
|
-
console.log(
|
|
5180
|
-
console.log(
|
|
5163
|
+
console.log(chalk27.red(` ${ko.cloud.pushFail} \u2014 gist id \uD30C\uC2F1 \uC2E4\uD328`));
|
|
5164
|
+
console.log(chalk27.dim(` \uCD9C\uB825: ${res.out}`));
|
|
5181
5165
|
process.exitCode = 1;
|
|
5182
5166
|
return;
|
|
5183
5167
|
}
|
|
5184
5168
|
writeCloudConfig(cwd, { gistId });
|
|
5185
|
-
console.log(
|
|
5186
|
-
console.log(
|
|
5169
|
+
console.log(chalk27.green.bold(` ${ko.cloud.pushDone}`));
|
|
5170
|
+
console.log(chalk27.dim(` gist: ${gistId} (\uC2E0\uADDC, secret) \u2192 .vhk/cloud.json \uC800\uC7A5`));
|
|
5187
5171
|
printPushNext();
|
|
5188
5172
|
}
|
|
5189
5173
|
async function cloudPull(gistIdArg) {
|
|
5190
|
-
console.log(
|
|
5174
|
+
console.log(chalk27.bold(`
|
|
5191
5175
|
${ko.cloud.pullTitle}
|
|
5192
5176
|
`));
|
|
5193
5177
|
const cwd = process.cwd();
|
|
5194
5178
|
const gistId = gistIdArg || readCloudConfig(cwd)?.gistId;
|
|
5195
5179
|
if (!gistId) {
|
|
5196
|
-
console.log(
|
|
5197
|
-
console.log(
|
|
5180
|
+
console.log(chalk27.yellow(` ${ko.cloud.noGistId}`));
|
|
5181
|
+
console.log(chalk27.dim(" \uC0AC\uC6A9\uBC95: vhk cloud pull <gistId> (\uB610\uB294 cloud.json \uC774 \uC788\uB294 \uACF3\uC5D0\uC11C \uC2E4\uD589)"));
|
|
5198
5182
|
return;
|
|
5199
5183
|
}
|
|
5200
5184
|
if (!ensureGhReady()) {
|
|
@@ -5203,16 +5187,16 @@ ${ko.cloud.pullTitle}
|
|
|
5203
5187
|
}
|
|
5204
5188
|
const allNames = listGistFiles(gistId);
|
|
5205
5189
|
if (allNames.length === 0) {
|
|
5206
|
-
console.log(
|
|
5190
|
+
console.log(chalk27.red(` ${ko.cloud.pullFail} \u2014 gist \uBE44\uC5C8\uAC70\uB098 \uC811\uADFC \uBD88\uAC00: ${gistId}`));
|
|
5207
5191
|
process.exitCode = 1;
|
|
5208
5192
|
return;
|
|
5209
5193
|
}
|
|
5210
5194
|
const { keep: names, excluded: skipped } = partitionGistFiles(allNames, loadVhkignore(cwd));
|
|
5211
5195
|
if (skipped.length > 0) {
|
|
5212
|
-
console.log(
|
|
5196
|
+
console.log(chalk27.dim(` \u{1F512} \uC81C\uC678 \uB300\uC0C1 ${skipped.length}\uAC1C \uBCF5\uC6D0 \uC2A4\uD0B5: ${skipped.join(", ")}`));
|
|
5213
5197
|
}
|
|
5214
5198
|
if (names.length === 0) {
|
|
5215
|
-
console.log(
|
|
5199
|
+
console.log(chalk27.yellow(` \uBCF5\uC6D0 \uB300\uC0C1\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 (gist \uD30C\uC77C\uC774 \uBAA8\uB450 \uC81C\uC678 \uADDC\uCE59\uC5D0 \uD574\uB2F9).`));
|
|
5216
5200
|
return;
|
|
5217
5201
|
}
|
|
5218
5202
|
const vhkDir = path13.join(cwd, VHK_DIR2);
|
|
@@ -5221,16 +5205,16 @@ ${ko.cloud.pullTitle}
|
|
|
5221
5205
|
for (const name of names) {
|
|
5222
5206
|
const res = safeExecFile("gh", ["gist", "view", gistId, "-f", name, "--raw"]);
|
|
5223
5207
|
if (!res.ok) {
|
|
5224
|
-
console.log(
|
|
5225
|
-
console.log(
|
|
5208
|
+
console.log(chalk27.red(` ${ko.cloud.pullFail}: ${name}`));
|
|
5209
|
+
console.log(chalk27.dim(` ${res.err}`));
|
|
5226
5210
|
continue;
|
|
5227
5211
|
}
|
|
5228
5212
|
fs12.writeFileSync(path13.join(vhkDir, name), ensureTrailingNewline(res.out), "utf-8");
|
|
5229
5213
|
restored++;
|
|
5230
5214
|
}
|
|
5231
5215
|
writeCloudConfig(cwd, { gistId });
|
|
5232
|
-
console.log(
|
|
5233
|
-
console.log(
|
|
5216
|
+
console.log(chalk27.green.bold(` ${ko.cloud.pullDone}`));
|
|
5217
|
+
console.log(chalk27.dim(` ${restored}\uAC1C \uD30C\uC77C \uBCF5\uC6D0 (gist: ${gistId})`));
|
|
5234
5218
|
printNextStep({
|
|
5235
5219
|
message: "\uD074\uB77C\uC6B0\uB4DC\uC5D0\uC11C .vhk/ \uBCF5\uC6D0 \uC644\uB8CC!",
|
|
5236
5220
|
command: "vhk \uB9E5\uB77D",
|
|
@@ -5278,7 +5262,7 @@ function printPushNext() {
|
|
|
5278
5262
|
}
|
|
5279
5263
|
|
|
5280
5264
|
// src/commands/help.ts
|
|
5281
|
-
import
|
|
5265
|
+
import chalk28 from "chalk";
|
|
5282
5266
|
var QUICK_ACTIONS = [
|
|
5283
5267
|
{ say: "\uC0C1\uD0DC \uC54C\uB824\uC918", does: "vhk status" },
|
|
5284
5268
|
{ say: "\uBB50 \uBC14\uB00C\uC5C8\uC5B4?", does: "vhk diff" },
|
|
@@ -5292,17 +5276,17 @@ var QUICK_ACTIONS = [
|
|
|
5292
5276
|
{ say: "\uC804\uCCB4 \uBA85\uB839\uC5B4 \uBCF4\uAE30", does: "vhk --help" }
|
|
5293
5277
|
];
|
|
5294
5278
|
function quickActions() {
|
|
5295
|
-
console.log(
|
|
5296
|
-
console.log(
|
|
5279
|
+
console.log(chalk28.bold("\n\u{1F9ED} VHK \u2014 \uC774\uB807\uAC8C \uB9D0\uD558\uBA74 \uB429\uB2C8\uB2E4 (quick actions)"));
|
|
5280
|
+
console.log(chalk28.gray("\u2500".repeat(40)));
|
|
5297
5281
|
for (const a of QUICK_ACTIONS) {
|
|
5298
|
-
console.log(` "${
|
|
5282
|
+
console.log(` "${chalk28.cyan(a.say)}" \u2192 ${chalk28.dim(a.does)}`);
|
|
5299
5283
|
}
|
|
5300
|
-
console.log(
|
|
5284
|
+
console.log(chalk28.gray("\n \uC804\uCCB4 \uBA85\uB839\uC740 `vhk --help` \uB610\uB294 COMMANDS.md \uB97C \uBCF4\uC138\uC694."));
|
|
5301
5285
|
console.log("");
|
|
5302
5286
|
}
|
|
5303
5287
|
|
|
5304
5288
|
// src/commands/mode.ts
|
|
5305
|
-
import
|
|
5289
|
+
import chalk29 from "chalk";
|
|
5306
5290
|
|
|
5307
5291
|
// src/lib/config.ts
|
|
5308
5292
|
import { existsSync as existsSync15, mkdirSync as mkdirSync10, writeFileSync as writeFileSync10 } from "fs";
|
|
@@ -5343,17 +5327,17 @@ function writeConfig(config, rootDir = process.cwd()) {
|
|
|
5343
5327
|
|
|
5344
5328
|
// src/commands/mode.ts
|
|
5345
5329
|
async function mode(target) {
|
|
5346
|
-
console.log(
|
|
5347
|
-
console.log(
|
|
5330
|
+
console.log(chalk29.bold("\n\u{1F6E1}\uFE0F Safety Mode"));
|
|
5331
|
+
console.log(chalk29.gray("\u2500".repeat(40)));
|
|
5348
5332
|
const current = readConfig().safetyMode;
|
|
5349
5333
|
if (!target) {
|
|
5350
|
-
console.log(
|
|
5351
|
-
\uD604\uC7AC \uBAA8\uB4DC: ${
|
|
5352
|
-
console.log(
|
|
5334
|
+
console.log(chalk29.cyan(`
|
|
5335
|
+
\uD604\uC7AC \uBAA8\uB4DC: ${chalk29.bold(current)}`));
|
|
5336
|
+
console.log(chalk29.dim(` ${SAFETY_MODE_DESC[current]}`));
|
|
5353
5337
|
console.log("");
|
|
5354
5338
|
for (const m of SAFETY_MODES) {
|
|
5355
5339
|
const mark = m === current ? "\u25CF" : "\u25CB";
|
|
5356
|
-
console.log(` ${mark} ${m.padEnd(9)} ${
|
|
5340
|
+
console.log(` ${mark} ${m.padEnd(9)} ${chalk29.dim(SAFETY_MODE_DESC[m])}`);
|
|
5357
5341
|
}
|
|
5358
5342
|
printNextStep({
|
|
5359
5343
|
message: "\uBAA8\uB4DC\uB97C \uBC14\uAFB8\uB824\uBA74:",
|
|
@@ -5363,43 +5347,221 @@ async function mode(target) {
|
|
|
5363
5347
|
return;
|
|
5364
5348
|
}
|
|
5365
5349
|
if (!isSafetyMode(target)) {
|
|
5366
|
-
console.log(
|
|
5350
|
+
console.log(chalk29.red(`
|
|
5367
5351
|
\u274C \uC54C \uC218 \uC5C6\uB294 \uBAA8\uB4DC: ${target}`));
|
|
5368
|
-
console.log(
|
|
5352
|
+
console.log(chalk29.dim(` \uAC00\uB2A5: ${SAFETY_MODES.join(" | ")}`));
|
|
5369
5353
|
process.exitCode = 1;
|
|
5370
5354
|
return;
|
|
5371
5355
|
}
|
|
5372
5356
|
writeConfig({ ...readConfig(), safetyMode: target });
|
|
5373
|
-
console.log(
|
|
5374
|
-
\u2705 Safety Mode \u2192 ${
|
|
5375
|
-
console.log(
|
|
5357
|
+
console.log(chalk29.green(`
|
|
5358
|
+
\u2705 Safety Mode \u2192 ${chalk29.bold(target)}`));
|
|
5359
|
+
console.log(chalk29.dim(` ${SAFETY_MODE_DESC[target]}`));
|
|
5376
5360
|
}
|
|
5377
5361
|
|
|
5378
5362
|
// src/commands/verify.ts
|
|
5379
|
-
import
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5363
|
+
import { execFileSync as execFileSync4 } from "child_process";
|
|
5364
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync11, writeFileSync as writeFileSync11 } from "fs";
|
|
5365
|
+
import { join as join10 } from "path";
|
|
5366
|
+
import chalk30 from "chalk";
|
|
5367
|
+
var REPORT_SCHEMA_VERSION = 1;
|
|
5368
|
+
var REPORT_DIR_REL = join10(".vhk", "reports");
|
|
5369
|
+
var REPORT_PATH_REL = join10(REPORT_DIR_REL, "latest.json");
|
|
5370
|
+
var SHIM = /* @__PURE__ */ new Set(["pnpm", "npm", "npx", "yarn"]);
|
|
5371
|
+
function detectPm(cwd) {
|
|
5372
|
+
if (existsSync16(join10(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
5373
|
+
if (existsSync16(join10(cwd, "yarn.lock"))) return "yarn";
|
|
5374
|
+
return "npm";
|
|
5375
|
+
}
|
|
5376
|
+
function execGate(cmd, args, cwd) {
|
|
5377
|
+
let bin = cmd;
|
|
5378
|
+
let argv = args;
|
|
5379
|
+
if (process.platform === "win32" && SHIM.has(cmd)) {
|
|
5380
|
+
bin = "cmd.exe";
|
|
5381
|
+
argv = ["/d", "/s", "/c", `${cmd}.cmd`, ...args];
|
|
5382
|
+
}
|
|
5383
|
+
try {
|
|
5384
|
+
execFileSync4(bin, argv, {
|
|
5385
|
+
cwd,
|
|
5386
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
5387
|
+
encoding: "utf-8",
|
|
5388
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
5389
|
+
timeout: 6e5,
|
|
5390
|
+
killSignal: "SIGTERM"
|
|
5391
|
+
});
|
|
5392
|
+
return { exitCode: 0, out: "" };
|
|
5393
|
+
} catch (e) {
|
|
5394
|
+
const err = e;
|
|
5395
|
+
const exitCode = typeof err.status === "number" ? err.status : 1;
|
|
5396
|
+
const out = ((err.stdout?.toString?.() ?? "") + (err.stderr?.toString?.() ?? "")).trim();
|
|
5397
|
+
return { exitCode, out };
|
|
5398
|
+
}
|
|
5399
|
+
}
|
|
5400
|
+
function runScriptGate(id, label, cwd, pm, argvFor) {
|
|
5401
|
+
const argv = argvFor(pm);
|
|
5402
|
+
if (!argv) {
|
|
5403
|
+
return { id, label, status: "skip", exitCode: null, skipped: true, detail: "\uD574\uB2F9 \uC2A4\uD06C\uB9BD\uD2B8/\uC124\uC815 \uC5C6\uC74C \u2014 skip(WARN)" };
|
|
5404
|
+
}
|
|
5405
|
+
const { exitCode } = execGate(pm, argv, cwd);
|
|
5406
|
+
return {
|
|
5407
|
+
id,
|
|
5408
|
+
label,
|
|
5409
|
+
status: exitCode === 0 ? "pass" : "fail",
|
|
5410
|
+
exitCode,
|
|
5411
|
+
skipped: false,
|
|
5412
|
+
detail: exitCode === 0 ? void 0 : `\uC885\uB8CC\uCF54\uB4DC ${exitCode}`
|
|
5413
|
+
};
|
|
5414
|
+
}
|
|
5415
|
+
function readPackageScripts(cwd) {
|
|
5416
|
+
const pkgPath = join10(cwd, "package.json");
|
|
5417
|
+
if (!existsSync16(pkgPath)) return {};
|
|
5418
|
+
try {
|
|
5419
|
+
const pkg = readJsonFile(pkgPath);
|
|
5420
|
+
return pkg.scripts ?? {};
|
|
5421
|
+
} catch {
|
|
5422
|
+
return {};
|
|
5423
|
+
}
|
|
5424
|
+
}
|
|
5425
|
+
function runGates(cwd) {
|
|
5426
|
+
const scripts = readPackageScripts(cwd);
|
|
5427
|
+
const pm = detectPm(cwd);
|
|
5428
|
+
const gates = [];
|
|
5429
|
+
gates.push(
|
|
5430
|
+
runScriptGate("typecheck", "tsc --noEmit", cwd, pm, () => {
|
|
5431
|
+
if (scripts.typecheck) return ["run", "typecheck"];
|
|
5432
|
+
if (existsSync16(join10(cwd, "tsconfig.json"))) return pm === "npm" ? ["exec", "--", "tsc", "--noEmit"] : ["exec", "tsc", "--noEmit"];
|
|
5433
|
+
return null;
|
|
5434
|
+
})
|
|
5435
|
+
);
|
|
5436
|
+
gates.push(
|
|
5437
|
+
runScriptGate("test", "test:run", cwd, pm, () => {
|
|
5438
|
+
if (scripts["test:run"]) return ["run", "test:run"];
|
|
5439
|
+
if (scripts.test && /vitest/.test(scripts.test)) return ["run", "test", "--", "--run"];
|
|
5440
|
+
if (scripts.test) return ["run", "test"];
|
|
5441
|
+
return null;
|
|
5442
|
+
})
|
|
5443
|
+
);
|
|
5444
|
+
gates.push(
|
|
5445
|
+
runScriptGate("build", "build", cwd, pm, () => scripts.build ? ["run", "build"] : null)
|
|
5446
|
+
);
|
|
5447
|
+
gates.push(runSecureGate(cwd));
|
|
5448
|
+
return gates;
|
|
5449
|
+
}
|
|
5450
|
+
function runSecureGate(cwd) {
|
|
5451
|
+
try {
|
|
5452
|
+
const severe = filterSevereFindings(scanProjectForSecrets(cwd).findings);
|
|
5453
|
+
const n = severe.length;
|
|
5454
|
+
return {
|
|
5455
|
+
id: "secure",
|
|
5456
|
+
label: "secure scan",
|
|
5457
|
+
status: n === 0 ? "pass" : "fail",
|
|
5458
|
+
exitCode: n === 0 ? 0 : 1,
|
|
5459
|
+
skipped: false,
|
|
5460
|
+
detail: n === 0 ? void 0 : `severe \uC2DC\uD06C\uB9BF ${n}\uAC74 (\uAC12 \uBBF8\uAE30\uB85D \u2014 vhk secure scan \uC73C\uB85C \uD655\uC778)`
|
|
5461
|
+
};
|
|
5462
|
+
} catch (e) {
|
|
5463
|
+
return {
|
|
5464
|
+
id: "secure",
|
|
5465
|
+
label: "secure scan",
|
|
5466
|
+
status: "fail",
|
|
5467
|
+
exitCode: 1,
|
|
5468
|
+
skipped: false,
|
|
5469
|
+
detail: `\uC2A4\uCE94 \uC2E4\uD589 \uC2E4\uD328: ${e instanceof Error ? e.message : String(e)}`
|
|
5470
|
+
};
|
|
5471
|
+
}
|
|
5472
|
+
}
|
|
5473
|
+
function aggregateStatus(gates) {
|
|
5474
|
+
if (gates.some((g) => g.status === "fail")) return "FAIL";
|
|
5475
|
+
if (gates.some((g) => g.status === "skip")) return "WARN";
|
|
5476
|
+
return "PASS";
|
|
5477
|
+
}
|
|
5478
|
+
function buildNextActions(gates) {
|
|
5479
|
+
const actions = [];
|
|
5480
|
+
for (const g of gates) {
|
|
5481
|
+
if (g.status === "fail") {
|
|
5482
|
+
if (g.id === "secure") actions.push("\uC2DC\uD06C\uB9BF \uC81C\uAC70 \uD6C4 \uC7AC\uAC80\uC99D \u2014 vhk secure scan \uC73C\uB85C \uC704\uCE58 \uD655\uC778");
|
|
5483
|
+
else actions.push(`${g.label} \uC2E4\uD328(\uC885\uB8CC\uCF54\uB4DC ${g.exitCode}) \u2014 \uB85C\uADF8 \uD655\uC778 \uD6C4 \uC218\uC815`);
|
|
5484
|
+
} else if (g.status === "skip") {
|
|
5485
|
+
actions.push(`${g.label} \uAC8C\uC774\uD2B8 \uC5C6\uC74C \u2014 package.json scripts \uC5D0 \uCD94\uAC00\uD558\uBA74 \uAC80\uC99D \uCEE4\uBC84\uB9AC\uC9C0 \u2191`);
|
|
5486
|
+
}
|
|
5487
|
+
}
|
|
5488
|
+
if (actions.length === 0) actions.push("\uAC80\uC99D \uD1B5\uACFC \u2014 vhk save \uB85C \uC800\uC7A5\uD558\uC138\uC694.");
|
|
5489
|
+
return actions;
|
|
5490
|
+
}
|
|
5491
|
+
function buildReport(gates, generatedAt, date) {
|
|
5492
|
+
const summary = {
|
|
5493
|
+
total: gates.length,
|
|
5494
|
+
pass: gates.filter((g) => g.status === "pass").length,
|
|
5495
|
+
fail: gates.filter((g) => g.status === "fail").length,
|
|
5496
|
+
skip: gates.filter((g) => g.status === "skip").length
|
|
5497
|
+
};
|
|
5498
|
+
return {
|
|
5499
|
+
schemaVersion: REPORT_SCHEMA_VERSION,
|
|
5500
|
+
generatedAt,
|
|
5501
|
+
date,
|
|
5502
|
+
status: aggregateStatus(gates),
|
|
5503
|
+
summary,
|
|
5504
|
+
gates,
|
|
5505
|
+
nextActions: buildNextActions(gates)
|
|
5506
|
+
};
|
|
5387
5507
|
}
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
5508
|
+
function verifyEvidence(cwd = process.cwd()) {
|
|
5509
|
+
const gates = runGates(cwd);
|
|
5510
|
+
const report = buildReport(gates, (/* @__PURE__ */ new Date()).toISOString(), localDate());
|
|
5511
|
+
const dir = join10(cwd, REPORT_DIR_REL);
|
|
5512
|
+
mkdirSync11(dir, { recursive: true });
|
|
5513
|
+
const path14 = join10(cwd, REPORT_PATH_REL);
|
|
5514
|
+
writeFileSync11(path14, JSON.stringify(report, null, 2) + "\n", "utf-8");
|
|
5515
|
+
try {
|
|
5516
|
+
ensureVhkIgnored(cwd, "reports/");
|
|
5517
|
+
} catch {
|
|
5518
|
+
}
|
|
5519
|
+
return { report, path: REPORT_PATH_REL };
|
|
5520
|
+
}
|
|
5521
|
+
var STATUS_BADGE = {
|
|
5522
|
+
PASS: chalk30.green.bold("PASS"),
|
|
5523
|
+
WARN: chalk30.yellow.bold("WARN"),
|
|
5524
|
+
FAIL: chalk30.red.bold("FAIL")
|
|
5525
|
+
};
|
|
5526
|
+
async function verify(opts = {}) {
|
|
5527
|
+
if (!ensureNotHardStopped("verify")) return;
|
|
5528
|
+
const cwd = process.cwd();
|
|
5529
|
+
const { report, path: path14 } = verifyEvidence(cwd);
|
|
5530
|
+
if (opts.json) {
|
|
5531
|
+
console.log(JSON.stringify(report, null, 2));
|
|
5532
|
+
process.exitCode = report.status === "FAIL" ? 1 : 0;
|
|
5533
|
+
return;
|
|
5534
|
+
}
|
|
5535
|
+
console.log(chalk30.bold("\n\u{1F50E} \uAC80\uC99D \uBB36\uC74C (verify)"));
|
|
5536
|
+
console.log(chalk30.gray("\u2500".repeat(40)));
|
|
5391
5537
|
const mode2 = readConfig().safetyMode;
|
|
5392
|
-
console.log(
|
|
5393
|
-
|
|
5394
|
-
for (const
|
|
5395
|
-
|
|
5538
|
+
console.log(chalk30.dim(` \uD604\uC7AC Safety Mode: ${mode2} \u2014 ${SAFETY_MODE_DESC[mode2]}`));
|
|
5539
|
+
const icon = (s2) => s2 === "pass" ? chalk30.green("\u2713") : s2 === "fail" ? chalk30.red("\u2717") : chalk30.yellow("\u2298");
|
|
5540
|
+
for (const g of report.gates) {
|
|
5541
|
+
const tail = g.detail ? chalk30.dim(` \u2014 ${g.detail}`) : "";
|
|
5542
|
+
console.log(` ${icon(g.status)} ${g.label}${tail}`);
|
|
5543
|
+
}
|
|
5544
|
+
const s = report.summary;
|
|
5545
|
+
console.log(
|
|
5546
|
+
`
|
|
5547
|
+
\uACB0\uACFC: ${STATUS_BADGE[report.status]} ` + chalk30.dim(`(pass ${s.pass} / fail ${s.fail} / skip ${s.skip}, \uCD1D ${s.total})`)
|
|
5548
|
+
);
|
|
5549
|
+
console.log(chalk30.dim(` \u{1F4C4} \uC99D\uAC70: ${path14}`));
|
|
5550
|
+
process.exitCode = report.status === "FAIL" ? 1 : 0;
|
|
5551
|
+
if (report.status === "FAIL") {
|
|
5552
|
+
printNextStep({
|
|
5553
|
+
message: "\uAC80\uC99D \uC2E4\uD328 \u2014 \uC544\uB798\uB97C \uBA3C\uC800 \uACE0\uCE58\uC138\uC694:",
|
|
5554
|
+
command: "vhk verify",
|
|
5555
|
+
cursorHint: "\uAC80\uC99D \uB2E4\uC2DC \uB3CC\uB824\uC918",
|
|
5556
|
+
alternative: report.nextActions[0]
|
|
5557
|
+
});
|
|
5558
|
+
} else {
|
|
5559
|
+
printNextStep({
|
|
5560
|
+
message: report.status === "WARN" ? "\uAC80\uC99D \uD1B5\uACFC(\uC77C\uBD80 \uAC8C\uC774\uD2B8 skip). \uC800\uC7A5\uD558\uB824\uBA74:" : "\uAC80\uC99D \uD1B5\uACFC! \uC800\uC7A5\uD558\uB824\uBA74:",
|
|
5561
|
+
command: "vhk save",
|
|
5562
|
+
cursorHint: "\uC800\uC7A5\uD574\uC918"
|
|
5563
|
+
});
|
|
5396
5564
|
}
|
|
5397
|
-
console.log(chalk31.dim("\n \u203B \uBA54\uD0C0\uB7EC\uB108(\uC790\uB3D9 \uC2E4\uD589) \uC790\uB9AC \u2014 \uD604\uC7AC\uB294 \uBB36\uC74C \uC548\uB0B4\uB9CC(lite)."));
|
|
5398
|
-
printNextStep({
|
|
5399
|
-
message: "\uAC80\uC99D \uD1B5\uACFC \uD6C4 \uC800\uC7A5\uD558\uC138\uC694:",
|
|
5400
|
-
command: "vhk save",
|
|
5401
|
-
cursorHint: "\uC800\uC7A5\uD574\uC918"
|
|
5402
|
-
});
|
|
5403
5565
|
}
|
|
5404
5566
|
|
|
5405
5567
|
// src/lib/risk-policy.ts
|
|
@@ -5580,14 +5742,14 @@ function requiresConfirmation(route) {
|
|
|
5580
5742
|
async function runNaturalLanguageRoute(input) {
|
|
5581
5743
|
const route = routeNaturalLanguage(input);
|
|
5582
5744
|
if (!route) {
|
|
5583
|
-
console.log(
|
|
5745
|
+
console.log(chalk31.yellow(`
|
|
5584
5746
|
\u2753 "${input}" \u2014 ${ko.nlp.notMatched}
|
|
5585
5747
|
`));
|
|
5586
5748
|
return;
|
|
5587
5749
|
}
|
|
5588
5750
|
console.log("");
|
|
5589
|
-
console.log(
|
|
5590
|
-
console.log(
|
|
5751
|
+
console.log(chalk31.cyan(` \u{1F4AC} "${input}"`));
|
|
5752
|
+
console.log(chalk31.cyan(` \u2192 ${route.explanation}`));
|
|
5591
5753
|
if (requiresConfirmation(route)) {
|
|
5592
5754
|
const { confirm } = await inquirer12.prompt([{
|
|
5593
5755
|
type: "confirm",
|
|
@@ -5596,7 +5758,7 @@ async function runNaturalLanguageRoute(input) {
|
|
|
5596
5758
|
default: true
|
|
5597
5759
|
}]);
|
|
5598
5760
|
if (!confirm) {
|
|
5599
|
-
console.log(
|
|
5761
|
+
console.log(chalk31.dim(` ${ko.nlp.menuHint}`));
|
|
5600
5762
|
return;
|
|
5601
5763
|
}
|
|
5602
5764
|
}
|
|
@@ -5605,7 +5767,7 @@ async function runNaturalLanguageRoute(input) {
|
|
|
5605
5767
|
if (riskAction) {
|
|
5606
5768
|
await runGuarded(
|
|
5607
5769
|
riskAction,
|
|
5608
|
-
{ channel: "nl", approved: false, log: (m) => console.log(
|
|
5770
|
+
{ channel: "nl", approved: false, log: (m) => console.log(chalk31.yellow(` ${m}`)) },
|
|
5609
5771
|
() => dispatchNlpRoute(route, input)
|
|
5610
5772
|
);
|
|
5611
5773
|
return;
|
|
@@ -5614,77 +5776,77 @@ async function runNaturalLanguageRoute(input) {
|
|
|
5614
5776
|
}
|
|
5615
5777
|
|
|
5616
5778
|
// src/commands/agent.ts
|
|
5617
|
-
import
|
|
5779
|
+
import chalk32 from "chalk";
|
|
5618
5780
|
function activeGoalId() {
|
|
5619
5781
|
const goals = listGoals("goals");
|
|
5620
5782
|
const id = selectActiveId(goals);
|
|
5621
5783
|
return id ?? void 0;
|
|
5622
5784
|
}
|
|
5623
5785
|
async function blocker(description) {
|
|
5624
|
-
console.log(
|
|
5786
|
+
console.log(chalk32.bold(`
|
|
5625
5787
|
${ko.agent.blockerTitle}
|
|
5626
5788
|
`));
|
|
5627
5789
|
if (!description || !description.trim()) {
|
|
5628
|
-
console.log(
|
|
5629
|
-
console.log(
|
|
5790
|
+
console.log(chalk32.red(" \u274C \uBE14\uB85C\uCEE4 \uC124\uBA85\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694."));
|
|
5791
|
+
console.log(chalk32.dim(' \uC608: vhk blocker "tsc \uC5D0\uB7EC \u2014 simple-git \uD0C0\uC785 \uD638\uD658"'));
|
|
5630
5792
|
process.exitCode = 1;
|
|
5631
5793
|
return;
|
|
5632
5794
|
}
|
|
5633
5795
|
const goalId = activeGoalId();
|
|
5634
5796
|
const r = appendBlocker(description, goalId);
|
|
5635
|
-
console.log(
|
|
5797
|
+
console.log(chalk32.green(` \u2705 blocker \uAE30\uB85D (\uD604\uC7AC \uD65C\uC131 ${r.count}\uAC74)`));
|
|
5636
5798
|
if (r.hardStopTripped) {
|
|
5637
|
-
console.log(
|
|
5638
|
-
console.log(
|
|
5799
|
+
console.log(chalk32.red.bold(" \u{1F6D1} HARD_STOP \uC790\uB3D9 \uC0DD\uC131 \u2014 \uBAA8\uB4E0 \uC790\uB3D9\uD654 \uC911\uB2E8."));
|
|
5800
|
+
console.log(chalk32.yellow(" \uC0AC\uB78C \uAC80\uD1A0 \uD6C4 `vhk resume --confirm` \uC73C\uB85C\uB9CC \uD574\uC81C."));
|
|
5639
5801
|
process.exitCode = 2;
|
|
5640
5802
|
}
|
|
5641
5803
|
}
|
|
5642
5804
|
async function learn(lesson) {
|
|
5643
|
-
console.log(
|
|
5805
|
+
console.log(chalk32.bold(`
|
|
5644
5806
|
${ko.agent.learnTitle}
|
|
5645
5807
|
`));
|
|
5646
5808
|
if (!lesson || !lesson.trim()) {
|
|
5647
|
-
console.log(
|
|
5648
|
-
console.log(
|
|
5809
|
+
console.log(chalk32.red(" \u274C \uAD50\uD6C8 \uB0B4\uC6A9\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694."));
|
|
5810
|
+
console.log(chalk32.dim(' \uC608: vhk learn "PowerShell \uC5D0\uC11C\uB294 ; \uC0AC\uC6A9 (&& \uBBF8\uC9C0\uC6D0)"'));
|
|
5649
5811
|
process.exitCode = 1;
|
|
5650
5812
|
return;
|
|
5651
5813
|
}
|
|
5652
5814
|
const goalId = activeGoalId();
|
|
5653
5815
|
appendLearning(lesson, goalId);
|
|
5654
|
-
console.log(
|
|
5816
|
+
console.log(chalk32.green(" \u2705 learnings.md append."));
|
|
5655
5817
|
console.log(
|
|
5656
|
-
|
|
5818
|
+
chalk32.dim(" \uACB0\uC815\uC0AC\uD56D(decision)\uC740 `vhk memory add` \uB85C \uBCC4\uB3C4 \uAE30\uB85D \u2014 SoT \uBD84\uB9AC.")
|
|
5657
5819
|
);
|
|
5658
5820
|
}
|
|
5659
5821
|
async function resume(opts = {}) {
|
|
5660
|
-
console.log(
|
|
5822
|
+
console.log(chalk32.bold(`
|
|
5661
5823
|
${ko.agent.resumeTitle}
|
|
5662
5824
|
`));
|
|
5663
5825
|
if (!isHardStopActive()) {
|
|
5664
|
-
console.log(
|
|
5826
|
+
console.log(chalk32.dim(" HARD_STOP \uD65C\uC131 \uC544\uB2D8 \u2014 \uD560 \uC77C \uC5C6\uC74C."));
|
|
5665
5827
|
return;
|
|
5666
5828
|
}
|
|
5667
5829
|
const reason = readHardStopReason();
|
|
5668
5830
|
if (reason) {
|
|
5669
|
-
console.log(
|
|
5670
|
-
console.log(
|
|
5831
|
+
console.log(chalk32.yellow(" \u{1F4CB} HARD_STOP \uC0AC\uC720:"));
|
|
5832
|
+
console.log(chalk32.dim(` ${reason.split("\n").join("\n ")}`));
|
|
5671
5833
|
console.log("");
|
|
5672
5834
|
}
|
|
5673
5835
|
if (!opts.confirm) {
|
|
5674
5836
|
console.log(
|
|
5675
|
-
|
|
5837
|
+
chalk32.red(
|
|
5676
5838
|
" \u274C --confirm \uD50C\uB798\uADF8 \uC5C6\uC774\uB294 \uD574\uC81C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uC790\uB3D9 \uD638\uCD9C \uAE08\uC9C0)."
|
|
5677
5839
|
)
|
|
5678
5840
|
);
|
|
5679
|
-
console.log(
|
|
5841
|
+
console.log(chalk32.yellow(" \uC0AC\uC720\uB97C \uD655\uC778\uD55C \uD6C4 \uB2E4\uC2DC: vhk resume --confirm"));
|
|
5680
5842
|
process.exitCode = 1;
|
|
5681
5843
|
return;
|
|
5682
5844
|
}
|
|
5683
5845
|
const removed = clearHardStop();
|
|
5684
5846
|
if (removed) {
|
|
5685
|
-
console.log(
|
|
5847
|
+
console.log(chalk32.green(" \u2705 HARD_STOP \uD574\uC81C. \uC790\uB3D9\uD654 \uC7AC\uAC1C \uAC00\uB2A5."));
|
|
5686
5848
|
} else {
|
|
5687
|
-
console.log(
|
|
5849
|
+
console.log(chalk32.dim(" \uD30C\uC77C\uC774 \uC774\uBBF8 \uC5C6\uC74C \u2014 no-op."));
|
|
5688
5850
|
}
|
|
5689
5851
|
}
|
|
5690
5852
|
|
|
@@ -5705,7 +5867,7 @@ async function guardCli(action, approved, run) {
|
|
|
5705
5867
|
}]);
|
|
5706
5868
|
return ok;
|
|
5707
5869
|
},
|
|
5708
|
-
log: (m) => console.log(
|
|
5870
|
+
log: (m) => console.log(chalk33.yellow(` ${m}`))
|
|
5709
5871
|
},
|
|
5710
5872
|
run
|
|
5711
5873
|
);
|
|
@@ -5718,7 +5880,7 @@ async function guardCliDefer(action, approved, run) {
|
|
|
5718
5880
|
approved,
|
|
5719
5881
|
// TTY 면 통과(명령이 자체 확인), 비대화형은 confirm 불가 → 가드가 차단.
|
|
5720
5882
|
confirm: async () => !!process.stdout.isTTY,
|
|
5721
|
-
log: (m) => console.log(
|
|
5883
|
+
log: (m) => console.log(chalk33.yellow(` ${m}`))
|
|
5722
5884
|
},
|
|
5723
5885
|
run
|
|
5724
5886
|
);
|
|
@@ -5845,8 +6007,8 @@ program.command("design").alias("\uB514\uC790\uC778").description("\uB514\uC790\
|
|
|
5845
6007
|
program.command("design-palette").alias("\uD314\uB808\uD2B8").description("\uCEEC\uB7EC \uD314\uB808\uD2B8 \uD504\uB9AC\uC14B \uC120\uD0DD + \uC801\uC6A9").action(async () => {
|
|
5846
6008
|
await designPalette();
|
|
5847
6009
|
});
|
|
5848
|
-
program.command("theme").alias("\uD14C\uB9C8").description("\uB2E4\uD06C/\uB77C\uC774\uD2B8 \uBAA8\uB4DC CSS + \uD1A0\uAE00 \uC720\uD2F8\uB9AC\uD2F0 \uC0DD\uC131").action(async () => {
|
|
5849
|
-
await theme();
|
|
6010
|
+
program.command("theme").alias("\uD14C\uB9C8").option("-y, --yes", "\uAE30\uC874 \uD30C\uC77C \uB36E\uC5B4\uC4F0\uAE30 \uD655\uC778 \uC2A4\uD0B5 (\uBE44\uB300\uD654\uD615 \uC790\uB3D9 \uB36E\uC5B4\uC4F0\uAE30)").description("\uB2E4\uD06C/\uB77C\uC774\uD2B8 \uBAA8\uB4DC CSS + \uD1A0\uAE00 \uC720\uD2F8\uB9AC\uD2F0 \uC0DD\uC131").action(async (opts) => {
|
|
6011
|
+
await theme(opts);
|
|
5850
6012
|
});
|
|
5851
6013
|
var refCmd = program.command("ref").alias("\uB808\uD37C\uB7F0\uC2A4").description("\uB808\uD37C\uB7F0\uC2A4 URL \uAD00\uB9AC (add / list / open)").action(async () => {
|
|
5852
6014
|
await refList();
|
|
@@ -5878,8 +6040,8 @@ program.command("context").alias("\uB9E5\uB77D").option("--compact", "\uD1A0\uD0
|
|
|
5878
6040
|
program.command("mode [target]").alias("\uBAA8\uB4DC").description("Safety Mode \uC870\uD68C/\uBCC0\uACBD (lite|standard|strict) \u2014 \uC704\uD5D8 \uC791\uC5C5 \uAC00\uB4DC \uAC15\uB3C4").action(async (target) => {
|
|
5879
6041
|
await mode(target);
|
|
5880
6042
|
});
|
|
5881
|
-
program.command("verify").alias("\uC0AC\uC804\uC810\uAC80").
|
|
5882
|
-
await verify();
|
|
6043
|
+
program.command("verify").alias("\uC0AC\uC804\uC810\uAC80").option("--json", "\uB9AC\uD3EC\uD2B8 JSON \uC744 stdout \uC73C\uB85C \uCD9C\uB825 (CI\uC6A9 \u2014 \uACBD\uB85C \uB300\uC2E0)").description("\uAC80\uC99D \uAC8C\uC774\uD2B8(tsc/test/build/secure) \uC2E4\uC81C \uC2E4\uD589 + \uC99D\uAC70 \uAE30\uB85D (.vhk/reports/latest.json)").action(async (opts) => {
|
|
6044
|
+
await verify(opts);
|
|
5883
6045
|
});
|
|
5884
6046
|
program.command("context-show").alias("\uB9E5\uB77D\uBCF4\uAE30").description("\uD604\uC7AC \uCEE8\uD14D\uC2A4\uD2B8 \uD30C\uC77C \uB0B4\uC6A9 \uCD9C\uB825").action(async () => {
|
|
5885
6047
|
await contextShow();
|
|
@@ -5995,9 +6157,9 @@ if (isMainModule) {
|
|
|
5995
6157
|
}
|
|
5996
6158
|
} catch (err) {
|
|
5997
6159
|
if (isPromptAbortError(err)) {
|
|
5998
|
-
console.error(
|
|
6160
|
+
console.error(chalk33.yellow("\n \u26A0\uFE0F \uB300\uD654\uD615 \uC785\uB825\uC774 \uCDE8\uC18C/\uC885\uB8CC\uB410\uC2B5\uB2C8\uB2E4. (\uBE44\uB300\uD654\uD615 \uD658\uACBD\uC5D0\uC11C\uB294 \uD574\uB2F9 \uBA85\uB839\uC744 \uC4F8 \uC218 \uC5C6\uC5B4\uC694)"));
|
|
5999
6161
|
} else {
|
|
6000
|
-
console.error(
|
|
6162
|
+
console.error(chalk33.red(`
|
|
6001
6163
|
\u274C ${err instanceof Error ? err.message : String(err)}`));
|
|
6002
6164
|
}
|
|
6003
6165
|
process.exitCode = 1;
|