@byh3071/vhk 0.8.0 β 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-NQ4V3VN4.js β chunk-UPXCLOBF.js} +27 -4
- package/dist/index.js +485 -47
- package/dist/mcp/index.js +1 -1
- package/package.json +65 -65
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) {
|
|
@@ -442,21 +455,31 @@ function platformCmd(cmd) {
|
|
|
442
455
|
}
|
|
443
456
|
return cmd;
|
|
444
457
|
}
|
|
458
|
+
function resolveCmd(cmd, args) {
|
|
459
|
+
if (process.platform === "win32" && SHIM_BINARIES.has(cmd)) {
|
|
460
|
+
return { bin: "cmd.exe", argv: ["/d", "/s", "/c", `${cmd}.cmd`, ...args] };
|
|
461
|
+
}
|
|
462
|
+
return { bin: platformCmd(cmd), argv: args };
|
|
463
|
+
}
|
|
445
464
|
function safeExecFile(cmd, args) {
|
|
465
|
+
const { bin, argv } = resolveCmd(cmd, args);
|
|
446
466
|
try {
|
|
447
|
-
const out = execFileSync(
|
|
467
|
+
const out = execFileSync(bin, argv, {
|
|
448
468
|
encoding: "utf-8",
|
|
449
469
|
stdio: ["pipe", "pipe", "pipe"]
|
|
450
470
|
}).toString();
|
|
451
471
|
return { ok: true, out: out.trim() };
|
|
452
472
|
} catch (err) {
|
|
453
|
-
const
|
|
454
|
-
|
|
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() };
|
|
455
477
|
}
|
|
456
478
|
}
|
|
457
479
|
function safeExecFileStream(cmd, args) {
|
|
480
|
+
const { bin, argv } = resolveCmd(cmd, args);
|
|
458
481
|
try {
|
|
459
|
-
execFileSync(
|
|
482
|
+
execFileSync(bin, argv, {
|
|
460
483
|
encoding: "utf-8",
|
|
461
484
|
stdio: "inherit"
|
|
462
485
|
});
|
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({
|
|
@@ -310,7 +310,7 @@ var require_ignore = __commonJS({
|
|
|
310
310
|
// path matching.
|
|
311
311
|
// - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
|
|
312
312
|
// @returns {TestResult} true if a file is ignored
|
|
313
|
-
test(
|
|
313
|
+
test(path16, checkUnignored, mode) {
|
|
314
314
|
let ignored = false;
|
|
315
315
|
let unignored = false;
|
|
316
316
|
let matchedRule;
|
|
@@ -319,7 +319,7 @@ var require_ignore = __commonJS({
|
|
|
319
319
|
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
|
320
320
|
return;
|
|
321
321
|
}
|
|
322
|
-
const matched = rule[mode].test(
|
|
322
|
+
const matched = rule[mode].test(path16);
|
|
323
323
|
if (!matched) {
|
|
324
324
|
return;
|
|
325
325
|
}
|
|
@@ -340,17 +340,17 @@ var require_ignore = __commonJS({
|
|
|
340
340
|
var throwError = (message, Ctor) => {
|
|
341
341
|
throw new Ctor(message);
|
|
342
342
|
};
|
|
343
|
-
var checkPath = (
|
|
344
|
-
if (!isString(
|
|
343
|
+
var checkPath = (path16, originalPath, doThrow) => {
|
|
344
|
+
if (!isString(path16)) {
|
|
345
345
|
return doThrow(
|
|
346
346
|
`path must be a string, but got \`${originalPath}\``,
|
|
347
347
|
TypeError
|
|
348
348
|
);
|
|
349
349
|
}
|
|
350
|
-
if (!
|
|
350
|
+
if (!path16) {
|
|
351
351
|
return doThrow(`path must not be empty`, TypeError);
|
|
352
352
|
}
|
|
353
|
-
if (checkPath.isNotRelative(
|
|
353
|
+
if (checkPath.isNotRelative(path16)) {
|
|
354
354
|
const r = "`path.relative()`d";
|
|
355
355
|
return doThrow(
|
|
356
356
|
`path should be a ${r} string, but got "${originalPath}"`,
|
|
@@ -359,7 +359,7 @@ var require_ignore = __commonJS({
|
|
|
359
359
|
}
|
|
360
360
|
return true;
|
|
361
361
|
};
|
|
362
|
-
var isNotRelative = (
|
|
362
|
+
var isNotRelative = (path16) => REGEX_TEST_INVALID_PATH.test(path16);
|
|
363
363
|
checkPath.isNotRelative = isNotRelative;
|
|
364
364
|
checkPath.convert = (p) => p;
|
|
365
365
|
var Ignore = class {
|
|
@@ -389,19 +389,19 @@ var require_ignore = __commonJS({
|
|
|
389
389
|
}
|
|
390
390
|
// @returns {TestResult}
|
|
391
391
|
_test(originalPath, cache, checkUnignored, slices) {
|
|
392
|
-
const
|
|
392
|
+
const path16 = originalPath && checkPath.convert(originalPath);
|
|
393
393
|
checkPath(
|
|
394
|
-
|
|
394
|
+
path16,
|
|
395
395
|
originalPath,
|
|
396
396
|
this._strictPathCheck ? throwError : RETURN_FALSE
|
|
397
397
|
);
|
|
398
|
-
return this._t(
|
|
398
|
+
return this._t(path16, cache, checkUnignored, slices);
|
|
399
399
|
}
|
|
400
|
-
checkIgnore(
|
|
401
|
-
if (!REGEX_TEST_TRAILING_SLASH.test(
|
|
402
|
-
return this.test(
|
|
400
|
+
checkIgnore(path16) {
|
|
401
|
+
if (!REGEX_TEST_TRAILING_SLASH.test(path16)) {
|
|
402
|
+
return this.test(path16);
|
|
403
403
|
}
|
|
404
|
-
const slices =
|
|
404
|
+
const slices = path16.split(SLASH).filter(Boolean);
|
|
405
405
|
slices.pop();
|
|
406
406
|
if (slices.length) {
|
|
407
407
|
const parent = this._t(
|
|
@@ -414,18 +414,18 @@ var require_ignore = __commonJS({
|
|
|
414
414
|
return parent;
|
|
415
415
|
}
|
|
416
416
|
}
|
|
417
|
-
return this._rules.test(
|
|
417
|
+
return this._rules.test(path16, false, MODE_CHECK_IGNORE);
|
|
418
418
|
}
|
|
419
|
-
_t(
|
|
420
|
-
if (
|
|
421
|
-
return cache[
|
|
419
|
+
_t(path16, cache, checkUnignored, slices) {
|
|
420
|
+
if (path16 in cache) {
|
|
421
|
+
return cache[path16];
|
|
422
422
|
}
|
|
423
423
|
if (!slices) {
|
|
424
|
-
slices =
|
|
424
|
+
slices = path16.split(SLASH).filter(Boolean);
|
|
425
425
|
}
|
|
426
426
|
slices.pop();
|
|
427
427
|
if (!slices.length) {
|
|
428
|
-
return cache[
|
|
428
|
+
return cache[path16] = this._rules.test(path16, checkUnignored, MODE_IGNORE);
|
|
429
429
|
}
|
|
430
430
|
const parent = this._t(
|
|
431
431
|
slices.join(SLASH) + SLASH,
|
|
@@ -433,29 +433,29 @@ var require_ignore = __commonJS({
|
|
|
433
433
|
checkUnignored,
|
|
434
434
|
slices
|
|
435
435
|
);
|
|
436
|
-
return cache[
|
|
436
|
+
return cache[path16] = parent.ignored ? parent : this._rules.test(path16, checkUnignored, MODE_IGNORE);
|
|
437
437
|
}
|
|
438
|
-
ignores(
|
|
439
|
-
return this._test(
|
|
438
|
+
ignores(path16) {
|
|
439
|
+
return this._test(path16, this._ignoreCache, false).ignored;
|
|
440
440
|
}
|
|
441
441
|
createFilter() {
|
|
442
|
-
return (
|
|
442
|
+
return (path16) => !this.ignores(path16);
|
|
443
443
|
}
|
|
444
444
|
filter(paths) {
|
|
445
445
|
return makeArray(paths).filter(this.createFilter());
|
|
446
446
|
}
|
|
447
447
|
// @returns {TestResult}
|
|
448
|
-
test(
|
|
449
|
-
return this._test(
|
|
448
|
+
test(path16) {
|
|
449
|
+
return this._test(path16, this._testCache, true);
|
|
450
450
|
}
|
|
451
451
|
};
|
|
452
452
|
var factory = (options) => new Ignore(options);
|
|
453
|
-
var isPathValid = (
|
|
453
|
+
var isPathValid = (path16) => checkPath(path16 && checkPath.convert(path16), path16, RETURN_FALSE);
|
|
454
454
|
var setupWindows = () => {
|
|
455
455
|
const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
|
|
456
456
|
checkPath.convert = makePosix;
|
|
457
457
|
const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
458
|
-
checkPath.isNotRelative = (
|
|
458
|
+
checkPath.isNotRelative = (path16) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path16) || isNotRelative(path16);
|
|
459
459
|
};
|
|
460
460
|
if (
|
|
461
461
|
// Detect `process` so that it can run in browsers.
|
|
@@ -472,7 +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
|
+
import fs16 from "fs";
|
|
477
|
+
import path15 from "path";
|
|
478
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
476
479
|
|
|
477
480
|
// src/lib/nlp-router.ts
|
|
478
481
|
function normalize(input) {
|
|
@@ -508,7 +511,7 @@ var RULES = [
|
|
|
508
511
|
command: "init",
|
|
509
512
|
explanation: "\uD504\uB85C\uC81D\uD2B8 \uC2DC\uC791 (vhk \uC2DC\uC791)",
|
|
510
513
|
confidence: "high",
|
|
511
|
-
test: (t2) => /νλ‘μ νΈ.*(λ§λ€|μμ)|ν΄λ.*λ§λ€|λ§λ€κ³ \s*μΆ|νλ€μ€|μ΄κΈ°ν/.test(t2) || /^μμ$/.test(t2)
|
|
514
|
+
test: (t2) => (/νλ‘μ νΈ.*(λ§λ€|μμ)|ν΄λ.*λ§λ€|λ§λ€κ³ \s*μΆ|νλ€μ€|μ΄κΈ°ν/.test(t2) || /^μμ$/.test(t2)) && !/λμμΈ|design|νλ νΈ|palette|ν
λ§|theme|λ νΌλ°μ€|reference|λ€ν¬\s*λͺ¨λ|λΌμ΄νΈ\s*λͺ¨λ|μμ\s*λͺ¨λ/.test(t2)
|
|
512
515
|
},
|
|
513
516
|
{
|
|
514
517
|
command: "mcp-init",
|
|
@@ -526,13 +529,13 @@ var RULES = [
|
|
|
526
529
|
command: "design",
|
|
527
530
|
explanation: "\uB514\uC790\uC778 \uD1A0\uD070 \uC0DD\uC131 (vhk design)",
|
|
528
531
|
confidence: "high",
|
|
529
|
-
test: (t2) => /λμμΈ\s*(ν ν°|μμ€ν
|λ§λ€|μμ±|μ
μ
|μ€μ )|design\s*(token|system|setup)|ν ν°\s*λ§λ€|css\s*λ³μ.*λ§λ€|tailwind\s*(컬λ¬|μ€μ )/.test(t2)
|
|
532
|
+
test: (t2) => /λμμΈ\s*(ν ν°|μμ€ν
|λ§λ€|μμ±|μ
μ
|μ€μ )|design\s*(token|system|setup)|ν ν°\s*λ§λ€|css\s*λ³μ.*λ§λ€|tailwind\s*(컬λ¬|μ€μ )/.test(t2) && !/λ°°ν¬|deploy|vercel|netlify|cloudflare|wrangler|μΆμ|publish|npm/.test(t2)
|
|
530
533
|
},
|
|
531
534
|
{
|
|
532
535
|
command: "theme",
|
|
533
536
|
explanation: "\uB2E4\uD06C/\uB77C\uC774\uD2B8 \uD14C\uB9C8 \uC801\uC6A9 (vhk theme)",
|
|
534
537
|
confidence: "high",
|
|
535
|
-
test: (t2) => /ν
λ§(?!\s*(νμΌ|μ΄λ¦))|theme|λ€ν¬\s*λͺ¨λ|λΌμ΄νΈ\s*λͺ¨λ|dark\s*mode|light\s*mode|μμ\s*λͺ¨λ|λͺ¨λ\s*μ ν/.test(t2)
|
|
538
|
+
test: (t2) => /ν
λ§(?!\s*(νμΌ|μ΄λ¦))|theme|λ€ν¬\s*λͺ¨λ|λΌμ΄νΈ\s*λͺ¨λ|dark\s*mode|light\s*mode|μμ\s*λͺ¨λ|λͺ¨λ\s*μ ν/.test(t2) && !/보μ|μν¬λ¦Ώ|λΉλ°|ν€\s*μ μΆ|secure|scan|μ€μΊ|λ°°ν¬|deploy/.test(t2)
|
|
536
539
|
},
|
|
537
540
|
{
|
|
538
541
|
command: "ref",
|
|
@@ -540,6 +543,30 @@ var RULES = [
|
|
|
540
543
|
confidence: "high",
|
|
541
544
|
test: (t2) => /^λ νΌλ°μ€$|^ref$|λ νΌλ°μ€.*(보|λͺ©λ‘|νμΈ|μ|λ)|μ°Έκ³ \s*(μ¬μ΄νΈ|λͺ©λ‘|λ§ν¬)|reference.*list/.test(t2) && !/(add|μΆκ°|open|μ΄|https?:\/\/)/.test(t2)
|
|
542
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
|
+
},
|
|
543
570
|
{
|
|
544
571
|
command: "secure",
|
|
545
572
|
explanation: "\uBCF4\uC548 \uC2A4\uCE94 (vhk \uBCF4\uC548)",
|
|
@@ -706,6 +733,14 @@ var KNOWN_COMMAND_TOKENS = /* @__PURE__ */ new Set([
|
|
|
706
733
|
"\uD14C\uB9C8",
|
|
707
734
|
"ref",
|
|
708
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",
|
|
709
744
|
"help"
|
|
710
745
|
]);
|
|
711
746
|
function isOptionToken(token) {
|
|
@@ -729,8 +764,8 @@ function detectNaturalLanguageInput(argv) {
|
|
|
729
764
|
}
|
|
730
765
|
|
|
731
766
|
// src/lib/nlp-run.ts
|
|
732
|
-
import
|
|
733
|
-
import
|
|
767
|
+
import chalk25 from "chalk";
|
|
768
|
+
import inquirer13 from "inquirer";
|
|
734
769
|
|
|
735
770
|
// src/commands/gate.ts
|
|
736
771
|
import inquirer from "inquirer";
|
|
@@ -3567,17 +3602,21 @@ async function publish() {
|
|
|
3567
3602
|
console.log(chalk17.gray("\uCDE8\uC18C\uB428. \uBC84\uC804\uC774 \uC6D0\uB798\uB300\uB85C \uBCF5\uAD6C\uB429\uB2C8\uB2E4."));
|
|
3568
3603
|
return;
|
|
3569
3604
|
}
|
|
3570
|
-
|
|
3571
|
-
|
|
3605
|
+
console.log(chalk17.cyan(`
|
|
3606
|
+
\u{1F4E4} ${t("publish.publishing")}`));
|
|
3607
|
+
console.log(chalk17.gray(" 2FA \uD65C\uC131\uD654 \uC2DC: OTP 6\uC790\uB9AC \uC785\uB825 \uB610\uB294 \uBE0C\uB77C\uC6B0\uC800 \uC778\uC99D URL \uD074\uB9AD (Windows Hello / PIN \uC9C0\uC6D0)"));
|
|
3608
|
+
const pubResult = safeExecFileStream("npm", ["publish", "--access", "public"]);
|
|
3572
3609
|
if (!pubResult.ok) {
|
|
3573
|
-
|
|
3610
|
+
console.log(chalk17.red(`
|
|
3611
|
+
\u2716 ${t("publish.publishFailed")}`));
|
|
3574
3612
|
console.log(chalk17.red(pubResult.err.slice(0, 500)));
|
|
3575
3613
|
pkg.version = currentVersion;
|
|
3576
3614
|
writeFileSync2("package.json", JSON.stringify(pkg, null, 2) + "\n", "utf-8");
|
|
3577
3615
|
console.log(chalk17.gray(`\u{1F4E6} package.json \uBC84\uC804\uC744 v${currentVersion}\uB85C \uBCF5\uAD6C\uD588\uC2B5\uB2C8\uB2E4.`));
|
|
3578
3616
|
return;
|
|
3579
3617
|
}
|
|
3580
|
-
|
|
3618
|
+
console.log(chalk17.green(`
|
|
3619
|
+
\u2714 ${t("publish.publishSuccess")}`));
|
|
3581
3620
|
const addResult = safeExecFile("git", ["add", "package.json"]);
|
|
3582
3621
|
if (addResult.ok) {
|
|
3583
3622
|
safeExecFile("git", ["commit", "-m", `chore: release v${newVersion}`]);
|
|
@@ -3926,6 +3965,367 @@ async function refOpen(indexStr) {
|
|
|
3926
3965
|
}
|
|
3927
3966
|
}
|
|
3928
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
|
+
|
|
3929
4329
|
// src/lib/nlp-run.ts
|
|
3930
4330
|
async function dispatchNlpRoute(route, input) {
|
|
3931
4331
|
switch (route.command) {
|
|
@@ -3974,28 +4374,36 @@ async function dispatchNlpRoute(route, input) {
|
|
|
3974
4374
|
return theme();
|
|
3975
4375
|
case "ref":
|
|
3976
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();
|
|
3977
4385
|
}
|
|
3978
4386
|
}
|
|
3979
4387
|
async function runNaturalLanguageRoute(input) {
|
|
3980
4388
|
const route = routeNaturalLanguage(input);
|
|
3981
4389
|
if (!route) {
|
|
3982
|
-
console.log(
|
|
4390
|
+
console.log(chalk25.yellow(`
|
|
3983
4391
|
\u2753 "${input}" \u2014 ${ko.nlp.notMatched}
|
|
3984
4392
|
`));
|
|
3985
4393
|
return;
|
|
3986
4394
|
}
|
|
3987
4395
|
console.log("");
|
|
3988
|
-
console.log(
|
|
3989
|
-
console.log(
|
|
4396
|
+
console.log(chalk25.cyan(` \u{1F4AC} "${input}"`));
|
|
4397
|
+
console.log(chalk25.cyan(` \u2192 ${route.explanation}`));
|
|
3990
4398
|
if (route.confidence === "low") {
|
|
3991
|
-
const { confirm } = await
|
|
4399
|
+
const { confirm } = await inquirer13.prompt([{
|
|
3992
4400
|
type: "confirm",
|
|
3993
4401
|
name: "confirm",
|
|
3994
4402
|
message: `${route.explanation} \u2014 ${ko.nlp.matched}`,
|
|
3995
4403
|
default: true
|
|
3996
4404
|
}]);
|
|
3997
4405
|
if (!confirm) {
|
|
3998
|
-
console.log(
|
|
4406
|
+
console.log(chalk25.dim(` ${ko.nlp.menuHint}`));
|
|
3999
4407
|
return;
|
|
4000
4408
|
}
|
|
4001
4409
|
}
|
|
@@ -4004,6 +4412,20 @@ async function runNaturalLanguageRoute(input) {
|
|
|
4004
4412
|
}
|
|
4005
4413
|
|
|
4006
4414
|
// src/index.ts
|
|
4415
|
+
function getVersion() {
|
|
4416
|
+
const dir = path15.dirname(fileURLToPath4(import.meta.url));
|
|
4417
|
+
for (const pkgPath of [path15.join(dir, "../package.json"), path15.join(dir, "../../package.json")]) {
|
|
4418
|
+
try {
|
|
4419
|
+
if (fs16.existsSync(pkgPath)) {
|
|
4420
|
+
const pkg = JSON.parse(fs16.readFileSync(pkgPath, "utf-8"));
|
|
4421
|
+
if (pkg.version) return pkg.version;
|
|
4422
|
+
}
|
|
4423
|
+
} catch {
|
|
4424
|
+
continue;
|
|
4425
|
+
}
|
|
4426
|
+
}
|
|
4427
|
+
return "0.0.0";
|
|
4428
|
+
}
|
|
4007
4429
|
var program = new Command();
|
|
4008
4430
|
var defaultHelp = new Help();
|
|
4009
4431
|
var KO_ALIASES = {
|
|
@@ -4026,9 +4448,13 @@ var KO_ALIASES = {
|
|
|
4026
4448
|
design: "\uB514\uC790\uC778",
|
|
4027
4449
|
"design-palette": "\uD314\uB808\uD2B8",
|
|
4028
4450
|
theme: "\uD14C\uB9C8",
|
|
4029
|
-
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"
|
|
4030
4456
|
};
|
|
4031
|
-
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(
|
|
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());
|
|
4032
4458
|
program.configureHelp({
|
|
4033
4459
|
formatHelp(cmd, helper) {
|
|
4034
4460
|
if (cmd.parent) {
|
|
@@ -4110,6 +4536,18 @@ refCmd.command("list").alias("\uBAA9\uB85D").description("\uC800\uC7A5\uB41C \uB
|
|
|
4110
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) => {
|
|
4111
4537
|
await refOpen(index);
|
|
4112
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
|
+
});
|
|
4113
4551
|
program.on("command:*", async (operands) => {
|
|
4114
4552
|
const unknown = operands[0] ?? "";
|
|
4115
4553
|
const rest = operands.slice(1);
|
|
@@ -4118,7 +4556,7 @@ program.on("command:*", async (operands) => {
|
|
|
4118
4556
|
});
|
|
4119
4557
|
program.action(async () => {
|
|
4120
4558
|
console.log("\n\u{1F3AF} VHK \u2014 \uBC14\uC774\uBE0C\uCF54\uB529 \uD504\uB85C\uC81D\uD2B8 \uCF54\uCE58\n");
|
|
4121
|
-
const { choice } = await
|
|
4559
|
+
const { choice } = await inquirer14.prompt([{
|
|
4122
4560
|
type: "list",
|
|
4123
4561
|
name: "choice",
|
|
4124
4562
|
message: "\uBB58 \uB3C4\uC640\uB4DC\uB9B4\uAE4C\uC694?",
|
package/dist/mcp/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,65 +1,65 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@byh3071/vhk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Vibe Harness Kit β λ°μ΄λΈμ½λ© νμ¬μ΄ν΄ CLI",
|
|
5
|
-
"bin": {
|
|
6
|
-
"vhk": "dist/index.js",
|
|
7
|
-
"vhk-mcp": "dist/mcp/index.js"
|
|
8
|
-
},
|
|
9
|
-
"type": "module",
|
|
10
|
-
"scripts": {
|
|
11
|
-
"dev": "tsx src/index.ts",
|
|
12
|
-
"build": "tsup",
|
|
13
|
-
"test": "vitest",
|
|
14
|
-
"test:run": "vitest --run",
|
|
15
|
-
"prepublishOnly": "pnpm build && pnpm test:run",
|
|
16
|
-
"save": "vhk save",
|
|
17
|
-
"check": "vhk check",
|
|
18
|
-
"scan": "vhk secure scan",
|
|
19
|
-
"recap": "vhk recap",
|
|
20
|
-
"ship": "vhk ship",
|
|
21
|
-
"doctor": "vhk doctor"
|
|
22
|
-
},
|
|
23
|
-
"files": [
|
|
24
|
-
"dist",
|
|
25
|
-
"README.md",
|
|
26
|
-
"LICENSE"
|
|
27
|
-
],
|
|
28
|
-
"keywords": [
|
|
29
|
-
"vibe-coding",
|
|
30
|
-
"harness",
|
|
31
|
-
"cli",
|
|
32
|
-
"scaffold",
|
|
33
|
-
"session-log",
|
|
34
|
-
"rules-sync"
|
|
35
|
-
],
|
|
36
|
-
"author": "byh3071 <byh3071@gmail.com>",
|
|
37
|
-
"license": "MIT",
|
|
38
|
-
"repository": {
|
|
39
|
-
"type": "git",
|
|
40
|
-
"url": "git+https://github.com/byh3071-cpu/vhk.git"
|
|
41
|
-
},
|
|
42
|
-
"engines": {
|
|
43
|
-
"node": ">=20"
|
|
44
|
-
},
|
|
45
|
-
"dependencies": {
|
|
46
|
-
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
47
|
-
"@notionhq/client": "^5.22.0",
|
|
48
|
-
"chalk": "^5.6.2",
|
|
49
|
-
"commander": "^14.0.3",
|
|
50
|
-
"handlebars": "^4.7.9",
|
|
51
|
-
"inquirer": "^9.3.8",
|
|
52
|
-
"ora": "^9.4.0",
|
|
53
|
-
"simple-git": "^3.36.0",
|
|
54
|
-
"zod": "^4.4.3"
|
|
55
|
-
},
|
|
56
|
-
"devDependencies": {
|
|
57
|
-
"@types/inquirer": "^9.0.9",
|
|
58
|
-
"@types/node": "^25.9.1",
|
|
59
|
-
"ignore": "^7.0.5",
|
|
60
|
-
"tsup": "^8.5.1",
|
|
61
|
-
"tsx": "^4.22.3",
|
|
62
|
-
"typescript": "^6.0.3",
|
|
63
|
-
"vitest": "^4.1.7"
|
|
64
|
-
}
|
|
65
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@byh3071/vhk",
|
|
3
|
+
"version": "0.9.1",
|
|
4
|
+
"description": "Vibe Harness Kit β λ°μ΄λΈμ½λ© νμ¬μ΄ν΄ CLI",
|
|
5
|
+
"bin": {
|
|
6
|
+
"vhk": "dist/index.js",
|
|
7
|
+
"vhk-mcp": "dist/mcp/index.js"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"dev": "tsx src/index.ts",
|
|
12
|
+
"build": "tsup",
|
|
13
|
+
"test": "vitest",
|
|
14
|
+
"test:run": "vitest --run",
|
|
15
|
+
"prepublishOnly": "pnpm build && pnpm test:run",
|
|
16
|
+
"save": "vhk save",
|
|
17
|
+
"check": "vhk check",
|
|
18
|
+
"scan": "vhk secure scan",
|
|
19
|
+
"recap": "vhk recap",
|
|
20
|
+
"ship": "vhk ship",
|
|
21
|
+
"doctor": "vhk doctor"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"README.md",
|
|
26
|
+
"LICENSE"
|
|
27
|
+
],
|
|
28
|
+
"keywords": [
|
|
29
|
+
"vibe-coding",
|
|
30
|
+
"harness",
|
|
31
|
+
"cli",
|
|
32
|
+
"scaffold",
|
|
33
|
+
"session-log",
|
|
34
|
+
"rules-sync"
|
|
35
|
+
],
|
|
36
|
+
"author": "byh3071 <byh3071@gmail.com>",
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/byh3071-cpu/vhk.git"
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=20"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
47
|
+
"@notionhq/client": "^5.22.0",
|
|
48
|
+
"chalk": "^5.6.2",
|
|
49
|
+
"commander": "^14.0.3",
|
|
50
|
+
"handlebars": "^4.7.9",
|
|
51
|
+
"inquirer": "^9.3.8",
|
|
52
|
+
"ora": "^9.4.0",
|
|
53
|
+
"simple-git": "^3.36.0",
|
|
54
|
+
"zod": "^4.4.3"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@types/inquirer": "^9.0.9",
|
|
58
|
+
"@types/node": "^25.9.1",
|
|
59
|
+
"ignore": "^7.0.5",
|
|
60
|
+
"tsup": "^8.5.1",
|
|
61
|
+
"tsx": "^4.22.3",
|
|
62
|
+
"typescript": "^6.0.3",
|
|
63
|
+
"vitest": "^4.1.7"
|
|
64
|
+
}
|
|
65
|
+
}
|