@byh3071/vhk 0.8.1 β 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -2
- package/dist/{chunk-X3CIIDO2.js β chunk-UPXCLOBF.js} +17 -2
- package/dist/index.js +430 -13
- package/dist/mcp/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: vhk-readme
|
|
3
3
|
date: 2026-05-24
|
|
4
|
-
tags: [vhk, cli, readme, v0.
|
|
4
|
+
tags: [vhk, cli, readme, v0.9.0]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# π§ VHK β Vibe Harness Kit
|
|
8
8
|
|
|
9
|
-
> AI μ½λ© μμ΄μ νΈλ₯Ό λΆλ¦¬λ μ¬λμ μν **νκ΅μ΄ νμ¬μ΄ν΄ CLI** (v0.
|
|
9
|
+
> AI μ½λ© μμ΄μ νΈλ₯Ό λΆλ¦¬λ μ¬λμ μν **νκ΅μ΄ νμ¬μ΄ν΄ CLI** (v0.9.0)
|
|
10
10
|
>
|
|
11
11
|
> π½οΈ **VHKλ VHKλ‘ λΆνΈμ€νΈλ©λ¨** β μ΄ λ ν¬μ `docs/`, `CLAUDE.md`, `.cursorrules`λ `vhk init`μ΄ λ§λ€μμ΅λλ€.
|
|
12
12
|
|
|
@@ -99,6 +99,10 @@ vhk κΈ°ν λλ¬κ³ λ°λ‘ μμ
|
|
|
99
99
|
| `vhk design-palette` | `νλ νΈ` | μ»¬λ¬ νλ νΈ ν리μ
μ ν + μ μ© |
|
|
100
100
|
| `vhk theme` | `ν
λ§` | λ€ν¬/λΌμ΄νΈ λͺ¨λ CSS + ν κΈ μ νΈλ¦¬ν° μμ± |
|
|
101
101
|
| `vhk ref` | `λ νΌλ°μ€` | λ νΌλ°μ€ URL κ΄λ¦¬ (`add` / `list` / `open`) |
|
|
102
|
+
| `vhk harness` | `νλ€μ€` | ν΅ν© νμ§ μ κ² (lint + type-check + test + build μμ°¨ μ€ν + 리ν¬νΈ) |
|
|
103
|
+
| `vhk audit` | `κ°μ¬` | npm 보μ μ·¨μ½μ κ°μ¬ (`--fix`λ‘ μλ μμ ) |
|
|
104
|
+
| `vhk migrate [target]` | `μ ν` | ν¨ν€μ§ λ§€λμ μ ν (`npm` / `yarn` / `pnpm`, lockfile + node_modules μ¬κ΅¬μ±) |
|
|
105
|
+
| `vhk update` | `μ
λ°μ΄νΈ` | VHK CLI μ΅μ λ²μ μΌλ‘ μ
ν μ
λ°μ΄νΈ |
|
|
102
106
|
|
|
103
107
|
### init μ΅μ
|
|
104
108
|
|
|
@@ -133,6 +137,24 @@ MCP μλ²λ₯Ό μλμΌλ‘ λμ°λ €λ©΄:
|
|
|
133
137
|
vhk mcp # stdio μλ² μμ (Cursorκ° μλμΌλ‘ νΈμΆ)
|
|
134
138
|
```
|
|
135
139
|
|
|
140
|
+
## v0.9.0 νμ΄λΌμ΄νΈ
|
|
141
|
+
|
|
142
|
+
| κΈ°λ₯ | μ€λͺ
|
|
|
143
|
+
|------|------|
|
|
144
|
+
| **harness** | `package.json` scripts μλ κ°μ§ β `lint` / `type-check` / `test` / `build` μμ°¨ μ€ν + ν΅ν© 리ν¬νΈ. μΌλΆ μ€ν¨ν΄λ λκΉμ§ μ§ν |
|
|
145
|
+
| **audit** | `npm audit --json` λν + μ¬κ°λλ³ μμ½. `Critical`/`High` λ°κ²¬ μ μλ fix μ΅μ
. Windows PowerShell νΈν (`2>/dev/null` λ―Έμ¬μ©) |
|
|
146
|
+
| **migrate** | npm/yarn/pnpm μ ν β λμ CLI μ‘΄μ¬ νμΈ β νμΈ ν둬ννΈ β κΈ°μ‘΄ lockfile + node_modules μ 리 β `<pm> install` |
|
|
147
|
+
| **update** | npm registryμμ `@byh3071/vhk` μ΅μ λ²μ μ‘°ν β semver λΉκ΅ β `npm update -g` μ€ν. νμ¬ λ²μ μ΄ κ°κ±°λ λ λμΌλ©΄ μ€ν΅ |
|
|
148
|
+
| **μμ°μ΄ νμ₯** | `"νμ§ μ κ²ν΄μ€"` β harness Β· `"보μ κ°μ¬ ν΄μ€"` / `"μ·¨μ½μ νμΈ"` β audit Β· `"ν¨ν€μ§ λ§€λμ μ ν"` β migrate Β· `"vhk μ
λ°μ΄νΈ ν΄μ€"` β update. ν€μλ μΆ©λ κ°λ: `μ κ²` λ¨λ
μ κΈ°μ‘΄ `check`μ μ보, `보μ` λ¨λ
μ κΈ°μ‘΄ `secure`μ μ보 |
|
|
149
|
+
|
|
150
|
+
```powershell
|
|
151
|
+
vhk harness # lint + type-check + test + build μμ°¨ μ€ν
|
|
152
|
+
vhk audit # npm 보μ κ°μ¬ (Critical/High λ°κ²¬ μ μλ fix μ΅μ
)
|
|
153
|
+
vhk audit --fix # νμ npm audit fix μ€ν
|
|
154
|
+
vhk migrate pnpm # npm/yarn β pnpm μ ν (λνν νμΈ)
|
|
155
|
+
vhk update # @byh3071/vhk μ΅μ λ²μ μ²΄ν¬ + κΈλ‘λ² μ
λ°μ΄νΈ
|
|
156
|
+
```
|
|
157
|
+
|
|
136
158
|
## v0.8.0 νμ΄λΌμ΄νΈ
|
|
137
159
|
|
|
138
160
|
| κΈ°λ₯ | μ€λͺ
|
|
|
@@ -224,6 +246,10 @@ vhk ref open 1 # 1λ² λ νΌλ°μ€λ₯Ό λΈλΌμ°μ λ‘ μ΄κΈ°
|
|
|
224
246
|
| νλ νΈ κ³¨λΌμ€ | `vhk design-palette` |
|
|
225
247
|
| λ€ν¬ λͺ¨λ μ μ© | `vhk theme` |
|
|
226
248
|
| λ νΌλ°μ€ 보μ¬μ€ | `vhk ref` (list) |
|
|
249
|
+
| νμ§ μ κ²ν΄μ€ | `vhk harness` |
|
|
250
|
+
| 보μ κ°μ¬ ν΄μ€ / μ·¨μ½μ νμΈ | `vhk audit` |
|
|
251
|
+
| ν¨ν€μ§ λ§€λμ μ ν | `vhk migrate` |
|
|
252
|
+
| vhk μ
λ°μ΄νΈ ν΄μ€ | `vhk update` |
|
|
227
253
|
|
|
228
254
|
## νΉμ§
|
|
229
255
|
|
|
@@ -310,6 +310,19 @@ var ko = {
|
|
|
310
310
|
publishing: "npm \uBC30\uD3EC \uC911...",
|
|
311
311
|
publishSuccess: "npm \uBC30\uD3EC \uC131\uACF5!",
|
|
312
312
|
publishFailed: "npm \uBC30\uD3EC \uC2E4\uD328"
|
|
313
|
+
},
|
|
314
|
+
harness: {
|
|
315
|
+
title: "\uD1B5\uD569 \uD488\uC9C8 \uC810\uAC80"
|
|
316
|
+
},
|
|
317
|
+
audit: {
|
|
318
|
+
title: "\uBCF4\uC548 \uAC10\uC0AC"
|
|
319
|
+
},
|
|
320
|
+
migrate: {
|
|
321
|
+
title: "\uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800 \uC804\uD658",
|
|
322
|
+
selectTarget: "\uC5B4\uB5A4 \uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800\uB85C \uC804\uD658\uD560\uAE4C\uC694?"
|
|
323
|
+
},
|
|
324
|
+
update: {
|
|
325
|
+
title: "VHK CLI \uC5C5\uB370\uC774\uD2B8"
|
|
313
326
|
}
|
|
314
327
|
};
|
|
315
328
|
function lookup(path) {
|
|
@@ -457,8 +470,10 @@ function safeExecFile(cmd, args) {
|
|
|
457
470
|
}).toString();
|
|
458
471
|
return { ok: true, out: out.trim() };
|
|
459
472
|
} catch (err) {
|
|
460
|
-
const
|
|
461
|
-
|
|
473
|
+
const e = err;
|
|
474
|
+
const stdout = e.stdout ? e.stdout.toString() : "";
|
|
475
|
+
const msg = e.message ?? String(err);
|
|
476
|
+
return { ok: false, err: msg, out: stdout.trim() };
|
|
462
477
|
}
|
|
463
478
|
}
|
|
464
479
|
function safeExecFileStream(cmd, args) {
|
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-UPXCLOBF.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
|
|
475
|
+
import inquirer14 from "inquirer";
|
|
476
476
|
import fs16 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) {
|
|
@@ -543,6 +543,30 @@ 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
|
+
},
|
|
546
570
|
{
|
|
547
571
|
command: "secure",
|
|
548
572
|
explanation: "\uBCF4\uC548 \uC2A4\uCE94 (vhk \uBCF4\uC548)",
|
|
@@ -709,6 +733,14 @@ var KNOWN_COMMAND_TOKENS = /* @__PURE__ */ new Set([
|
|
|
709
733
|
"\uD14C\uB9C8",
|
|
710
734
|
"ref",
|
|
711
735
|
"\uB808\uD37C\uB7F0\uC2A4",
|
|
736
|
+
"harness",
|
|
737
|
+
"\uD558\uB124\uC2A4",
|
|
738
|
+
"audit",
|
|
739
|
+
"\uAC10\uC0AC",
|
|
740
|
+
"migrate",
|
|
741
|
+
"\uC804\uD658",
|
|
742
|
+
"update",
|
|
743
|
+
"\uC5C5\uB370\uC774\uD2B8",
|
|
712
744
|
"help"
|
|
713
745
|
]);
|
|
714
746
|
function isOptionToken(token) {
|
|
@@ -732,8 +764,8 @@ function detectNaturalLanguageInput(argv) {
|
|
|
732
764
|
}
|
|
733
765
|
|
|
734
766
|
// src/lib/nlp-run.ts
|
|
735
|
-
import
|
|
736
|
-
import
|
|
767
|
+
import chalk25 from "chalk";
|
|
768
|
+
import inquirer13 from "inquirer";
|
|
737
769
|
|
|
738
770
|
// src/commands/gate.ts
|
|
739
771
|
import inquirer from "inquirer";
|
|
@@ -3933,6 +3965,367 @@ async function refOpen(indexStr) {
|
|
|
3933
3965
|
}
|
|
3934
3966
|
}
|
|
3935
3967
|
|
|
3968
|
+
// src/commands/harness.ts
|
|
3969
|
+
import { existsSync as existsSync7, readFileSync as readFileSync4 } from "fs";
|
|
3970
|
+
import chalk21 from "chalk";
|
|
3971
|
+
import ora3 from "ora";
|
|
3972
|
+
function detectPM() {
|
|
3973
|
+
if (existsSync7("pnpm-lock.yaml")) return "pnpm";
|
|
3974
|
+
if (existsSync7("yarn.lock")) return "yarn";
|
|
3975
|
+
return "npm";
|
|
3976
|
+
}
|
|
3977
|
+
function pmRun(pm, script) {
|
|
3978
|
+
return pm === "npm" ? ["run", script] : [script];
|
|
3979
|
+
}
|
|
3980
|
+
function detectChecks() {
|
|
3981
|
+
const checks = [];
|
|
3982
|
+
let pkg = {};
|
|
3983
|
+
try {
|
|
3984
|
+
pkg = JSON.parse(readFileSync4("package.json", "utf-8"));
|
|
3985
|
+
} catch {
|
|
3986
|
+
return checks;
|
|
3987
|
+
}
|
|
3988
|
+
const s = pkg.scripts ?? {};
|
|
3989
|
+
const pm = detectPM();
|
|
3990
|
+
if (s.lint) {
|
|
3991
|
+
checks.push({ name: "lint", bin: pm, args: pmRun(pm, "lint") });
|
|
3992
|
+
} else if (existsSync7(".eslintrc.js") || existsSync7(".eslintrc.json") || existsSync7("eslint.config.js")) {
|
|
3993
|
+
checks.push({ name: "lint", bin: "npx", args: ["eslint", ".", "--ext", ".ts,.tsx"] });
|
|
3994
|
+
}
|
|
3995
|
+
if (s["type-check"]) {
|
|
3996
|
+
checks.push({ name: "type-check", bin: pm, args: pmRun(pm, "type-check") });
|
|
3997
|
+
} else if (s.typecheck) {
|
|
3998
|
+
checks.push({ name: "type-check", bin: pm, args: pmRun(pm, "typecheck") });
|
|
3999
|
+
} else if (existsSync7("tsconfig.json")) {
|
|
4000
|
+
checks.push({ name: "type-check", bin: "npx", args: ["tsc", "--noEmit"] });
|
|
4001
|
+
}
|
|
4002
|
+
if (s.test) {
|
|
4003
|
+
checks.push({ name: "test", bin: pm, args: pmRun(pm, "test") });
|
|
4004
|
+
}
|
|
4005
|
+
if (s.build) {
|
|
4006
|
+
checks.push({ name: "build", bin: pm, args: pmRun(pm, "build") });
|
|
4007
|
+
}
|
|
4008
|
+
return checks;
|
|
4009
|
+
}
|
|
4010
|
+
async function harness() {
|
|
4011
|
+
console.log(chalk21.bold("\n\u{1F527} " + t("harness.title")));
|
|
4012
|
+
console.log(chalk21.gray("\u2500".repeat(40)));
|
|
4013
|
+
const checks = detectChecks();
|
|
4014
|
+
if (checks.length === 0) {
|
|
4015
|
+
console.log(chalk21.yellow("\n\u26A0\uFE0F \uC2E4\uD589\uD560 \uC218 \uC788\uB294 \uC2A4\uD06C\uB9BD\uD2B8\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4."));
|
|
4016
|
+
console.log(chalk21.gray(" package.json\uC5D0 lint, test, build \uC2A4\uD06C\uB9BD\uD2B8\uB97C \uCD94\uAC00\uD574\uC8FC\uC138\uC694."));
|
|
4017
|
+
return;
|
|
4018
|
+
}
|
|
4019
|
+
console.log(chalk21.cyan(`
|
|
4020
|
+
\u{1F3C3} ${checks.length}\uAC1C \uC810\uAC80 \uC2DC\uC791:
|
|
4021
|
+
`));
|
|
4022
|
+
const results = [];
|
|
4023
|
+
for (const check2 of checks) {
|
|
4024
|
+
const display = `${check2.bin} ${check2.args.join(" ")}`;
|
|
4025
|
+
const spinner = ora3(`${check2.name} \uC2E4\uD589 \uC911...`).start();
|
|
4026
|
+
const start = Date.now();
|
|
4027
|
+
const result = safeExecFile(check2.bin, check2.args);
|
|
4028
|
+
const duration = Date.now() - start;
|
|
4029
|
+
const sec = (duration / 1e3).toFixed(1);
|
|
4030
|
+
if (result.ok) {
|
|
4031
|
+
spinner.succeed(`${check2.name} ${chalk21.gray(`(${sec}s)`)}`);
|
|
4032
|
+
results.push({ name: check2.name, command: display, passed: true, duration });
|
|
4033
|
+
} else {
|
|
4034
|
+
spinner.fail(`${check2.name} ${chalk21.gray(`(${sec}s)`)}`);
|
|
4035
|
+
results.push({
|
|
4036
|
+
name: check2.name,
|
|
4037
|
+
command: display,
|
|
4038
|
+
passed: false,
|
|
4039
|
+
duration,
|
|
4040
|
+
error: result.err.slice(0, 200)
|
|
4041
|
+
});
|
|
4042
|
+
}
|
|
4043
|
+
}
|
|
4044
|
+
console.log(chalk21.bold("\n\u{1F4CA} \uD1B5\uD569 \uB9AC\uD3EC\uD2B8:"));
|
|
4045
|
+
console.log(chalk21.gray("\u2500".repeat(40)));
|
|
4046
|
+
for (const r of results) {
|
|
4047
|
+
const icon = r.passed ? chalk21.green("\u2705") : chalk21.red("\u274C");
|
|
4048
|
+
const sec = (r.duration / 1e3).toFixed(1);
|
|
4049
|
+
console.log(` ${icon} ${r.name.padEnd(15)} ${chalk21.gray(`${sec}s`)}`);
|
|
4050
|
+
}
|
|
4051
|
+
const passed = results.filter((r) => r.passed).length;
|
|
4052
|
+
const all = passed === results.length;
|
|
4053
|
+
console.log(chalk21.gray("\u2500".repeat(40)));
|
|
4054
|
+
if (all) {
|
|
4055
|
+
console.log(chalk21.green.bold(`
|
|
4056
|
+
\u{1F389} \uC804\uCCB4 \uD1B5\uACFC! (${passed}/${results.length})`));
|
|
4057
|
+
} else {
|
|
4058
|
+
console.log(
|
|
4059
|
+
chalk21.red.bold(`
|
|
4060
|
+
\u26A0\uFE0F ${results.length - passed}\uAC1C \uC2E4\uD328 (${passed}/${results.length} \uD1B5\uACFC)`)
|
|
4061
|
+
);
|
|
4062
|
+
}
|
|
4063
|
+
printNextStep({
|
|
4064
|
+
message: all ? "\uD488\uC9C8 \uC810\uAC80 \uD1B5\uACFC!" : "\uC2E4\uD328 \uD56D\uBAA9\uC744 \uC218\uC815\uD558\uC138\uC694.",
|
|
4065
|
+
command: all ? "vhk ship" : "vhk doctor",
|
|
4066
|
+
cursorHint: all ? "\uBC30\uD3EC\uD574\uC918" : "\uBB38\uC81C \uC9C4\uB2E8\uD574\uC918"
|
|
4067
|
+
});
|
|
4068
|
+
}
|
|
4069
|
+
|
|
4070
|
+
// src/commands/audit.ts
|
|
4071
|
+
import { existsSync as existsSync8 } from "fs";
|
|
4072
|
+
import chalk22 from "chalk";
|
|
4073
|
+
import inquirer11 from "inquirer";
|
|
4074
|
+
import ora4 from "ora";
|
|
4075
|
+
function detectCurrentPM() {
|
|
4076
|
+
if (existsSync8("pnpm-lock.yaml")) return "pnpm";
|
|
4077
|
+
if (existsSync8("yarn.lock")) return "yarn";
|
|
4078
|
+
return "npm";
|
|
4079
|
+
}
|
|
4080
|
+
function parseAuditOutput(output, pm) {
|
|
4081
|
+
const empty = { critical: 0, high: 0, moderate: 0, low: 0, total: 0 };
|
|
4082
|
+
if (!output) return empty;
|
|
4083
|
+
try {
|
|
4084
|
+
const json = JSON.parse(output);
|
|
4085
|
+
const meta = json.metadata?.vulnerabilities;
|
|
4086
|
+
if (meta) {
|
|
4087
|
+
const summary = {
|
|
4088
|
+
critical: meta.critical ?? 0,
|
|
4089
|
+
high: meta.high ?? 0,
|
|
4090
|
+
moderate: meta.moderate ?? 0,
|
|
4091
|
+
low: meta.low ?? 0,
|
|
4092
|
+
total: meta.total ?? 0
|
|
4093
|
+
};
|
|
4094
|
+
if (!summary.total) {
|
|
4095
|
+
summary.total = summary.critical + summary.high + summary.moderate + summary.low;
|
|
4096
|
+
}
|
|
4097
|
+
return summary;
|
|
4098
|
+
}
|
|
4099
|
+
void pm;
|
|
4100
|
+
return empty;
|
|
4101
|
+
} catch {
|
|
4102
|
+
return empty;
|
|
4103
|
+
}
|
|
4104
|
+
}
|
|
4105
|
+
function runAuditJson(pm) {
|
|
4106
|
+
const result = safeExecFile(pm, ["audit", "--json"]);
|
|
4107
|
+
return result.out;
|
|
4108
|
+
}
|
|
4109
|
+
function runAuditFix(pm) {
|
|
4110
|
+
if (pm !== "npm") {
|
|
4111
|
+
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.` };
|
|
4112
|
+
}
|
|
4113
|
+
const result = safeExecFile("npm", ["audit", "fix"]);
|
|
4114
|
+
return result.ok ? { ok: true } : { ok: false, err: result.err };
|
|
4115
|
+
}
|
|
4116
|
+
async function audit(autoFix = false) {
|
|
4117
|
+
console.log(chalk22.bold("\n\u{1F6E1}\uFE0F " + t("audit.title")));
|
|
4118
|
+
console.log(chalk22.gray("\u2500".repeat(40)));
|
|
4119
|
+
const pm = detectCurrentPM();
|
|
4120
|
+
console.log(chalk22.cyan(`\u{1F4E6} \uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800: ${pm}`));
|
|
4121
|
+
const spinner = ora4("\uBCF4\uC548 \uAC10\uC0AC \uC2E4\uD589 \uC911...").start();
|
|
4122
|
+
const output = runAuditJson(pm);
|
|
4123
|
+
spinner.stop();
|
|
4124
|
+
const summary = parseAuditOutput(output, pm);
|
|
4125
|
+
if (summary.total === 0) {
|
|
4126
|
+
console.log(chalk22.green.bold("\n\u{1F389} \uCDE8\uC57D\uC810\uC774 \uBC1C\uACAC\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4!"));
|
|
4127
|
+
return;
|
|
4128
|
+
}
|
|
4129
|
+
console.log(chalk22.bold("\n\u{1F4CA} \uCDE8\uC57D\uC810 \uC694\uC57D:"));
|
|
4130
|
+
if (summary.critical > 0) console.log(chalk22.red(` \u{1F534} Critical: ${summary.critical}`));
|
|
4131
|
+
if (summary.high > 0) console.log(chalk22.red(` \u{1F7E0} High: ${summary.high}`));
|
|
4132
|
+
if (summary.moderate > 0) console.log(chalk22.yellow(` \u{1F7E1} Moderate: ${summary.moderate}`));
|
|
4133
|
+
if (summary.low > 0) console.log(chalk22.gray(` \u26AA Low: ${summary.low}`));
|
|
4134
|
+
console.log(chalk22.bold(`
|
|
4135
|
+
\uCD1D ${summary.total}\uAC1C\uC758 \uCDE8\uC57D\uC810`));
|
|
4136
|
+
const shouldRunFix = autoFix ? true : summary.critical > 0 || summary.high > 0 ? (await inquirer11.prompt([
|
|
4137
|
+
{
|
|
4138
|
+
type: "confirm",
|
|
4139
|
+
name: "shouldFix",
|
|
4140
|
+
message: "\uC790\uB3D9 \uC218\uC815\uC744 \uC2DC\uB3C4\uD560\uAE4C\uC694? (npm audit fix)",
|
|
4141
|
+
default: true
|
|
4142
|
+
}
|
|
4143
|
+
])).shouldFix : false;
|
|
4144
|
+
if (shouldRunFix) {
|
|
4145
|
+
const fixSpinner = ora4("\uC790\uB3D9 \uC218\uC815 \uC911...").start();
|
|
4146
|
+
const result = runAuditFix(pm);
|
|
4147
|
+
if (result.ok) {
|
|
4148
|
+
fixSpinner.succeed("\uC790\uB3D9 \uC218\uC815 \uC644\uB8CC!");
|
|
4149
|
+
} else {
|
|
4150
|
+
fixSpinner.warn(result.err ?? "\uC77C\uBD80 \uCDE8\uC57D\uC810\uC740 \uC218\uB3D9 \uC218\uC815\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.");
|
|
4151
|
+
}
|
|
4152
|
+
}
|
|
4153
|
+
printNextStep({
|
|
4154
|
+
message: "\uBCF4\uC548 \uAC10\uC0AC \uC644\uB8CC.",
|
|
4155
|
+
command: "vhk harness",
|
|
4156
|
+
cursorHint: "\uD488\uC9C8 \uC810\uAC80\uD574\uC918"
|
|
4157
|
+
});
|
|
4158
|
+
}
|
|
4159
|
+
|
|
4160
|
+
// src/commands/migrate.ts
|
|
4161
|
+
import { existsSync as existsSync9, unlinkSync, rmSync } from "fs";
|
|
4162
|
+
import chalk23 from "chalk";
|
|
4163
|
+
import inquirer12 from "inquirer";
|
|
4164
|
+
import ora5 from "ora";
|
|
4165
|
+
var LOCK_FILES = {
|
|
4166
|
+
npm: "package-lock.json",
|
|
4167
|
+
yarn: "yarn.lock",
|
|
4168
|
+
pnpm: "pnpm-lock.yaml"
|
|
4169
|
+
};
|
|
4170
|
+
function detectCurrentPM2() {
|
|
4171
|
+
if (existsSync9("pnpm-lock.yaml")) return "pnpm";
|
|
4172
|
+
if (existsSync9("yarn.lock")) return "yarn";
|
|
4173
|
+
if (existsSync9("package-lock.json")) return "npm";
|
|
4174
|
+
return null;
|
|
4175
|
+
}
|
|
4176
|
+
function isCLIAvailable2(pm) {
|
|
4177
|
+
return safeExecFile(pm, ["--version"]).ok;
|
|
4178
|
+
}
|
|
4179
|
+
async function migrate(target) {
|
|
4180
|
+
console.log(chalk23.bold("\n\u{1F504} " + t("migrate.title")));
|
|
4181
|
+
console.log(chalk23.gray("\u2500".repeat(40)));
|
|
4182
|
+
const current = detectCurrentPM2();
|
|
4183
|
+
console.log(chalk23.cyan(`
|
|
4184
|
+
\uD604\uC7AC \uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800: ${current ?? "\uAC10\uC9C0 \uBD88\uAC00"}`));
|
|
4185
|
+
let targetPM;
|
|
4186
|
+
if (target && ["npm", "yarn", "pnpm"].includes(target)) {
|
|
4187
|
+
targetPM = target;
|
|
4188
|
+
} else {
|
|
4189
|
+
const choices = ["npm", "yarn", "pnpm"].filter((pm) => pm !== current).map((pm) => ({ name: pm, value: pm }));
|
|
4190
|
+
const { selected } = await inquirer12.prompt([
|
|
4191
|
+
{
|
|
4192
|
+
type: "list",
|
|
4193
|
+
name: "selected",
|
|
4194
|
+
message: t("migrate.selectTarget"),
|
|
4195
|
+
choices
|
|
4196
|
+
}
|
|
4197
|
+
]);
|
|
4198
|
+
targetPM = selected;
|
|
4199
|
+
}
|
|
4200
|
+
if (targetPM === current) {
|
|
4201
|
+
console.log(chalk23.yellow(`
|
|
4202
|
+
\u26A0\uFE0F \uC774\uBBF8 ${targetPM}\uC744 \uC0AC\uC6A9 \uC911\uC785\uB2C8\uB2E4.`));
|
|
4203
|
+
return;
|
|
4204
|
+
}
|
|
4205
|
+
if (!isCLIAvailable2(targetPM)) {
|
|
4206
|
+
console.log(chalk23.red(`
|
|
4207
|
+
\u274C ${targetPM}\uC774 \uC124\uCE58\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.`));
|
|
4208
|
+
console.log(chalk23.yellow(` npm i -g ${targetPM}`));
|
|
4209
|
+
return;
|
|
4210
|
+
}
|
|
4211
|
+
const { confirm } = await inquirer12.prompt([
|
|
4212
|
+
{
|
|
4213
|
+
type: "confirm",
|
|
4214
|
+
name: "confirm",
|
|
4215
|
+
message: `${current ?? "\uD604\uC7AC"} \u2192 ${targetPM}\uC73C\uB85C \uC804\uD658\uD560\uAE4C\uC694? (node_modules \uC7AC\uC124\uCE58)`,
|
|
4216
|
+
default: true
|
|
4217
|
+
}
|
|
4218
|
+
]);
|
|
4219
|
+
if (!confirm) {
|
|
4220
|
+
console.log(chalk23.gray("\uCDE8\uC18C\uB428"));
|
|
4221
|
+
return;
|
|
4222
|
+
}
|
|
4223
|
+
const cleanup = ora5("\uAE30\uC874 lock \uD30C\uC77C \uC815\uB9AC \uC911...").start();
|
|
4224
|
+
for (const lockFile of Object.values(LOCK_FILES)) {
|
|
4225
|
+
if (existsSync9(lockFile)) {
|
|
4226
|
+
unlinkSync(lockFile);
|
|
4227
|
+
}
|
|
4228
|
+
}
|
|
4229
|
+
if (existsSync9("node_modules")) {
|
|
4230
|
+
cleanup.text = "node_modules \uC0AD\uC81C \uC911...";
|
|
4231
|
+
rmSync("node_modules", { recursive: true, force: true });
|
|
4232
|
+
}
|
|
4233
|
+
cleanup.succeed("\uAE30\uC874 \uD30C\uC77C \uC815\uB9AC \uC644\uB8CC");
|
|
4234
|
+
const install = ora5(`${targetPM} install \uC2E4\uD589 \uC911...`).start();
|
|
4235
|
+
const installResult = safeExecFile(targetPM, ["install"]);
|
|
4236
|
+
if (installResult.ok) {
|
|
4237
|
+
install.succeed(`${targetPM} install \uC644\uB8CC!`);
|
|
4238
|
+
} else {
|
|
4239
|
+
install.fail(`${targetPM} install \uC2E4\uD328`);
|
|
4240
|
+
console.log(chalk23.red(installResult.err.slice(0, 300)));
|
|
4241
|
+
return;
|
|
4242
|
+
}
|
|
4243
|
+
console.log(chalk23.green.bold(`
|
|
4244
|
+
\u{1F389} ${current ?? "\uC774\uC804"} \u2192 ${targetPM} \uC804\uD658 \uC644\uB8CC!`));
|
|
4245
|
+
printNextStep({
|
|
4246
|
+
message: "\uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800 \uC804\uD658 \uC644\uB8CC!",
|
|
4247
|
+
command: "vhk harness",
|
|
4248
|
+
cursorHint: "\uD488\uC9C8 \uC810\uAC80\uD574\uC918"
|
|
4249
|
+
});
|
|
4250
|
+
}
|
|
4251
|
+
|
|
4252
|
+
// src/commands/update.ts
|
|
4253
|
+
import { execSync as execSync3 } from "child_process";
|
|
4254
|
+
import { existsSync as existsSync10, readFileSync as readFileSync5 } from "fs";
|
|
4255
|
+
import { dirname, join as join2 } from "path";
|
|
4256
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
4257
|
+
import chalk24 from "chalk";
|
|
4258
|
+
import ora6 from "ora";
|
|
4259
|
+
var PACKAGE = "@byh3071/vhk";
|
|
4260
|
+
function getCurrentVersion() {
|
|
4261
|
+
const dir = dirname(fileURLToPath3(import.meta.url));
|
|
4262
|
+
for (const pkgPath of [join2(dir, "../package.json"), join2(dir, "../../package.json")]) {
|
|
4263
|
+
try {
|
|
4264
|
+
if (existsSync10(pkgPath)) {
|
|
4265
|
+
const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
|
|
4266
|
+
if (pkg.version) return pkg.version;
|
|
4267
|
+
}
|
|
4268
|
+
} catch {
|
|
4269
|
+
continue;
|
|
4270
|
+
}
|
|
4271
|
+
}
|
|
4272
|
+
return "0.0.0";
|
|
4273
|
+
}
|
|
4274
|
+
function getLatestVersion() {
|
|
4275
|
+
try {
|
|
4276
|
+
const out = execSync3(`npm view ${PACKAGE} version`, {
|
|
4277
|
+
encoding: "utf-8",
|
|
4278
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4279
|
+
}).toString();
|
|
4280
|
+
return out.trim();
|
|
4281
|
+
} catch {
|
|
4282
|
+
return null;
|
|
4283
|
+
}
|
|
4284
|
+
}
|
|
4285
|
+
function isUpToDate(current, latest) {
|
|
4286
|
+
const parse = (v) => v.split(".").map((n) => parseInt(n, 10) || 0);
|
|
4287
|
+
const [ca, cb, cc] = parse(current);
|
|
4288
|
+
const [la, lb, lc] = parse(latest);
|
|
4289
|
+
if (ca !== la) return ca > la;
|
|
4290
|
+
if (cb !== lb) return cb > lb;
|
|
4291
|
+
return cc >= lc;
|
|
4292
|
+
}
|
|
4293
|
+
async function update() {
|
|
4294
|
+
console.log(chalk24.bold("\n\u2B06\uFE0F " + t("update.title")));
|
|
4295
|
+
console.log(chalk24.gray("\u2500".repeat(40)));
|
|
4296
|
+
const current = getCurrentVersion();
|
|
4297
|
+
console.log(chalk24.cyan(`
|
|
4298
|
+
\u{1F4CC} \uD604\uC7AC \uBC84\uC804: v${current}`));
|
|
4299
|
+
const spinner = ora6("\uCD5C\uC2E0 \uBC84\uC804 \uD655\uC778 \uC911...").start();
|
|
4300
|
+
const latest = getLatestVersion();
|
|
4301
|
+
if (!latest) {
|
|
4302
|
+
spinner.fail("\uCD5C\uC2E0 \uBC84\uC804\uC744 \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
4303
|
+
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:"));
|
|
4304
|
+
console.log(chalk24.gray(` npm update -g ${PACKAGE}`));
|
|
4305
|
+
return;
|
|
4306
|
+
}
|
|
4307
|
+
spinner.stop();
|
|
4308
|
+
console.log(chalk24.cyan(`\u{1F195} \uCD5C\uC2E0 \uBC84\uC804: v${latest}`));
|
|
4309
|
+
if (isUpToDate(current, latest)) {
|
|
4310
|
+
console.log(chalk24.green("\n\u2705 \uC774\uBBF8 \uCD5C\uC2E0 \uBC84\uC804\uC785\uB2C8\uB2E4!"));
|
|
4311
|
+
return;
|
|
4312
|
+
}
|
|
4313
|
+
const updateSpinner = ora6(`v${latest}\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8 \uC911...`).start();
|
|
4314
|
+
try {
|
|
4315
|
+
execSync3(`npm update -g ${PACKAGE}`, { stdio: ["pipe", "pipe", "pipe"] });
|
|
4316
|
+
updateSpinner.succeed(`v${latest}\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC!`);
|
|
4317
|
+
console.log(chalk24.green.bold(`
|
|
4318
|
+
\u{1F389} VHK CLI v${latest} \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC!`));
|
|
4319
|
+
console.log(chalk24.gray(" \uBCC0\uACBD \uC0AC\uD56D\uC740 GitHub Releases\uB97C \uD655\uC778\uD558\uC138\uC694."));
|
|
4320
|
+
} catch (err) {
|
|
4321
|
+
updateSpinner.fail("\uC5C5\uB370\uC774\uD2B8 \uC2E4\uD328");
|
|
4322
|
+
const msg = err instanceof Error ? err.message.slice(0, 300) : String(err);
|
|
4323
|
+
console.log(chalk24.red(msg));
|
|
4324
|
+
console.log(chalk24.yellow("\n\uC218\uB3D9\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694:"));
|
|
4325
|
+
console.log(chalk24.gray(` npm update -g ${PACKAGE}`));
|
|
4326
|
+
}
|
|
4327
|
+
}
|
|
4328
|
+
|
|
3936
4329
|
// src/lib/nlp-run.ts
|
|
3937
4330
|
async function dispatchNlpRoute(route, input) {
|
|
3938
4331
|
switch (route.command) {
|
|
@@ -3981,28 +4374,36 @@ async function dispatchNlpRoute(route, input) {
|
|
|
3981
4374
|
return theme();
|
|
3982
4375
|
case "ref":
|
|
3983
4376
|
return refList();
|
|
4377
|
+
case "harness":
|
|
4378
|
+
return harness();
|
|
4379
|
+
case "audit":
|
|
4380
|
+
return audit();
|
|
4381
|
+
case "migrate":
|
|
4382
|
+
return migrate();
|
|
4383
|
+
case "update":
|
|
4384
|
+
return update();
|
|
3984
4385
|
}
|
|
3985
4386
|
}
|
|
3986
4387
|
async function runNaturalLanguageRoute(input) {
|
|
3987
4388
|
const route = routeNaturalLanguage(input);
|
|
3988
4389
|
if (!route) {
|
|
3989
|
-
console.log(
|
|
4390
|
+
console.log(chalk25.yellow(`
|
|
3990
4391
|
\u2753 "${input}" \u2014 ${ko.nlp.notMatched}
|
|
3991
4392
|
`));
|
|
3992
4393
|
return;
|
|
3993
4394
|
}
|
|
3994
4395
|
console.log("");
|
|
3995
|
-
console.log(
|
|
3996
|
-
console.log(
|
|
4396
|
+
console.log(chalk25.cyan(` \u{1F4AC} "${input}"`));
|
|
4397
|
+
console.log(chalk25.cyan(` \u2192 ${route.explanation}`));
|
|
3997
4398
|
if (route.confidence === "low") {
|
|
3998
|
-
const { confirm } = await
|
|
4399
|
+
const { confirm } = await inquirer13.prompt([{
|
|
3999
4400
|
type: "confirm",
|
|
4000
4401
|
name: "confirm",
|
|
4001
4402
|
message: `${route.explanation} \u2014 ${ko.nlp.matched}`,
|
|
4002
4403
|
default: true
|
|
4003
4404
|
}]);
|
|
4004
4405
|
if (!confirm) {
|
|
4005
|
-
console.log(
|
|
4406
|
+
console.log(chalk25.dim(` ${ko.nlp.menuHint}`));
|
|
4006
4407
|
return;
|
|
4007
4408
|
}
|
|
4008
4409
|
}
|
|
@@ -4012,7 +4413,7 @@ async function runNaturalLanguageRoute(input) {
|
|
|
4012
4413
|
|
|
4013
4414
|
// src/index.ts
|
|
4014
4415
|
function getVersion() {
|
|
4015
|
-
const dir = path15.dirname(
|
|
4416
|
+
const dir = path15.dirname(fileURLToPath4(import.meta.url));
|
|
4016
4417
|
for (const pkgPath of [path15.join(dir, "../package.json"), path15.join(dir, "../../package.json")]) {
|
|
4017
4418
|
try {
|
|
4018
4419
|
if (fs16.existsSync(pkgPath)) {
|
|
@@ -4047,7 +4448,11 @@ var KO_ALIASES = {
|
|
|
4047
4448
|
design: "\uB514\uC790\uC778",
|
|
4048
4449
|
"design-palette": "\uD314\uB808\uD2B8",
|
|
4049
4450
|
theme: "\uD14C\uB9C8",
|
|
4050
|
-
ref: "\uB808\uD37C\uB7F0\uC2A4"
|
|
4451
|
+
ref: "\uB808\uD37C\uB7F0\uC2A4",
|
|
4452
|
+
harness: "\uD558\uB124\uC2A4",
|
|
4453
|
+
audit: "\uAC10\uC0AC",
|
|
4454
|
+
migrate: "\uC804\uD658",
|
|
4455
|
+
update: "\uC5C5\uB370\uC774\uD2B8"
|
|
4051
4456
|
};
|
|
4052
4457
|
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
4458
|
program.configureHelp({
|
|
@@ -4131,6 +4536,18 @@ refCmd.command("list").alias("\uBAA9\uB85D").description("\uC800\uC7A5\uB41C \uB
|
|
|
4131
4536
|
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
4537
|
await refOpen(index);
|
|
4133
4538
|
});
|
|
4539
|
+
program.command("harness").alias("\uD558\uB124\uC2A4").description("\uD1B5\uD569 \uD488\uC9C8 \uC810\uAC80 (lint + type-check + test + build)").action(async () => {
|
|
4540
|
+
await harness();
|
|
4541
|
+
});
|
|
4542
|
+
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) => {
|
|
4543
|
+
await audit(opts.fix);
|
|
4544
|
+
});
|
|
4545
|
+
program.command("migrate [target]").alias("\uC804\uD658").description("\uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800 \uC804\uD658 (npm/yarn/pnpm)").action(async (target) => {
|
|
4546
|
+
await migrate(target);
|
|
4547
|
+
});
|
|
4548
|
+
program.command("update").alias("\uC5C5\uB370\uC774\uD2B8").description("VHK CLI \uCD5C\uC2E0 \uBC84\uC804 \uC5C5\uB370\uC774\uD2B8").action(async () => {
|
|
4549
|
+
await update();
|
|
4550
|
+
});
|
|
4134
4551
|
program.on("command:*", async (operands) => {
|
|
4135
4552
|
const unknown = operands[0] ?? "";
|
|
4136
4553
|
const rest = operands.slice(1);
|
|
@@ -4139,7 +4556,7 @@ program.on("command:*", async (operands) => {
|
|
|
4139
4556
|
});
|
|
4140
4557
|
program.action(async () => {
|
|
4141
4558
|
console.log("\n\u{1F3AF} VHK \u2014 \uBC14\uC774\uBE0C\uCF54\uB529 \uD504\uB85C\uC81D\uD2B8 \uCF54\uCE58\n");
|
|
4142
|
-
const { choice } = await
|
|
4559
|
+
const { choice } = await inquirer14.prompt([{
|
|
4143
4560
|
type: "list",
|
|
4144
4561
|
name: "choice",
|
|
4145
4562
|
message: "\uBB58 \uB3C4\uC640\uB4DC\uB9B4\uAE4C\uC694?",
|
package/dist/mcp/index.js
CHANGED