@byh3071/vhk 0.8.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -2
- package/dist/{chunk-X3CIIDO2.js → chunk-7NGBIIA3.js} +28 -2
- package/dist/index.js +876 -30
- package/dist/mcp/index.js +1 -1
- package/package.json +65 -65
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
safeExecFileStream,
|
|
11
11
|
startMcpServer,
|
|
12
12
|
t
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-7NGBIIA3.js";
|
|
14
14
|
|
|
15
15
|
// node_modules/.pnpm/ignore@7.0.5/node_modules/ignore/index.js
|
|
16
16
|
var require_ignore = __commonJS({
|
|
@@ -472,10 +472,10 @@ var require_ignore = __commonJS({
|
|
|
472
472
|
|
|
473
473
|
// src/index.ts
|
|
474
474
|
import { Command, Help } from "commander";
|
|
475
|
-
import
|
|
476
|
-
import
|
|
475
|
+
import inquirer14 from "inquirer";
|
|
476
|
+
import fs15 from "fs";
|
|
477
477
|
import path15 from "path";
|
|
478
|
-
import { fileURLToPath as
|
|
478
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
479
479
|
|
|
480
480
|
// src/lib/nlp-router.ts
|
|
481
481
|
function normalize(input) {
|
|
@@ -511,7 +511,7 @@ var RULES = [
|
|
|
511
511
|
command: "init",
|
|
512
512
|
explanation: "\uD504\uB85C\uC81D\uD2B8 \uC2DC\uC791 (vhk \uC2DC\uC791)",
|
|
513
513
|
confidence: "high",
|
|
514
|
-
test: (t2) => (/프로젝트.*(만들|시작)|폴더.*만들|만들고\s*싶|하네스|초기화/.test(t2) || /^시작$/.test(t2)) && !/디자인|design|팔레트|palette|테마|theme|레퍼런스|reference|다크\s*모드|라이트\s*모드|색상\s
|
|
514
|
+
test: (t2) => (/프로젝트.*(만들|시작)|폴더.*만들|만들고\s*싶|하네스|초기화/.test(t2) || /^시작$/.test(t2)) && !/디자인|design|팔레트|palette|테마|theme|레퍼런스|reference|다크\s*모드|라이트\s*모드|색상\s*모드|브리핑|brief|컨텍스트|context|맥락|기억|memory/.test(t2)
|
|
515
515
|
},
|
|
516
516
|
{
|
|
517
517
|
command: "mcp-init",
|
|
@@ -543,6 +543,54 @@ var RULES = [
|
|
|
543
543
|
confidence: "high",
|
|
544
544
|
test: (t2) => /^레퍼런스$|^ref$|레퍼런스.*(보|목록|확인|있|뭐)|참고\s*(사이트|목록|링크)|reference.*list/.test(t2) && !/(add|추가|open|열|https?:\/\/)/.test(t2)
|
|
545
545
|
},
|
|
546
|
+
{
|
|
547
|
+
command: "harness",
|
|
548
|
+
explanation: "\uD1B5\uD569 \uD488\uC9C8 \uC810\uAC80 (vhk harness)",
|
|
549
|
+
confidence: "high",
|
|
550
|
+
test: (t2) => /하네스|harness|통합\s*점검|품질\s*점검|빌드\s*테스트|lint.*(test|build)|전체\s*점검|품질\s*확인/.test(t2)
|
|
551
|
+
},
|
|
552
|
+
{
|
|
553
|
+
command: "audit",
|
|
554
|
+
explanation: "\uBCF4\uC548 \uCDE8\uC57D\uC810 \uAC10\uC0AC (vhk audit)",
|
|
555
|
+
confidence: "high",
|
|
556
|
+
test: (t2) => /감사|취약점|audit|vulnerability|보안\s*감사|보안\s*취약|의존성\s*취약/.test(t2)
|
|
557
|
+
},
|
|
558
|
+
{
|
|
559
|
+
command: "migrate",
|
|
560
|
+
explanation: "\uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800 \uC804\uD658 (vhk migrate)",
|
|
561
|
+
confidence: "high",
|
|
562
|
+
test: (t2) => /전환|마이그레이트|migrate|패키지\s*매니저|npm.*pnpm|pnpm.*npm|yarn.*전환|npm.*전환|pnpm.*전환/.test(t2)
|
|
563
|
+
},
|
|
564
|
+
{
|
|
565
|
+
command: "update",
|
|
566
|
+
explanation: "VHK CLI \uCD5C\uC2E0 \uBC84\uC804 \uC5C5\uB370\uC774\uD2B8 (vhk update)",
|
|
567
|
+
confidence: "high",
|
|
568
|
+
test: (t2) => /업데이트|update|버전\s*업|최신\s*버전|셀프\s*업데이트|vhk.*최신|vhk.*업데이트/.test(t2)
|
|
569
|
+
},
|
|
570
|
+
{
|
|
571
|
+
command: "context-show",
|
|
572
|
+
explanation: "\uCEE8\uD14D\uC2A4\uD2B8 \uD30C\uC77C \uBCF4\uAE30 (vhk context-show)",
|
|
573
|
+
confidence: "high",
|
|
574
|
+
test: (t2) => /맥락\s*(보|확인|보여)|컨텍스트\s*(보|확인|보여)|context\s*show/.test(t2)
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
command: "context",
|
|
578
|
+
explanation: "\uD504\uB85C\uC81D\uD2B8 \uB9E5\uB77D \uC0DD\uC131 (vhk context)",
|
|
579
|
+
confidence: "high",
|
|
580
|
+
test: (t2) => /(^맥락$|^컨텍스트$|^context$|맥락\s*(만들|생성|갱신|업데이트)|컨텍스트\s*(만들|생성|갱신|업데이트)|프로젝트\s*맥락|프로젝트\s*정보\s*생성)/.test(t2) && !/보|확인|보여|show/.test(t2)
|
|
581
|
+
},
|
|
582
|
+
{
|
|
583
|
+
command: "memory",
|
|
584
|
+
explanation: "\uAE30\uC5B5 \uBAA9\uB85D \uC870\uD68C (vhk memory list)",
|
|
585
|
+
confidence: "high",
|
|
586
|
+
test: (t2) => /^기억$|기억\s*(목록|보|확인|뭐)|memory.*list|결정사항\s*(목록|확인|보여)/.test(t2) && !/(추가|add|삭제|remove|저장|기록해)/.test(t2)
|
|
587
|
+
},
|
|
588
|
+
{
|
|
589
|
+
command: "brief",
|
|
590
|
+
explanation: "\uD504\uB85C\uC81D\uD2B8 \uC0C1\uD0DC \uC694\uC57D (vhk brief)",
|
|
591
|
+
confidence: "high",
|
|
592
|
+
test: (t2) => /브리핑|brief|상태\s*요약|프로젝트\s*요약|요약\s*(보고|리포트|보여|만들)|보고서\s*(만들|생성|보여)/.test(t2)
|
|
593
|
+
},
|
|
546
594
|
{
|
|
547
595
|
command: "secure",
|
|
548
596
|
explanation: "\uBCF4\uC548 \uC2A4\uCE94 (vhk \uBCF4\uC548)",
|
|
@@ -709,6 +757,22 @@ var KNOWN_COMMAND_TOKENS = /* @__PURE__ */ new Set([
|
|
|
709
757
|
"\uD14C\uB9C8",
|
|
710
758
|
"ref",
|
|
711
759
|
"\uB808\uD37C\uB7F0\uC2A4",
|
|
760
|
+
"harness",
|
|
761
|
+
"\uD558\uB124\uC2A4",
|
|
762
|
+
"audit",
|
|
763
|
+
"\uAC10\uC0AC",
|
|
764
|
+
"migrate",
|
|
765
|
+
"\uC804\uD658",
|
|
766
|
+
"update",
|
|
767
|
+
"\uC5C5\uB370\uC774\uD2B8",
|
|
768
|
+
"context",
|
|
769
|
+
"\uB9E5\uB77D",
|
|
770
|
+
"context-show",
|
|
771
|
+
"\uB9E5\uB77D\uBCF4\uAE30",
|
|
772
|
+
"memory",
|
|
773
|
+
"\uAE30\uC5B5",
|
|
774
|
+
"brief",
|
|
775
|
+
"\uBE0C\uB9AC\uD551",
|
|
712
776
|
"help"
|
|
713
777
|
]);
|
|
714
778
|
function isOptionToken(token) {
|
|
@@ -732,8 +796,8 @@ function detectNaturalLanguageInput(argv) {
|
|
|
732
796
|
}
|
|
733
797
|
|
|
734
798
|
// src/lib/nlp-run.ts
|
|
735
|
-
import
|
|
736
|
-
import
|
|
799
|
+
import chalk28 from "chalk";
|
|
800
|
+
import inquirer13 from "inquirer";
|
|
737
801
|
|
|
738
802
|
// src/commands/gate.ts
|
|
739
803
|
import inquirer from "inquirer";
|
|
@@ -1658,7 +1722,7 @@ function nextAdrNumber(adrDir) {
|
|
|
1658
1722
|
function slugify(title) {
|
|
1659
1723
|
return title.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9가-힣-]/g, "").slice(0, 40) || "decision";
|
|
1660
1724
|
}
|
|
1661
|
-
function createAdrFile(cwd, title,
|
|
1725
|
+
function createAdrFile(cwd, title, context2, decision, consequences) {
|
|
1662
1726
|
const adrDir = path5.join(cwd, "docs", "adr");
|
|
1663
1727
|
if (!fs4.existsSync(adrDir)) fs4.mkdirSync(adrDir, { recursive: true });
|
|
1664
1728
|
const num = nextAdrNumber(adrDir);
|
|
@@ -1676,7 +1740,7 @@ function createAdrFile(cwd, title, context, decision, consequences) {
|
|
|
1676
1740
|
`# ADR-${String(num).padStart(3, "0")}: ${title}`,
|
|
1677
1741
|
"",
|
|
1678
1742
|
"## \uB9E5\uB77D (Context)",
|
|
1679
|
-
|
|
1743
|
+
context2,
|
|
1680
1744
|
"",
|
|
1681
1745
|
"## \uACB0\uC815 (Decision)",
|
|
1682
1746
|
decision,
|
|
@@ -3104,17 +3168,17 @@ ${t("undo.recentHeader")}`));
|
|
|
3104
3168
|
|
|
3105
3169
|
// src/commands/status.ts
|
|
3106
3170
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
3107
|
-
import
|
|
3171
|
+
import fs14 from "fs";
|
|
3108
3172
|
import path14 from "path";
|
|
3109
3173
|
import chalk13 from "chalk";
|
|
3110
3174
|
|
|
3111
3175
|
// src/lib/read-json.ts
|
|
3112
|
-
import
|
|
3176
|
+
import { readFileSync } from "fs";
|
|
3113
3177
|
function stripBom(text) {
|
|
3114
3178
|
return text.charCodeAt(0) === 65279 ? text.slice(1) : text;
|
|
3115
3179
|
}
|
|
3116
3180
|
function readJsonFile(filePath) {
|
|
3117
|
-
const raw = stripBom(
|
|
3181
|
+
const raw = stripBom(readFileSync(filePath, "utf-8"));
|
|
3118
3182
|
return JSON.parse(raw);
|
|
3119
3183
|
}
|
|
3120
3184
|
|
|
@@ -3157,7 +3221,7 @@ function parseRecentCommitLines(logOutput) {
|
|
|
3157
3221
|
}
|
|
3158
3222
|
function readProjectPackage(cwd = process.cwd()) {
|
|
3159
3223
|
const pkgPath = path14.join(cwd, "package.json");
|
|
3160
|
-
if (!
|
|
3224
|
+
if (!fs14.existsSync(pkgPath)) return null;
|
|
3161
3225
|
try {
|
|
3162
3226
|
const pkg = readJsonFile(pkgPath);
|
|
3163
3227
|
if (!pkg.name && !pkg.version) return null;
|
|
@@ -3326,7 +3390,7 @@ ${t("diff.summaryHeader")}`));
|
|
|
3326
3390
|
}
|
|
3327
3391
|
|
|
3328
3392
|
// src/commands/mcp-init.ts
|
|
3329
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
3393
|
+
import { existsSync, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
3330
3394
|
import { join } from "path";
|
|
3331
3395
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3332
3396
|
import chalk15 from "chalk";
|
|
@@ -3334,7 +3398,7 @@ function resolveVhkMcpPath() {
|
|
|
3334
3398
|
try {
|
|
3335
3399
|
const pkgPath = join(process.cwd(), "package.json");
|
|
3336
3400
|
if (existsSync(pkgPath)) {
|
|
3337
|
-
const pkg = JSON.parse(
|
|
3401
|
+
const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
|
|
3338
3402
|
if (pkg.name === "@byh3071/vhk") {
|
|
3339
3403
|
return join(process.cwd(), "dist", "mcp", "index.js");
|
|
3340
3404
|
}
|
|
@@ -3363,7 +3427,7 @@ async function mcpInit() {
|
|
|
3363
3427
|
let config;
|
|
3364
3428
|
if (existsSync(configPath)) {
|
|
3365
3429
|
try {
|
|
3366
|
-
const parsed = JSON.parse(
|
|
3430
|
+
const parsed = JSON.parse(readFileSync2(configPath, "utf-8"));
|
|
3367
3431
|
config = {
|
|
3368
3432
|
mcpServers: { ...parsed.mcpServers ?? {}, vhk: vhkEntry }
|
|
3369
3433
|
};
|
|
@@ -3486,7 +3550,7 @@ ${t("deploy.deploying")}
|
|
|
3486
3550
|
}
|
|
3487
3551
|
|
|
3488
3552
|
// src/commands/publish.ts
|
|
3489
|
-
import { existsSync as existsSync3, readFileSync as
|
|
3553
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
3490
3554
|
import chalk17 from "chalk";
|
|
3491
3555
|
import inquirer8 from "inquirer";
|
|
3492
3556
|
import ora2 from "ora";
|
|
@@ -3510,7 +3574,7 @@ async function publish() {
|
|
|
3510
3574
|
}
|
|
3511
3575
|
let pkg;
|
|
3512
3576
|
try {
|
|
3513
|
-
pkg = JSON.parse(
|
|
3577
|
+
pkg = JSON.parse(readFileSync3("package.json", "utf-8"));
|
|
3514
3578
|
} catch {
|
|
3515
3579
|
console.log(chalk17.red("\u274C package.json \uD30C\uC2F1 \uC2E4\uD328"));
|
|
3516
3580
|
return;
|
|
@@ -3835,13 +3899,13 @@ async function theme() {
|
|
|
3835
3899
|
}
|
|
3836
3900
|
|
|
3837
3901
|
// src/commands/ref.ts
|
|
3838
|
-
import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as
|
|
3902
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
3839
3903
|
import chalk20 from "chalk";
|
|
3840
3904
|
var REFS_PATH = ".vhk/refs.json";
|
|
3841
3905
|
function loadRefs() {
|
|
3842
3906
|
if (!existsSync6(REFS_PATH)) return [];
|
|
3843
3907
|
try {
|
|
3844
|
-
const raw =
|
|
3908
|
+
const raw = readFileSync4(REFS_PATH, "utf-8");
|
|
3845
3909
|
const parsed = JSON.parse(raw);
|
|
3846
3910
|
return Array.isArray(parsed) ? parsed : [];
|
|
3847
3911
|
} catch {
|
|
@@ -3933,6 +3997,730 @@ async function refOpen(indexStr) {
|
|
|
3933
3997
|
}
|
|
3934
3998
|
}
|
|
3935
3999
|
|
|
4000
|
+
// src/commands/harness.ts
|
|
4001
|
+
import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
|
|
4002
|
+
import chalk21 from "chalk";
|
|
4003
|
+
import ora3 from "ora";
|
|
4004
|
+
function detectPM() {
|
|
4005
|
+
if (existsSync7("pnpm-lock.yaml")) return "pnpm";
|
|
4006
|
+
if (existsSync7("yarn.lock")) return "yarn";
|
|
4007
|
+
return "npm";
|
|
4008
|
+
}
|
|
4009
|
+
function pmRun(pm, script) {
|
|
4010
|
+
return pm === "npm" ? ["run", script] : [script];
|
|
4011
|
+
}
|
|
4012
|
+
function detectChecks() {
|
|
4013
|
+
const checks = [];
|
|
4014
|
+
let pkg = {};
|
|
4015
|
+
try {
|
|
4016
|
+
pkg = JSON.parse(readFileSync5("package.json", "utf-8"));
|
|
4017
|
+
} catch {
|
|
4018
|
+
return checks;
|
|
4019
|
+
}
|
|
4020
|
+
const s = pkg.scripts ?? {};
|
|
4021
|
+
const pm = detectPM();
|
|
4022
|
+
if (s.lint) {
|
|
4023
|
+
checks.push({ name: "lint", bin: pm, args: pmRun(pm, "lint") });
|
|
4024
|
+
} else if (existsSync7(".eslintrc.js") || existsSync7(".eslintrc.json") || existsSync7("eslint.config.js")) {
|
|
4025
|
+
checks.push({ name: "lint", bin: "npx", args: ["eslint", ".", "--ext", ".ts,.tsx"] });
|
|
4026
|
+
}
|
|
4027
|
+
if (s["type-check"]) {
|
|
4028
|
+
checks.push({ name: "type-check", bin: pm, args: pmRun(pm, "type-check") });
|
|
4029
|
+
} else if (s.typecheck) {
|
|
4030
|
+
checks.push({ name: "type-check", bin: pm, args: pmRun(pm, "typecheck") });
|
|
4031
|
+
} else if (existsSync7("tsconfig.json")) {
|
|
4032
|
+
checks.push({ name: "type-check", bin: "npx", args: ["tsc", "--noEmit"] });
|
|
4033
|
+
}
|
|
4034
|
+
if (s.test) {
|
|
4035
|
+
checks.push({ name: "test", bin: pm, args: pmRun(pm, "test") });
|
|
4036
|
+
}
|
|
4037
|
+
if (s.build) {
|
|
4038
|
+
checks.push({ name: "build", bin: pm, args: pmRun(pm, "build") });
|
|
4039
|
+
}
|
|
4040
|
+
return checks;
|
|
4041
|
+
}
|
|
4042
|
+
async function harness() {
|
|
4043
|
+
console.log(chalk21.bold("\n\u{1F527} " + t("harness.title")));
|
|
4044
|
+
console.log(chalk21.gray("\u2500".repeat(40)));
|
|
4045
|
+
const checks = detectChecks();
|
|
4046
|
+
if (checks.length === 0) {
|
|
4047
|
+
console.log(chalk21.yellow("\n\u26A0\uFE0F \uC2E4\uD589\uD560 \uC218 \uC788\uB294 \uC2A4\uD06C\uB9BD\uD2B8\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4."));
|
|
4048
|
+
console.log(chalk21.gray(" package.json\uC5D0 lint, test, build \uC2A4\uD06C\uB9BD\uD2B8\uB97C \uCD94\uAC00\uD574\uC8FC\uC138\uC694."));
|
|
4049
|
+
return;
|
|
4050
|
+
}
|
|
4051
|
+
console.log(chalk21.cyan(`
|
|
4052
|
+
\u{1F3C3} ${checks.length}\uAC1C \uC810\uAC80 \uC2DC\uC791:
|
|
4053
|
+
`));
|
|
4054
|
+
const results = [];
|
|
4055
|
+
for (const check2 of checks) {
|
|
4056
|
+
const display = `${check2.bin} ${check2.args.join(" ")}`;
|
|
4057
|
+
const spinner = ora3(`${check2.name} \uC2E4\uD589 \uC911...`).start();
|
|
4058
|
+
const start = Date.now();
|
|
4059
|
+
const result = safeExecFile(check2.bin, check2.args);
|
|
4060
|
+
const duration = Date.now() - start;
|
|
4061
|
+
const sec = (duration / 1e3).toFixed(1);
|
|
4062
|
+
if (result.ok) {
|
|
4063
|
+
spinner.succeed(`${check2.name} ${chalk21.gray(`(${sec}s)`)}`);
|
|
4064
|
+
results.push({ name: check2.name, command: display, passed: true, duration });
|
|
4065
|
+
} else {
|
|
4066
|
+
spinner.fail(`${check2.name} ${chalk21.gray(`(${sec}s)`)}`);
|
|
4067
|
+
results.push({
|
|
4068
|
+
name: check2.name,
|
|
4069
|
+
command: display,
|
|
4070
|
+
passed: false,
|
|
4071
|
+
duration,
|
|
4072
|
+
error: result.err.slice(0, 200)
|
|
4073
|
+
});
|
|
4074
|
+
}
|
|
4075
|
+
}
|
|
4076
|
+
console.log(chalk21.bold("\n\u{1F4CA} \uD1B5\uD569 \uB9AC\uD3EC\uD2B8:"));
|
|
4077
|
+
console.log(chalk21.gray("\u2500".repeat(40)));
|
|
4078
|
+
for (const r of results) {
|
|
4079
|
+
const icon = r.passed ? chalk21.green("\u2705") : chalk21.red("\u274C");
|
|
4080
|
+
const sec = (r.duration / 1e3).toFixed(1);
|
|
4081
|
+
console.log(` ${icon} ${r.name.padEnd(15)} ${chalk21.gray(`${sec}s`)}`);
|
|
4082
|
+
}
|
|
4083
|
+
const passed = results.filter((r) => r.passed).length;
|
|
4084
|
+
const all = passed === results.length;
|
|
4085
|
+
console.log(chalk21.gray("\u2500".repeat(40)));
|
|
4086
|
+
if (all) {
|
|
4087
|
+
console.log(chalk21.green.bold(`
|
|
4088
|
+
\u{1F389} \uC804\uCCB4 \uD1B5\uACFC! (${passed}/${results.length})`));
|
|
4089
|
+
} else {
|
|
4090
|
+
console.log(
|
|
4091
|
+
chalk21.red.bold(`
|
|
4092
|
+
\u26A0\uFE0F ${results.length - passed}\uAC1C \uC2E4\uD328 (${passed}/${results.length} \uD1B5\uACFC)`)
|
|
4093
|
+
);
|
|
4094
|
+
}
|
|
4095
|
+
printNextStep({
|
|
4096
|
+
message: all ? "\uD488\uC9C8 \uC810\uAC80 \uD1B5\uACFC!" : "\uC2E4\uD328 \uD56D\uBAA9\uC744 \uC218\uC815\uD558\uC138\uC694.",
|
|
4097
|
+
command: all ? "vhk ship" : "vhk doctor",
|
|
4098
|
+
cursorHint: all ? "\uBC30\uD3EC\uD574\uC918" : "\uBB38\uC81C \uC9C4\uB2E8\uD574\uC918"
|
|
4099
|
+
});
|
|
4100
|
+
}
|
|
4101
|
+
|
|
4102
|
+
// src/commands/audit.ts
|
|
4103
|
+
import { existsSync as existsSync8 } from "fs";
|
|
4104
|
+
import chalk22 from "chalk";
|
|
4105
|
+
import inquirer11 from "inquirer";
|
|
4106
|
+
import ora4 from "ora";
|
|
4107
|
+
function detectCurrentPM() {
|
|
4108
|
+
if (existsSync8("pnpm-lock.yaml")) return "pnpm";
|
|
4109
|
+
if (existsSync8("yarn.lock")) return "yarn";
|
|
4110
|
+
return "npm";
|
|
4111
|
+
}
|
|
4112
|
+
function parseAuditOutput(output, pm) {
|
|
4113
|
+
const empty = { critical: 0, high: 0, moderate: 0, low: 0, total: 0 };
|
|
4114
|
+
if (!output) return empty;
|
|
4115
|
+
try {
|
|
4116
|
+
const json = JSON.parse(output);
|
|
4117
|
+
const meta = json.metadata?.vulnerabilities;
|
|
4118
|
+
if (meta) {
|
|
4119
|
+
const summary = {
|
|
4120
|
+
critical: meta.critical ?? 0,
|
|
4121
|
+
high: meta.high ?? 0,
|
|
4122
|
+
moderate: meta.moderate ?? 0,
|
|
4123
|
+
low: meta.low ?? 0,
|
|
4124
|
+
total: meta.total ?? 0
|
|
4125
|
+
};
|
|
4126
|
+
if (!summary.total) {
|
|
4127
|
+
summary.total = summary.critical + summary.high + summary.moderate + summary.low;
|
|
4128
|
+
}
|
|
4129
|
+
return summary;
|
|
4130
|
+
}
|
|
4131
|
+
void pm;
|
|
4132
|
+
return empty;
|
|
4133
|
+
} catch {
|
|
4134
|
+
return empty;
|
|
4135
|
+
}
|
|
4136
|
+
}
|
|
4137
|
+
function runAuditJson(pm) {
|
|
4138
|
+
const result = safeExecFile(pm, ["audit", "--json"]);
|
|
4139
|
+
return result.out;
|
|
4140
|
+
}
|
|
4141
|
+
function runAuditFix(pm) {
|
|
4142
|
+
if (pm !== "npm") {
|
|
4143
|
+
return { ok: false, err: `${pm}\uC740 \uC790\uB3D9 fix\uB97C \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. npm \uD658\uACBD\uC5D0\uC11C\uB9CC \uB3D9\uC791\uD569\uB2C8\uB2E4.` };
|
|
4144
|
+
}
|
|
4145
|
+
const result = safeExecFile("npm", ["audit", "fix"]);
|
|
4146
|
+
return result.ok ? { ok: true } : { ok: false, err: result.err };
|
|
4147
|
+
}
|
|
4148
|
+
async function audit(autoFix = false) {
|
|
4149
|
+
console.log(chalk22.bold("\n\u{1F6E1}\uFE0F " + t("audit.title")));
|
|
4150
|
+
console.log(chalk22.gray("\u2500".repeat(40)));
|
|
4151
|
+
const pm = detectCurrentPM();
|
|
4152
|
+
console.log(chalk22.cyan(`\u{1F4E6} \uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800: ${pm}`));
|
|
4153
|
+
const spinner = ora4("\uBCF4\uC548 \uAC10\uC0AC \uC2E4\uD589 \uC911...").start();
|
|
4154
|
+
const output = runAuditJson(pm);
|
|
4155
|
+
spinner.stop();
|
|
4156
|
+
const summary = parseAuditOutput(output, pm);
|
|
4157
|
+
if (summary.total === 0) {
|
|
4158
|
+
console.log(chalk22.green.bold("\n\u{1F389} \uCDE8\uC57D\uC810\uC774 \uBC1C\uACAC\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4!"));
|
|
4159
|
+
return;
|
|
4160
|
+
}
|
|
4161
|
+
console.log(chalk22.bold("\n\u{1F4CA} \uCDE8\uC57D\uC810 \uC694\uC57D:"));
|
|
4162
|
+
if (summary.critical > 0) console.log(chalk22.red(` \u{1F534} Critical: ${summary.critical}`));
|
|
4163
|
+
if (summary.high > 0) console.log(chalk22.red(` \u{1F7E0} High: ${summary.high}`));
|
|
4164
|
+
if (summary.moderate > 0) console.log(chalk22.yellow(` \u{1F7E1} Moderate: ${summary.moderate}`));
|
|
4165
|
+
if (summary.low > 0) console.log(chalk22.gray(` \u26AA Low: ${summary.low}`));
|
|
4166
|
+
console.log(chalk22.bold(`
|
|
4167
|
+
\uCD1D ${summary.total}\uAC1C\uC758 \uCDE8\uC57D\uC810`));
|
|
4168
|
+
const shouldRunFix = autoFix ? true : summary.critical > 0 || summary.high > 0 ? (await inquirer11.prompt([
|
|
4169
|
+
{
|
|
4170
|
+
type: "confirm",
|
|
4171
|
+
name: "shouldFix",
|
|
4172
|
+
message: "\uC790\uB3D9 \uC218\uC815\uC744 \uC2DC\uB3C4\uD560\uAE4C\uC694? (npm audit fix)",
|
|
4173
|
+
default: true
|
|
4174
|
+
}
|
|
4175
|
+
])).shouldFix : false;
|
|
4176
|
+
if (shouldRunFix) {
|
|
4177
|
+
const fixSpinner = ora4("\uC790\uB3D9 \uC218\uC815 \uC911...").start();
|
|
4178
|
+
const result = runAuditFix(pm);
|
|
4179
|
+
if (result.ok) {
|
|
4180
|
+
fixSpinner.succeed("\uC790\uB3D9 \uC218\uC815 \uC644\uB8CC!");
|
|
4181
|
+
} else {
|
|
4182
|
+
fixSpinner.warn(result.err ?? "\uC77C\uBD80 \uCDE8\uC57D\uC810\uC740 \uC218\uB3D9 \uC218\uC815\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.");
|
|
4183
|
+
}
|
|
4184
|
+
}
|
|
4185
|
+
printNextStep({
|
|
4186
|
+
message: "\uBCF4\uC548 \uAC10\uC0AC \uC644\uB8CC.",
|
|
4187
|
+
command: "vhk harness",
|
|
4188
|
+
cursorHint: "\uD488\uC9C8 \uC810\uAC80\uD574\uC918"
|
|
4189
|
+
});
|
|
4190
|
+
}
|
|
4191
|
+
|
|
4192
|
+
// src/commands/migrate.ts
|
|
4193
|
+
import { existsSync as existsSync9, unlinkSync, rmSync } from "fs";
|
|
4194
|
+
import chalk23 from "chalk";
|
|
4195
|
+
import inquirer12 from "inquirer";
|
|
4196
|
+
import ora5 from "ora";
|
|
4197
|
+
var LOCK_FILES = {
|
|
4198
|
+
npm: "package-lock.json",
|
|
4199
|
+
yarn: "yarn.lock",
|
|
4200
|
+
pnpm: "pnpm-lock.yaml"
|
|
4201
|
+
};
|
|
4202
|
+
function detectCurrentPM2() {
|
|
4203
|
+
if (existsSync9("pnpm-lock.yaml")) return "pnpm";
|
|
4204
|
+
if (existsSync9("yarn.lock")) return "yarn";
|
|
4205
|
+
if (existsSync9("package-lock.json")) return "npm";
|
|
4206
|
+
return null;
|
|
4207
|
+
}
|
|
4208
|
+
function isCLIAvailable2(pm) {
|
|
4209
|
+
return safeExecFile(pm, ["--version"]).ok;
|
|
4210
|
+
}
|
|
4211
|
+
async function migrate(target) {
|
|
4212
|
+
console.log(chalk23.bold("\n\u{1F504} " + t("migrate.title")));
|
|
4213
|
+
console.log(chalk23.gray("\u2500".repeat(40)));
|
|
4214
|
+
const current = detectCurrentPM2();
|
|
4215
|
+
console.log(chalk23.cyan(`
|
|
4216
|
+
\uD604\uC7AC \uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800: ${current ?? "\uAC10\uC9C0 \uBD88\uAC00"}`));
|
|
4217
|
+
let targetPM;
|
|
4218
|
+
if (target && ["npm", "yarn", "pnpm"].includes(target)) {
|
|
4219
|
+
targetPM = target;
|
|
4220
|
+
} else {
|
|
4221
|
+
const choices = ["npm", "yarn", "pnpm"].filter((pm) => pm !== current).map((pm) => ({ name: pm, value: pm }));
|
|
4222
|
+
const { selected } = await inquirer12.prompt([
|
|
4223
|
+
{
|
|
4224
|
+
type: "list",
|
|
4225
|
+
name: "selected",
|
|
4226
|
+
message: t("migrate.selectTarget"),
|
|
4227
|
+
choices
|
|
4228
|
+
}
|
|
4229
|
+
]);
|
|
4230
|
+
targetPM = selected;
|
|
4231
|
+
}
|
|
4232
|
+
if (targetPM === current) {
|
|
4233
|
+
console.log(chalk23.yellow(`
|
|
4234
|
+
\u26A0\uFE0F \uC774\uBBF8 ${targetPM}\uC744 \uC0AC\uC6A9 \uC911\uC785\uB2C8\uB2E4.`));
|
|
4235
|
+
return;
|
|
4236
|
+
}
|
|
4237
|
+
if (!isCLIAvailable2(targetPM)) {
|
|
4238
|
+
console.log(chalk23.red(`
|
|
4239
|
+
\u274C ${targetPM}\uC774 \uC124\uCE58\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.`));
|
|
4240
|
+
console.log(chalk23.yellow(` npm i -g ${targetPM}`));
|
|
4241
|
+
return;
|
|
4242
|
+
}
|
|
4243
|
+
const { confirm } = await inquirer12.prompt([
|
|
4244
|
+
{
|
|
4245
|
+
type: "confirm",
|
|
4246
|
+
name: "confirm",
|
|
4247
|
+
message: `${current ?? "\uD604\uC7AC"} \u2192 ${targetPM}\uC73C\uB85C \uC804\uD658\uD560\uAE4C\uC694? (node_modules \uC7AC\uC124\uCE58)`,
|
|
4248
|
+
default: true
|
|
4249
|
+
}
|
|
4250
|
+
]);
|
|
4251
|
+
if (!confirm) {
|
|
4252
|
+
console.log(chalk23.gray("\uCDE8\uC18C\uB428"));
|
|
4253
|
+
return;
|
|
4254
|
+
}
|
|
4255
|
+
const cleanup = ora5("\uAE30\uC874 lock \uD30C\uC77C \uC815\uB9AC \uC911...").start();
|
|
4256
|
+
for (const lockFile of Object.values(LOCK_FILES)) {
|
|
4257
|
+
if (existsSync9(lockFile)) {
|
|
4258
|
+
unlinkSync(lockFile);
|
|
4259
|
+
}
|
|
4260
|
+
}
|
|
4261
|
+
if (existsSync9("node_modules")) {
|
|
4262
|
+
cleanup.text = "node_modules \uC0AD\uC81C \uC911...";
|
|
4263
|
+
rmSync("node_modules", { recursive: true, force: true });
|
|
4264
|
+
}
|
|
4265
|
+
cleanup.succeed("\uAE30\uC874 \uD30C\uC77C \uC815\uB9AC \uC644\uB8CC");
|
|
4266
|
+
const install = ora5(`${targetPM} install \uC2E4\uD589 \uC911...`).start();
|
|
4267
|
+
const installResult = safeExecFile(targetPM, ["install"]);
|
|
4268
|
+
if (installResult.ok) {
|
|
4269
|
+
install.succeed(`${targetPM} install \uC644\uB8CC!`);
|
|
4270
|
+
} else {
|
|
4271
|
+
install.fail(`${targetPM} install \uC2E4\uD328`);
|
|
4272
|
+
console.log(chalk23.red(installResult.err.slice(0, 300)));
|
|
4273
|
+
return;
|
|
4274
|
+
}
|
|
4275
|
+
console.log(chalk23.green.bold(`
|
|
4276
|
+
\u{1F389} ${current ?? "\uC774\uC804"} \u2192 ${targetPM} \uC804\uD658 \uC644\uB8CC!`));
|
|
4277
|
+
printNextStep({
|
|
4278
|
+
message: "\uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800 \uC804\uD658 \uC644\uB8CC!",
|
|
4279
|
+
command: "vhk harness",
|
|
4280
|
+
cursorHint: "\uD488\uC9C8 \uC810\uAC80\uD574\uC918"
|
|
4281
|
+
});
|
|
4282
|
+
}
|
|
4283
|
+
|
|
4284
|
+
// src/commands/update.ts
|
|
4285
|
+
import { execSync as execSync3 } from "child_process";
|
|
4286
|
+
import { existsSync as existsSync10, readFileSync as readFileSync6 } from "fs";
|
|
4287
|
+
import { dirname, join as join2 } from "path";
|
|
4288
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
4289
|
+
import chalk24 from "chalk";
|
|
4290
|
+
import ora6 from "ora";
|
|
4291
|
+
var PACKAGE = "@byh3071/vhk";
|
|
4292
|
+
function getCurrentVersion() {
|
|
4293
|
+
const dir = dirname(fileURLToPath3(import.meta.url));
|
|
4294
|
+
for (const pkgPath of [join2(dir, "../package.json"), join2(dir, "../../package.json")]) {
|
|
4295
|
+
try {
|
|
4296
|
+
if (existsSync10(pkgPath)) {
|
|
4297
|
+
const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
|
|
4298
|
+
if (pkg.version) return pkg.version;
|
|
4299
|
+
}
|
|
4300
|
+
} catch {
|
|
4301
|
+
continue;
|
|
4302
|
+
}
|
|
4303
|
+
}
|
|
4304
|
+
return "0.0.0";
|
|
4305
|
+
}
|
|
4306
|
+
function getLatestVersion() {
|
|
4307
|
+
try {
|
|
4308
|
+
const out = execSync3(`npm view ${PACKAGE} version`, {
|
|
4309
|
+
encoding: "utf-8",
|
|
4310
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4311
|
+
}).toString();
|
|
4312
|
+
return out.trim();
|
|
4313
|
+
} catch {
|
|
4314
|
+
return null;
|
|
4315
|
+
}
|
|
4316
|
+
}
|
|
4317
|
+
function isUpToDate(current, latest) {
|
|
4318
|
+
const parse = (v) => v.split(".").map((n) => parseInt(n, 10) || 0);
|
|
4319
|
+
const [ca, cb, cc] = parse(current);
|
|
4320
|
+
const [la, lb, lc] = parse(latest);
|
|
4321
|
+
if (ca !== la) return ca > la;
|
|
4322
|
+
if (cb !== lb) return cb > lb;
|
|
4323
|
+
return cc >= lc;
|
|
4324
|
+
}
|
|
4325
|
+
async function update() {
|
|
4326
|
+
console.log(chalk24.bold("\n\u2B06\uFE0F " + t("update.title")));
|
|
4327
|
+
console.log(chalk24.gray("\u2500".repeat(40)));
|
|
4328
|
+
const current = getCurrentVersion();
|
|
4329
|
+
console.log(chalk24.cyan(`
|
|
4330
|
+
\u{1F4CC} \uD604\uC7AC \uBC84\uC804: v${current}`));
|
|
4331
|
+
const spinner = ora6("\uCD5C\uC2E0 \uBC84\uC804 \uD655\uC778 \uC911...").start();
|
|
4332
|
+
const latest = getLatestVersion();
|
|
4333
|
+
if (!latest) {
|
|
4334
|
+
spinner.fail("\uCD5C\uC2E0 \uBC84\uC804\uC744 \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
4335
|
+
console.log(chalk24.yellow(" \uB124\uD2B8\uC6CC\uD06C\uB97C \uD655\uC778\uD558\uAC70\uB098 \uC218\uB3D9\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694:"));
|
|
4336
|
+
console.log(chalk24.gray(` npm update -g ${PACKAGE}`));
|
|
4337
|
+
return;
|
|
4338
|
+
}
|
|
4339
|
+
spinner.stop();
|
|
4340
|
+
console.log(chalk24.cyan(`\u{1F195} \uCD5C\uC2E0 \uBC84\uC804: v${latest}`));
|
|
4341
|
+
if (isUpToDate(current, latest)) {
|
|
4342
|
+
console.log(chalk24.green("\n\u2705 \uC774\uBBF8 \uCD5C\uC2E0 \uBC84\uC804\uC785\uB2C8\uB2E4!"));
|
|
4343
|
+
return;
|
|
4344
|
+
}
|
|
4345
|
+
const updateSpinner = ora6(`v${latest}\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8 \uC911...`).start();
|
|
4346
|
+
try {
|
|
4347
|
+
execSync3(`npm update -g ${PACKAGE}`, { stdio: ["pipe", "pipe", "pipe"] });
|
|
4348
|
+
updateSpinner.succeed(`v${latest}\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC!`);
|
|
4349
|
+
console.log(chalk24.green.bold(`
|
|
4350
|
+
\u{1F389} VHK CLI v${latest} \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC!`));
|
|
4351
|
+
console.log(chalk24.gray(" \uBCC0\uACBD \uC0AC\uD56D\uC740 GitHub Releases\uB97C \uD655\uC778\uD558\uC138\uC694."));
|
|
4352
|
+
} catch (err) {
|
|
4353
|
+
updateSpinner.fail("\uC5C5\uB370\uC774\uD2B8 \uC2E4\uD328");
|
|
4354
|
+
const msg = err instanceof Error ? err.message.slice(0, 300) : String(err);
|
|
4355
|
+
console.log(chalk24.red(msg));
|
|
4356
|
+
console.log(chalk24.yellow("\n\uC218\uB3D9\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694:"));
|
|
4357
|
+
console.log(chalk24.gray(` npm update -g ${PACKAGE}`));
|
|
4358
|
+
}
|
|
4359
|
+
}
|
|
4360
|
+
|
|
4361
|
+
// src/commands/context.ts
|
|
4362
|
+
import {
|
|
4363
|
+
existsSync as existsSync11,
|
|
4364
|
+
mkdirSync as mkdirSync5,
|
|
4365
|
+
readFileSync as readFileSync7,
|
|
4366
|
+
readdirSync,
|
|
4367
|
+
statSync,
|
|
4368
|
+
writeFileSync as writeFileSync6
|
|
4369
|
+
} from "fs";
|
|
4370
|
+
import { join as join3 } from "path";
|
|
4371
|
+
import chalk25 from "chalk";
|
|
4372
|
+
var CONTEXT_PATH = ".vhk/context.md";
|
|
4373
|
+
var IGNORE_DIRS2 = /* @__PURE__ */ new Set([
|
|
4374
|
+
"node_modules",
|
|
4375
|
+
".git",
|
|
4376
|
+
"dist",
|
|
4377
|
+
".next",
|
|
4378
|
+
".nuxt",
|
|
4379
|
+
".output",
|
|
4380
|
+
"coverage",
|
|
4381
|
+
".cache",
|
|
4382
|
+
".turbo",
|
|
4383
|
+
".vhk"
|
|
4384
|
+
]);
|
|
4385
|
+
function buildTree(dir, prefix = "", maxDepth = 3, depth = 0) {
|
|
4386
|
+
if (depth >= maxDepth) return [];
|
|
4387
|
+
const lines = [];
|
|
4388
|
+
try {
|
|
4389
|
+
const entries = readdirSync(dir);
|
|
4390
|
+
const filtered = entries.filter(
|
|
4391
|
+
(e) => (!e.startsWith(".") || e === ".env.example") && !IGNORE_DIRS2.has(e)
|
|
4392
|
+
);
|
|
4393
|
+
filtered.forEach((entry, index) => {
|
|
4394
|
+
const isLast = index === filtered.length - 1;
|
|
4395
|
+
const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
4396
|
+
const fullPath = join3(dir, entry);
|
|
4397
|
+
const stat = statSync(fullPath);
|
|
4398
|
+
const isDir = stat.isDirectory();
|
|
4399
|
+
lines.push(`${prefix}${connector}${entry}${isDir ? "/" : ""}`);
|
|
4400
|
+
if (isDir) {
|
|
4401
|
+
const nextPrefix = prefix + (isLast ? " " : "\u2502 ");
|
|
4402
|
+
lines.push(...buildTree(fullPath, nextPrefix, maxDepth, depth + 1));
|
|
4403
|
+
}
|
|
4404
|
+
});
|
|
4405
|
+
} catch {
|
|
4406
|
+
}
|
|
4407
|
+
return lines;
|
|
4408
|
+
}
|
|
4409
|
+
function extractTechStack() {
|
|
4410
|
+
const stack = {};
|
|
4411
|
+
try {
|
|
4412
|
+
const pkg = readJsonFile("package.json");
|
|
4413
|
+
const all = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
4414
|
+
if (all.next) stack["\uD504\uB808\uC784\uC6CC\uD06C"] = `Next.js ${all.next}`;
|
|
4415
|
+
else if (all.nuxt) stack["\uD504\uB808\uC784\uC6CC\uD06C"] = `Nuxt ${all.nuxt}`;
|
|
4416
|
+
else if (all.react) stack["\uD504\uB808\uC784\uC6CC\uD06C"] = `React ${all.react}`;
|
|
4417
|
+
else if (all.vue) stack["\uD504\uB808\uC784\uC6CC\uD06C"] = `Vue ${all.vue}`;
|
|
4418
|
+
else if (all.svelte) stack["\uD504\uB808\uC784\uC6CC\uD06C"] = `Svelte ${all.svelte}`;
|
|
4419
|
+
if (all.typescript) stack["\uC5B8\uC5B4"] = `TypeScript ${all.typescript}`;
|
|
4420
|
+
if (all.tailwindcss) stack["\uC2A4\uD0C0\uC77C"] = `Tailwind CSS ${all.tailwindcss}`;
|
|
4421
|
+
if (all.tsup) stack["\uBE4C\uB4DC"] = "tsup";
|
|
4422
|
+
else if (all.vite) stack["\uBE4C\uB4DC"] = `Vite ${all.vite}`;
|
|
4423
|
+
else if (all.webpack) stack["\uBE4C\uB4DC"] = "webpack";
|
|
4424
|
+
if (all.vitest) stack["\uD14C\uC2A4\uD2B8"] = "vitest";
|
|
4425
|
+
else if (all.jest) stack["\uD14C\uC2A4\uD2B8"] = "jest";
|
|
4426
|
+
if (all.commander) stack["CLI"] = "commander";
|
|
4427
|
+
if (all.inquirer) stack["\uC778\uD130\uB799\uD2F0\uBE0C"] = "inquirer";
|
|
4428
|
+
if (existsSync11("pnpm-lock.yaml")) stack["\uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800"] = "pnpm";
|
|
4429
|
+
else if (existsSync11("yarn.lock")) stack["\uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800"] = "yarn";
|
|
4430
|
+
else stack["\uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800"] = "npm";
|
|
4431
|
+
if (pkg.name) stack["\uD328\uD0A4\uC9C0 \uC774\uB984"] = pkg.name;
|
|
4432
|
+
if (pkg.version) stack["\uBC84\uC804"] = pkg.version;
|
|
4433
|
+
} catch {
|
|
4434
|
+
}
|
|
4435
|
+
return stack;
|
|
4436
|
+
}
|
|
4437
|
+
function getVhkCommands() {
|
|
4438
|
+
return [
|
|
4439
|
+
"gate \u2014 \uC544\uC774\uB514\uC5B4 \uAC80\uC99D",
|
|
4440
|
+
"init \u2014 \uD504\uB85C\uC81D\uD2B8 \uCD08\uAE30\uD654",
|
|
4441
|
+
"recap \u2014 \uC138\uC158 \uC694\uC57D \uC800\uC7A5",
|
|
4442
|
+
"sync \u2014 \uADDC\uCE59 \uD30C\uC77C \uB3D9\uAE30\uD654",
|
|
4443
|
+
"check \u2014 \uADDC\uCE59 \uC810\uAC80",
|
|
4444
|
+
"secure \u2014 \uBCF4\uC548 \uC2A4\uCE94",
|
|
4445
|
+
"ship \u2014 \uBC30\uD3EC \uCCB4\uD06C + \uD68C\uACE0",
|
|
4446
|
+
"doctor \u2014 \uD658\uACBD \uC9C4\uB2E8",
|
|
4447
|
+
"save \u2014 git \uC800\uC7A5 (add+commit+push)",
|
|
4448
|
+
"undo \u2014 \uCD5C\uADFC \uCEE4\uBC0B \uB418\uB3CC\uB9AC\uAE30",
|
|
4449
|
+
"status \u2014 git \uC0C1\uD0DC \uD655\uC778",
|
|
4450
|
+
"diff \u2014 git \uBCC0\uACBD \uC0AC\uD56D \uC694\uC57D",
|
|
4451
|
+
"deploy \u2014 \uD504\uB85C\uB355\uC158 \uBC30\uD3EC",
|
|
4452
|
+
"env \u2014 \uD658\uACBD\uBCC0\uC218 \uAD00\uB9AC",
|
|
4453
|
+
"publish \u2014 npm \uBC30\uD3EC \uC790\uB3D9\uD654",
|
|
4454
|
+
"design \u2014 \uB514\uC790\uC778 \uD1A0\uD070 \uC0DD\uC131",
|
|
4455
|
+
"design-palette \u2014 \uCEEC\uB7EC \uD314\uB808\uD2B8 \uC120\uD0DD",
|
|
4456
|
+
"theme \u2014 \uB2E4\uD06C/\uB77C\uC774\uD2B8 \uBAA8\uB4DC",
|
|
4457
|
+
"ref add|list|open \u2014 \uB808\uD37C\uB7F0\uC2A4 URL \uAD00\uB9AC",
|
|
4458
|
+
"harness \u2014 \uD1B5\uD569 \uD488\uC9C8 \uC810\uAC80",
|
|
4459
|
+
"audit \u2014 \uBCF4\uC548 \uCDE8\uC57D\uC810 \uAC10\uC0AC",
|
|
4460
|
+
"migrate \u2014 \uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800 \uC804\uD658",
|
|
4461
|
+
"update \u2014 VHK CLI \uC140\uD504 \uC5C5\uB370\uC774\uD2B8",
|
|
4462
|
+
"context \u2014 \uD504\uB85C\uC81D\uD2B8 \uB9E5\uB77D \uC0DD\uC131",
|
|
4463
|
+
"context-show \u2014 \uB9E5\uB77D \uD30C\uC77C \uBCF4\uAE30",
|
|
4464
|
+
"memory add|list|remove \u2014 \uACB0\uC815\uC0AC\uD56D \uAE30\uC5B5",
|
|
4465
|
+
"brief \u2014 \uD504\uB85C\uC81D\uD2B8 \uC694\uC57D \uBCF4\uACE0\uC11C",
|
|
4466
|
+
"mcp \u2014 MCP \uC11C\uBC84 \uC2DC\uC791",
|
|
4467
|
+
"mcp-init \u2014 Cursor MCP \uC124\uC815"
|
|
4468
|
+
];
|
|
4469
|
+
}
|
|
4470
|
+
async function context() {
|
|
4471
|
+
console.log(chalk25.bold("\n\u{1F9E0} " + t("context.title")));
|
|
4472
|
+
console.log(chalk25.gray("\u2500".repeat(40)));
|
|
4473
|
+
const stack = extractTechStack();
|
|
4474
|
+
const tree = buildTree(".").join("\n");
|
|
4475
|
+
const commands = getVhkCommands();
|
|
4476
|
+
const lines = [];
|
|
4477
|
+
lines.push("# \uD504\uB85C\uC81D\uD2B8 \uCEE8\uD14D\uC2A4\uD2B8");
|
|
4478
|
+
lines.push("");
|
|
4479
|
+
lines.push("> \uC774 \uD30C\uC77C\uC740 `vhk context`\uB85C \uC790\uB3D9 \uC0DD\uC131\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
|
|
4480
|
+
lines.push("> AI \uC5B4\uC2DC\uC2A4\uD134\uD2B8\uC5D0\uAC8C \uD504\uB85C\uC81D\uD2B8 \uB9E5\uB77D\uC744 \uC81C\uACF5\uD569\uB2C8\uB2E4.");
|
|
4481
|
+
lines.push("");
|
|
4482
|
+
lines.push("## \uAE30\uC220 \uC2A4\uD0DD");
|
|
4483
|
+
lines.push("");
|
|
4484
|
+
for (const [key, value] of Object.entries(stack)) {
|
|
4485
|
+
lines.push(`- **${key}**: ${value}`);
|
|
4486
|
+
}
|
|
4487
|
+
lines.push("");
|
|
4488
|
+
lines.push("## \uB514\uB809\uD1A0\uB9AC \uAD6C\uC870");
|
|
4489
|
+
lines.push("");
|
|
4490
|
+
lines.push("```");
|
|
4491
|
+
lines.push(tree);
|
|
4492
|
+
lines.push("```");
|
|
4493
|
+
lines.push("");
|
|
4494
|
+
lines.push("## VHK CLI \uBA85\uB839\uC5B4");
|
|
4495
|
+
lines.push("");
|
|
4496
|
+
for (const cmd of commands) {
|
|
4497
|
+
lines.push(`- \`vhk ${cmd}\``);
|
|
4498
|
+
}
|
|
4499
|
+
lines.push("");
|
|
4500
|
+
if (existsSync11(".vhk/memory.json")) {
|
|
4501
|
+
try {
|
|
4502
|
+
const memories = readJsonFile(
|
|
4503
|
+
".vhk/memory.json"
|
|
4504
|
+
);
|
|
4505
|
+
if (Array.isArray(memories) && memories.length > 0) {
|
|
4506
|
+
lines.push("## \uC800\uC7A5\uB41C \uACB0\uC815\uC0AC\uD56D");
|
|
4507
|
+
lines.push("");
|
|
4508
|
+
for (const m of memories) {
|
|
4509
|
+
const date = new Date(m.addedAt).toLocaleDateString("ko-KR");
|
|
4510
|
+
lines.push(`- ${m.content} _(${date})_`);
|
|
4511
|
+
}
|
|
4512
|
+
lines.push("");
|
|
4513
|
+
}
|
|
4514
|
+
} catch {
|
|
4515
|
+
}
|
|
4516
|
+
}
|
|
4517
|
+
lines.push("---");
|
|
4518
|
+
lines.push("");
|
|
4519
|
+
lines.push(`_\uC0DD\uC131: ${(/* @__PURE__ */ new Date()).toLocaleString("ko-KR")}_`);
|
|
4520
|
+
lines.push("");
|
|
4521
|
+
mkdirSync5(".vhk", { recursive: true });
|
|
4522
|
+
writeFileSync6(CONTEXT_PATH, lines.join("\n"), "utf-8");
|
|
4523
|
+
console.log(chalk25.green(`
|
|
4524
|
+
\u2705 ${CONTEXT_PATH} \uC0DD\uC131 \uC644\uB8CC!`));
|
|
4525
|
+
console.log(chalk25.gray(` \uAE30\uC220 \uC2A4\uD0DD ${Object.keys(stack).length}\uAC1C \uAC10\uC9C0`));
|
|
4526
|
+
console.log(chalk25.gray(" AI \uC5B4\uC2DC\uC2A4\uD134\uD2B8\uC5D0\uAC8C \uC774 \uD30C\uC77C\uC744 \uCC38\uC870\uD558\uAC8C \uD558\uC138\uC694."));
|
|
4527
|
+
printNextStep({
|
|
4528
|
+
message: "\uCEE8\uD14D\uC2A4\uD2B8 \uD30C\uC77C \uC0DD\uC131 \uC644\uB8CC!",
|
|
4529
|
+
command: "vhk context-show",
|
|
4530
|
+
cursorHint: "\uCEE8\uD14D\uC2A4\uD2B8 \uBCF4\uC5EC\uC918"
|
|
4531
|
+
});
|
|
4532
|
+
}
|
|
4533
|
+
async function contextShow() {
|
|
4534
|
+
console.log(chalk25.bold("\n\u{1F4C4} " + t("context.showTitle")));
|
|
4535
|
+
console.log(chalk25.gray("\u2500".repeat(40)));
|
|
4536
|
+
if (!existsSync11(CONTEXT_PATH)) {
|
|
4537
|
+
console.log(chalk25.yellow("\n\u26A0\uFE0F \uCEE8\uD14D\uC2A4\uD2B8 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
|
|
4538
|
+
console.log(chalk25.gray(" vhk context\uB97C \uBA3C\uC800 \uC2E4\uD589\uD558\uC138\uC694."));
|
|
4539
|
+
return;
|
|
4540
|
+
}
|
|
4541
|
+
const content = readFileSync7(CONTEXT_PATH, "utf-8");
|
|
4542
|
+
console.log("\n" + content);
|
|
4543
|
+
}
|
|
4544
|
+
|
|
4545
|
+
// src/commands/memory.ts
|
|
4546
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
|
|
4547
|
+
import chalk26 from "chalk";
|
|
4548
|
+
var MEMORY_PATH = ".vhk/memory.json";
|
|
4549
|
+
function loadMemories() {
|
|
4550
|
+
if (!existsSync12(MEMORY_PATH)) return [];
|
|
4551
|
+
try {
|
|
4552
|
+
const parsed = readJsonFile(MEMORY_PATH);
|
|
4553
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
4554
|
+
} catch {
|
|
4555
|
+
return [];
|
|
4556
|
+
}
|
|
4557
|
+
}
|
|
4558
|
+
function saveMemories(memories) {
|
|
4559
|
+
mkdirSync6(".vhk", { recursive: true });
|
|
4560
|
+
writeFileSync7(MEMORY_PATH, JSON.stringify(memories, null, 2) + "\n", "utf-8");
|
|
4561
|
+
}
|
|
4562
|
+
async function memoryAdd(content, tags) {
|
|
4563
|
+
console.log(chalk26.bold("\n\u{1F9E0} " + t("memory.addTitle")));
|
|
4564
|
+
console.log(chalk26.gray("\u2500".repeat(40)));
|
|
4565
|
+
if (!content) {
|
|
4566
|
+
console.log(chalk26.red("\u274C \uAE30\uC5B5\uD560 \uB0B4\uC6A9\uC744 \uC785\uB825\uD574\uC8FC\uC138\uC694."));
|
|
4567
|
+
console.log(chalk26.gray(' \uC608: vhk memory add "API\uB294 tRPC \uC0AC\uC6A9\uD558\uAE30\uB85C \uACB0\uC815"'));
|
|
4568
|
+
return;
|
|
4569
|
+
}
|
|
4570
|
+
const memories = loadMemories();
|
|
4571
|
+
memories.push({
|
|
4572
|
+
content,
|
|
4573
|
+
addedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4574
|
+
tags: tags && tags.length > 0 ? tags : []
|
|
4575
|
+
});
|
|
4576
|
+
saveMemories(memories);
|
|
4577
|
+
console.log(chalk26.green(`
|
|
4578
|
+
\u2705 \uAE30\uC5B5 \uC800\uC7A5\uB428 (#${memories.length})`));
|
|
4579
|
+
console.log(chalk26.cyan(` \u{1F4DD} ${content}`));
|
|
4580
|
+
printNextStep({
|
|
4581
|
+
message: "\uAE30\uC5B5 \uC800\uC7A5 \uC644\uB8CC!",
|
|
4582
|
+
command: "vhk memory list",
|
|
4583
|
+
cursorHint: "\uAE30\uC5B5 \uBAA9\uB85D \uBCF4\uC5EC\uC918"
|
|
4584
|
+
});
|
|
4585
|
+
}
|
|
4586
|
+
async function memoryList() {
|
|
4587
|
+
console.log(chalk26.bold("\n\u{1F9E0} " + t("memory.listTitle")));
|
|
4588
|
+
console.log(chalk26.gray("\u2500".repeat(40)));
|
|
4589
|
+
const memories = loadMemories();
|
|
4590
|
+
if (memories.length === 0) {
|
|
4591
|
+
console.log(chalk26.yellow("\n\u{1F4ED} \uC800\uC7A5\uB41C \uAE30\uC5B5\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
|
|
4592
|
+
console.log(chalk26.gray(' vhk memory add "\uB0B4\uC6A9"\uC73C\uB85C \uCD94\uAC00\uD558\uC138\uC694.'));
|
|
4593
|
+
return;
|
|
4594
|
+
}
|
|
4595
|
+
console.log(chalk26.cyan(`
|
|
4596
|
+
\uCD1D ${memories.length}\uAC1C\uC758 \uAE30\uC5B5:
|
|
4597
|
+
`));
|
|
4598
|
+
memories.forEach((m, index) => {
|
|
4599
|
+
const date = new Date(m.addedAt).toLocaleDateString("ko-KR");
|
|
4600
|
+
console.log(chalk26.white(` [${index + 1}] ${m.content}`));
|
|
4601
|
+
if (m.tags && m.tags.length > 0) {
|
|
4602
|
+
console.log(chalk26.blue(` \u{1F3F7}\uFE0F ${m.tags.join(", ")}`));
|
|
4603
|
+
}
|
|
4604
|
+
console.log(chalk26.gray(` \u{1F4C5} ${date}`));
|
|
4605
|
+
console.log("");
|
|
4606
|
+
});
|
|
4607
|
+
}
|
|
4608
|
+
async function memoryRemove(indexStr) {
|
|
4609
|
+
const memories = loadMemories();
|
|
4610
|
+
const idx = parseInt(indexStr, 10) - 1;
|
|
4611
|
+
if (Number.isNaN(idx) || idx < 0 || idx >= memories.length) {
|
|
4612
|
+
console.log(chalk26.red(`\u274C \uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uBC88\uD638\uC785\uB2C8\uB2E4. (1~${memories.length || 0})`));
|
|
4613
|
+
return;
|
|
4614
|
+
}
|
|
4615
|
+
const removed = memories.splice(idx, 1)[0];
|
|
4616
|
+
saveMemories(memories);
|
|
4617
|
+
console.log(chalk26.green("\n\u2705 \uAE30\uC5B5 \uC0AD\uC81C\uB428:"));
|
|
4618
|
+
console.log(chalk26.gray(` ${removed.content}`));
|
|
4619
|
+
}
|
|
4620
|
+
|
|
4621
|
+
// src/commands/brief.ts
|
|
4622
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync7, writeFileSync as writeFileSync8 } from "fs";
|
|
4623
|
+
import chalk27 from "chalk";
|
|
4624
|
+
var BRIEF_PATH = ".vhk/brief.md";
|
|
4625
|
+
function git2(args) {
|
|
4626
|
+
const result = safeExecFile("git", args);
|
|
4627
|
+
return result.ok ? result.out : "";
|
|
4628
|
+
}
|
|
4629
|
+
async function brief() {
|
|
4630
|
+
console.log(chalk27.bold("\n\u{1F4CB} " + t("brief.title")));
|
|
4631
|
+
console.log(chalk27.gray("\u2500".repeat(40)));
|
|
4632
|
+
const lines = [];
|
|
4633
|
+
lines.push("# \uD504\uB85C\uC81D\uD2B8 \uBE0C\uB9AC\uD551");
|
|
4634
|
+
lines.push("");
|
|
4635
|
+
lines.push(`> \uC0DD\uC131: ${(/* @__PURE__ */ new Date()).toLocaleString("ko-KR")}`);
|
|
4636
|
+
lines.push("");
|
|
4637
|
+
try {
|
|
4638
|
+
const pkg = readJsonFile("package.json");
|
|
4639
|
+
lines.push("## \uD504\uB85C\uC81D\uD2B8 \uC815\uBCF4");
|
|
4640
|
+
lines.push("");
|
|
4641
|
+
lines.push(`- **\uC774\uB984**: ${pkg.name ?? "\uBBF8\uC815"}`);
|
|
4642
|
+
lines.push(`- **\uBC84\uC804**: ${pkg.version ?? "\uBBF8\uC815"}`);
|
|
4643
|
+
lines.push(`- **\uC124\uBA85**: ${pkg.description ?? "\uC5C6\uC74C"}`);
|
|
4644
|
+
const deps = Object.keys(pkg.dependencies ?? {}).length;
|
|
4645
|
+
const devDeps = Object.keys(pkg.devDependencies ?? {}).length;
|
|
4646
|
+
lines.push(`- **\uC758\uC874\uC131**: ${deps}\uAC1C (dev: ${devDeps}\uAC1C)`);
|
|
4647
|
+
lines.push("");
|
|
4648
|
+
} catch {
|
|
4649
|
+
lines.push("## \uD504\uB85C\uC81D\uD2B8 \uC815\uBCF4");
|
|
4650
|
+
lines.push("");
|
|
4651
|
+
lines.push("\u26A0\uFE0F package.json\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
4652
|
+
lines.push("");
|
|
4653
|
+
}
|
|
4654
|
+
const branch = git2(["branch", "--show-current"]);
|
|
4655
|
+
const lastCommit = git2(["log", "-1", "--pretty=format:%h %s (%cr)"]);
|
|
4656
|
+
const uncommitted = git2(["status", "--porcelain"]);
|
|
4657
|
+
const totalCommits = git2(["rev-list", "--count", "HEAD"]);
|
|
4658
|
+
lines.push("## Git \uC0C1\uD0DC");
|
|
4659
|
+
lines.push("");
|
|
4660
|
+
lines.push(`- **\uD604\uC7AC \uBE0C\uB79C\uCE58**: ${branch || "\uC54C \uC218 \uC5C6\uC74C"}`);
|
|
4661
|
+
lines.push(`- **\uB9C8\uC9C0\uB9C9 \uCEE4\uBC0B**: ${lastCommit || "\uC5C6\uC74C"}`);
|
|
4662
|
+
lines.push(`- **\uCD1D \uCEE4\uBC0B \uC218**: ${totalCommits || "\uC54C \uC218 \uC5C6\uC74C"}`);
|
|
4663
|
+
lines.push(
|
|
4664
|
+
`- **\uBBF8\uCEE4\uBC0B \uBCC0\uACBD**: ${uncommitted ? `${uncommitted.split("\n").length}\uAC1C \uD30C\uC77C` : "\uC5C6\uC74C \u2705"}`
|
|
4665
|
+
);
|
|
4666
|
+
lines.push("");
|
|
4667
|
+
if (existsSync13(".vhk/memory.json")) {
|
|
4668
|
+
try {
|
|
4669
|
+
const memories = readJsonFile(".vhk/memory.json");
|
|
4670
|
+
if (Array.isArray(memories) && memories.length > 0) {
|
|
4671
|
+
lines.push(`## \uC800\uC7A5\uB41C \uACB0\uC815\uC0AC\uD56D (${memories.length}\uAC1C)`);
|
|
4672
|
+
lines.push("");
|
|
4673
|
+
for (const m of memories.slice(-5)) {
|
|
4674
|
+
lines.push(`- ${m.content}`);
|
|
4675
|
+
}
|
|
4676
|
+
if (memories.length > 5) {
|
|
4677
|
+
lines.push(`- ... \uC678 ${memories.length - 5}\uAC1C`);
|
|
4678
|
+
}
|
|
4679
|
+
lines.push("");
|
|
4680
|
+
}
|
|
4681
|
+
} catch {
|
|
4682
|
+
}
|
|
4683
|
+
}
|
|
4684
|
+
if (existsSync13(".vhk/refs.json")) {
|
|
4685
|
+
try {
|
|
4686
|
+
const refs = readJsonFile(".vhk/refs.json");
|
|
4687
|
+
if (Array.isArray(refs) && refs.length > 0) {
|
|
4688
|
+
lines.push(`## \uB808\uD37C\uB7F0\uC2A4 (${refs.length}\uAC1C)`);
|
|
4689
|
+
lines.push("");
|
|
4690
|
+
for (const r of refs.slice(-3)) {
|
|
4691
|
+
const label = r.memo && r.memo.length > 0 ? r.memo : r.url;
|
|
4692
|
+
lines.push(`- [${label}](${r.url})`);
|
|
4693
|
+
}
|
|
4694
|
+
lines.push("");
|
|
4695
|
+
}
|
|
4696
|
+
} catch {
|
|
4697
|
+
}
|
|
4698
|
+
}
|
|
4699
|
+
lines.push("## \uB2E4\uC74C \uB2E8\uACC4 \uC81C\uC548");
|
|
4700
|
+
lines.push("");
|
|
4701
|
+
const steps = [];
|
|
4702
|
+
if (uncommitted) steps.push("\uBBF8\uCEE4\uBC0B \uBCC0\uACBD \uC0AC\uD56D\uC744 \uCEE4\uBC0B\uD558\uC138\uC694: `vhk save`");
|
|
4703
|
+
steps.push("\uD488\uC9C8 \uC810\uAC80 \uC2E4\uD589: `vhk harness`");
|
|
4704
|
+
steps.push("\uBCF4\uC548 \uAC10\uC0AC: `vhk audit`");
|
|
4705
|
+
steps.push("\uCEE8\uD14D\uC2A4\uD2B8 \uAC31\uC2E0: `vhk context`");
|
|
4706
|
+
steps.forEach((s, i) => lines.push(`${i + 1}. ${s}`));
|
|
4707
|
+
lines.push("");
|
|
4708
|
+
lines.push("---");
|
|
4709
|
+
lines.push("");
|
|
4710
|
+
lines.push("_VHK CLI \uBE0C\uB9AC\uD551_");
|
|
4711
|
+
lines.push("");
|
|
4712
|
+
mkdirSync7(".vhk", { recursive: true });
|
|
4713
|
+
writeFileSync8(BRIEF_PATH, lines.join("\n"), "utf-8");
|
|
4714
|
+
console.log("\n" + lines.join("\n"));
|
|
4715
|
+
console.log(chalk27.green(`
|
|
4716
|
+
\u2705 ${BRIEF_PATH} \uC800\uC7A5 \uC644\uB8CC`));
|
|
4717
|
+
printNextStep({
|
|
4718
|
+
message: "\uBE0C\uB9AC\uD551 \uC0DD\uC131 \uC644\uB8CC!",
|
|
4719
|
+
command: "vhk context",
|
|
4720
|
+
cursorHint: "\uCEE8\uD14D\uC2A4\uD2B8 \uC5C5\uB370\uC774\uD2B8\uD574\uC918"
|
|
4721
|
+
});
|
|
4722
|
+
}
|
|
4723
|
+
|
|
3936
4724
|
// src/lib/nlp-run.ts
|
|
3937
4725
|
async function dispatchNlpRoute(route, input) {
|
|
3938
4726
|
switch (route.command) {
|
|
@@ -3981,28 +4769,44 @@ async function dispatchNlpRoute(route, input) {
|
|
|
3981
4769
|
return theme();
|
|
3982
4770
|
case "ref":
|
|
3983
4771
|
return refList();
|
|
4772
|
+
case "harness":
|
|
4773
|
+
return harness();
|
|
4774
|
+
case "audit":
|
|
4775
|
+
return audit();
|
|
4776
|
+
case "migrate":
|
|
4777
|
+
return migrate();
|
|
4778
|
+
case "update":
|
|
4779
|
+
return update();
|
|
4780
|
+
case "context":
|
|
4781
|
+
return context();
|
|
4782
|
+
case "context-show":
|
|
4783
|
+
return contextShow();
|
|
4784
|
+
case "memory":
|
|
4785
|
+
return memoryList();
|
|
4786
|
+
case "brief":
|
|
4787
|
+
return brief();
|
|
3984
4788
|
}
|
|
3985
4789
|
}
|
|
3986
4790
|
async function runNaturalLanguageRoute(input) {
|
|
3987
4791
|
const route = routeNaturalLanguage(input);
|
|
3988
4792
|
if (!route) {
|
|
3989
|
-
console.log(
|
|
4793
|
+
console.log(chalk28.yellow(`
|
|
3990
4794
|
\u2753 "${input}" \u2014 ${ko.nlp.notMatched}
|
|
3991
4795
|
`));
|
|
3992
4796
|
return;
|
|
3993
4797
|
}
|
|
3994
4798
|
console.log("");
|
|
3995
|
-
console.log(
|
|
3996
|
-
console.log(
|
|
4799
|
+
console.log(chalk28.cyan(` \u{1F4AC} "${input}"`));
|
|
4800
|
+
console.log(chalk28.cyan(` \u2192 ${route.explanation}`));
|
|
3997
4801
|
if (route.confidence === "low") {
|
|
3998
|
-
const { confirm } = await
|
|
4802
|
+
const { confirm } = await inquirer13.prompt([{
|
|
3999
4803
|
type: "confirm",
|
|
4000
4804
|
name: "confirm",
|
|
4001
4805
|
message: `${route.explanation} \u2014 ${ko.nlp.matched}`,
|
|
4002
4806
|
default: true
|
|
4003
4807
|
}]);
|
|
4004
4808
|
if (!confirm) {
|
|
4005
|
-
console.log(
|
|
4809
|
+
console.log(chalk28.dim(` ${ko.nlp.menuHint}`));
|
|
4006
4810
|
return;
|
|
4007
4811
|
}
|
|
4008
4812
|
}
|
|
@@ -4012,11 +4816,11 @@ async function runNaturalLanguageRoute(input) {
|
|
|
4012
4816
|
|
|
4013
4817
|
// src/index.ts
|
|
4014
4818
|
function getVersion() {
|
|
4015
|
-
const dir = path15.dirname(
|
|
4819
|
+
const dir = path15.dirname(fileURLToPath4(import.meta.url));
|
|
4016
4820
|
for (const pkgPath of [path15.join(dir, "../package.json"), path15.join(dir, "../../package.json")]) {
|
|
4017
4821
|
try {
|
|
4018
|
-
if (
|
|
4019
|
-
const pkg = JSON.parse(
|
|
4822
|
+
if (fs15.existsSync(pkgPath)) {
|
|
4823
|
+
const pkg = JSON.parse(fs15.readFileSync(pkgPath, "utf-8"));
|
|
4020
4824
|
if (pkg.version) return pkg.version;
|
|
4021
4825
|
}
|
|
4022
4826
|
} catch {
|
|
@@ -4047,7 +4851,15 @@ var KO_ALIASES = {
|
|
|
4047
4851
|
design: "\uB514\uC790\uC778",
|
|
4048
4852
|
"design-palette": "\uD314\uB808\uD2B8",
|
|
4049
4853
|
theme: "\uD14C\uB9C8",
|
|
4050
|
-
ref: "\uB808\uD37C\uB7F0\uC2A4"
|
|
4854
|
+
ref: "\uB808\uD37C\uB7F0\uC2A4",
|
|
4855
|
+
harness: "\uD558\uB124\uC2A4",
|
|
4856
|
+
audit: "\uAC10\uC0AC",
|
|
4857
|
+
migrate: "\uC804\uD658",
|
|
4858
|
+
update: "\uC5C5\uB370\uC774\uD2B8",
|
|
4859
|
+
context: "\uB9E5\uB77D",
|
|
4860
|
+
"context-show": "\uB9E5\uB77D\uBCF4\uAE30",
|
|
4861
|
+
memory: "\uAE30\uC5B5",
|
|
4862
|
+
brief: "\uBE0C\uB9AC\uD551"
|
|
4051
4863
|
};
|
|
4052
4864
|
program.name("vhk").description("VHK \u2014 \uBC14\uC774\uBE0C\uCF54\uB529 \uD504\uB85C\uC81D\uD2B8 \uCF54\uCE58 (\uD55C\uAD6D\uC5B4\uB85C \uC548\uB0B4\uD569\uB2C8\uB2E4)").version(getVersion());
|
|
4053
4865
|
program.configureHelp({
|
|
@@ -4131,6 +4943,40 @@ refCmd.command("list").alias("\uBAA9\uB85D").description("\uC800\uC7A5\uB41C \uB
|
|
|
4131
4943
|
refCmd.command("open <index>").alias("\uC5F4\uAE30").description("\uB808\uD37C\uB7F0\uC2A4\uB97C \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uC5F4\uAE30").action(async (index) => {
|
|
4132
4944
|
await refOpen(index);
|
|
4133
4945
|
});
|
|
4946
|
+
program.command("harness").alias("\uD558\uB124\uC2A4").description("\uD1B5\uD569 \uD488\uC9C8 \uC810\uAC80 (lint + type-check + test + build)").action(async () => {
|
|
4947
|
+
await harness();
|
|
4948
|
+
});
|
|
4949
|
+
program.command("audit").alias("\uAC10\uC0AC").option("--fix", "\uC790\uB3D9 \uC218\uC815 \uC2DC\uB3C4").description("\uBCF4\uC548 \uCDE8\uC57D\uC810 \uAC10\uC0AC (npm audit \uB798\uD551)").action(async (opts) => {
|
|
4950
|
+
await audit(opts.fix);
|
|
4951
|
+
});
|
|
4952
|
+
program.command("migrate [target]").alias("\uC804\uD658").description("\uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800 \uC804\uD658 (npm/yarn/pnpm)").action(async (target) => {
|
|
4953
|
+
await migrate(target);
|
|
4954
|
+
});
|
|
4955
|
+
program.command("update").alias("\uC5C5\uB370\uC774\uD2B8").description("VHK CLI \uCD5C\uC2E0 \uBC84\uC804 \uC5C5\uB370\uC774\uD2B8").action(async () => {
|
|
4956
|
+
await update();
|
|
4957
|
+
});
|
|
4958
|
+
program.command("context").alias("\uB9E5\uB77D").description("\uD504\uB85C\uC81D\uD2B8 \uB9E5\uB77D \uD30C\uC77C \uC0DD\uC131 (.vhk/context.md)").action(async () => {
|
|
4959
|
+
await context();
|
|
4960
|
+
});
|
|
4961
|
+
program.command("context-show").alias("\uB9E5\uB77D\uBCF4\uAE30").description("\uD604\uC7AC \uCEE8\uD14D\uC2A4\uD2B8 \uD30C\uC77C \uB0B4\uC6A9 \uCD9C\uB825").action(async () => {
|
|
4962
|
+
await contextShow();
|
|
4963
|
+
});
|
|
4964
|
+
var memoryCmd = program.command("memory").alias("\uAE30\uC5B5").description("\uACB0\uC815\uC0AC\uD56D \uAE30\uC5B5 \uAD00\uB9AC (add / list / remove)").action(async () => {
|
|
4965
|
+
await memoryList();
|
|
4966
|
+
});
|
|
4967
|
+
memoryCmd.command("add <content>").option("--tags <tags>", "\uD0DC\uADF8 (\uC27C\uD45C \uAD6C\uBD84)").description("\uACB0\uC815\uC0AC\uD56D \uAE30\uC5B5 \uC800\uC7A5").action(async (content, opts) => {
|
|
4968
|
+
const tags = opts.tags ? opts.tags.split(",").map((s) => s.trim()) : void 0;
|
|
4969
|
+
await memoryAdd(content, tags);
|
|
4970
|
+
});
|
|
4971
|
+
memoryCmd.command("list").alias("\uBAA9\uB85D").description("\uC800\uC7A5\uB41C \uAE30\uC5B5 \uBAA9\uB85D").action(async () => {
|
|
4972
|
+
await memoryList();
|
|
4973
|
+
});
|
|
4974
|
+
memoryCmd.command("remove <index>").alias("\uC0AD\uC81C").description("\uAE30\uC5B5 \uC0AD\uC81C (1\uBD80\uD130 \uC2DC\uC791\uD558\uB294 \uBC88\uD638)").action(async (index) => {
|
|
4975
|
+
await memoryRemove(index);
|
|
4976
|
+
});
|
|
4977
|
+
program.command("brief").alias("\uBE0C\uB9AC\uD551").description("\uD504\uB85C\uC81D\uD2B8 \uC0C1\uD0DC \uC694\uC57D \uBCF4\uACE0\uC11C \uC0DD\uC131 (.vhk/brief.md)").action(async () => {
|
|
4978
|
+
await brief();
|
|
4979
|
+
});
|
|
4134
4980
|
program.on("command:*", async (operands) => {
|
|
4135
4981
|
const unknown = operands[0] ?? "";
|
|
4136
4982
|
const rest = operands.slice(1);
|
|
@@ -4139,7 +4985,7 @@ program.on("command:*", async (operands) => {
|
|
|
4139
4985
|
});
|
|
4140
4986
|
program.action(async () => {
|
|
4141
4987
|
console.log("\n\u{1F3AF} VHK \u2014 \uBC14\uC774\uBE0C\uCF54\uB529 \uD504\uB85C\uC81D\uD2B8 \uCF54\uCE58\n");
|
|
4142
|
-
const { choice } = await
|
|
4988
|
+
const { choice } = await inquirer14.prompt([{
|
|
4143
4989
|
type: "list",
|
|
4144
4990
|
name: "choice",
|
|
4145
4991
|
message: "\uBB58 \uB3C4\uC640\uB4DC\uB9B4\uAE4C\uC694?",
|