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