@byh3071/vhk 1.6.3 → 1.6.4

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.
@@ -2980,6 +2980,7 @@ export {
2980
2980
  require_ignore,
2981
2981
  printSecurityWarnings,
2982
2982
  filterTrackedPaths,
2983
+ stripBom,
2983
2984
  readJsonFile,
2984
2985
  NETWORK_EXEC_TIMEOUT_MS,
2985
2986
  safeExecFile,
package/dist/index.js CHANGED
@@ -35,9 +35,10 @@ import {
35
35
  safeExecFile,
36
36
  scanProjectForSecrets,
37
37
  startMcpServer,
38
+ stripBom,
38
39
  sync,
39
40
  t
40
- } from "./chunk-53RJHPP6.js";
41
+ } from "./chunk-EJTVXWUZ.js";
41
42
 
42
43
  // src/index.ts
43
44
  import { Command, Help } from "commander";
@@ -504,7 +505,37 @@ import inquirer12 from "inquirer";
504
505
 
505
506
  // src/commands/gate.ts
506
507
  import inquirer from "inquirer";
508
+ import chalk2 from "chalk";
509
+
510
+ // src/lib/interactive.ts
507
511
  import chalk from "chalk";
512
+ function isInteractive(opts) {
513
+ if (opts?.yes) return false;
514
+ if (process.env.VHK_FORCE_INTERACTIVE === "1") return true;
515
+ return !!process.stdin.isTTY;
516
+ }
517
+ async function promptOrDefault(ask, fallback, opts) {
518
+ if (!isInteractive(opts)) return fallback;
519
+ try {
520
+ return await ask();
521
+ } catch (err) {
522
+ if (isPromptAbortError(err)) return fallback;
523
+ throw err;
524
+ }
525
+ }
526
+ function ensureInteractive(hint = "") {
527
+ if (isInteractive()) return true;
528
+ console.error(chalk.yellow(" \u26A0\uFE0F \uC774 \uBA85\uB839\uC740 \uB300\uD654\uD615 \uC785\uB825\uC774 \uD544\uC694\uD569\uB2C8\uB2E4 \u2014 \uBE44-TTY/\uD30C\uC774\uD504 \uD658\uACBD\uC5D0\uC11C\uB294 \uC2E4\uD589\uD560 \uC218 \uC5C6\uC5B4\uC694."));
529
+ if (hint) console.error(chalk.dim(` ${hint}`));
530
+ process.exitCode = 1;
531
+ return false;
532
+ }
533
+ function isPromptAbortError(err) {
534
+ const msg = err instanceof Error ? err.message : String(err);
535
+ return /ERR_USE_AFTER_CLOSE|force closed|ExitPromptError|readline was closed|User force closed/i.test(msg);
536
+ }
537
+
538
+ // src/commands/gate.ts
508
539
  var GATE_QUESTIONS = [
509
540
  { id: 1, stage: "\uBB38\uC81C \uC815\uC758", question: "\uC774 \uC544\uC774\uB514\uC5B4\uAC00 \uD574\uACB0\uD558\uB294 \uBB38\uC81C\uB97C \uD55C \uBB38\uC7A5\uC73C\uB85C \uB9D0\uD574\uBCF4\uC138\uC694.", failIf: "\uD55C \uBB38\uC7A5 \uBD88\uAC00 \u2192 \uBBF8\uC131\uC219", quick: true },
510
541
  { id: 2, stage: "\uD575\uC2EC \uAE30\uB2A5", question: "\uB531 1\uAC1C \uAE30\uB2A5\uB9CC \uACE0\uB974\uBA74?", failIf: "2\uAC1C \uC774\uC0C1 \u2192 \uBC94\uC704 \uCD08\uACFC", quick: true },
@@ -526,7 +557,8 @@ function judgeGate(failCount, holdCount) {
526
557
  return "DROP";
527
558
  }
528
559
  async function gate() {
529
- console.log(chalk.bold(`
560
+ if (!ensureInteractive("\uC544\uC774\uB514\uC5B4 \uAC80\uC99D\uC740 \uB300\uD654\uD615 \uC9C8\uBB38\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. \uD130\uBBF8\uB110(PowerShell \uB4F1)\uC5D0\uC11C \uC9C1\uC811 \uC2E4\uD589\uD558\uC138\uC694. Git Bash \uBA74 VHK_FORCE_INTERACTIVE=1.")) return;
561
+ console.log(chalk2.bold(`
530
562
  ${ko.gate.title}
531
563
  `));
532
564
  const { mode: mode2 } = await inquirer.prompt([{
@@ -545,33 +577,33 @@ ${ko.gate.title}
545
577
  name: "source",
546
578
  message: ko.gate.skipSourcePrompt
547
579
  }]);
548
- console.log(chalk.green.bold(`
580
+ console.log(chalk2.green.bold(`
549
581
  ${ko.gate.skipGo}`));
550
- console.log(chalk.dim(ko.gate.skipSourceLabel(source)));
582
+ console.log(chalk2.dim(ko.gate.skipSourceLabel(source)));
551
583
  return;
552
584
  }
553
585
  const questions = mode2 === "quick" ? GATE_QUESTIONS.filter((q) => q.quick) : GATE_QUESTIONS;
554
586
  const total = questions.length;
555
587
  const header = mode2 === "quick" ? ko.gate.quickHeader : ko.gate.fullHeader;
556
- console.log(chalk.dim(`
588
+ console.log(chalk2.dim(`
557
589
  ${header} ${ko.gate.modeCountSuffix(total)}
558
590
  `));
559
- console.log(chalk.dim(`
591
+ console.log(chalk2.dim(`
560
592
  ${ko.gate.welcome}
561
593
  `));
562
- console.log(chalk.dim(` ${ko.gate.ideaHint}`));
594
+ console.log(chalk2.dim(` ${ko.gate.ideaHint}`));
563
595
  const { idea } = await inquirer.prompt([
564
596
  { type: "input", name: "idea", message: ko.gate.idea }
565
597
  ]);
566
- console.log(chalk.dim(` ${ko.gate.painPointHint}`));
598
+ console.log(chalk2.dim(` ${ko.gate.painPointHint}`));
567
599
  const { painPoint } = await inquirer.prompt([
568
600
  { type: "input", name: "painPoint", message: ko.gate.painPoint }
569
601
  ]);
570
- console.log(chalk.dim(` ${ko.gate.edgeHint}`));
602
+ console.log(chalk2.dim(` ${ko.gate.edgeHint}`));
571
603
  const { edge } = await inquirer.prompt([
572
604
  { type: "input", name: "edge", message: ko.gate.edge }
573
605
  ]);
574
- console.log(chalk.dim(`
606
+ console.log(chalk2.dim(`
575
607
  ${ko.gate.checklistStart}
576
608
  `));
577
609
  let failCount = 0;
@@ -579,7 +611,7 @@ ${ko.gate.checklistStart}
579
611
  const results = [];
580
612
  for (let i = 0; i < questions.length; i++) {
581
613
  const q = questions[i];
582
- if (q.hint) console.log(chalk.dim(`${ko.gate.hintPrefix} ${q.hint}`));
614
+ if (q.hint) console.log(chalk2.dim(`${ko.gate.hintPrefix} ${q.hint}`));
583
615
  const { answer } = await inquirer.prompt([{
584
616
  type: "input",
585
617
  name: "answer",
@@ -598,22 +630,22 @@ ${ko.gate.checklistStart}
598
630
  if (status2 === "fail") failCount++;
599
631
  if (status2 === "hold") holdCount++;
600
632
  results.push({ id: q.id, stage: q.stage, status: status2, answer });
601
- const icon = status2 === "pass" ? chalk.green(ko.gate.statusPassLine) : status2 === "hold" ? chalk.yellow(ko.gate.statusHoldLine) : chalk.red(ko.gate.statusFailLine);
633
+ const icon = status2 === "pass" ? chalk2.green(ko.gate.statusPassLine) : status2 === "hold" ? chalk2.yellow(ko.gate.statusHoldLine) : chalk2.red(ko.gate.statusFailLine);
602
634
  console.log(icon);
603
635
  }
604
- console.log(chalk.bold(`
636
+ console.log(chalk2.bold(`
605
637
  ${ko.gate.verdictTitle}
606
638
  `));
607
- console.log(`${ko.gate.ideaLabel} ${chalk.cyan(idea)}`);
639
+ console.log(`${ko.gate.ideaLabel} ${chalk2.cyan(idea)}`);
608
640
  console.log(`${ko.gate.painPointLabel} ${painPoint}`);
609
641
  console.log(`${ko.gate.edgeLabel} ${edge}`);
610
642
  console.log(`${ko.gate.countLine(failCount, holdCount, total)}
611
643
  `);
612
644
  const verdict = judgeGate(failCount, holdCount);
613
645
  if (verdict === "GO") {
614
- console.log(chalk.green.bold(ko.gate.go));
646
+ console.log(chalk2.green.bold(ko.gate.go));
615
647
  if (holdCount > 0) {
616
- console.log(chalk.yellow(ko.gate.holdRemainHint));
648
+ console.log(chalk2.yellow(ko.gate.holdRemainHint));
617
649
  }
618
650
  printNextStep({
619
651
  message: "\uC544\uC774\uB514\uC5B4 \uD1B5\uACFC! \uC774\uC81C \uD504\uB85C\uC81D\uD2B8\uB97C \uB9CC\uB4E4\uC5B4\uBCF4\uC138\uC694.",
@@ -621,20 +653,20 @@ ${ko.gate.verdictTitle}
621
653
  cursorHint: "\uD504\uB85C\uC81D\uD2B8 \uB9CC\uB4E4\uC5B4\uC918"
622
654
  });
623
655
  } else if (verdict === "REFINE") {
624
- console.log(chalk.yellow.bold(ko.gate.refine));
656
+ console.log(chalk2.yellow.bold(ko.gate.refine));
625
657
  printNextStep({
626
658
  message: "\uC870\uAE08 \uB354 \uB2E4\uB4EC\uC740 \uD6C4 \uB2E4\uC2DC \uAC80\uC99D\uD574\uBCF4\uC138\uC694.",
627
659
  command: "vhk \uAC80\uC99D",
628
660
  cursorHint: "\uC544\uC774\uB514\uC5B4 \uB2E4\uC2DC \uAC80\uC99D\uD574\uC918"
629
661
  });
630
662
  } else {
631
- console.log(chalk.red.bold(ko.gate.drop));
663
+ console.log(chalk2.red.bold(ko.gate.drop));
632
664
  }
633
665
  }
634
666
 
635
667
  // src/commands/init.ts
636
668
  import inquirer2 from "inquirer";
637
- import chalk3 from "chalk";
669
+ import chalk4 from "chalk";
638
670
  import fs2 from "fs";
639
671
  import path2 from "path";
640
672
 
@@ -956,13 +988,13 @@ function VHK_CONTEXT_SEED(name, type, stack) {
956
988
  }
957
989
 
958
990
  // src/utils/logger.ts
959
- import chalk2 from "chalk";
991
+ import chalk3 from "chalk";
960
992
  var log = {
961
- success: (msg) => console.log(chalk2.green(`\u2705 ${msg}`)),
962
- error: (msg) => console.log(chalk2.red(`\u274C ${msg}`)),
963
- warn: (msg) => console.log(chalk2.yellow(`\u26A0\uFE0F ${msg}`)),
964
- info: (msg) => console.log(chalk2.blue(`\u2139\uFE0F ${msg}`)),
965
- step: (msg) => console.log(chalk2.bold(`
993
+ success: (msg) => console.log(chalk3.green(`\u2705 ${msg}`)),
994
+ error: (msg) => console.log(chalk3.red(`\u274C ${msg}`)),
995
+ warn: (msg) => console.log(chalk3.yellow(`\u26A0\uFE0F ${msg}`)),
996
+ info: (msg) => console.log(chalk3.blue(`\u2139\uFE0F ${msg}`)),
997
+ step: (msg) => console.log(chalk3.bold(`
966
998
  \u25B8 ${msg}`))
967
999
  };
968
1000
 
@@ -1180,12 +1212,9 @@ function resolveType(type) {
1180
1212
  }
1181
1213
  return type;
1182
1214
  }
1183
- function isNonInteractive(options) {
1184
- return Boolean(options.yes) || !process.stdin.isTTY || !process.stdout.isTTY;
1185
- }
1186
1215
  var DEFAULT_TYPE = PROJECT_TYPES[0].value;
1187
1216
  async function collectAnswers(options, defaults = {}) {
1188
- const noninteractive = isNonInteractive(options);
1217
+ const noninteractive = !isInteractive(options);
1189
1218
  const prompts = [];
1190
1219
  if (!noninteractive) {
1191
1220
  if (!options.name && !defaults.name) {
@@ -1208,11 +1237,11 @@ async function collectAnswers(options, defaults = {}) {
1208
1237
  async function init(options = {}) {
1209
1238
  const skipGate = Boolean(options.skipGate || options.fromNotion);
1210
1239
  if (skipGate) {
1211
- console.log(chalk3.dim(`
1240
+ console.log(chalk4.dim(`
1212
1241
  ${ko.init.skipGate}
1213
1242
  `));
1214
1243
  }
1215
- console.log(chalk3.bold(`
1244
+ console.log(chalk4.bold(`
1216
1245
  ${ko.init.title}
1217
1246
  `));
1218
1247
  printSecurityWarnings();
@@ -1238,11 +1267,11 @@ ${ko.init.title}
1238
1267
  }
1239
1268
  const detected = detectProjectStack(process.cwd());
1240
1269
  const stack = detected ?? STACK_PRESETS[answers.type];
1241
- if (detected) console.log(chalk3.dim(" \u{1F50E} package.json \uC758\uC874\uC131\uC5D0\uC11C \uC2E4\uC81C \uC2A4\uD0DD \uAC10\uC9C0"));
1242
- console.log(chalk3.dim(`
1270
+ if (detected) console.log(chalk4.dim(" \u{1F50E} package.json \uC758\uC874\uC131\uC5D0\uC11C \uC2E4\uC81C \uC2A4\uD0DD \uAC10\uC9C0"));
1271
+ console.log(chalk4.dim(`
1243
1272
  ${ko.init.recommendedStack} ${stack.join(" + ")}
1244
1273
  `));
1245
- if (!isNonInteractive(options)) {
1274
+ if (isInteractive(options)) {
1246
1275
  const { confirmStack } = await inquirer2.prompt([{
1247
1276
  type: "confirm",
1248
1277
  name: "confirmStack",
@@ -1256,7 +1285,7 @@ ${ko.init.recommendedStack} ${stack.join(" + ")}
1256
1285
  }
1257
1286
  const cwd = process.cwd();
1258
1287
  let adoptedRules = null;
1259
- if (!isNonInteractive(options) && !options.fromNotion) {
1288
+ if (isInteractive(options) && !options.fromNotion) {
1260
1289
  const existingRules = detectExistingRuleFiles(cwd);
1261
1290
  if (existingRules.length > 0) {
1262
1291
  const { adopt } = await inquirer2.prompt([{
@@ -1270,7 +1299,7 @@ ${ko.init.recommendedStack} ${stack.join(" + ")}
1270
1299
  }]);
1271
1300
  if (adopt) {
1272
1301
  adoptedRules = buildAdoptedRules(existingRules, answers.name);
1273
- console.log(chalk3.dim(` ${ko.init.adoptPreview(existingRules.length)}`));
1302
+ console.log(chalk4.dim(` ${ko.init.adoptPreview(existingRules.length)}`));
1274
1303
  }
1275
1304
  }
1276
1305
  }
@@ -1280,7 +1309,7 @@ ${ko.init.recommendedStack} ${stack.join(" + ")}
1280
1309
  for (const [filePath, content] of Object.entries(files)) {
1281
1310
  const fullPath = path2.join(cwd, filePath);
1282
1311
  if (fileExists(fullPath)) {
1283
- const overwrite = isNonInteractive(options) ? false : (await inquirer2.prompt([{
1312
+ const overwrite = !isInteractive(options) ? false : (await inquirer2.prompt([{
1284
1313
  type: "confirm",
1285
1314
  name: "overwrite",
1286
1315
  message: ko.init.overwrite(filePath),
@@ -1294,22 +1323,22 @@ ${ko.init.recommendedStack} ${stack.join(" + ")}
1294
1323
  writeFile(fullPath, content);
1295
1324
  log.success(filePath);
1296
1325
  }
1297
- await writeInitExtras(cwd, isNonInteractive(options));
1298
- console.log(chalk3.bold.green(`
1326
+ await writeInitExtras(cwd, !isInteractive(options));
1327
+ console.log(chalk4.bold.green(`
1299
1328
  ${ko.init.done}`));
1300
- console.log(chalk3.dim(`
1329
+ console.log(chalk4.dim(`
1301
1330
  ${ko.init.nextSteps}`));
1302
1331
  if (options.fromNotion) {
1303
1332
  console.log(` 1. ${ko.init.notionReviewHint}`);
1304
1333
  console.log(` 2. ${ko.init.gitHintLabel}`);
1305
- console.log(` ${chalk3.cyan(ko.init.gitHintCommand)}`);
1334
+ console.log(` ${chalk4.cyan(ko.init.gitHintCommand)}`);
1306
1335
  console.log(` 3. ${ko.init.startDev}
1307
1336
  `);
1308
1337
  } else {
1309
1338
  console.log(` 1. ${ko.init.fillHint}`);
1310
1339
  console.log(` 2. ${ko.init.prdHint}`);
1311
1340
  console.log(` 3. ${ko.init.gitHintLabel}`);
1312
- console.log(` ${chalk3.cyan(ko.init.gitHintCommand)}`);
1341
+ console.log(` ${chalk4.cyan(ko.init.gitHintCommand)}`);
1313
1342
  console.log(` 4. ${ko.init.startDev}
1314
1343
  `);
1315
1344
  }
@@ -1626,20 +1655,6 @@ function createAdrFile(cwd, title, context2, decision, consequences) {
1626
1655
  return filePath;
1627
1656
  }
1628
1657
 
1629
- // src/lib/interactive.ts
1630
- import chalk4 from "chalk";
1631
- function ensureInteractive(hint = "") {
1632
- if (process.stdin.isTTY) return true;
1633
- console.error(chalk4.yellow(" \u26A0\uFE0F \uC774 \uBA85\uB839\uC740 \uB300\uD654\uD615 \uC785\uB825\uC774 \uD544\uC694\uD569\uB2C8\uB2E4 \u2014 \uBE44-TTY/\uD30C\uC774\uD504 \uD658\uACBD\uC5D0\uC11C\uB294 \uC2E4\uD589\uD560 \uC218 \uC5C6\uC5B4\uC694."));
1634
- if (hint) console.error(chalk4.dim(` ${hint}`));
1635
- process.exitCode = 1;
1636
- return false;
1637
- }
1638
- function isPromptAbortError(err) {
1639
- const msg = err instanceof Error ? err.message : String(err);
1640
- return /ERR_USE_AFTER_CLOSE|force closed|ExitPromptError|readline was closed|User force closed/i.test(msg);
1641
- }
1642
-
1643
1658
  // src/lib/hard-stop-guard.ts
1644
1659
  import chalk5 from "chalk";
1645
1660
 
@@ -2178,8 +2193,9 @@ import { existsSync as existsSync2, readFileSync as readFileSync2, readdirSync,
2178
2193
  import { join as join3 } from "path";
2179
2194
  var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
2180
2195
  function parseFrontmatter(content) {
2181
- const m = content.match(FRONTMATTER_RE);
2182
- if (!m) return { frontmatter: {}, body: content };
2196
+ const normalized = stripBom(content);
2197
+ const m = normalized.match(FRONTMATTER_RE);
2198
+ if (!m) return { frontmatter: {}, body: normalized };
2183
2199
  const fm = parseSimpleYaml(m[1]);
2184
2200
  const body = (m[2] ?? "").replace(/^\r?\n+/, "");
2185
2201
  return { frontmatter: fm, body };
@@ -3236,12 +3252,16 @@ async function save() {
3236
3252
  if (severe.length > 5) {
3237
3253
  console.log(chalk12.dim(` ... \uC678 ${severe.length - 5}\uAC74 (vhk \uBCF4\uC548 scan)`));
3238
3254
  }
3239
- const { proceed } = await inquirer5.prompt([{
3240
- type: "confirm",
3241
- name: "proceed",
3242
- message: t("save.secretsConfirm"),
3243
- default: false
3244
- }]);
3255
+ const proceed = await promptOrDefault(
3256
+ async () => (await inquirer5.prompt([{
3257
+ type: "confirm",
3258
+ name: "proceed",
3259
+ message: t("save.secretsConfirm"),
3260
+ default: false
3261
+ }])).proceed,
3262
+ false
3263
+ // 비대화형 = 시크릿 커밋 안 함 (안전)
3264
+ );
3245
3265
  if (!proceed) {
3246
3266
  console.log(chalk12.gray(t("save.cancelled")));
3247
3267
  return;
@@ -3259,12 +3279,15 @@ async function save() {
3259
3279
  const name = line.substring(3);
3260
3280
  console.log(` ${statusIcon(code)} ${name}`);
3261
3281
  });
3262
- const { message } = await inquirer5.prompt([{
3263
- type: "input",
3264
- name: "message",
3265
- message: t("save.commitMessage"),
3266
- default: formatDefaultCommitMessage()
3267
- }]);
3282
+ const message = await promptOrDefault(
3283
+ async () => (await inquirer5.prompt([{
3284
+ type: "input",
3285
+ name: "message",
3286
+ message: t("save.commitMessage"),
3287
+ default: formatDefaultCommitMessage()
3288
+ }])).message,
3289
+ "chore: vhk save"
3290
+ );
3268
3291
  const spinner = ora(t("save.saving")).start();
3269
3292
  let didAdd = false;
3270
3293
  try {
@@ -5393,7 +5416,8 @@ var HIGH_RISK_ACTIONS = [
5393
5416
  "cloud-pull",
5394
5417
  "resume",
5395
5418
  "env-write",
5396
- "delete"
5419
+ "delete",
5420
+ "restore"
5397
5421
  ];
5398
5422
  var STRICT_EXTRA_ACTIONS = /* @__PURE__ */ new Set(["save", "sync"]);
5399
5423
  var NL_GUARDED_ACTIONS = {
@@ -5404,7 +5428,8 @@ var NL_GUARDED_ACTIONS = {
5404
5428
  "cloud-pull": "cloud-pull",
5405
5429
  env: "env-write",
5406
5430
  save: "save",
5407
- sync: "sync"
5431
+ sync: "sync",
5432
+ restore: "restore"
5408
5433
  };
5409
5434
  function isHighRisk(action) {
5410
5435
  return HIGH_RISK_ACTIONS.includes(action);
@@ -5426,6 +5451,11 @@ async function runGuarded(action, deps, run) {
5426
5451
  return { outcome: { ran: true, guard, reason: "low-risk" }, result: await run() };
5427
5452
  }
5428
5453
  if (guard === "warn") {
5454
+ const canConfirm = deps.isTTY ?? !!process.stdin.isTTY;
5455
+ if (!deps.approved && !canConfirm) {
5456
+ log2(`\u26A0\uFE0F \uC704\uD5D8 \uC791\uC5C5(${action}) \u2014 lite \uC9C0\uB9CC \uBE44\uB300\uD654\uD615+\uBBF8\uC2B9\uC778 \u2192 \uC911\uB2E8. (--yes \uB85C \uC2B9\uC778)`);
5457
+ return { outcome: { ran: false, guard, reason: "lite-noninteractive-block" } };
5458
+ }
5429
5459
  log2(`\u26A0\uFE0F \uC704\uD5D8 \uC791\uC5C5(${action}) \u2014 lite \uBAA8\uB4DC: \uACBD\uACE0\uB9CC \uD558\uACE0 \uC9C4\uD589\uD569\uB2C8\uB2E4.`);
5430
5460
  return { outcome: { ran: true, guard, reason: "lite-warn" }, result: await run() };
5431
5461
  }
@@ -5433,7 +5463,7 @@ async function runGuarded(action, deps, run) {
5433
5463
  if (deps.approved === true) {
5434
5464
  return { outcome: { ran: true, guard, reason: "approved" }, result: await run() };
5435
5465
  }
5436
- const tty = deps.isTTY ?? !!process.stdout.isTTY;
5466
+ const tty = deps.isTTY ?? !!process.stdin.isTTY;
5437
5467
  if (tty && deps.confirm) {
5438
5468
  const ok = await deps.confirm();
5439
5469
  if (ok) return { outcome: { ran: true, guard, reason: "confirmed" }, result: await run() };
@@ -5789,8 +5819,8 @@ program.command("save").alias("\uC800\uC7A5").option("--yes", "\uD655\uC778 \uC5
5789
5819
  program.command("undo").alias("\uB418\uB3CC\uB9AC\uAE30").option("--yes", "\uD655\uC778 \uC5C6\uC774 \uC2E4\uD589 (\uC704\uD5D8 \uC791\uC5C5 \uBA85\uC2DC \uC2B9\uC778)").description("\uCD5C\uADFC \uCEE4\uBC0B \uB418\uB3CC\uB9AC\uAE30").action(async (opts) => {
5790
5820
  await guardCliDefer("undo", opts?.yes === true, () => undo());
5791
5821
  });
5792
- program.command("restore").alias("\uBCF5\uC6D0").argument("[id]", "\uBCF5\uC6D0\uD560 \uBC31\uC5C5 id (\uC0DD\uB7B5 \uC2DC \uBAA9\uB85D\uC5D0\uC11C \uC120\uD0DD)").description("sync \uBC31\uC5C5 \uBCF5\uC6D0 (.vhk/backups/ \u2014 \uC5B8\uCEE4\uBC0B \uB36E\uC5B4\uC4F0\uAE30 \uBCF5\uAD6C)").action(async (id) => {
5793
- await restore(id);
5822
+ program.command("restore").alias("\uBCF5\uC6D0").argument("[id]", "\uBCF5\uC6D0\uD560 \uBC31\uC5C5 id (\uC0DD\uB7B5 \uC2DC \uBAA9\uB85D\uC5D0\uC11C \uC120\uD0DD)").option("--yes", "\uD655\uC778 \uC5C6\uC774 \uC2E4\uD589 (\uC704\uD5D8 \uC791\uC5C5 \uBA85\uC2DC \uC2B9\uC778)").description("sync \uBC31\uC5C5 \uBCF5\uC6D0 (.vhk/backups/ \u2014 \uC5B8\uCEE4\uBC0B \uB36E\uC5B4\uC4F0\uAE30 \uBCF5\uAD6C)").action(async (id, opts) => {
5823
+ await guardCli("restore", opts?.yes === true, () => restore(id));
5794
5824
  });
5795
5825
  program.command("status").alias("\uC0C1\uD0DC").description("\uD504\uB85C\uC81D\uD2B8 \uC0C1\uD0DC \uB300\uC2DC\uBCF4\uB4DC").action(async () => {
5796
5826
  await status();
package/dist/mcp/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  resolveVhkCliInvocation,
4
4
  startMcpServer
5
- } from "../chunk-53RJHPP6.js";
5
+ } from "../chunk-EJTVXWUZ.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.3",
3
+ "version": "1.6.4",
4
4
  "description": "Vibe Harness Kit — AI 코딩 도구·기기를 바꿔도 규칙·맥락이 따라가는 포터빌리티 CLI (sync: Cursor·Claude·Windsurf·Copilot·Antigravity / cloud 백업)",
5
5
  "bin": {
6
6
  "vhk": "dist/index.js",