@byh3071/vhk 1.6.6 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2987,6 +2987,7 @@ export {
2987
2987
  ko,
2988
2988
  t,
2989
2989
  localDate,
2990
+ ensureVhkIgnored,
2990
2991
  listBackups,
2991
2992
  restoreBackup,
2992
2993
  detectExistingRuleFiles,
package/dist/index.js CHANGED
@@ -13,6 +13,7 @@ import {
13
13
  deploy,
14
14
  detectExistingRuleFiles,
15
15
  ensureInteractive,
16
+ ensureVhkIgnored,
16
17
  env,
17
18
  envCheck,
18
19
  filterSevereFindings,
@@ -42,7 +43,7 @@ import {
42
43
  stripBom,
43
44
  sync,
44
45
  t
45
- } from "./chunk-HLUFOT2T.js";
46
+ } from "./chunk-GXFZ7JXX.js";
46
47
 
47
48
  // src/index.ts
48
49
  import { Command, Help } from "commander";
@@ -5359,30 +5360,208 @@ async function mode(target) {
5359
5360
  }
5360
5361
 
5361
5362
  // src/commands/verify.ts
5363
+ import { execFileSync as execFileSync4 } from "child_process";
5364
+ import { existsSync as existsSync16, mkdirSync as mkdirSync11, writeFileSync as writeFileSync11 } from "fs";
5365
+ import { join as join10 } from "path";
5362
5366
  import chalk30 from "chalk";
5363
- function verificationChecklist() {
5364
- return [
5365
- "\uD0C0\uC785 \uCCB4\uD06C \u2014 pnpm exec tsc --noEmit",
5366
- "\uD14C\uC2A4\uD2B8 \u2014 pnpm run test:run",
5367
- "\uBE4C\uB4DC \u2014 pnpm run build",
5368
- "\uBCF4\uC548 \uC2A4\uCE94 \u2014 vhk secure scan"
5369
- ];
5367
+ var REPORT_SCHEMA_VERSION = 1;
5368
+ var REPORT_DIR_REL = join10(".vhk", "reports");
5369
+ var REPORT_PATH_REL = join10(REPORT_DIR_REL, "latest.json");
5370
+ var SHIM = /* @__PURE__ */ new Set(["pnpm", "npm", "npx", "yarn"]);
5371
+ function detectPm(cwd) {
5372
+ if (existsSync16(join10(cwd, "pnpm-lock.yaml"))) return "pnpm";
5373
+ if (existsSync16(join10(cwd, "yarn.lock"))) return "yarn";
5374
+ return "npm";
5375
+ }
5376
+ function execGate(cmd, args, cwd) {
5377
+ let bin = cmd;
5378
+ let argv = args;
5379
+ if (process.platform === "win32" && SHIM.has(cmd)) {
5380
+ bin = "cmd.exe";
5381
+ argv = ["/d", "/s", "/c", `${cmd}.cmd`, ...args];
5382
+ }
5383
+ try {
5384
+ execFileSync4(bin, argv, {
5385
+ cwd,
5386
+ stdio: ["pipe", "pipe", "pipe"],
5387
+ encoding: "utf-8",
5388
+ maxBuffer: 64 * 1024 * 1024,
5389
+ timeout: 6e5,
5390
+ killSignal: "SIGTERM"
5391
+ });
5392
+ return { exitCode: 0, out: "" };
5393
+ } catch (e) {
5394
+ const err = e;
5395
+ const exitCode = typeof err.status === "number" ? err.status : 1;
5396
+ const out = ((err.stdout?.toString?.() ?? "") + (err.stderr?.toString?.() ?? "")).trim();
5397
+ return { exitCode, out };
5398
+ }
5399
+ }
5400
+ function runScriptGate(id, label, cwd, pm, argvFor) {
5401
+ const argv = argvFor(pm);
5402
+ if (!argv) {
5403
+ return { id, label, status: "skip", exitCode: null, skipped: true, detail: "\uD574\uB2F9 \uC2A4\uD06C\uB9BD\uD2B8/\uC124\uC815 \uC5C6\uC74C \u2014 skip(WARN)" };
5404
+ }
5405
+ const { exitCode } = execGate(pm, argv, cwd);
5406
+ return {
5407
+ id,
5408
+ label,
5409
+ status: exitCode === 0 ? "pass" : "fail",
5410
+ exitCode,
5411
+ skipped: false,
5412
+ detail: exitCode === 0 ? void 0 : `\uC885\uB8CC\uCF54\uB4DC ${exitCode}`
5413
+ };
5414
+ }
5415
+ function readPackageScripts(cwd) {
5416
+ const pkgPath = join10(cwd, "package.json");
5417
+ if (!existsSync16(pkgPath)) return {};
5418
+ try {
5419
+ const pkg = readJsonFile(pkgPath);
5420
+ return pkg.scripts ?? {};
5421
+ } catch {
5422
+ return {};
5423
+ }
5424
+ }
5425
+ function runGates(cwd) {
5426
+ const scripts = readPackageScripts(cwd);
5427
+ const pm = detectPm(cwd);
5428
+ const gates = [];
5429
+ gates.push(
5430
+ runScriptGate("typecheck", "tsc --noEmit", cwd, pm, () => {
5431
+ if (scripts.typecheck) return ["run", "typecheck"];
5432
+ if (existsSync16(join10(cwd, "tsconfig.json"))) return pm === "npm" ? ["exec", "--", "tsc", "--noEmit"] : ["exec", "tsc", "--noEmit"];
5433
+ return null;
5434
+ })
5435
+ );
5436
+ gates.push(
5437
+ runScriptGate("test", "test:run", cwd, pm, () => {
5438
+ if (scripts["test:run"]) return ["run", "test:run"];
5439
+ if (scripts.test && /vitest/.test(scripts.test)) return ["run", "test", "--", "--run"];
5440
+ if (scripts.test) return ["run", "test"];
5441
+ return null;
5442
+ })
5443
+ );
5444
+ gates.push(
5445
+ runScriptGate("build", "build", cwd, pm, () => scripts.build ? ["run", "build"] : null)
5446
+ );
5447
+ gates.push(runSecureGate(cwd));
5448
+ return gates;
5449
+ }
5450
+ function runSecureGate(cwd) {
5451
+ try {
5452
+ const severe = filterSevereFindings(scanProjectForSecrets(cwd).findings);
5453
+ const n = severe.length;
5454
+ return {
5455
+ id: "secure",
5456
+ label: "secure scan",
5457
+ status: n === 0 ? "pass" : "fail",
5458
+ exitCode: n === 0 ? 0 : 1,
5459
+ skipped: false,
5460
+ detail: n === 0 ? void 0 : `severe \uC2DC\uD06C\uB9BF ${n}\uAC74 (\uAC12 \uBBF8\uAE30\uB85D \u2014 vhk secure scan \uC73C\uB85C \uD655\uC778)`
5461
+ };
5462
+ } catch (e) {
5463
+ return {
5464
+ id: "secure",
5465
+ label: "secure scan",
5466
+ status: "fail",
5467
+ exitCode: 1,
5468
+ skipped: false,
5469
+ detail: `\uC2A4\uCE94 \uC2E4\uD589 \uC2E4\uD328: ${e instanceof Error ? e.message : String(e)}`
5470
+ };
5471
+ }
5472
+ }
5473
+ function aggregateStatus(gates) {
5474
+ if (gates.some((g) => g.status === "fail")) return "FAIL";
5475
+ if (gates.some((g) => g.status === "skip")) return "WARN";
5476
+ return "PASS";
5370
5477
  }
5371
- async function verify() {
5372
- console.log(chalk30.bold("\n\u{1F50E} \uAC80\uC99D \uBB36\uC74C (verify \u2014 lite)"));
5478
+ function buildNextActions(gates) {
5479
+ const actions = [];
5480
+ for (const g of gates) {
5481
+ if (g.status === "fail") {
5482
+ if (g.id === "secure") actions.push("\uC2DC\uD06C\uB9BF \uC81C\uAC70 \uD6C4 \uC7AC\uAC80\uC99D \u2014 vhk secure scan \uC73C\uB85C \uC704\uCE58 \uD655\uC778");
5483
+ else actions.push(`${g.label} \uC2E4\uD328(\uC885\uB8CC\uCF54\uB4DC ${g.exitCode}) \u2014 \uB85C\uADF8 \uD655\uC778 \uD6C4 \uC218\uC815`);
5484
+ } else if (g.status === "skip") {
5485
+ actions.push(`${g.label} \uAC8C\uC774\uD2B8 \uC5C6\uC74C \u2014 package.json scripts \uC5D0 \uCD94\uAC00\uD558\uBA74 \uAC80\uC99D \uCEE4\uBC84\uB9AC\uC9C0 \u2191`);
5486
+ }
5487
+ }
5488
+ if (actions.length === 0) actions.push("\uAC80\uC99D \uD1B5\uACFC \u2014 vhk save \uB85C \uC800\uC7A5\uD558\uC138\uC694.");
5489
+ return actions;
5490
+ }
5491
+ function buildReport(gates, generatedAt, date) {
5492
+ const summary = {
5493
+ total: gates.length,
5494
+ pass: gates.filter((g) => g.status === "pass").length,
5495
+ fail: gates.filter((g) => g.status === "fail").length,
5496
+ skip: gates.filter((g) => g.status === "skip").length
5497
+ };
5498
+ return {
5499
+ schemaVersion: REPORT_SCHEMA_VERSION,
5500
+ generatedAt,
5501
+ date,
5502
+ status: aggregateStatus(gates),
5503
+ summary,
5504
+ gates,
5505
+ nextActions: buildNextActions(gates)
5506
+ };
5507
+ }
5508
+ function verifyEvidence(cwd = process.cwd()) {
5509
+ const gates = runGates(cwd);
5510
+ const report = buildReport(gates, (/* @__PURE__ */ new Date()).toISOString(), localDate());
5511
+ const dir = join10(cwd, REPORT_DIR_REL);
5512
+ mkdirSync11(dir, { recursive: true });
5513
+ const path14 = join10(cwd, REPORT_PATH_REL);
5514
+ writeFileSync11(path14, JSON.stringify(report, null, 2) + "\n", "utf-8");
5515
+ try {
5516
+ ensureVhkIgnored(cwd, "reports/");
5517
+ } catch {
5518
+ }
5519
+ return { report, path: REPORT_PATH_REL };
5520
+ }
5521
+ var STATUS_BADGE = {
5522
+ PASS: chalk30.green.bold("PASS"),
5523
+ WARN: chalk30.yellow.bold("WARN"),
5524
+ FAIL: chalk30.red.bold("FAIL")
5525
+ };
5526
+ async function verify(opts = {}) {
5527
+ if (!ensureNotHardStopped("verify")) return;
5528
+ const cwd = process.cwd();
5529
+ const { report, path: path14 } = verifyEvidence(cwd);
5530
+ if (opts.json) {
5531
+ console.log(JSON.stringify(report, null, 2));
5532
+ process.exitCode = report.status === "FAIL" ? 1 : 0;
5533
+ return;
5534
+ }
5535
+ console.log(chalk30.bold("\n\u{1F50E} \uAC80\uC99D \uBB36\uC74C (verify)"));
5373
5536
  console.log(chalk30.gray("\u2500".repeat(40)));
5374
5537
  const mode2 = readConfig().safetyMode;
5375
5538
  console.log(chalk30.dim(` \uD604\uC7AC Safety Mode: ${mode2} \u2014 ${SAFETY_MODE_DESC[mode2]}`));
5376
- console.log(chalk30.cyan("\n \uC704\uD5D8 \uC791\uC5C5/\uC800\uC7A5 \uC804 \uAD8C\uC7A5 \uAC80\uC99D:"));
5377
- for (const item of verificationChecklist()) {
5378
- console.log(` \u2610 ${item}`);
5539
+ const icon = (s2) => s2 === "pass" ? chalk30.green("\u2713") : s2 === "fail" ? chalk30.red("\u2717") : chalk30.yellow("\u2298");
5540
+ for (const g of report.gates) {
5541
+ const tail = g.detail ? chalk30.dim(` \u2014 ${g.detail}`) : "";
5542
+ console.log(` ${icon(g.status)} ${g.label}${tail}`);
5543
+ }
5544
+ const s = report.summary;
5545
+ console.log(
5546
+ `
5547
+ \uACB0\uACFC: ${STATUS_BADGE[report.status]} ` + chalk30.dim(`(pass ${s.pass} / fail ${s.fail} / skip ${s.skip}, \uCD1D ${s.total})`)
5548
+ );
5549
+ console.log(chalk30.dim(` \u{1F4C4} \uC99D\uAC70: ${path14}`));
5550
+ process.exitCode = report.status === "FAIL" ? 1 : 0;
5551
+ if (report.status === "FAIL") {
5552
+ printNextStep({
5553
+ message: "\uAC80\uC99D \uC2E4\uD328 \u2014 \uC544\uB798\uB97C \uBA3C\uC800 \uACE0\uCE58\uC138\uC694:",
5554
+ command: "vhk verify",
5555
+ cursorHint: "\uAC80\uC99D \uB2E4\uC2DC \uB3CC\uB824\uC918",
5556
+ alternative: report.nextActions[0]
5557
+ });
5558
+ } else {
5559
+ printNextStep({
5560
+ message: report.status === "WARN" ? "\uAC80\uC99D \uD1B5\uACFC(\uC77C\uBD80 \uAC8C\uC774\uD2B8 skip). \uC800\uC7A5\uD558\uB824\uBA74:" : "\uAC80\uC99D \uD1B5\uACFC! \uC800\uC7A5\uD558\uB824\uBA74:",
5561
+ command: "vhk save",
5562
+ cursorHint: "\uC800\uC7A5\uD574\uC918"
5563
+ });
5379
5564
  }
5380
- console.log(chalk30.dim("\n \u203B \uBA54\uD0C0\uB7EC\uB108(\uC790\uB3D9 \uC2E4\uD589) \uC790\uB9AC \u2014 \uD604\uC7AC\uB294 \uBB36\uC74C \uC548\uB0B4\uB9CC(lite)."));
5381
- printNextStep({
5382
- message: "\uAC80\uC99D \uD1B5\uACFC \uD6C4 \uC800\uC7A5\uD558\uC138\uC694:",
5383
- command: "vhk save",
5384
- cursorHint: "\uC800\uC7A5\uD574\uC918"
5385
- });
5386
5565
  }
5387
5566
 
5388
5567
  // src/lib/risk-policy.ts
@@ -5861,8 +6040,8 @@ program.command("context").alias("\uB9E5\uB77D").option("--compact", "\uD1A0\uD0
5861
6040
  program.command("mode [target]").alias("\uBAA8\uB4DC").description("Safety Mode \uC870\uD68C/\uBCC0\uACBD (lite|standard|strict) \u2014 \uC704\uD5D8 \uC791\uC5C5 \uAC00\uB4DC \uAC15\uB3C4").action(async (target) => {
5862
6041
  await mode(target);
5863
6042
  });
5864
- program.command("verify").alias("\uC0AC\uC804\uC810\uAC80").description("\uC800\uC7A5/\uC704\uD5D8 \uC791\uC5C5 \uC804 \uAC80\uC99D \uBB36\uC74C \uC548\uB0B4 (lite)").action(async () => {
5865
- await verify();
6043
+ program.command("verify").alias("\uC0AC\uC804\uC810\uAC80").option("--json", "\uB9AC\uD3EC\uD2B8 JSON \uC744 stdout \uC73C\uB85C \uCD9C\uB825 (CI\uC6A9 \u2014 \uACBD\uB85C \uB300\uC2E0)").description("\uAC80\uC99D \uAC8C\uC774\uD2B8(tsc/test/build/secure) \uC2E4\uC81C \uC2E4\uD589 + \uC99D\uAC70 \uAE30\uB85D (.vhk/reports/latest.json)").action(async (opts) => {
6044
+ await verify(opts);
5866
6045
  });
5867
6046
  program.command("context-show").alias("\uB9E5\uB77D\uBCF4\uAE30").description("\uD604\uC7AC \uCEE8\uD14D\uC2A4\uD2B8 \uD30C\uC77C \uB0B4\uC6A9 \uCD9C\uB825").action(async () => {
5868
6047
  await contextShow();
package/dist/mcp/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  resolveVhkCliInvocation,
4
4
  startMcpServer
5
- } from "../chunk-HLUFOT2T.js";
5
+ } from "../chunk-GXFZ7JXX.js";
6
6
 
7
7
  // src/mcp/index.ts
8
8
  var cli = resolveVhkCliInvocation();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@byh3071/vhk",
3
- "version": "1.6.6",
3
+ "version": "1.7.0",
4
4
  "description": "Vibe Harness Kit — AI 코딩 도구·기기를 바꿔도 규칙·맥락이 따라가는 포터빌리티 CLI (sync: Cursor·Claude·Windsurf·Copilot·Antigravity / cloud 백업)",
5
5
  "bin": {
6
6
  "vhk": "dist/index.js",