@byh3071/vhk 2.4.1 → 2.4.2

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/dist/index.js CHANGED
@@ -5,25 +5,31 @@ import {
5
5
  MAX_SECRET_FINDINGS,
6
6
  NETWORK_EXEC_TIMEOUT_MS,
7
7
  __toESM,
8
+ appendBlocker,
9
+ atomicWriteFile,
8
10
  audit,
9
11
  buildAdoptedRules,
10
12
  checkContextDrift,
11
13
  checkRuleDrift,
14
+ clearHardStop,
12
15
  countLocalCommits,
13
16
  deploy,
14
17
  detectExistingRuleFiles,
15
18
  ensureInteractive,
19
+ ensureNotHardStopped,
16
20
  ensureVhkIgnored,
17
21
  env,
18
22
  envCheck,
19
23
  filterSevereFindings,
20
24
  filterTrackedPaths,
25
+ getActiveBlockers,
21
26
  getExecErrorMessage,
22
27
  getGitRoot,
23
28
  getVhkVersion,
24
29
  gitOut,
25
30
  gitRun,
26
31
  hasGitRemote,
32
+ isHardStopActive,
27
33
  isInteractive,
28
34
  isPromptAbortError,
29
35
  ko,
@@ -34,6 +40,7 @@ import {
34
40
  printSecurityWarnings,
35
41
  promptOrDefault,
36
42
  publish,
43
+ readHardStopReason,
37
44
  readJsonFile,
38
45
  require_ignore,
39
46
  restoreBackup,
@@ -43,13 +50,13 @@ import {
43
50
  stripBom,
44
51
  sync,
45
52
  t
46
- } from "./chunk-Y7SJVHGS.js";
53
+ } from "./chunk-WZH5CA4H.js";
47
54
 
48
55
  // src/index.ts
49
56
  import { Command, Help } from "commander";
50
57
  import { fileURLToPath as fileURLToPath4 } from "url";
51
58
  import fs14 from "fs";
52
- import chalk38 from "chalk";
59
+ import chalk39 from "chalk";
53
60
  import inquirer15 from "inquirer";
54
61
 
55
62
  // src/lib/nlp-router.ts
@@ -471,6 +478,8 @@ var TOP_LEVEL_COMMANDS = [
471
478
  { name: "context", desc: "\uD504\uB85C\uC81D\uD2B8 \uB9E5\uB77D \uD30C\uC77C \uC0DD\uC131 (.vhk/context.md)" },
472
479
  { name: "mode", desc: "Safety Mode \uC870\uD68C/\uBCC0\uACBD (lite|standard|strict)" },
473
480
  { name: "verify", desc: "\uAC80\uC99D \uAC8C\uC774\uD2B8 \uC2E4\uD589 + \uC99D\uAC70 \uAE30\uB85D" },
481
+ { name: "preflight", desc: "\uCD9C\uACE0 \uC804 \uC548\uC804\uC810\uAC80 (2FA\xB7shim\xB7env\xB7lint\xB7\uD0C0\uC785\xB7\uD14C\uC2A4\uD2B8\xB7git, \uCE58\uBA85 \uC2DC \uCC28\uB2E8)" },
482
+ { name: "standup", desc: "\uC544\uCE68 \uBE0C\uB9AC\uD551 (\uC5B4\uC81C \uD55C \uC77C + \uC624\uB298 \uCD94\uCC9C goal + \uBBF8\uD574\uACB0)" },
474
483
  { name: "review", desc: "\uC801\uB300\uC801 \uC790\uAE30\uAC80\uC99D (\uAC70\uC9D3\uC644\uB8CC \uC758\uC2EC \uD0D0\uC9C0)" },
475
484
  { name: "mission", desc: "\uBBF8\uC158 \uACC4\uC57D \u2014 \uC791\uC5C5 \uBAA9\uD45C\xB7\uD5C8\uC6A9/\uAE08\uC9C0 \uBC94\uC704 \uC120\uC5B8\xB7\uAC80\uC99D" },
476
485
  { name: "context-show", desc: "\uCEE8\uD14D\uC2A4\uD2B8 \uD30C\uC77C \uB0B4\uC6A9 \uCD9C\uB825" },
@@ -575,6 +584,10 @@ var KNOWN_COMMAND_TOKENS = /* @__PURE__ */ new Set([
575
584
  "\uBAA8\uB4DC",
576
585
  "verify",
577
586
  "\uC0AC\uC804\uC810\uAC80",
587
+ "preflight",
588
+ "\uCD9C\uACE0\uC810\uAC80",
589
+ "standup",
590
+ "\uC544\uCE68",
578
591
  "review",
579
592
  "\uAC80\uD1A0",
580
593
  "mission",
@@ -622,7 +635,7 @@ function detectNaturalLanguageInput(argv) {
622
635
  }
623
636
 
624
637
  // src/lib/nlp-run.ts
625
- import chalk36 from "chalk";
638
+ import chalk35 from "chalk";
626
639
  import inquirer14 from "inquirer";
627
640
 
628
641
  // src/commands/gate.ts
@@ -1565,7 +1578,7 @@ async function writeInitExtras(projectDir, noninteractive = false) {
1565
1578
 
1566
1579
  // src/commands/recap.ts
1567
1580
  import inquirer3 from "inquirer";
1568
- import chalk5 from "chalk";
1581
+ import chalk4 from "chalk";
1569
1582
  import fs4 from "fs";
1570
1583
  import path5 from "path";
1571
1584
 
@@ -1610,10 +1623,13 @@ function buildSessionDiffFromSummary(diffSummary) {
1610
1623
  };
1611
1624
  }
1612
1625
  var EMPTY_TREE_SHA = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
1626
+ function withMidnight(d) {
1627
+ return /\d{1,2}:\d{2}/.test(d) ? d : `${d} 00:00:00`;
1628
+ }
1613
1629
  async function getSessionDiff(since) {
1614
1630
  const sinceDate = since || localDate();
1615
1631
  try {
1616
- const boundary = (await git.raw(["rev-list", "-1", `--before=${sinceDate}`, "HEAD"])).trim();
1632
+ const boundary = (await git.raw(["rev-list", "-1", `--before=${withMidnight(sinceDate)}`, "HEAD"])).trim();
1617
1633
  const base = boundary || EMPTY_TREE_SHA;
1618
1634
  const diffSummary = await git.diffSummary([`${base}..HEAD`]);
1619
1635
  const normalized = diffSummary.files.map((f) => ({
@@ -1632,7 +1648,7 @@ async function getSessionDiff(since) {
1632
1648
  }
1633
1649
  async function getRecentCommits(count = 10, since) {
1634
1650
  const options = { maxCount: count };
1635
- if (since) options["--since"] = since;
1651
+ if (since) options["--since"] = withMidnight(since);
1636
1652
  try {
1637
1653
  const log2 = await git.log(options);
1638
1654
  return log2.all.map((entry) => ({
@@ -1750,147 +1766,48 @@ function createAdrFile(cwd, title, context2, decision, consequences) {
1750
1766
  return filePath;
1751
1767
  }
1752
1768
 
1753
- // src/lib/hard-stop-guard.ts
1754
- import chalk4 from "chalk";
1755
-
1756
- // src/lib/state-files.ts
1757
- import { existsSync, mkdirSync, readFileSync, writeFileSync, appendFileSync, rmSync } from "fs";
1758
- import { join as join2 } from "path";
1759
- var STATE_DIR = "docs/state";
1760
- var BLOCKERS_PATH = join2(STATE_DIR, "blockers.md");
1761
- var LEARNINGS_PATH = join2(STATE_DIR, "learnings.md");
1762
- var VHK_DIR = ".vhk";
1763
- var HARD_STOP_PATH = join2(VHK_DIR, "HARD_STOP");
1764
- var HARD_STOP_BLOCKER_THRESHOLD = 3;
1765
- function ensureStateDir() {
1766
- mkdirSync(STATE_DIR, { recursive: true });
1767
- }
1768
- function ensureVhkDir() {
1769
- mkdirSync(VHK_DIR, { recursive: true });
1770
- }
1771
- function isoDate() {
1772
- return localDate();
1773
- }
1774
- var ACTIVE_BLOCKER_RE = /^- (?!~~)\[/;
1775
- function countActiveBlockers(content) {
1776
- let count = 0;
1777
- for (const line of content.split(/\r?\n/)) {
1778
- if (ACTIVE_BLOCKER_RE.test(line)) count++;
1779
- }
1780
- return count;
1781
- }
1782
- function appendBlocker(description, goalId) {
1783
- ensureStateDir();
1784
- const tag = goalId !== void 0 ? `goal-${goalId}` : "no-goal";
1785
- const line = `- [${isoDate()} ${tag}] ${description.trim()}`;
1786
- if (!existsSync(BLOCKERS_PATH)) {
1787
- writeFileSync(
1788
- BLOCKERS_PATH,
1789
- `# Blockers
1790
-
1791
- _Append-only. \uD574\uACB0 \uD56D\uBAA9\uC740 ~~\uCDE8\uC18C\uC120~~\uC73C\uB85C \uD45C\uAE30._
1792
-
1793
- ${line}
1794
- `,
1795
- "utf-8"
1796
- );
1797
- } else {
1798
- appendFileSync(BLOCKERS_PATH, `${line}
1799
- `, "utf-8");
1800
- }
1801
- const current = readFileSync(BLOCKERS_PATH, "utf-8");
1802
- const count = countActiveBlockers(current);
1803
- let hardStopTripped = false;
1804
- if (count >= HARD_STOP_BLOCKER_THRESHOLD && !existsSync(HARD_STOP_PATH)) {
1805
- writeHardStop(`auto: ${count} active blockers (threshold ${HARD_STOP_BLOCKER_THRESHOLD})`);
1806
- hardStopTripped = true;
1807
- }
1808
- return { count, hardStopTripped };
1809
- }
1810
- function getActiveBlockers(limit = 3) {
1811
- if (!existsSync(BLOCKERS_PATH)) return [];
1812
- const lines = readFileSync(BLOCKERS_PATH, "utf-8").split(/\r?\n/);
1813
- const entries = lines.filter((l) => ACTIVE_BLOCKER_RE.test(l));
1814
- return entries.slice(-limit);
1815
- }
1816
- function writeHardStop(reason) {
1817
- ensureVhkDir();
1818
- const ts = (/* @__PURE__ */ new Date()).toISOString();
1819
- writeFileSync(HARD_STOP_PATH, `${ts}
1820
- ${reason}
1821
- `, "utf-8");
1822
- }
1823
- function isHardStopActive() {
1824
- return existsSync(HARD_STOP_PATH);
1825
- }
1826
- function readHardStopReason() {
1827
- if (!existsSync(HARD_STOP_PATH)) return null;
1828
- try {
1829
- return readFileSync(HARD_STOP_PATH, "utf-8").trim();
1830
- } catch {
1831
- return null;
1832
- }
1833
- }
1834
- function clearHardStop() {
1835
- if (!existsSync(HARD_STOP_PATH)) return false;
1836
- rmSync(HARD_STOP_PATH, { force: true });
1837
- return true;
1838
- }
1839
-
1840
- // src/lib/hard-stop-guard.ts
1841
- function ensureNotHardStopped(action) {
1842
- if (!isHardStopActive()) return true;
1843
- console.error(chalk4.red.bold(`
1844
- \u{1F6D1} HARD STOP \uD65C\uC131 \u2014 '${action}' \uC744(\uB97C) \uC2E4\uD589\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.`));
1845
- const reason = readHardStopReason();
1846
- if (reason) console.error(chalk4.dim(` \uC0AC\uC720: ${reason.replace(/\s*\n\s*/g, " ")}`));
1847
- console.error(chalk4.dim(" \uD574\uC81C: vhk resume --confirm (\uC0AC\uB78C\uC774 \uC9C1\uC811 \uC2E4\uD589)"));
1848
- process.exitCode = 1;
1849
- return false;
1850
- }
1851
-
1852
1769
  // src/commands/recap.ts
1853
1770
  async function recap(options = {}) {
1854
1771
  if (!ensureNotHardStopped("recap")) return;
1855
- console.log(chalk5.bold(`
1772
+ console.log(chalk4.bold(`
1856
1773
  ${ko.recap.title}
1857
1774
  `));
1858
1775
  if (!await isGitRepo()) {
1859
- console.log(chalk5.red(ko.recap.noRepo));
1776
+ console.log(chalk4.red(ko.recap.noRepo));
1860
1777
  return;
1861
1778
  }
1862
1779
  if (!await hasAnyCommits()) {
1863
- console.log(chalk5.yellow("\u26A0\uFE0F \uC544\uC9C1 \uCEE4\uBC0B\uC774 \uC5C6\uC5B4\uC694."));
1864
- console.log(chalk5.gray(" \uD30C\uC77C\uC744 \uCD94\uAC00\uD558\uACE0 `vhk save` \uB610\uB294 `git commit`\uC73C\uB85C \uCCAB \uCEE4\uBC0B\uC744 \uB9CC\uB4E4\uC5B4 \uBCF4\uC138\uC694."));
1780
+ console.log(chalk4.yellow("\u26A0\uFE0F \uC544\uC9C1 \uCEE4\uBC0B\uC774 \uC5C6\uC5B4\uC694."));
1781
+ console.log(chalk4.gray(" \uD30C\uC77C\uC744 \uCD94\uAC00\uD558\uACE0 `vhk save` \uB610\uB294 `git commit`\uC73C\uB85C \uCCAB \uCEE4\uBC0B\uC744 \uB9CC\uB4E4\uC5B4 \uBCF4\uC138\uC694."));
1865
1782
  return;
1866
1783
  }
1867
1784
  printSecurityWarnings();
1868
- console.log(chalk5.dim(`${ko.recap.analyzing}
1785
+ console.log(chalk4.dim(`${ko.recap.analyzing}
1869
1786
  `));
1870
1787
  const since = options.since || localDate();
1871
1788
  const diff2 = await getSessionDiff(since);
1872
1789
  const commits = await getRecentCommits(10, since);
1873
1790
  if (diff2.filesChanged === 0 && commits.length === 0) {
1874
- console.log(chalk5.yellow(ko.recap.noChanges));
1791
+ console.log(chalk4.yellow(ko.recap.noChanges));
1875
1792
  return;
1876
1793
  }
1877
- console.log(chalk5.bold("\u{1F4CA} \uBCC0\uACBD \uC694\uC57D:"));
1878
- console.log(` \uD30C\uC77C: ${chalk5.cyan(String(diff2.filesChanged))}\uAC1C \uBCC0\uACBD`);
1879
- console.log(` \uCD94\uAC00: ${chalk5.green("+" + diff2.insertions)} / \uC0AD\uC81C: ${chalk5.red("-" + diff2.deletions)}`);
1794
+ console.log(chalk4.bold("\u{1F4CA} \uBCC0\uACBD \uC694\uC57D:"));
1795
+ console.log(` \uD30C\uC77C: ${chalk4.cyan(String(diff2.filesChanged))}\uAC1C \uBCC0\uACBD`);
1796
+ console.log(` \uCD94\uAC00: ${chalk4.green("+" + diff2.insertions)} / \uC0AD\uC81C: ${chalk4.red("-" + diff2.deletions)}`);
1880
1797
  if (diff2.files.length > 0) {
1881
- console.log(chalk5.dim("\n \uBCC0\uACBD \uD30C\uC77C:"));
1798
+ console.log(chalk4.dim("\n \uBCC0\uACBD \uD30C\uC77C:"));
1882
1799
  diff2.files.slice(0, 15).forEach((f) => {
1883
- const icon = f.status === "new" ? chalk5.green("\u{1F195}") : f.status === "deleted" ? chalk5.red("\u{1F5D1}\uFE0F") : chalk5.yellow("\u270F\uFE0F");
1800
+ const icon = f.status === "new" ? chalk4.green("\u{1F195}") : f.status === "deleted" ? chalk4.red("\u{1F5D1}\uFE0F") : chalk4.yellow("\u270F\uFE0F");
1884
1801
  console.log(` ${icon} ${f.file}`);
1885
1802
  });
1886
1803
  if (diff2.files.length > 15) {
1887
- console.log(chalk5.dim(` ... \uC678 ${diff2.files.length - 15}\uAC1C`));
1804
+ console.log(chalk4.dim(` ... \uC678 ${diff2.files.length - 15}\uAC1C`));
1888
1805
  }
1889
1806
  }
1890
1807
  if (commits.length > 0) {
1891
- console.log(chalk5.dim("\n \uCD5C\uADFC \uCEE4\uBC0B:"));
1808
+ console.log(chalk4.dim("\n \uCD5C\uADFC \uCEE4\uBC0B:"));
1892
1809
  commits.slice(0, 5).forEach((c) => {
1893
- console.log(chalk5.dim(` \u2022 ${c.message}`));
1810
+ console.log(chalk4.dim(` \u2022 ${c.message}`));
1894
1811
  });
1895
1812
  }
1896
1813
  console.log("");
@@ -1959,11 +1876,11 @@ ${ko.recap.title}
1959
1876
  fs4.writeFileSync(filePath, content, "utf-8");
1960
1877
  const adrCandidates = detectAdrCandidates(diff2);
1961
1878
  if (adrCandidates.length > 0) {
1962
- console.log(chalk5.cyan.bold(`
1879
+ console.log(chalk4.cyan.bold(`
1963
1880
  ${ko.recap.adrDetected} (${adrCandidates.length}\uAC74)`));
1964
1881
  for (const candidate of adrCandidates) {
1965
- console.log(chalk5.cyan(` \u2022 ${candidate.title}: ${candidate.context}`));
1966
- candidate.files.forEach((f) => console.log(chalk5.dim(` ${f}`)));
1882
+ console.log(chalk4.cyan(` \u2022 ${candidate.title}: ${candidate.context}`));
1883
+ candidate.files.forEach((f) => console.log(chalk4.dim(` ${f}`)));
1967
1884
  }
1968
1885
  const { createAdr } = await inquirer3.prompt([{
1969
1886
  type: "confirm",
@@ -1993,17 +1910,17 @@ ${ko.recap.adrDetected} (${adrCandidates.length}\uAC74)`));
1993
1910
  adrAnswers.decision,
1994
1911
  adrAnswers.consequences
1995
1912
  );
1996
- console.log(chalk5.green(` \u2705 ADR \uC0DD\uC131: ${path5.relative(process.cwd(), adrPath)}`));
1913
+ console.log(chalk4.green(` \u2705 ADR \uC0DD\uC131: ${path5.relative(process.cwd(), adrPath)}`));
1997
1914
  }
1998
1915
  }
1999
1916
  }
2000
1917
  const troubleshootingKeywords = /fix|bug|error|crash|hotfix|patch|revert|트러블|에러|버그|수정|핫픽스/i;
2001
1918
  const troubleCommits = commits.filter((c) => troubleshootingKeywords.test(c.message));
2002
1919
  if (troubleCommits.length > 0) {
2003
- console.log(chalk5.yellow.bold(`
1920
+ console.log(chalk4.yellow.bold(`
2004
1921
  ${ko.recap.troubleDetected} (${troubleCommits.length}\uAC74)`));
2005
1922
  troubleCommits.forEach((c) => {
2006
- console.log(chalk5.dim(` \u2022 ${c.message}`));
1923
+ console.log(chalk4.dim(` \u2022 ${c.message}`));
2007
1924
  });
2008
1925
  const { createTroubleshoot } = await inquirer3.prompt([{
2009
1926
  type: "confirm",
@@ -2054,12 +1971,12 @@ ${ko.recap.troubleDetected} (${troubleCommits.length}\uAC74)`));
2054
1971
  `*Generated by \`vhk recap\` at ${(/* @__PURE__ */ new Date()).toISOString()}*`
2055
1972
  ].join("\n");
2056
1973
  fs4.writeFileSync(tsFilePath, tsContent, "utf-8");
2057
- console.log(chalk5.green(` \u2705 \uD2B8\uB7EC\uBE14\uC288\uD305 \uBB38\uC11C \uC0DD\uC131: ${path5.relative(process.cwd(), tsFilePath)}`));
1974
+ console.log(chalk4.green(` \u2705 \uD2B8\uB7EC\uBE14\uC288\uD305 \uBB38\uC11C \uC0DD\uC131: ${path5.relative(process.cwd(), tsFilePath)}`));
2058
1975
  }
2059
1976
  }
2060
- console.log(chalk5.green.bold(`
1977
+ console.log(chalk4.green.bold(`
2061
1978
  ${ko.recap.done}`));
2062
- console.log(chalk5.dim(` \u{1F4C4} ${path5.relative(process.cwd(), filePath)}`));
1979
+ console.log(chalk4.dim(` \u{1F4C4} ${path5.relative(process.cwd(), filePath)}`));
2063
1980
  const claudeMdPath = path5.join(process.cwd(), "CLAUDE.md");
2064
1981
  if (fs4.existsSync(claudeMdPath)) {
2065
1982
  const { updateClaude } = await inquirer3.prompt([{
@@ -2079,7 +1996,7 @@ ${ko.recap.done}`));
2079
1996
  `- **\uB2E4\uC74C \uC561\uC158:** ${answers.nextTodo}`
2080
1997
  );
2081
1998
  fs4.writeFileSync(claudeMdPath, claudeContent, "utf-8");
2082
- console.log(chalk5.green(" \u2705 CLAUDE.md \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC"));
1999
+ console.log(chalk4.green(" \u2705 CLAUDE.md \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC"));
2083
2000
  }
2084
2001
  }
2085
2002
  const gitSaveCmd = process.platform === "win32" ? 'git add .; git commit -m "recap: \uC138\uC158 \uAE30\uB85D"' : 'git add . && git commit -m "recap: \uC138\uC158 \uAE30\uB85D"';
@@ -2091,7 +2008,7 @@ ${ko.recap.done}`));
2091
2008
  }
2092
2009
 
2093
2010
  // src/commands/check.ts
2094
- import chalk7 from "chalk";
2011
+ import chalk6 from "chalk";
2095
2012
  import path7 from "path";
2096
2013
  import fs6 from "fs";
2097
2014
 
@@ -2128,7 +2045,7 @@ function parseRules(rulesPath) {
2128
2045
  }
2129
2046
  const banToken = extractBanToken(ruleText);
2130
2047
  if (banToken) {
2131
- rules.push(createContentRule(`ban-L${lineNo}`, currentSection, ruleText, banToken, "banned"));
2048
+ rules.push(createContentRule(`ban-L${lineNo}`, currentSection, ruleText, banToken));
2132
2049
  }
2133
2050
  }
2134
2051
  return rules;
@@ -2169,15 +2086,22 @@ function createNamingRule(id, section, desc, convention) {
2169
2086
  if (!fs5.existsSync(srcDir)) return violations;
2170
2087
  walkFiles(srcDir, (filePath) => {
2171
2088
  const name = path6.basename(filePath, path6.extname(filePath));
2089
+ const exempt = ["index", "vite.config", "tsconfig"];
2090
+ if (exempt.includes(name)) return;
2172
2091
  if (convention === "kebab-case" && !/^[a-z0-9]+(-[a-z0-9]+)*$/.test(name)) {
2173
- if (!["index", "vite.config", "tsconfig"].includes(name)) {
2174
- violations.push({
2175
- ruleId: id,
2176
- severity: "warning",
2177
- message: `\uD30C\uC77C\uBA85\uC774 kebab-case\uAC00 \uC544\uB2D8: ${name}`,
2178
- file: path6.relative(cwd, filePath)
2179
- });
2180
- }
2092
+ violations.push({
2093
+ ruleId: id,
2094
+ severity: "warning",
2095
+ message: `\uD30C\uC77C\uBA85\uC774 kebab-case\uAC00 \uC544\uB2D8: ${name}`,
2096
+ file: path6.relative(cwd, filePath)
2097
+ });
2098
+ } else if (convention === "camelCase" && !/^[a-z][a-zA-Z0-9]*$/.test(name)) {
2099
+ violations.push({
2100
+ ruleId: id,
2101
+ severity: "warning",
2102
+ message: `\uD30C\uC77C\uBA85\uC774 camelCase\uAC00 \uC544\uB2D8: ${name}`,
2103
+ file: path6.relative(cwd, filePath)
2104
+ });
2181
2105
  }
2182
2106
  });
2183
2107
  return violations;
@@ -2203,7 +2127,14 @@ function createStructureRule(id, section, desc, expectedPath) {
2203
2127
  }
2204
2128
  };
2205
2129
  }
2206
- function createContentRule(id, section, desc, pattern, type) {
2130
+ function codePortionForScan(line) {
2131
+ const t2 = line.trimStart();
2132
+ if (t2.startsWith("//") || t2.startsWith("*") || t2.startsWith("/*")) return "";
2133
+ const noStr = line.replace(/(['"`])(?:\\.|(?!\1).)*?\1/g, "");
2134
+ const ci = noStr.indexOf("//");
2135
+ return ci >= 0 ? noStr.slice(0, ci) : noStr;
2136
+ }
2137
+ function createContentRule(id, section, desc, pattern) {
2207
2138
  return {
2208
2139
  id,
2209
2140
  section,
@@ -2219,11 +2150,11 @@ function createContentRule(id, section, desc, pattern, type) {
2219
2150
  const fileContent = fs5.readFileSync(filePath, "utf-8");
2220
2151
  const fileLines = fileContent.split("\n");
2221
2152
  fileLines.forEach((line, idx) => {
2222
- if (regex.test(line)) {
2153
+ if (regex.test(codePortionForScan(line))) {
2223
2154
  violations.push({
2224
2155
  ruleId: id,
2225
- severity: type === "banned" ? "error" : "warning",
2226
- message: type === "banned" ? `\uAE08\uC9C0 \uD328\uD134 \uBC1C\uACAC: \`${pattern}\`` : `\uD544\uC218 \uD328\uD134 \uB204\uB77D: \`${pattern}\``,
2156
+ severity: "error",
2157
+ message: `\uAE08\uC9C0 \uD328\uD134 \uBC1C\uACAC: \`${pattern}\``,
2227
2158
  file: path6.relative(cwd, filePath),
2228
2159
  line: idx + 1
2229
2160
  });
@@ -2253,13 +2184,13 @@ function escapeRegex(str) {
2253
2184
  }
2254
2185
 
2255
2186
  // src/commands/goal.ts
2256
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, readFileSync as readFileSync3 } from "fs";
2257
- import { join as join4 } from "path";
2258
- import chalk6 from "chalk";
2187
+ import { existsSync as existsSync2, mkdirSync, writeFileSync, readFileSync as readFileSync2 } from "fs";
2188
+ import { join as join3 } from "path";
2189
+ import chalk5 from "chalk";
2259
2190
 
2260
2191
  // src/lib/goal-frontmatter.ts
2261
- import { existsSync as existsSync2, readFileSync as readFileSync2, readdirSync, statSync } from "fs";
2262
- import { join as join3 } from "path";
2192
+ import { existsSync, readFileSync, readdirSync, statSync } from "fs";
2193
+ import { join as join2 } from "path";
2263
2194
  var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
2264
2195
  function parseFrontmatter(content) {
2265
2196
  const normalized = stripBom(content);
@@ -2292,9 +2223,9 @@ function parseSimpleYaml(yaml) {
2292
2223
  return out;
2293
2224
  }
2294
2225
  function parseGoalFile(filePath) {
2295
- if (!existsSync2(filePath)) return null;
2226
+ if (!existsSync(filePath)) return null;
2296
2227
  try {
2297
- const content = readFileSync2(filePath, "utf-8");
2228
+ const content = readFileSync(filePath, "utf-8");
2298
2229
  const { frontmatter, body } = parseFrontmatter(content);
2299
2230
  return { filePath, frontmatter, body };
2300
2231
  } catch {
@@ -2302,7 +2233,7 @@ function parseGoalFile(filePath) {
2302
2233
  }
2303
2234
  }
2304
2235
  function listGoals(goalsDir) {
2305
- if (!existsSync2(goalsDir)) return [];
2236
+ if (!existsSync(goalsDir)) return [];
2306
2237
  let entries;
2307
2238
  try {
2308
2239
  entries = readdirSync(goalsDir);
@@ -2313,7 +2244,7 @@ function listGoals(goalsDir) {
2313
2244
  for (const name of entries) {
2314
2245
  if (!name.endsWith(".md")) continue;
2315
2246
  if (name === "_meta.md") continue;
2316
- const fp = join3(goalsDir, name);
2247
+ const fp = join2(goalsDir, name);
2317
2248
  try {
2318
2249
  if (!statSync(fp).isFile()) continue;
2319
2250
  } catch {
@@ -2329,7 +2260,7 @@ function listGoals(goalsDir) {
2329
2260
  return parsed;
2330
2261
  }
2331
2262
  function findSkippedGoalFiles(goalsDir) {
2332
- if (!existsSync2(goalsDir)) return [];
2263
+ if (!existsSync(goalsDir)) return [];
2333
2264
  let entries;
2334
2265
  try {
2335
2266
  entries = readdirSync(goalsDir);
@@ -2340,7 +2271,7 @@ function findSkippedGoalFiles(goalsDir) {
2340
2271
  for (const name of entries) {
2341
2272
  if (!name.endsWith(".md")) continue;
2342
2273
  if (name === "_meta.md") continue;
2343
- const fp = join3(goalsDir, name);
2274
+ const fp = join2(goalsDir, name);
2344
2275
  try {
2345
2276
  if (!statSync(fp).isFile()) continue;
2346
2277
  } catch {
@@ -2409,7 +2340,7 @@ ${body}`;
2409
2340
 
2410
2341
  // src/commands/goal.ts
2411
2342
  var GOALS_DIR = "goals";
2412
- var STATE_DIR2 = "docs/state";
2343
+ var STATE_DIR = "docs/state";
2413
2344
  var SCRIPTS_DIR = "scripts";
2414
2345
  var STATUS_ICON = {
2415
2346
  NOT_STARTED: "\u26AA",
@@ -2435,14 +2366,14 @@ function resolveGoalId(optId, goals) {
2435
2366
  return selectActiveId(goals);
2436
2367
  }
2437
2368
  async function goalList() {
2438
- console.log(chalk6.bold(`
2369
+ console.log(chalk5.bold(`
2439
2370
  ${ko.goal.listTitle}
2440
2371
  `));
2441
2372
  const goals = listGoals(GOALS_DIR);
2442
2373
  const skipped = findSkippedGoalFiles(GOALS_DIR);
2443
2374
  if (goals.length === 0) {
2444
- console.log(chalk6.yellow(" \u{1F4ED} goals/ \uB514\uB809\uD1A0\uB9AC\uC5D0 goal \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
2445
- console.log(chalk6.dim(" vhk goal init \uC73C\uB85C \uC2DC\uC791\uD558\uC138\uC694."));
2375
+ console.log(chalk5.yellow(" \u{1F4ED} goals/ \uB514\uB809\uD1A0\uB9AC\uC5D0 goal \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
2376
+ console.log(chalk5.dim(" vhk goal init \uC73C\uB85C \uC2DC\uC791\uD558\uC138\uC694."));
2446
2377
  printSkippedGoalWarnings(skipped);
2447
2378
  return;
2448
2379
  }
@@ -2460,33 +2391,34 @@ ${ko.goal.listTitle}
2460
2391
  const dups = findDuplicateIds(goals);
2461
2392
  if (dups.length > 0) {
2462
2393
  console.log("");
2463
- console.log(chalk6.yellow(` ${ko.goal.duplicateId(dups.join(", "))}`));
2394
+ console.log(chalk5.yellow(` ${ko.goal.duplicateId(dups.join(", "))}`));
2464
2395
  }
2465
2396
  printSkippedGoalWarnings(skipped);
2466
2397
  }
2467
2398
  function printSkippedGoalWarnings(skipped) {
2468
2399
  if (skipped.length > 0) {
2469
2400
  console.log("");
2470
- console.log(chalk6.yellow(` ${ko.goal.skippedFiles(skipped.length)}`));
2401
+ console.log(chalk5.yellow(` ${ko.goal.skippedFiles(skipped.length)}`));
2471
2402
  for (const s of skipped) {
2472
- console.log(chalk6.yellow(` - goals/${s.file}: ${s.reason}`));
2403
+ console.log(chalk5.yellow(` - goals/${s.file}: ${s.reason}`));
2473
2404
  }
2474
- console.log(chalk6.dim(" \uD544\uC218: type: goal + \uC22B\uC790 id. \uC2A4\uD0A4\uB9C8 \uC804\uCCB4: goals/_meta.md"));
2405
+ console.log(chalk5.dim(" \uD544\uC218: type: goal + \uC22B\uC790 id. \uC2A4\uD0A4\uB9C8 \uC804\uCCB4: goals/_meta.md"));
2475
2406
  }
2476
2407
  }
2477
2408
  async function goalNext() {
2478
- console.log(chalk6.bold(`
2409
+ if (!ensureNotHardStopped("goal next")) return;
2410
+ console.log(chalk5.bold(`
2479
2411
  ${ko.goal.nextTitle}
2480
2412
  `));
2481
2413
  const goals = listGoals(GOALS_DIR);
2482
2414
  if (goals.length === 0) {
2483
- console.log(chalk6.yellow(" \u{1F4ED} \uC815\uC758\uB41C goal \uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
2484
- console.log(chalk6.dim(" vhk goal init \uC73C\uB85C \uC2DC\uC791\uD558\uC138\uC694."));
2415
+ console.log(chalk5.yellow(" \u{1F4ED} \uC815\uC758\uB41C goal \uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
2416
+ console.log(chalk5.dim(" vhk goal init \uC73C\uB85C \uC2DC\uC791\uD558\uC138\uC694."));
2485
2417
  return;
2486
2418
  }
2487
2419
  const activeId = selectActiveId(goals);
2488
2420
  if (activeId === null) {
2489
- console.log(chalk6.green(" \u{1F389} \uBAA8\uB4E0 goal \uC774 \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4!"));
2421
+ console.log(chalk5.green(" \u{1F389} \uBAA8\uB4E0 goal \uC774 \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4!"));
2490
2422
  return;
2491
2423
  }
2492
2424
  const active = goals.find((g) => g.frontmatter.id === activeId);
@@ -2505,10 +2437,10 @@ ${ko.goal.nextTitle}
2505
2437
  "```",
2506
2438
  ""
2507
2439
  ].join("\n");
2508
- mkdirSync2(STATE_DIR2, { recursive: true });
2509
- writeFileSync2(join4(STATE_DIR2, "next-task.md"), text, "utf-8");
2440
+ mkdirSync(STATE_DIR, { recursive: true });
2441
+ atomicWriteFile(join3(STATE_DIR, "next-task.md"), text);
2510
2442
  console.log(
2511
- chalk6.green(
2443
+ chalk5.green(
2512
2444
  ` \u2705 next-task.md \uAC31\uC2E0 \u2014 Goal ${activeId}: ${active.frontmatter.title ?? ""}`
2513
2445
  )
2514
2446
  );
@@ -2567,30 +2499,31 @@ var STATE_NEXT_TASK_TEMPLATE = "# Next Task\n\n```\nTASK: (vhk goal next \uB85C
2567
2499
  var STATE_BLOCKERS_TEMPLATE = "# Blockers\n\n_Append-only. \uD574\uACB0 \uD56D\uBAA9\uC740 ~~\uCDE8\uC18C\uC120~~\uC73C\uB85C \uD45C\uAE30._\n";
2568
2500
  var STATE_LEARNINGS_TEMPLATE = "# Learnings\n\n_Append-only. \uD55C \uC904 = \uD55C \uAD50\uD6C8._\n";
2569
2501
  async function goalInit() {
2570
- console.log(chalk6.bold(`
2502
+ if (!ensureNotHardStopped("goal init")) return;
2503
+ console.log(chalk5.bold(`
2571
2504
  ${ko.goal.initTitle}
2572
2505
  `));
2573
2506
  const targets = [
2574
- { path: join4(GOALS_DIR, "_meta.md"), content: META_TEMPLATE },
2575
- { path: join4(STATE_DIR2, "next-task.md"), content: STATE_NEXT_TASK_TEMPLATE },
2576
- { path: join4(STATE_DIR2, "blockers.md"), content: STATE_BLOCKERS_TEMPLATE },
2577
- { path: join4(STATE_DIR2, "learnings.md"), content: STATE_LEARNINGS_TEMPLATE }
2507
+ { path: join3(GOALS_DIR, "_meta.md"), content: META_TEMPLATE },
2508
+ { path: join3(STATE_DIR, "next-task.md"), content: STATE_NEXT_TASK_TEMPLATE },
2509
+ { path: join3(STATE_DIR, "blockers.md"), content: STATE_BLOCKERS_TEMPLATE },
2510
+ { path: join3(STATE_DIR, "learnings.md"), content: STATE_LEARNINGS_TEMPLATE }
2578
2511
  ];
2579
- mkdirSync2(GOALS_DIR, { recursive: true });
2580
- mkdirSync2(STATE_DIR2, { recursive: true });
2512
+ mkdirSync(GOALS_DIR, { recursive: true });
2513
+ mkdirSync(STATE_DIR, { recursive: true });
2581
2514
  let created = 0;
2582
2515
  let skipped = 0;
2583
2516
  for (const t2 of targets) {
2584
- if (existsSync3(t2.path)) {
2585
- console.log(chalk6.gray(` \u2298 skip (\uC774\uBBF8 \uC874\uC7AC): ${t2.path}`));
2517
+ if (existsSync2(t2.path)) {
2518
+ console.log(chalk5.gray(` \u2298 skip (\uC774\uBBF8 \uC874\uC7AC): ${t2.path}`));
2586
2519
  skipped++;
2587
2520
  } else {
2588
- writeFileSync2(t2.path, t2.content, "utf-8");
2589
- console.log(chalk6.green(` \u2713 created: ${t2.path}`));
2521
+ atomicWriteFile(t2.path, t2.content);
2522
+ console.log(chalk5.green(` \u2713 created: ${t2.path}`));
2590
2523
  created++;
2591
2524
  }
2592
2525
  }
2593
- console.log(chalk6.bold(`
2526
+ console.log(chalk5.bold(`
2594
2527
  \u{1F4CA} created=${created} skipped=${skipped}`));
2595
2528
  if (created > 0) {
2596
2529
  printNextStep({
@@ -2601,10 +2534,10 @@ ${ko.goal.initTitle}
2601
2534
  }
2602
2535
  }
2603
2536
  function findGateScript(id) {
2604
- const mjs = join4(SCRIPTS_DIR, `check-goal-${id}.mjs`);
2605
- if (existsSync3(mjs)) return mjs;
2606
- const sh = join4(SCRIPTS_DIR, `check-goal-${id}.sh`);
2607
- if (existsSync3(sh)) return sh;
2537
+ const mjs = join3(SCRIPTS_DIR, `check-goal-${id}.mjs`);
2538
+ if (existsSync2(mjs)) return mjs;
2539
+ const sh = join3(SCRIPTS_DIR, `check-goal-${id}.sh`);
2540
+ if (existsSync2(sh)) return sh;
2608
2541
  return null;
2609
2542
  }
2610
2543
  function runGate(scriptPath) {
@@ -2616,76 +2549,77 @@ function runGate(scriptPath) {
2616
2549
  function warnIfBashOnWindows(scriptPath) {
2617
2550
  if (process.platform === "win32" && scriptPath.endsWith(".sh")) {
2618
2551
  console.log(
2619
- chalk6.yellow(
2552
+ chalk5.yellow(
2620
2553
  " \u26A0 Windows: .sh \uAC8C\uC774\uD2B8\uB294 bash \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. cross-platform .mjs \uB85C \uBC31\uD544\uD558\uC138\uC694 \u2192 vhk goal sync"
2621
2554
  )
2622
2555
  );
2623
2556
  }
2624
2557
  }
2625
2558
  async function goalCheck(opts) {
2626
- console.log(chalk6.bold(`
2559
+ console.log(chalk5.bold(`
2627
2560
  ${ko.goal.checkTitle}
2628
2561
  `));
2629
2562
  const goals = listGoals(GOALS_DIR);
2630
2563
  const id = resolveGoalId(opts.id, goals);
2631
2564
  if (id === null) {
2632
2565
  console.log(
2633
- chalk6.yellow(" \u26A0 \uB300\uC0C1 goal \uC744 \uACB0\uC815\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (--id \uBA85\uC2DC \uB610\uB294 active goal \uD544\uC694).")
2566
+ chalk5.yellow(" \u26A0 \uB300\uC0C1 goal \uC744 \uACB0\uC815\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (--id \uBA85\uC2DC \uB610\uB294 active goal \uD544\uC694).")
2634
2567
  );
2635
2568
  process.exitCode = 1;
2636
2569
  return;
2637
2570
  }
2638
2571
  if (!goals.some((g) => g.frontmatter.id === id)) {
2639
- console.log(chalk6.red(` \u274C ${ko.goal.notFound(id)}`));
2572
+ console.log(chalk5.red(` \u274C ${ko.goal.notFound(id)}`));
2640
2573
  process.exitCode = 1;
2641
2574
  return;
2642
2575
  }
2643
2576
  const scriptPath = findGateScript(id);
2644
2577
  if (!scriptPath) {
2645
2578
  console.log(
2646
- chalk6.red(` \u274C \uAC8C\uC774\uD2B8 \uC2A4\uD06C\uB9BD\uD2B8 \uC5C6\uC74C: scripts/check-goal-${id}.{mjs,sh}`)
2579
+ chalk5.red(` \u274C \uAC8C\uC774\uD2B8 \uC2A4\uD06C\uB9BD\uD2B8 \uC5C6\uC74C: scripts/check-goal-${id}.{mjs,sh}`)
2647
2580
  );
2648
2581
  process.exitCode = 1;
2649
2582
  return;
2650
2583
  }
2651
2584
  warnIfBashOnWindows(scriptPath);
2652
2585
  const gate2 = runGate(scriptPath);
2653
- console.log(chalk6.dim(` \u25B6 ${gate2.runner} ${scriptPath}
2586
+ console.log(chalk5.dim(` \u25B6 ${gate2.runner} ${scriptPath}
2654
2587
  `));
2655
2588
  if (gate2.out) console.log(gate2.out);
2656
2589
  if (gate2.ok) {
2657
- console.log(chalk6.green(`
2590
+ console.log(chalk5.green(`
2658
2591
  \u2705 Goal ${id} \uAC8C\uC774\uD2B8 \uD1B5\uACFC`));
2659
2592
  } else {
2660
- console.log(chalk6.red(`
2593
+ console.log(chalk5.red(`
2661
2594
  \u274C Goal ${id} \uAC8C\uC774\uD2B8 \uC2E4\uD328`));
2662
- if (gate2.err && !gate2.out) console.log(chalk6.dim(gate2.err.slice(0, 500)));
2595
+ if (gate2.err && !gate2.out) console.log(chalk5.dim(gate2.err.slice(0, 500)));
2663
2596
  process.exitCode = 1;
2664
2597
  }
2665
2598
  }
2666
2599
  async function goalDone(opts) {
2667
- console.log(chalk6.bold(`
2600
+ if (!ensureNotHardStopped("goal done")) return;
2601
+ console.log(chalk5.bold(`
2668
2602
  ${ko.goal.doneTitle}
2669
2603
  `));
2670
2604
  const goals = listGoals(GOALS_DIR);
2671
2605
  const id = resolveGoalId(opts.id, goals);
2672
2606
  if (id === null) {
2673
2607
  console.log(
2674
- chalk6.yellow(" \u26A0 \uB300\uC0C1 goal \uC744 \uACB0\uC815\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (--id \uBA85\uC2DC \uB610\uB294 active goal \uD544\uC694).")
2608
+ chalk5.yellow(" \u26A0 \uB300\uC0C1 goal \uC744 \uACB0\uC815\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (--id \uBA85\uC2DC \uB610\uB294 active goal \uD544\uC694).")
2675
2609
  );
2676
2610
  process.exitCode = 1;
2677
2611
  return;
2678
2612
  }
2679
2613
  const target = goals.find((g) => g.frontmatter.id === id);
2680
2614
  if (!target) {
2681
- console.log(chalk6.red(` \u274C ${ko.goal.notFound(id)}`));
2615
+ console.log(chalk5.red(` \u274C ${ko.goal.notFound(id)}`));
2682
2616
  process.exitCode = 1;
2683
2617
  return;
2684
2618
  }
2685
2619
  const scriptPath = findGateScript(id);
2686
2620
  if (!scriptPath) {
2687
2621
  console.log(
2688
- chalk6.red(
2622
+ chalk5.red(
2689
2623
  ` \u274C \uAC8C\uC774\uD2B8 \uC2A4\uD06C\uB9BD\uD2B8 \uC5C6\uC74C \u2014 done \uCC98\uB9AC \uAC70\uBD80: scripts/check-goal-${id}.{mjs,sh}`
2690
2624
  )
2691
2625
  );
@@ -2694,12 +2628,12 @@ ${ko.goal.doneTitle}
2694
2628
  }
2695
2629
  warnIfBashOnWindows(scriptPath);
2696
2630
  const gate2 = runGate(scriptPath);
2697
- console.log(chalk6.dim(` \u25B6 \uAC8C\uC774\uD2B8 \uAC80\uC99D: ${gate2.runner} ${scriptPath}
2631
+ console.log(chalk5.dim(` \u25B6 \uAC8C\uC774\uD2B8 \uAC80\uC99D: ${gate2.runner} ${scriptPath}
2698
2632
  `));
2699
2633
  if (gate2.out) console.log(gate2.out);
2700
2634
  if (!gate2.ok) {
2701
2635
  console.log(
2702
- chalk6.red(
2636
+ chalk5.red(
2703
2637
  `
2704
2638
  \u274C \uAC8C\uC774\uD2B8 \uC2E4\uD328 \u2014 frontmatter \uBCC0\uACBD \uC5C6\uC774 \uC885\uB8CC. (Forbidden: \uC2E4\uD328 = \uBCF4\uC874)`
2705
2639
  )
@@ -2707,11 +2641,11 @@ ${ko.goal.doneTitle}
2707
2641
  process.exitCode = 1;
2708
2642
  return;
2709
2643
  }
2710
- const content = readFileSync3(target.filePath, "utf-8");
2644
+ const content = readFileSync2(target.filePath, "utf-8");
2711
2645
  const today = localDate();
2712
2646
  const updated = updateFrontmatterStatus(content, "DONE", { completed: today });
2713
- writeFileSync2(target.filePath, updated, "utf-8");
2714
- console.log(chalk6.green(`
2647
+ atomicWriteFile(target.filePath, updated);
2648
+ console.log(chalk5.green(`
2715
2649
  \u2705 Goal ${id} \u2192 DONE (completed: ${today})`));
2716
2650
  printNextStep({
2717
2651
  message: `Goal ${id} \uC644\uB8CC! \uB2E4\uC74C goal \uB85C:`,
@@ -2786,36 +2720,36 @@ function generateGateScript(id) {
2786
2720
  ].join("\n");
2787
2721
  }
2788
2722
  async function goalSync() {
2789
- console.log(chalk6.bold(`
2723
+ console.log(chalk5.bold(`
2790
2724
  ${ko.goal.syncTitle}
2791
2725
  `));
2792
2726
  const goals = listGoals(GOALS_DIR);
2793
2727
  const result = { created: [], skipped: [] };
2794
2728
  if (goals.length === 0) {
2795
2729
  console.log(
2796
- chalk6.yellow(" \u{1F4ED} goals/ \uC5D0 goal \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. vhk goal init \uC73C\uB85C \uC2DC\uC791\uD558\uC138\uC694.")
2730
+ chalk5.yellow(" \u{1F4ED} goals/ \uC5D0 goal \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. vhk goal init \uC73C\uB85C \uC2DC\uC791\uD558\uC138\uC694.")
2797
2731
  );
2798
2732
  return result;
2799
2733
  }
2800
- mkdirSync2(SCRIPTS_DIR, { recursive: true });
2734
+ mkdirSync(SCRIPTS_DIR, { recursive: true });
2801
2735
  for (const g of goals) {
2802
2736
  const id = g.frontmatter.id;
2803
2737
  if (typeof id !== "number") continue;
2804
- const target = join4(SCRIPTS_DIR, `check-goal-${id}.mjs`);
2805
- if (existsSync3(target)) {
2806
- console.log(chalk6.gray(` \u2298 skip (\uC774\uBBF8 \uC874\uC7AC): ${target}`));
2738
+ const target = join3(SCRIPTS_DIR, `check-goal-${id}.mjs`);
2739
+ if (existsSync2(target)) {
2740
+ console.log(chalk5.gray(` \u2298 skip (\uC774\uBBF8 \uC874\uC7AC): ${target}`));
2807
2741
  result.skipped.push(id);
2808
2742
  continue;
2809
2743
  }
2810
- const shOnly = existsSync3(join4(SCRIPTS_DIR, `check-goal-${id}.sh`));
2811
- writeFileSync2(target, generateGateScript(id), "utf-8");
2744
+ const shOnly = existsSync2(join3(SCRIPTS_DIR, `check-goal-${id}.sh`));
2745
+ writeFileSync(target, generateGateScript(id), "utf-8");
2812
2746
  console.log(
2813
- chalk6.green(` \u2713 created: ${target}${shOnly ? " (.sh \u2192 .mjs \uBC31\uD544, Windows 1\uAE09)" : ""}`)
2747
+ chalk5.green(` \u2713 created: ${target}${shOnly ? " (.sh \u2192 .mjs \uBC31\uD544, Windows 1\uAE09)" : ""}`)
2814
2748
  );
2815
2749
  result.created.push(id);
2816
2750
  }
2817
2751
  console.log(
2818
- chalk6.bold(`
2752
+ chalk5.bold(`
2819
2753
  \u{1F4CA} created=${result.created.length} skipped=${result.skipped.length}`)
2820
2754
  );
2821
2755
  if (result.created.length > 0) {
@@ -2836,22 +2770,22 @@ async function check(opts = {}) {
2836
2770
  return checkRules();
2837
2771
  }
2838
2772
  async function checkRules() {
2839
- console.log(chalk7.bold(`
2773
+ console.log(chalk6.bold(`
2840
2774
  ${ko.check.title}
2841
2775
  `));
2842
2776
  const cwd = process.cwd();
2843
2777
  const rulesPath = path7.join(cwd, "RULES.md");
2844
2778
  if (!fs6.existsSync(rulesPath)) {
2845
- console.log(chalk7.yellow(ko.check.noRules));
2846
- console.log(chalk7.dim(" vhk init\uC73C\uB85C \uC2DC\uC791\uD558\uAC70\uB098 RULES.md\uB97C \uB9CC\uB4E4\uC5B4 \uBCF4\uC138\uC694."));
2779
+ console.log(chalk6.yellow(ko.check.noRules));
2780
+ console.log(chalk6.dim(" vhk init\uC73C\uB85C \uC2DC\uC791\uD558\uAC70\uB098 RULES.md\uB97C \uB9CC\uB4E4\uC5B4 \uBCF4\uC138\uC694."));
2847
2781
  return;
2848
2782
  }
2849
2783
  const rules = parseRules(rulesPath);
2850
- console.log(chalk7.dim(` \u{1F4CF} \uC790\uB3D9 \uAC80\uC99D \uAC00\uB2A5\uD55C \uADDC\uCE59 ${rules.length}\uAC1C \uAC10\uC9C0 (\uB098\uBA38\uC9C0 \uADDC\uCE59\uC740 \uC218\uB3D9/\uB3C4\uAD6C \uD655\uC778)
2784
+ console.log(chalk6.dim(` \u{1F4CF} \uC790\uB3D9 \uAC80\uC99D \uAC00\uB2A5\uD55C \uADDC\uCE59 ${rules.length}\uAC1C \uAC10\uC9C0 (\uB098\uBA38\uC9C0 \uADDC\uCE59\uC740 \uC218\uB3D9/\uB3C4\uAD6C \uD655\uC778)
2851
2785
  `));
2852
2786
  if (rules.length === 0) {
2853
- console.log(chalk7.yellow(ko.check.noAutoRules));
2854
- console.log(chalk7.dim(" RULES.md\uC5D0 \uD30C\uC77C \uC774\uB984\xB7\uD3F4\uB354 \uADDC\uCE59\uC744 \uC801\uC73C\uBA74 \uC790\uB3D9\uC73C\uB85C \uC810\uAC80\uD574\uC694."));
2787
+ console.log(chalk6.yellow(ko.check.noAutoRules));
2788
+ console.log(chalk6.dim(" RULES.md\uC5D0 \uD30C\uC77C \uC774\uB984\xB7\uD3F4\uB354 \uADDC\uCE59\uC744 \uC801\uC73C\uBA74 \uC790\uB3D9\uC73C\uB85C \uC810\uAC80\uD574\uC694."));
2855
2789
  return;
2856
2790
  }
2857
2791
  const allViolations = [];
@@ -2859,14 +2793,14 @@ ${ko.check.title}
2859
2793
  for (const rule of rules) {
2860
2794
  const violations = rule.check(cwd);
2861
2795
  if (violations.length === 0) {
2862
- const patternHint = rule.type === "content" && rule.pattern ? chalk7.dim(` [\uAC80\uC0AC: ${rule.pattern.source}]`) : "";
2863
- console.log(chalk7.green(` \u2705 ${rule.id}`) + chalk7.dim(` \u2014 ${rule.description.slice(0, 60)}`) + patternHint);
2796
+ const patternHint = rule.type === "content" && rule.pattern ? chalk6.dim(` [\uAC80\uC0AC: ${rule.pattern.source}]`) : "";
2797
+ console.log(chalk6.green(` \u2705 ${rule.id}`) + chalk6.dim(` \u2014 ${rule.description.slice(0, 60)}`) + patternHint);
2864
2798
  passCount++;
2865
2799
  } else {
2866
- console.log(chalk7.red(` \u274C ${rule.id}`) + chalk7.dim(` \u2014 ${violations.length}\uAC74 \uC704\uBC18`));
2800
+ console.log(chalk6.red(` \u274C ${rule.id}`) + chalk6.dim(` \u2014 ${violations.length}\uAC74 \uC704\uBC18`));
2867
2801
  violations.forEach((v) => {
2868
- const loc = v.file ? chalk7.dim(` (${v.file}${v.line ? ":" + v.line : ""})`) : "";
2869
- const icon = v.severity === "error" ? chalk7.red("\u2716") : v.severity === "warning" ? chalk7.yellow("\u26A0") : chalk7.blue("\u2139");
2802
+ const loc = v.file ? chalk6.dim(` (${v.file}${v.line ? ":" + v.line : ""})`) : "";
2803
+ const icon = v.severity === "error" ? chalk6.red("\u2716") : v.severity === "warning" ? chalk6.yellow("\u26A0") : chalk6.blue("\u2139");
2870
2804
  console.log(` ${icon} ${v.message}${loc}`);
2871
2805
  });
2872
2806
  allViolations.push(...violations);
@@ -2876,18 +2810,18 @@ ${ko.check.title}
2876
2810
  const errors = allViolations.filter((v) => v.severity === "error").length;
2877
2811
  const warnings = allViolations.filter((v) => v.severity === "warning").length;
2878
2812
  if (allViolations.length === 0) {
2879
- console.log(chalk7.green.bold(`\u2705 \uC790\uB3D9 \uAC80\uC99D \uAC00\uB2A5\uD55C \uADDC\uCE59 ${passCount}\uAC1C \uD1B5\uACFC`));
2880
- console.log(chalk7.dim(" (RULES.md \uC758 \uB098\uBA38\uC9C0 \uADDC\uCE59\uC740 \uCF54\uB4DC \uC790\uB3D9 \uAC80\uC0AC \uBD88\uAC00 \u2014 \uC9C1\uC811/\uB3C4\uAD6C\uB85C \uD655\uC778\uD558\uC138\uC694.)"));
2813
+ console.log(chalk6.green.bold(`\u2705 \uC790\uB3D9 \uAC80\uC99D \uAC00\uB2A5\uD55C \uADDC\uCE59 ${passCount}\uAC1C \uD1B5\uACFC`));
2814
+ console.log(chalk6.dim(" (RULES.md \uC758 \uB098\uBA38\uC9C0 \uADDC\uCE59\uC740 \uCF54\uB4DC \uC790\uB3D9 \uAC80\uC0AC \uBD88\uAC00 \u2014 \uC9C1\uC811/\uB3C4\uAD6C\uB85C \uD655\uC778\uD558\uC138\uC694.)"));
2881
2815
  printNextStep({
2882
2816
  message: "\uBAA8\uB4E0 \uADDC\uCE59 \uD1B5\uACFC! \uBCF4\uC548 \uC2A4\uCE94\uB3C4 \uD574\uBCFC\uAE4C\uC694?",
2883
2817
  command: "vhk \uBCF4\uC548 scan",
2884
2818
  cursorHint: "\uBCF4\uC548 \uC2A4\uCE94 \uB3CC\uB824\uC918"
2885
2819
  });
2886
2820
  } else {
2887
- console.log(chalk7.bold(ko.check.summary));
2888
- console.log(` \uADDC\uCE59: ${chalk7.cyan(String(rules.length))}\uAC1C | \uD1B5\uACFC: ${chalk7.green(String(passCount))}\uAC1C | \uC704\uBC18: ${chalk7.red(String(allViolations.length))}\uAC74`);
2889
- if (errors > 0) console.log(` ${chalk7.red(`\u2716 ${errors}\uAC1C \uC5D0\uB7EC`)}`);
2890
- if (warnings > 0) console.log(` ${chalk7.yellow(`\u26A0 ${warnings}\uAC1C \uACBD\uACE0`)}`);
2821
+ console.log(chalk6.bold(ko.check.summary));
2822
+ console.log(` \uADDC\uCE59: ${chalk6.cyan(String(rules.length))}\uAC1C | \uD1B5\uACFC: ${chalk6.green(String(passCount))}\uAC1C | \uC704\uBC18: ${chalk6.red(String(allViolations.length))}\uAC74`);
2823
+ if (errors > 0) console.log(` ${chalk6.red(`\u2716 ${errors}\uAC1C \uC5D0\uB7EC`)}`);
2824
+ if (warnings > 0) console.log(` ${chalk6.yellow(`\u26A0 ${warnings}\uAC1C \uACBD\uACE0`)}`);
2891
2825
  printNextStep({
2892
2826
  message: "\uC704\uBC18 \uD56D\uBAA9\uC744 \uC218\uC815\uD55C \uD6C4 \uB2E4\uC2DC \uC810\uAC80\uD558\uC138\uC694.",
2893
2827
  command: "vhk \uC810\uAC80",
@@ -2900,36 +2834,36 @@ ${ko.check.title}
2900
2834
  }
2901
2835
 
2902
2836
  // src/commands/secure.ts
2903
- import chalk8 from "chalk";
2837
+ import chalk7 from "chalk";
2904
2838
  import fs7 from "fs";
2905
2839
  import path8 from "path";
2906
2840
  async function secure() {
2907
- console.log(chalk8.bold(`
2841
+ console.log(chalk7.bold(`
2908
2842
  ${ko.secure.title}
2909
2843
  `));
2910
2844
  const cwd = process.cwd();
2911
2845
  const gitignorePath = path8.join(cwd, ".gitignore");
2912
2846
  const hasGitignore = fs7.existsSync(gitignorePath);
2913
2847
  if (!hasGitignore) {
2914
- console.log(chalk8.yellow(` ${ko.secure.noGitignore}`));
2915
- console.log(chalk8.dim(" .env \uD30C\uC77C\uC774 \uCEE4\uBC0B\uB420 \uC218 \uC788\uC2B5\uB2C8\uB2E4.\n"));
2848
+ console.log(chalk7.yellow(` ${ko.secure.noGitignore}`));
2849
+ console.log(chalk7.dim(" .env \uD30C\uC77C\uC774 \uCEE4\uBC0B\uB420 \uC218 \uC788\uC2B5\uB2C8\uB2E4.\n"));
2916
2850
  } else {
2917
2851
  const gitignoreContent = fs7.readFileSync(gitignorePath, "utf-8");
2918
2852
  if (!gitignoreContent.includes(".env")) {
2919
- console.log(chalk8.yellow(` ${ko.secure.noEnvInGitignore}`));
2920
- console.log(chalk8.dim(" \uCD94\uAC00\uB97C \uAD8C\uC7A5\uD569\uB2C8\uB2E4.\n"));
2853
+ console.log(chalk7.yellow(` ${ko.secure.noEnvInGitignore}`));
2854
+ console.log(chalk7.dim(" \uCD94\uAC00\uB97C \uAD8C\uC7A5\uD569\uB2C8\uB2E4.\n"));
2921
2855
  }
2922
2856
  }
2923
- console.log(chalk8.dim(` ${ko.secure.scanning}
2857
+ console.log(chalk7.dim(` ${ko.secure.scanning}
2924
2858
  `));
2925
2859
  const { findings, scannedFiles, truncated } = scanProjectForSecrets(cwd);
2926
- console.log(chalk8.dim(` \u{1F4C2} ${scannedFiles}\uAC1C \uD30C\uC77C \uC2A4\uCE94 \uC644\uB8CC (lock\xB7node_modules\xB7>${MAX_SCAN_FILE_BYTES / 1024}KB \uC81C\uC678)`));
2860
+ console.log(chalk7.dim(` \u{1F4C2} ${scannedFiles}\uAC1C \uD30C\uC77C \uC2A4\uCE94 \uC644\uB8CC (lock\xB7node_modules\xB7>${MAX_SCAN_FILE_BYTES / 1024}KB \uC81C\uC678)`));
2927
2861
  if (truncated) {
2928
- console.log(chalk8.yellow(` \u26A0\uFE0F \uACB0\uACFC ${MAX_SECRET_FINDINGS}\uAC74\uC5D0\uC11C \uCD9C\uB825\uC744 \uC81C\uD55C\uD588\uC2B5\uB2C8\uB2E4. lock \uD30C\uC77C \uB4F1\uC740 \uC790\uB3D9 \uC81C\uC678\uB429\uB2C8\uB2E4.`));
2862
+ console.log(chalk7.yellow(` \u26A0\uFE0F \uACB0\uACFC ${MAX_SECRET_FINDINGS}\uAC74\uC5D0\uC11C \uCD9C\uB825\uC744 \uC81C\uD55C\uD588\uC2B5\uB2C8\uB2E4. lock \uD30C\uC77C \uB4F1\uC740 \uC790\uB3D9 \uC81C\uC678\uB429\uB2C8\uB2E4.`));
2929
2863
  }
2930
2864
  console.log("");
2931
2865
  if (findings.length === 0) {
2932
- console.log(chalk8.green.bold(` ${ko.secure.clean}`));
2866
+ console.log(chalk7.green.bold(` ${ko.secure.clean}`));
2933
2867
  printNextStep({
2934
2868
  message: "\uBCF4\uC548 \uC774\uC0C1 \uC5C6\uC74C! \uAE68\uB057\uD569\uB2C8\uB2E4.",
2935
2869
  command: "vhk \uC815\uB9AC",
@@ -2941,46 +2875,239 @@ ${ko.secure.title}
2941
2875
  const high = findings.filter((f) => f.severity === "high");
2942
2876
  const medium = findings.filter((f) => f.severity === "medium");
2943
2877
  if (critical.length > 0) {
2944
- console.log(chalk8.red.bold(` \u{1F6A8} CRITICAL \u2014 ${critical.length}\uAC74`));
2878
+ console.log(chalk7.red.bold(` \u{1F6A8} CRITICAL \u2014 ${critical.length}\uAC74`));
2945
2879
  critical.forEach((f) => {
2946
- console.log(chalk8.red(` \u2716 ${f.patternName}`));
2947
- console.log(chalk8.dim(` ${f.file}:${f.line} \u2192 ${f.match}`));
2880
+ console.log(chalk7.red(` \u2716 ${f.patternName}`));
2881
+ console.log(chalk7.dim(` ${f.file}:${f.line} \u2192 ${f.match}`));
2948
2882
  });
2949
2883
  console.log("");
2950
2884
  }
2951
2885
  if (high.length > 0) {
2952
- console.log(chalk8.yellow.bold(` \u26A0\uFE0F HIGH \u2014 ${high.length}\uAC74`));
2886
+ console.log(chalk7.yellow.bold(` \u26A0\uFE0F HIGH \u2014 ${high.length}\uAC74`));
2953
2887
  high.forEach((f) => {
2954
- console.log(chalk8.yellow(` \u26A0 ${f.patternName}`));
2955
- console.log(chalk8.dim(` ${f.file}:${f.line} \u2192 ${f.match}`));
2888
+ console.log(chalk7.yellow(` \u26A0 ${f.patternName}`));
2889
+ console.log(chalk7.dim(` ${f.file}:${f.line} \u2192 ${f.match}`));
2956
2890
  });
2957
2891
  console.log("");
2958
2892
  }
2959
2893
  if (medium.length > 0) {
2960
- console.log(chalk8.blue.bold(` \u2139 MEDIUM \u2014 ${medium.length}\uAC74`));
2894
+ console.log(chalk7.blue.bold(` \u2139 MEDIUM \u2014 ${medium.length}\uAC74`));
2961
2895
  medium.forEach((f) => {
2962
- console.log(chalk8.blue(` \u2139 ${f.patternName}`));
2963
- console.log(chalk8.dim(` ${f.file}:${f.line} \u2192 ${f.match}`));
2896
+ console.log(chalk7.blue(` \u2139 ${f.patternName}`));
2897
+ console.log(chalk7.dim(` ${f.file}:${f.line} \u2192 ${f.match}`));
2964
2898
  });
2965
2899
  console.log("");
2966
2900
  }
2967
- console.log(chalk8.bold(` ${ko.secure.summary}`));
2968
- console.log(` \uCD1D ${chalk8.red(String(findings.length))}\uAC74 \uAC10\uC9C0 | CRITICAL: ${critical.length} | HIGH: ${high.length} | MEDIUM: ${medium.length}`);
2901
+ console.log(chalk7.bold(` ${ko.secure.summary}`));
2902
+ console.log(` \uCD1D ${chalk7.red(String(findings.length))}\uAC74 \uAC10\uC9C0 | CRITICAL: ${critical.length} | HIGH: ${high.length} | MEDIUM: ${medium.length}`);
2969
2903
  console.log("");
2970
- console.log(chalk8.dim(" \u{1F4A1} \uC870\uCE58 \uBC29\uBC95:"));
2971
- console.log(chalk8.dim(" 1. \uD574\uB2F9 \uD30C\uC77C\uC5D0\uC11C \uC2DC\uD06C\uB9BF\uC744 \uC81C\uAC70\uD558\uACE0 \uD658\uACBD\uBCC0\uC218\uB85C \uC774\uB3D9"));
2972
- console.log(chalk8.dim(" 2. git history\uC5D0\uC11C\uB3C4 \uC81C\uAC70: git filter-branch \uB610\uB294 BFG Repo-Cleaner"));
2973
- console.log(chalk8.dim(" 3. \uC720\uCD9C\uB41C \uD0A4\uB294 \uC989\uC2DC \uD3D0\uAE30\uD558\uACE0 \uC7AC\uBC1C\uAE09\n"));
2904
+ console.log(chalk7.dim(" \u{1F4A1} \uC870\uCE58 \uBC29\uBC95:"));
2905
+ console.log(chalk7.dim(" 1. \uD574\uB2F9 \uD30C\uC77C\uC5D0\uC11C \uC2DC\uD06C\uB9BF\uC744 \uC81C\uAC70\uD558\uACE0 \uD658\uACBD\uBCC0\uC218\uB85C \uC774\uB3D9"));
2906
+ console.log(chalk7.dim(" 2. git history\uC5D0\uC11C\uB3C4 \uC81C\uAC70: git filter-branch \uB610\uB294 BFG Repo-Cleaner"));
2907
+ console.log(chalk7.dim(" 3. \uC720\uCD9C\uB41C \uD0A4\uB294 \uC989\uC2DC \uD3D0\uAE30\uD558\uACE0 \uC7AC\uBC1C\uAE09\n"));
2974
2908
  if (critical.length > 0 || high.length > 0) {
2975
2909
  process.exitCode = 1;
2976
2910
  }
2977
2911
  }
2978
2912
 
2979
2913
  // src/commands/doctor.ts
2980
- import chalk9 from "chalk";
2914
+ import chalk8 from "chalk";
2981
2915
  import fs9 from "fs";
2982
2916
  import path10 from "path";
2983
2917
  import { fileURLToPath } from "url";
2918
+ import os2 from "os";
2919
+
2920
+ // src/doctor/runner.ts
2921
+ async function runDiagnostics(diags, opts, deps) {
2922
+ return Promise.all(
2923
+ diags.map(async (fn) => {
2924
+ try {
2925
+ return await fn(opts, deps);
2926
+ } catch (e) {
2927
+ const msg = e instanceof Error ? e.message : String(e);
2928
+ return { name: fn.name || "diagnostic", status: "fail", value: "\uC9C4\uB2E8 \uC2E4\uD328", advice: msg.slice(0, 200) };
2929
+ }
2930
+ })
2931
+ );
2932
+ }
2933
+
2934
+ // src/doctor/report.ts
2935
+ function diagIcon(status2) {
2936
+ return status2 === "ok" ? "\u{1F7E2}" : status2 === "warn" ? "\u{1F7E1}" : status2 === "fail" ? "\u{1F534}" : "\u26AA";
2937
+ }
2938
+ function formatDiagnostics(diags) {
2939
+ const lines = [];
2940
+ for (const d of diags) {
2941
+ lines.push(` ${diagIcon(d.status)} ${d.name.padEnd(8)} ${d.value}`);
2942
+ }
2943
+ const advices = diags.filter((d) => d.status !== "ok" && d.advice);
2944
+ if (advices.length > 0) {
2945
+ lines.push("");
2946
+ lines.push(" \uAD8C\uC7A5 \uC870\uCE58:");
2947
+ for (const d of advices) lines.push(` - ${d.name}: ${d.advice}`);
2948
+ }
2949
+ return lines;
2950
+ }
2951
+
2952
+ // src/lib/preflight.ts
2953
+ var check2 = (name, status2, detail, severity) => ({ name, status: status2, detail, severity });
2954
+ function nodeMeetsShimSafe(version) {
2955
+ const m = version.replace(/^v/, "").split(".");
2956
+ const major = Number(m[0]);
2957
+ const minor = Number(m[1] ?? 0);
2958
+ if (!Number.isFinite(major)) return false;
2959
+ if (major > 21) return true;
2960
+ if (major === 21) return minor >= 7;
2961
+ if (major === 20) return minor >= 12;
2962
+ return false;
2963
+ }
2964
+ function checkNpmAuth(run) {
2965
+ const r = run("npm", ["whoami"]);
2966
+ if (r.ok && r.out.trim()) {
2967
+ return check2("2FA / npm", "pass", `logged in as ${r.out.trim()}`, "high");
2968
+ }
2969
+ return check2("2FA / npm", "warn", "npm \uB85C\uADF8\uC778 \uD544\uC694 (npm login). publish \uC2DC 2FA OTP\uB294 \uC0AC\uB78C\uC774 \uC9C1\uC811 \uC785\uB825", "high");
2970
+ }
2971
+ function checkShim(nodeVersion) {
2972
+ if (nodeMeetsShimSafe(nodeVersion)) {
2973
+ return check2("shim", "pass", `Node ${nodeVersion} (safe)`, "high");
2974
+ }
2975
+ return check2("shim", "warn", `Node ${nodeVersion} \u2014 .cmd shim CVE \uCDE8\uC57D(20.12+/21.7+ \uAD8C\uC7A5)`, "high");
2976
+ }
2977
+ function checkLint(run, hasLinter) {
2978
+ if (!hasLinter) {
2979
+ return check2("lint", "skip", "eslint \uBBF8\uC124\uC815 \u2014 \uB9B0\uD2B8 \uC2A4\uD0B5", "critical");
2980
+ }
2981
+ const r = run("npx", ["eslint", "."]);
2982
+ return r.ok ? check2("lint", "pass", "0 errors", "critical") : check2("lint", "fail", "eslint \uC624\uB958 \u2014 \uCD9C\uB825 \uD655\uC778", "critical");
2983
+ }
2984
+ function checkTypecheck(run) {
2985
+ const r = run("npx", ["tsc", "--noEmit"]);
2986
+ return r.ok ? check2("typecheck", "pass", "tsc --noEmit pass", "critical") : check2("typecheck", "fail", "tsc \uD0C0\uC785 \uC624\uB958 \u2014 \uCD9C\uB825 \uD655\uC778", "critical");
2987
+ }
2988
+ function checkTests(run, opts) {
2989
+ const args = opts.full ? ["vitest", "--run"] : ["vitest", "--changed", "--run"];
2990
+ const r = run("npx", args);
2991
+ return r.ok ? check2("tests", "pass", opts.full ? "\uC804\uCCB4 \uD1B5\uACFC" : "\uBCC0\uACBD\uBD84 \uD1B5\uACFC(\uCE90\uC2DC \uC2A4\uD0B5)", "critical") : check2("tests", "fail", "\uD14C\uC2A4\uD2B8 \uC2E4\uD328 \u2014 \uCD9C\uB825 \uD655\uC778", "critical");
2992
+ }
2993
+ function checkGitClean(run) {
2994
+ const r = run("git", ["status", "--porcelain"]);
2995
+ if (!r.ok) return check2("git", "warn", "git \uC0C1\uD0DC \uD655\uC778 \uC2E4\uD328", "normal");
2996
+ const lines = r.out.split("\n").filter((l) => l.trim());
2997
+ return lines.length === 0 ? check2("git", "pass", "clean", "normal") : check2("git", "warn", `uncommitted ${lines.length} files`, "normal");
2998
+ }
2999
+ function checkBranch(run) {
3000
+ const r = run("git", ["rev-parse", "--abbrev-ref", "HEAD"]);
3001
+ const branch = r.out.trim();
3002
+ if (branch === "main" || branch === "master") {
3003
+ return check2("branch", "warn", `${branch} \uC9C1\uC811 \u2014 feature \uBE0C\uB79C\uCE58 \uAD8C\uC7A5(\uD55C PR \uD55C goal)`, "normal");
3004
+ }
3005
+ return check2("branch", "pass", branch || "(unknown)", "normal");
3006
+ }
3007
+ function summarizePreflight(checks) {
3008
+ const s = { blocked: false, passed: 0, warned: 0, failed: 0, skipped: 0 };
3009
+ for (const c of checks) {
3010
+ if (c.status === "pass") s.passed++;
3011
+ else if (c.status === "warn") s.warned++;
3012
+ else if (c.status === "fail") s.failed++;
3013
+ else if (c.status === "skip") s.skipped++;
3014
+ if (c.status === "fail" && c.severity === "critical") s.blocked = true;
3015
+ }
3016
+ return s;
3017
+ }
3018
+ function detectHasLinter(input) {
3019
+ return input.hasLintScript || input.hasEslintConfig;
3020
+ }
3021
+ function statusIcon(c) {
3022
+ if (c.status === "pass") return "\u{1F7E2}";
3023
+ if (c.status === "fail") return "\u{1F534}";
3024
+ if (c.status === "skip") return "\u26AA";
3025
+ return c.severity === "high" ? "\u{1F7E0}" : "\u{1F7E1}";
3026
+ }
3027
+ function runPreflight(opts, deps) {
3028
+ return [
3029
+ checkNpmAuth(deps.run),
3030
+ checkShim(deps.nodeVersion),
3031
+ deps.worktreeEnv(),
3032
+ checkLint(deps.run, deps.hasLinter),
3033
+ checkTypecheck(deps.run),
3034
+ checkTests(deps.run, { full: opts.full }),
3035
+ checkGitClean(deps.run),
3036
+ checkBranch(deps.run)
3037
+ ];
3038
+ }
3039
+
3040
+ // src/doctor/diagnostics/node.ts
3041
+ function diagNode(_opts, deps) {
3042
+ const reach = deps.run("node", ["--version"]);
3043
+ if (!reach.ok) {
3044
+ return {
3045
+ name: "Node",
3046
+ status: "fail",
3047
+ value: "PATH \uBBF8\uC778\uC2DD",
3048
+ advice: "node \uAC00 PATH \uC5D0 \uC5C6\uC74C \u2014 \uC178/\uD658\uACBD\uBCC0\uC218(nvm\xB7volta \uCD08\uAE30\uD654) \uD655\uC778"
3049
+ };
3050
+ }
3051
+ const v = deps.nodeVersion;
3052
+ if (nodeMeetsShimSafe(v)) {
3053
+ return { name: "Node", status: "ok", value: `${v} (shim-safe)` };
3054
+ }
3055
+ return {
3056
+ name: "Node",
3057
+ status: "warn",
3058
+ value: v,
3059
+ advice: "Node 20.12+ / 21.7+ \uAD8C\uC7A5 (.cmd shim CVE-2024-27980)"
3060
+ };
3061
+ }
3062
+
3063
+ // src/doctor/diagnostics/npm.ts
3064
+ function diagNpm(_opts, deps) {
3065
+ const r = deps.run("npm", ["--version"]);
3066
+ if (r.ok && r.out.trim()) {
3067
+ return { name: "npm", status: "ok", value: r.out.trim().split("\n")[0] };
3068
+ }
3069
+ return {
3070
+ name: "npm",
3071
+ status: "fail",
3072
+ value: "\uC5C6\uC74C",
3073
+ advice: "Node.js \uC7AC\uC124\uCE58 \uB610\uB294 PATH \uD655\uC778 (publish\xB7\uC5C5\uB370\uC774\uD2B8\uAC00 npm \uC758\uC874)"
3074
+ };
3075
+ }
3076
+
3077
+ // src/doctor/diagnostics/pnpm.ts
3078
+ function diagPnpm(_opts, deps) {
3079
+ const r = deps.run("pnpm", ["--version"]);
3080
+ if (r.ok && r.out.trim()) {
3081
+ return { name: "pnpm", status: "ok", value: r.out.trim().split("\n")[0] };
3082
+ }
3083
+ return { name: "pnpm", status: "fail", value: "\uC5C6\uC74C", advice: "\uC124\uCE58: npm i -g pnpm" };
3084
+ }
3085
+
3086
+ // src/doctor/diagnostics/git.ts
3087
+ function diagGit(_opts, deps) {
3088
+ const ver = deps.run("git", ["--version"]);
3089
+ if (!ver.ok) {
3090
+ return { name: "git", status: "fail", value: "\uC5C6\uC74C", advice: "\uC124\uCE58: https://git-scm.com" };
3091
+ }
3092
+ const v = ver.out.trim().replace(/^git version /, "");
3093
+ const name = deps.run("git", ["config", "user.name"]);
3094
+ const email = deps.run("git", ["config", "user.email"]);
3095
+ const configured = name.ok && name.out.trim() !== "" && email.ok && email.out.trim() !== "";
3096
+ if (configured) {
3097
+ return { name: "git", status: "ok", value: `${v} (user configured)` };
3098
+ }
3099
+ return {
3100
+ name: "git",
3101
+ status: "warn",
3102
+ value: v,
3103
+ advice: "git config user.name / user.email \uC124\uC815 \uAD8C\uC7A5"
3104
+ };
3105
+ }
3106
+
3107
+ // src/doctor/diagnostics/os.ts
3108
+ function diagOs(_opts, deps) {
3109
+ return { name: "OS", status: "ok", value: `${deps.platform} ${deps.osRelease}` };
3110
+ }
2984
3111
 
2985
3112
  // src/lib/version-check.ts
2986
3113
  import fs8 from "fs";
@@ -3064,12 +3191,6 @@ function getUpdateInfo(now = Date.now()) {
3064
3191
  }
3065
3192
 
3066
3193
  // src/commands/doctor.ts
3067
- function checkCommand(name, command, hint) {
3068
- const result = safeExecFile(command, ["--version"]);
3069
- if (!result.ok) return { name, command, ok: false, hint };
3070
- const version = result.out.split("\n")[0];
3071
- return { name, command, version, ok: true, hint };
3072
- }
3073
3194
  function getVhkVersion2() {
3074
3195
  const dir = path10.dirname(fileURLToPath(import.meta.url));
3075
3196
  const candidates = [
@@ -3089,43 +3210,41 @@ function getVhkVersion2() {
3089
3210
  return void 0;
3090
3211
  }
3091
3212
  async function doctor(opts = {}) {
3092
- console.log(chalk9.bold(`
3213
+ console.log(chalk8.bold(`
3093
3214
  ${ko.doctor.title}
3094
3215
  `));
3095
- const checks = [
3096
- checkCommand("Node.js", "node", "\uC124\uCE58: https://nodejs.org (LTS \uAD8C\uC7A5)"),
3097
- checkCommand("npm", "npm", "Node.js \uC124\uCE58 \uC2DC \uD568\uAED8 \uC124\uCE58\uB429\uB2C8\uB2E4"),
3098
- checkCommand("pnpm", "pnpm", "\uC124\uCE58: npm i -g pnpm"),
3099
- checkCommand("Git", "git", "\uC124\uCE58: https://git-scm.com")
3100
- ];
3101
- let allOk = true;
3102
- for (const check2 of checks) {
3103
- if (check2.ok) {
3104
- console.log(chalk9.green(` \u2705 ${check2.name}`) + chalk9.dim(` \u2014 ${check2.version}`));
3105
- } else {
3106
- console.log(chalk9.red(` \u274C ${check2.name} \uC5C6\uC74C`));
3107
- console.log(chalk9.dim(` \u2192 ${check2.hint}`));
3108
- allOk = false;
3109
- }
3110
- }
3216
+ const run = (cmd, args) => {
3217
+ const r = safeExecFile(cmd, args);
3218
+ return r.ok ? { ok: true, out: r.out } : { ok: false, out: r.out, err: r.err };
3219
+ };
3220
+ const deps = {
3221
+ run,
3222
+ nodeVersion: process.version,
3223
+ platform: process.platform,
3224
+ osRelease: os2.release()
3225
+ };
3226
+ const diags = await runDiagnostics([diagNode, diagNpm, diagPnpm, diagGit, diagOs], opts, deps);
3227
+ for (const line of formatDiagnostics(diags)) console.log(line);
3228
+ const anyFail = diags.some((d) => d.status === "fail");
3229
+ const warnCount = diags.filter((d) => d.status === "warn").length;
3111
3230
  console.log("");
3112
3231
  const vhkVersion = getVhkVersion2();
3113
3232
  if (vhkVersion) {
3114
- console.log(chalk9.green(" \u2705 VHK") + chalk9.dim(` \u2014 v${vhkVersion}`));
3233
+ console.log(chalk8.green(" \u2705 VHK") + chalk8.dim(` \u2014 v${vhkVersion}`));
3115
3234
  } else {
3116
- console.log(chalk9.green(" \u2705 VHK") + chalk9.dim(" \u2014 \uC124\uCE58\uB428"));
3235
+ console.log(chalk8.green(" \u2705 VHK") + chalk8.dim(" \u2014 \uC124\uCE58\uB428"));
3117
3236
  }
3118
3237
  if (vhkVersion) {
3119
3238
  const latest = fetchLatestNpmVersion("@byh3071/vhk");
3120
3239
  if (latest) recordLatest(latest);
3121
3240
  if (latest && compareSemver(latest, vhkVersion) > 0) {
3122
- console.log(chalk9.yellow(` ${ko.doctor.updateAvailable(latest)}`));
3241
+ console.log(chalk8.yellow(` ${ko.doctor.updateAvailable(latest)}`));
3123
3242
  } else if (latest) {
3124
- console.log(chalk9.dim(` ${ko.doctor.updateCurrent}`));
3243
+ console.log(chalk8.dim(` ${ko.doctor.updateCurrent}`));
3125
3244
  }
3126
3245
  }
3127
3246
  console.log("");
3128
- console.log(chalk9.bold(` ${ko.doctor.projectFiles}`));
3247
+ console.log(chalk8.bold(` ${ko.doctor.projectFiles}`));
3129
3248
  const cwd = process.cwd();
3130
3249
  const projectFiles = [
3131
3250
  { name: "RULES.md", hint: "vhk init\uC73C\uB85C \uC0DD\uC131 \uAC00\uB2A5" },
@@ -3137,67 +3256,74 @@ ${ko.doctor.title}
3137
3256
  for (const file of projectFiles) {
3138
3257
  const exists = fs9.existsSync(path10.join(cwd, file.name));
3139
3258
  if (exists) {
3140
- console.log(chalk9.green(` \u2705 ${file.name}`));
3259
+ console.log(chalk8.green(` \u2705 ${file.name}`));
3141
3260
  if (file.name === ".env") {
3142
3261
  const gitignorePath = path10.join(cwd, ".gitignore");
3143
3262
  if (fs9.existsSync(gitignorePath)) {
3144
3263
  const gitignore = fs9.readFileSync(gitignorePath, "utf-8");
3145
3264
  if (!gitignore.includes(".env")) {
3146
- console.log(chalk9.yellow(` ${ko.doctor.envNotIgnored}`));
3265
+ console.log(chalk8.yellow(` ${ko.doctor.envNotIgnored}`));
3147
3266
  }
3148
3267
  }
3149
3268
  }
3150
3269
  } else if (file.name === ".env" && fs9.existsSync(path10.join(cwd, ".env.local"))) {
3151
- console.log(chalk9.green(" \u2705 .env.local") + chalk9.dim(" \u2014 \uB85C\uCEF4 env \uC0AC\uC6A9 \uC911 (.env \uC5C6\uC5B4\uB3C4 \uC815\uC0C1)"));
3270
+ console.log(chalk8.green(" \u2705 .env.local") + chalk8.dim(" \u2014 \uB85C\uCEF4 env \uC0AC\uC6A9 \uC911 (.env \uC5C6\uC5B4\uB3C4 \uC815\uC0C1)"));
3152
3271
  } else {
3153
- console.log(chalk9.dim(` \u26AB ${file.name}`) + chalk9.dim(` \u2014 ${file.hint}`));
3272
+ console.log(chalk8.dim(` \u26AB ${file.name}`) + chalk8.dim(` \u2014 ${file.hint}`));
3154
3273
  }
3155
3274
  }
3156
3275
  console.log("");
3157
- console.log(chalk9.bold(` ${ko.doctor.driftTitle}`));
3276
+ console.log(chalk8.bold(` ${ko.doctor.driftTitle}`));
3158
3277
  const ruleDrift = checkRuleDrift(cwd);
3159
3278
  let ruleDrifted = false;
3160
3279
  if (!ruleDrift.checked) {
3161
- console.log(chalk9.dim(` ${ko.doctor.driftNoRules}`));
3280
+ console.log(chalk8.dim(` ${ko.doctor.driftNoRules}`));
3162
3281
  } else {
3163
3282
  const drifted = ruleDrift.results.filter((r) => r.status === "drifted");
3164
3283
  if (drifted.length === 0) {
3165
- console.log(chalk9.green(` ${ko.doctor.driftRuleClean}`));
3284
+ console.log(chalk8.green(` ${ko.doctor.driftRuleClean}`));
3166
3285
  } else {
3167
- console.log(chalk9.yellow(` ${ko.doctor.driftRuleWarn(drifted.map((d) => d.path).join(", "))}`));
3286
+ console.log(chalk8.yellow(` ${ko.doctor.driftRuleWarn(drifted.map((d) => d.path).join(", "))}`));
3168
3287
  ruleDrifted = true;
3169
3288
  }
3170
3289
  }
3171
3290
  const ctxDrift = checkContextDrift(cwd);
3172
3291
  if (ctxDrift.checked && ctxDrift.stale) {
3173
- console.log(chalk9.yellow(` ${ko.doctor.driftContextWarn}`));
3292
+ console.log(chalk8.yellow(` ${ko.doctor.driftContextWarn}`));
3174
3293
  }
3175
3294
  console.log("");
3176
- if (allOk) {
3177
- console.log(chalk9.green.bold(` ${ko.doctor.allOk}`));
3178
- printNextStep({
3179
- message: ko.doctor.nextOkMessage,
3180
- command: "vhk \uC2DC\uC791",
3181
- cursorHint: "\uD504\uB85C\uC81D\uD2B8 \uB9CC\uB4E4\uC5B4\uC918"
3182
- });
3183
- } else {
3184
- console.log(chalk9.yellow.bold(` ${ko.doctor.missing} ${ko.doctor.missingHint}`));
3295
+ if (anyFail) {
3296
+ console.log(chalk8.yellow.bold(` ${ko.doctor.missing} ${ko.doctor.missingHint}`));
3185
3297
  printNextStep({
3186
3298
  message: ko.doctor.nextRetryMessage,
3187
3299
  command: "vhk doctor",
3188
3300
  cursorHint: "\uD658\uACBD \uB2E4\uC2DC \uC810\uAC80\uD574\uC918"
3189
3301
  });
3190
3302
  process.exitCode = 1;
3303
+ } else if (warnCount > 0) {
3304
+ console.log(chalk8.yellow.bold(` ${ko.doctor.warnSummary(warnCount)}`));
3305
+ printNextStep({
3306
+ message: "\uC704 \uAD8C\uC7A5 \uC870\uCE58 \uD655\uC778 (\uD544\uC218\uB294 \uC544\uB2D8).",
3307
+ command: "vhk doctor",
3308
+ cursorHint: "\uD658\uACBD \uB2E4\uC2DC \uC810\uAC80\uD574\uC918"
3309
+ });
3310
+ } else {
3311
+ console.log(chalk8.green.bold(` ${ko.doctor.allOk}`));
3312
+ printNextStep({
3313
+ message: ko.doctor.nextOkMessage,
3314
+ command: "vhk \uC2DC\uC791",
3315
+ cursorHint: "\uD504\uB85C\uC81D\uD2B8 \uB9CC\uB4E4\uC5B4\uC918"
3316
+ });
3191
3317
  }
3192
3318
  if (opts.strict && ruleDrifted) {
3193
3319
  console.log("");
3194
- console.log(chalk9.red.bold(" \u274C --strict: \uADDC\uCE59 \uB4DC\uB9AC\uD504\uD2B8 \uBC1C\uACAC \u2192 \uC2E4\uD328 \uCC98\uB9AC (vhk sync \uB85C \uB3D9\uAE30\uD654 \uD6C4 \uB2E4\uC2DC \uC2E4\uD589)"));
3320
+ console.log(chalk8.red.bold(" \u274C --strict: \uADDC\uCE59 \uB4DC\uB9AC\uD504\uD2B8 \uBC1C\uACAC \u2192 \uC2E4\uD328 \uCC98\uB9AC (vhk sync \uB85C \uB3D9\uAE30\uD654 \uD6C4 \uB2E4\uC2DC \uC2E4\uD589)"));
3195
3321
  process.exitCode = 1;
3196
3322
  }
3197
3323
  }
3198
3324
 
3199
3325
  // src/commands/ship.ts
3200
- import chalk10 from "chalk";
3326
+ import chalk9 from "chalk";
3201
3327
  import inquirer4 from "inquirer";
3202
3328
  import fs10 from "fs";
3203
3329
  import path11 from "path";
@@ -3238,29 +3364,29 @@ function updateChangelogUnreleased(cwd, version, date) {
3238
3364
  async function ship() {
3239
3365
  if (!ensureNotHardStopped("ship")) return;
3240
3366
  if (!ensureInteractive("\uBC30\uD3EC \uCCB4\uD06C\uB9AC\uC2A4\uD2B8\xB7\uD68C\uACE0\uB294 \uB300\uD654\uD615 \uC785\uB825\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. PowerShell \uB4F1 TTY \uC5D0\uC11C \uC2E4\uD589\uD558\uC138\uC694.")) return;
3241
- console.log(chalk10.bold(`
3367
+ console.log(chalk9.bold(`
3242
3368
  ${ko.ship.title}
3243
3369
  `));
3244
3370
  const cwd = process.cwd();
3245
- console.log(chalk10.cyan.bold(` ${ko.ship.checklist}
3371
+ console.log(chalk9.cyan.bold(` ${ko.ship.checklist}
3246
3372
  `));
3247
3373
  const { passed } = await inquirer4.prompt([{
3248
3374
  type: "checkbox",
3249
3375
  name: "passed",
3250
3376
  message: ko.ship.checkboxPrompt,
3251
3377
  choices: CHECKLIST.map((c) => ({
3252
- name: `${ko.ship[c.questionKey]} ${chalk10.dim(`(${ko.ship[c.hintKey]})`)}`,
3378
+ name: `${ko.ship[c.questionKey]} ${chalk9.dim(`(${ko.ship[c.hintKey]})`)}`,
3253
3379
  value: c.id
3254
3380
  }))
3255
3381
  }]);
3256
3382
  const allPassed = passed.length === CHECKLIST.length;
3257
3383
  const skipped = CHECKLIST.filter((c) => !passed.includes(c.id));
3258
3384
  if (!allPassed) {
3259
- console.log(chalk10.yellow(`
3385
+ console.log(chalk9.yellow(`
3260
3386
  ${ko.ship.incompleteHeader}`));
3261
3387
  skipped.forEach((s) => {
3262
- console.log(chalk10.yellow(` \u2022 ${ko.ship[s.questionKey]}`));
3263
- console.log(chalk10.dim(` \u2192 ${ko.ship[s.hintKey]}`));
3388
+ console.log(chalk9.yellow(` \u2022 ${ko.ship[s.questionKey]}`));
3389
+ console.log(chalk9.dim(` \u2192 ${ko.ship[s.hintKey]}`));
3264
3390
  });
3265
3391
  const { proceed } = await inquirer4.prompt([{
3266
3392
  type: "confirm",
@@ -3277,13 +3403,13 @@ ${ko.ship.title}
3277
3403
  return;
3278
3404
  }
3279
3405
  } else {
3280
- console.log(chalk10.green(`
3406
+ console.log(chalk9.green(`
3281
3407
  ${ko.ship.allPassed}
3282
3408
  `));
3283
3409
  }
3284
- console.log(chalk10.cyan.bold(` ${ko.ship.retro}
3410
+ console.log(chalk9.cyan.bold(` ${ko.ship.retro}
3285
3411
  `));
3286
- console.log(chalk10.dim(` ${ko.ship.versionHint}`));
3412
+ console.log(chalk9.dim(` ${ko.ship.versionHint}`));
3287
3413
  const retro = await inquirer4.prompt([
3288
3414
  { type: "input", name: "version", message: ko.ship.versionPrompt },
3289
3415
  { type: "input", name: "whatWentWell", message: ko.ship.questionWell },
@@ -3326,7 +3452,7 @@ ${ko.ship.title}
3326
3452
  `*Generated by \`vhk ship\` at ${(/* @__PURE__ */ new Date()).toISOString()}*`
3327
3453
  ].join("\n");
3328
3454
  fs10.writeFileSync(filePath, content, "utf-8");
3329
- console.log(chalk10.green(`
3455
+ console.log(chalk9.green(`
3330
3456
  ${ko.ship.buildLogDone(path11.relative(cwd, filePath))}`));
3331
3457
  const changelogResult = updateChangelogUnreleased(cwd, versionSlug, today);
3332
3458
  if (changelogResult.status === "updated") {
@@ -3346,7 +3472,7 @@ ${ko.ship.title}
3346
3472
 
3347
3473
  // src/commands/save.ts
3348
3474
  import { execFileSync } from "child_process";
3349
- import chalk11 from "chalk";
3475
+ import chalk10 from "chalk";
3350
3476
  import ora from "ora";
3351
3477
  import inquirer5 from "inquirer";
3352
3478
 
@@ -3367,36 +3493,36 @@ function formatDefaultCommitMessage(date = /* @__PURE__ */ new Date()) {
3367
3493
  const min = String(date.getMinutes()).padStart(2, "0");
3368
3494
  return `\u2728 vhk save: ${y}-${m}-${d} ${h}:${min}`;
3369
3495
  }
3370
- function statusIcon(code) {
3496
+ function statusIcon2(code) {
3371
3497
  if (code.includes("M")) return "\u270F\uFE0F";
3372
3498
  if (code.includes("A") || code.includes("?")) return "\u2795";
3373
3499
  if (code.includes("D")) return "\u{1F5D1}\uFE0F";
3374
3500
  return "\u{1F4C4}";
3375
3501
  }
3376
3502
  async function save() {
3377
- console.log(chalk11.bold(`
3503
+ console.log(chalk10.bold(`
3378
3504
  \u{1F4BE} ${t("save.title")}`));
3379
- console.log(chalk11.gray("\u2500".repeat(40)));
3505
+ console.log(chalk10.gray("\u2500".repeat(40)));
3380
3506
  let gitRoot;
3381
3507
  try {
3382
3508
  execFileSync("git", ["rev-parse", "--is-inside-work-tree"], { stdio: "pipe" });
3383
3509
  gitRoot = getGitRoot();
3384
3510
  } catch {
3385
- console.log(chalk11.red(`\u274C ${t("save.notGitRepo")}`));
3511
+ console.log(chalk10.red(`\u274C ${t("save.notGitRepo")}`));
3386
3512
  return;
3387
3513
  }
3388
- console.log(chalk11.cyan(`
3514
+ console.log(chalk10.cyan(`
3389
3515
  \u{1F512} ${t("save.securityWarnHeader")}`));
3390
3516
  printSecurityWarnings(gitRoot);
3391
3517
  const severe = filterSevereFindings(scanProjectForSecrets(gitRoot).findings);
3392
3518
  if (severe.length > 0) {
3393
- console.log(chalk11.red(`
3519
+ console.log(chalk10.red(`
3394
3520
  \u26A0\uFE0F ${t("save.secretsFound", severe.length)}`));
3395
3521
  severe.slice(0, 5).forEach((f) => {
3396
- console.log(chalk11.dim(` ${f.file}:${f.line} \u2014 ${f.patternName}`));
3522
+ console.log(chalk10.dim(` ${f.file}:${f.line} \u2014 ${f.patternName}`));
3397
3523
  });
3398
3524
  if (severe.length > 5) {
3399
- console.log(chalk11.dim(` ... \uC678 ${severe.length - 5}\uAC74 (vhk \uBCF4\uC548 scan)`));
3525
+ console.log(chalk10.dim(` ... \uC678 ${severe.length - 5}\uAC74 (vhk \uBCF4\uC548 scan)`));
3400
3526
  }
3401
3527
  const proceed = await promptOrDefault(
3402
3528
  async () => (await inquirer5.prompt([{
@@ -3409,21 +3535,21 @@ async function save() {
3409
3535
  // 비대화형 = 시크릿 커밋 안 함 (안전)
3410
3536
  );
3411
3537
  if (!proceed) {
3412
- console.log(chalk11.gray(t("save.cancelled")));
3538
+ console.log(chalk10.gray(t("save.cancelled")));
3413
3539
  return;
3414
3540
  }
3415
3541
  }
3416
3542
  const lines = parsePorcelainLines(gitOut(["status", "--porcelain"], gitRoot));
3417
3543
  if (lines.length === 0) {
3418
- console.log(chalk11.yellow(`\u{1F4ED} ${t("save.noChanges")}`));
3544
+ console.log(chalk10.yellow(`\u{1F4ED} ${t("save.noChanges")}`));
3419
3545
  return;
3420
3546
  }
3421
- console.log(chalk11.cyan(`
3547
+ console.log(chalk10.cyan(`
3422
3548
  \u{1F4CB} ${t("save.filesHeader", lines.length)}`));
3423
3549
  lines.forEach((line) => {
3424
3550
  const code = line.substring(0, 2);
3425
3551
  const name = line.substring(3);
3426
- console.log(` ${statusIcon(code)} ${name}`);
3552
+ console.log(` ${statusIcon2(code)} ${name}`);
3427
3553
  });
3428
3554
  const message = await promptOrDefault(
3429
3555
  async () => (await inquirer5.prompt([{
@@ -3443,21 +3569,21 @@ async function save() {
3443
3569
  spinner.text = t("save.pushing");
3444
3570
  if (!hasGitRemote(gitRoot)) {
3445
3571
  spinner.succeed(t("save.successLocal"));
3446
- console.log(chalk11.yellow(` \u{1F4A1} ${t("save.noRemote")}`));
3572
+ console.log(chalk10.yellow(` \u{1F4A1} ${t("save.noRemote")}`));
3447
3573
  } else {
3448
3574
  try {
3449
3575
  gitRun(["push"], gitRoot);
3450
3576
  spinner.succeed(t("save.successWithPush"));
3451
3577
  } catch (pushErr) {
3452
3578
  spinner.fail(t("save.pushFailed"));
3453
- console.log(chalk11.red(getExecErrorMessage(pushErr)));
3454
- console.log(chalk11.yellow(`
3579
+ console.log(chalk10.red(getExecErrorMessage(pushErr)));
3580
+ console.log(chalk10.yellow(`
3455
3581
  \u{1F4A1} ${t("save.commitOkPushFailed")}`));
3456
3582
  process.exitCode = 1;
3457
3583
  }
3458
3584
  }
3459
3585
  if (process.exitCode !== 1) {
3460
- console.log(chalk11.green(`
3586
+ console.log(chalk10.green(`
3461
3587
  \u2705 ${t("save.done", lines.length)}`));
3462
3588
  printNextStep({
3463
3589
  message: t("save.nextOkMessage"),
@@ -3465,7 +3591,7 @@ async function save() {
3465
3591
  cursorHint: t("save.nextOkCursor")
3466
3592
  });
3467
3593
  } else {
3468
- console.log(chalk11.green(`
3594
+ console.log(chalk10.green(`
3469
3595
  \u2705 ${t("save.doneLocalOnly", lines.length)}`));
3470
3596
  printNextStep({
3471
3597
  message: t("save.nextPushFailMessage"),
@@ -3475,12 +3601,12 @@ async function save() {
3475
3601
  }
3476
3602
  } catch (err) {
3477
3603
  spinner.fail(t("save.failed"));
3478
- console.log(chalk11.red(getExecErrorMessage(err)));
3604
+ console.log(chalk10.red(getExecErrorMessage(err)));
3479
3605
  if (didAdd) {
3480
3606
  try {
3481
3607
  const staged = gitOut(["diff", "--cached", "--stat"], gitRoot).trim();
3482
3608
  if (staged) {
3483
- console.log(chalk11.yellow(`
3609
+ console.log(chalk10.yellow(`
3484
3610
  \u{1F4A1} ${t("save.stagedAfterFail")}`));
3485
3611
  }
3486
3612
  } catch {
@@ -3492,7 +3618,7 @@ async function save() {
3492
3618
 
3493
3619
  // src/commands/undo.ts
3494
3620
  import { execFileSync as execFileSync2 } from "child_process";
3495
- import chalk12 from "chalk";
3621
+ import chalk11 from "chalk";
3496
3622
  import inquirer6 from "inquirer";
3497
3623
  function parseRecentCommits(logOutput) {
3498
3624
  return logOutput.split("\n").map((l) => l.trim()).filter(Boolean);
@@ -3516,30 +3642,30 @@ function isUndoRisky(undoCount, unpushedCount, hasRemote) {
3516
3642
  return false;
3517
3643
  }
3518
3644
  async function undo() {
3519
- console.log(chalk12.bold(`
3645
+ console.log(chalk11.bold(`
3520
3646
  \u23EA ${t("undo.title")}`));
3521
- console.log(chalk12.gray("\u2500".repeat(40)));
3647
+ console.log(chalk11.gray("\u2500".repeat(40)));
3522
3648
  let gitRoot;
3523
3649
  try {
3524
3650
  execFileSync2("git", ["rev-parse", "--is-inside-work-tree"], { stdio: "pipe" });
3525
3651
  gitRoot = getGitRoot();
3526
3652
  } catch {
3527
- console.log(chalk12.red(`\u274C ${t("undo.notGitRepo")}`));
3653
+ console.log(chalk11.red(`\u274C ${t("undo.notGitRepo")}`));
3528
3654
  return;
3529
3655
  }
3530
3656
  let logOutput;
3531
3657
  try {
3532
3658
  logOutput = gitOut(["log", "--oneline", "-5"], gitRoot).trim();
3533
3659
  } catch {
3534
- console.log(chalk12.yellow(`\u{1F4ED} ${t("undo.noCommits")}`));
3660
+ console.log(chalk11.yellow(`\u{1F4ED} ${t("undo.noCommits")}`));
3535
3661
  return;
3536
3662
  }
3537
3663
  const commits = parseRecentCommits(logOutput);
3538
3664
  if (commits.length === 0) {
3539
- console.log(chalk12.yellow(`\u{1F4ED} ${t("undo.noCommits")}`));
3665
+ console.log(chalk11.yellow(`\u{1F4ED} ${t("undo.noCommits")}`));
3540
3666
  return;
3541
3667
  }
3542
- console.log(chalk12.cyan(`
3668
+ console.log(chalk11.cyan(`
3543
3669
  ${t("undo.recentHeader")}`));
3544
3670
  commits.forEach((c, i) => {
3545
3671
  console.log(` ${i === 0 ? "\u{1F449}" : " "} ${c}`);
@@ -3556,7 +3682,7 @@ ${t("undo.recentHeader")}`));
3556
3682
  const undoCount = Math.min(Math.max(1, count || 1), maxUndo);
3557
3683
  const headCount = countLocalCommits(gitRoot);
3558
3684
  if (undoCount >= headCount) {
3559
- console.log(chalk12.yellow(`
3685
+ console.log(chalk11.yellow(`
3560
3686
  \u{1F4ED} ${t("undo.rootCommit")}`));
3561
3687
  return;
3562
3688
  }
@@ -3565,10 +3691,10 @@ ${t("undo.recentHeader")}`));
3565
3691
  const risky = isUndoRisky(undoCount, unpushed, remote);
3566
3692
  if (risky) {
3567
3693
  if (unpushed < 0) {
3568
- console.log(chalk12.red(`
3694
+ console.log(chalk11.red(`
3569
3695
  \u26A0\uFE0F ${t("undo.noUpstreamWarning")}`));
3570
3696
  } else {
3571
- console.log(chalk12.red(`
3697
+ console.log(chalk11.red(`
3572
3698
  \u26A0\uFE0F ${t("undo.alreadyPushed")}`));
3573
3699
  }
3574
3700
  }
@@ -3579,16 +3705,16 @@ ${t("undo.recentHeader")}`));
3579
3705
  default: false
3580
3706
  }]);
3581
3707
  if (!confirm) {
3582
- console.log(chalk12.gray(t("undo.cancelled")));
3708
+ console.log(chalk11.gray(t("undo.cancelled")));
3583
3709
  return;
3584
3710
  }
3585
3711
  try {
3586
3712
  gitRun(["reset", "--soft", `HEAD~${undoCount}`], gitRoot);
3587
- console.log(chalk12.green(`
3713
+ console.log(chalk11.green(`
3588
3714
  \u2705 ${t("undo.success")}`));
3589
- console.log(chalk12.gray(` \u{1F4A1} ${t("undo.stagedHint")}`));
3715
+ console.log(chalk11.gray(` \u{1F4A1} ${t("undo.stagedHint")}`));
3590
3716
  if (risky) {
3591
- console.log(chalk12.yellow(`
3717
+ console.log(chalk11.yellow(`
3592
3718
  \u{1F4A1} ${t("undo.forcePushHint")}`));
3593
3719
  }
3594
3720
  printNextStep({
@@ -3597,35 +3723,35 @@ ${t("undo.recentHeader")}`));
3597
3723
  cursorHint: t("undo.nextCursor")
3598
3724
  });
3599
3725
  } catch (err) {
3600
- console.log(chalk12.red(`\u274C ${t("undo.failed")}`));
3726
+ console.log(chalk11.red(`\u274C ${t("undo.failed")}`));
3601
3727
  const msg = err instanceof Error ? err.message : String(err);
3602
- console.log(chalk12.red(msg));
3728
+ console.log(chalk11.red(msg));
3603
3729
  process.exitCode = 1;
3604
3730
  }
3605
3731
  }
3606
3732
 
3607
3733
  // src/commands/restore.ts
3608
- import chalk13 from "chalk";
3734
+ import chalk12 from "chalk";
3609
3735
  import inquirer7 from "inquirer";
3610
3736
  async function restore(id) {
3611
- console.log(chalk13.bold(`
3737
+ console.log(chalk12.bold(`
3612
3738
  ${ko.restore.title}`));
3613
- console.log(chalk13.gray("\u2500".repeat(40)));
3614
- console.log(chalk13.dim(` ${ko.restore.notGitNote}`));
3739
+ console.log(chalk12.gray("\u2500".repeat(40)));
3740
+ console.log(chalk12.dim(` ${ko.restore.notGitNote}`));
3615
3741
  const cwd = process.cwd();
3616
3742
  const backups = listBackups(cwd);
3617
3743
  if (backups.length === 0) {
3618
- console.log(chalk13.yellow(`
3744
+ console.log(chalk12.yellow(`
3619
3745
  ${ko.restore.noBackups}`));
3620
3746
  return;
3621
3747
  }
3622
3748
  let targetId = id;
3623
3749
  if (!targetId) {
3624
3750
  if (!process.stdout.isTTY) {
3625
- console.log(chalk13.cyan(`
3751
+ console.log(chalk12.cyan(`
3626
3752
  ${ko.restore.listHeader}`));
3627
3753
  for (const b of backups) console.log(` ${b.id} (${b.files.length}\uAC1C \uD30C\uC77C)`);
3628
- console.log(chalk13.yellow(`
3754
+ console.log(chalk12.yellow(`
3629
3755
  ${ko.restore.nonTtyHint}`));
3630
3756
  return;
3631
3757
  }
@@ -3644,16 +3770,16 @@ ${ko.restore.nonTtyHint}`));
3644
3770
  }
3645
3771
  try {
3646
3772
  const restored = restoreBackup(targetId, cwd);
3647
- console.log(chalk13.green(`
3773
+ console.log(chalk12.green(`
3648
3774
  ${ko.restore.restored(restored.length, targetId)}`));
3649
- for (const r of restored) console.log(chalk13.gray(` ${r}`));
3775
+ for (const r of restored) console.log(chalk12.gray(` ${r}`));
3650
3776
  printNextStep({
3651
3777
  message: "\uBC31\uC5C5 \uBCF5\uC6D0 \uC644\uB8CC! \uBCC0\uACBD \uB0B4\uC6A9\uC744 \uD655\uC778\uD558\uC138\uC694.",
3652
3778
  command: "vhk diff",
3653
3779
  cursorHint: "\uBCC0\uACBD\uC0AC\uD56D \uBCF4\uC5EC\uC918"
3654
3780
  });
3655
3781
  } catch {
3656
- console.log(chalk13.red(`
3782
+ console.log(chalk12.red(`
3657
3783
  ${ko.restore.notFound(targetId)}`));
3658
3784
  process.exitCode = 1;
3659
3785
  }
@@ -3663,7 +3789,7 @@ ${ko.restore.notFound(targetId)}`));
3663
3789
  import { execFileSync as execFileSync3 } from "child_process";
3664
3790
  import fs11 from "fs";
3665
3791
  import path12 from "path";
3666
- import chalk14 from "chalk";
3792
+ import chalk13 from "chalk";
3667
3793
  function countFileChanges(porcelain) {
3668
3794
  const lines = porcelain.split("\n").filter(Boolean);
3669
3795
  let staged = 0;
@@ -3738,15 +3864,15 @@ function getSyncCounts(gitRoot) {
3738
3864
  }
3739
3865
  }
3740
3866
  async function status() {
3741
- console.log(chalk14.bold(`
3867
+ console.log(chalk13.bold(`
3742
3868
  \u{1F4CA} ${t("status.title")}`));
3743
- console.log(chalk14.gray("\u2500".repeat(40)));
3869
+ console.log(chalk13.gray("\u2500".repeat(40)));
3744
3870
  let gitRoot;
3745
3871
  try {
3746
3872
  execFileSync3("git", ["rev-parse", "--is-inside-work-tree"], { stdio: "pipe" });
3747
3873
  gitRoot = getGitRoot();
3748
3874
  } catch {
3749
- console.log(chalk14.red(`\u274C ${t("status.notGitRepo")}`));
3875
+ console.log(chalk13.red(`\u274C ${t("status.notGitRepo")}`));
3750
3876
  return;
3751
3877
  }
3752
3878
  let branch;
@@ -3765,29 +3891,29 @@ async function status() {
3765
3891
  commits = [];
3766
3892
  }
3767
3893
  const pkg = readProjectPackage();
3768
- console.log(chalk14.cyan(`
3769
- \u{1F33F} ${t("status.branch")}`) + chalk14.white(` ${branch}`));
3894
+ console.log(chalk13.cyan(`
3895
+ \u{1F33F} ${t("status.branch")}`) + chalk13.white(` ${branch}`));
3770
3896
  console.log(
3771
- chalk14.cyan(`\u{1F4C1} ${t("status.changes")}`) + chalk14.white(
3897
+ chalk13.cyan(`\u{1F4C1} ${t("status.changes")}`) + chalk13.white(
3772
3898
  ` staged ${counts.staged} \xB7 unstaged ${counts.unstaged} \xB7 untracked ${counts.untracked}`
3773
3899
  )
3774
3900
  );
3775
- console.log(chalk14.cyan(`
3901
+ console.log(chalk13.cyan(`
3776
3902
  \u{1F4CB} ${t("status.recentCommits", commits.length)}`));
3777
3903
  if (commits.length === 0) {
3778
- console.log(chalk14.dim(` ${t("status.noCommits")}`));
3904
+ console.log(chalk13.dim(` ${t("status.noCommits")}`));
3779
3905
  } else {
3780
- commits.forEach((c) => console.log(` ${chalk14.dim("\u2022")} ${c}`));
3906
+ commits.forEach((c) => console.log(` ${chalk13.dim("\u2022")} ${c}`));
3781
3907
  }
3782
3908
  console.log(
3783
- chalk14.cyan(`
3784
- \u{1F504} ${t("status.remote")}`) + chalk14.white(` ${formatSyncLabel(sync2)}`)
3909
+ chalk13.cyan(`
3910
+ \u{1F504} ${t("status.remote")}`) + chalk13.white(` ${formatSyncLabel(sync2)}`)
3785
3911
  );
3786
- console.log(chalk14.gray("\n" + "\u2500".repeat(40)));
3912
+ console.log(chalk13.gray("\n" + "\u2500".repeat(40)));
3787
3913
  if (pkg) {
3788
- console.log(chalk14.cyan(`\u{1F4E6} ${t("status.package")}`) + chalk14.white(` ${pkg.name} v${pkg.version}`));
3914
+ console.log(chalk13.cyan(`\u{1F4E6} ${t("status.package")}`) + chalk13.white(` ${pkg.name} v${pkg.version}`));
3789
3915
  } else {
3790
- console.log(chalk14.dim(`\u{1F4E6} ${t("status.noPackage")}`));
3916
+ console.log(chalk13.dim(`\u{1F4E6} ${t("status.noPackage")}`));
3791
3917
  }
3792
3918
  const hasChanges = counts.staged + counts.unstaged + counts.untracked > 0;
3793
3919
  printNextStep(selectStatusNextStep(hasChanges));
@@ -3795,7 +3921,7 @@ async function status() {
3795
3921
  }
3796
3922
 
3797
3923
  // src/commands/diff.ts
3798
- import chalk15 from "chalk";
3924
+ import chalk14 from "chalk";
3799
3925
  function gitOut2(args) {
3800
3926
  const r = safeExecFile("git", args);
3801
3927
  return r.ok ? r.out : "";
@@ -3832,67 +3958,67 @@ function summarizeNumstat(numstat) {
3832
3958
  return { fileCount, totalAdd, totalDel };
3833
3959
  }
3834
3960
  function printFile(f) {
3835
- const adds = f.additions > 0 ? chalk15.green(`+${f.additions}`) : "";
3836
- const dels = f.deletions > 0 ? chalk15.red(`-${f.deletions}`) : "";
3961
+ const adds = f.additions > 0 ? chalk14.green(`+${f.additions}`) : "";
3962
+ const dels = f.deletions > 0 ? chalk14.red(`-${f.deletions}`) : "";
3837
3963
  const change = [adds, dels].filter(Boolean).join(" ");
3838
3964
  console.log(` ${f.name} ${change}`);
3839
3965
  }
3840
3966
  async function diff() {
3841
- console.log(chalk15.bold(`
3967
+ console.log(chalk14.bold(`
3842
3968
  \u{1F50D} ${t("diff.title")}`));
3843
- console.log(chalk15.gray("\u2500".repeat(40)));
3969
+ console.log(chalk14.gray("\u2500".repeat(40)));
3844
3970
  if (!safeExecFile("git", ["rev-parse", "--is-inside-work-tree"]).ok) {
3845
- console.log(chalk15.red(`\u274C ${t("diff.notGitRepo")}`));
3971
+ console.log(chalk14.red(`\u274C ${t("diff.notGitRepo")}`));
3846
3972
  return;
3847
3973
  }
3848
3974
  const unstaged = gitOut2(["diff", "--stat"]);
3849
3975
  const staged = gitOut2(["diff", "--cached", "--stat"]);
3850
3976
  const untracked = gitOut2(["ls-files", "--others", "--exclude-standard"]);
3851
3977
  if (!unstaged && !staged && !untracked) {
3852
- console.log(chalk15.green(`
3978
+ console.log(chalk14.green(`
3853
3979
  \u2705 ${t("diff.noChanges")}`));
3854
3980
  return;
3855
3981
  }
3856
3982
  if (staged) {
3857
- console.log(chalk15.cyan(`
3983
+ console.log(chalk14.cyan(`
3858
3984
  ${t("diff.stagedHeader")}`));
3859
3985
  parseDiffStat(staged).forEach((f) => printFile(f));
3860
3986
  }
3861
3987
  if (unstaged) {
3862
- console.log(chalk15.cyan(`
3988
+ console.log(chalk14.cyan(`
3863
3989
  ${t("diff.unstagedHeader")}`));
3864
3990
  parseDiffStat(unstaged).forEach((f) => printFile(f));
3865
3991
  }
3866
3992
  if (untracked) {
3867
3993
  const files = untracked.split("\n").filter(Boolean);
3868
- console.log(chalk15.cyan(`
3994
+ console.log(chalk14.cyan(`
3869
3995
  ${t("diff.untrackedHeader", files.length)}`));
3870
- files.forEach((f) => console.log(` ${chalk15.green("+")} ${f}`));
3996
+ files.forEach((f) => console.log(` ${chalk14.green("+")} ${f}`));
3871
3997
  }
3872
3998
  const numstat = gitOut2(["diff", "--numstat", "HEAD"]);
3873
3999
  if (numstat) {
3874
4000
  const { fileCount, totalAdd, totalDel } = summarizeNumstat(numstat);
3875
- console.log(chalk15.cyan(`
4001
+ console.log(chalk14.cyan(`
3876
4002
  ${t("diff.summaryHeader")}`));
3877
4003
  console.log(` ${t("diff.filesLine", fileCount)}`);
3878
- console.log(` \uCD94\uAC00: ${chalk15.green(`+${totalAdd}`)}\uC904`);
3879
- console.log(` \uC0AD\uC81C: ${chalk15.red(`-${totalDel}`)}\uC904`);
4004
+ console.log(` \uCD94\uAC00: ${chalk14.green(`+${totalAdd}`)}\uC904`);
4005
+ console.log(` \uC0AD\uC81C: ${chalk14.red(`-${totalDel}`)}\uC904`);
3880
4006
  }
3881
4007
  console.log("");
3882
4008
  }
3883
4009
 
3884
4010
  // src/commands/mcp-init.ts
3885
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
3886
- import { join as join5, dirname } from "path";
4011
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
4012
+ import { join as join4, dirname } from "path";
3887
4013
  import { fileURLToPath as fileURLToPath2 } from "url";
3888
- import chalk16 from "chalk";
4014
+ import chalk15 from "chalk";
3889
4015
  function resolveMcpEntryPoint() {
3890
4016
  try {
3891
4017
  const here = fileURLToPath2(import.meta.url);
3892
4018
  const dir = dirname(here);
3893
4019
  for (const rel of [["mcp", "index.js"], ["..", "mcp", "index.js"]]) {
3894
- const candidate = join5(dir, ...rel);
3895
- if (existsSync4(candidate)) return candidate;
4020
+ const candidate = join4(dir, ...rel);
4021
+ if (existsSync3(candidate)) return candidate;
3896
4022
  }
3897
4023
  } catch {
3898
4024
  }
@@ -3900,17 +4026,17 @@ function resolveMcpEntryPoint() {
3900
4026
  const url = import.meta.resolve?.("@byh3071/vhk/dist/mcp/index.js");
3901
4027
  if (typeof url === "string") {
3902
4028
  const p = fileURLToPath2(url);
3903
- if (existsSync4(p)) return p;
4029
+ if (existsSync3(p)) return p;
3904
4030
  }
3905
4031
  } catch {
3906
4032
  }
3907
4033
  try {
3908
- const pkgPath = join5(process.cwd(), "package.json");
3909
- if (existsSync4(pkgPath)) {
4034
+ const pkgPath = join4(process.cwd(), "package.json");
4035
+ if (existsSync3(pkgPath)) {
3910
4036
  const pkg = readJsonFile(pkgPath);
3911
4037
  if (pkg.name === "@byh3071/vhk") {
3912
- const local = join5(process.cwd(), "dist", "mcp", "index.js");
3913
- if (existsSync4(local)) return local;
4038
+ const local = join4(process.cwd(), "dist", "mcp", "index.js");
4039
+ if (existsSync3(local)) return local;
3914
4040
  }
3915
4041
  }
3916
4042
  } catch {
@@ -3925,31 +4051,31 @@ function resolveVhkMcpEntry() {
3925
4051
  return { command: "vhk-mcp", args: [] };
3926
4052
  }
3927
4053
  async function mcpInit() {
3928
- console.log(chalk16.bold("\n\u{1F50C} " + t("mcp.initTitle")));
3929
- console.log(chalk16.gray("\u2500".repeat(40)));
3930
- const cursorDir = join5(process.cwd(), ".cursor");
3931
- if (!existsSync4(cursorDir)) {
3932
- mkdirSync3(cursorDir, { recursive: true });
4054
+ console.log(chalk15.bold("\n\u{1F50C} " + t("mcp.initTitle")));
4055
+ console.log(chalk15.gray("\u2500".repeat(40)));
4056
+ const cursorDir = join4(process.cwd(), ".cursor");
4057
+ if (!existsSync3(cursorDir)) {
4058
+ mkdirSync2(cursorDir, { recursive: true });
3933
4059
  }
3934
- const configPath = join5(cursorDir, "mcp.json");
4060
+ const configPath = join4(cursorDir, "mcp.json");
3935
4061
  const vhkEntry = resolveVhkMcpEntry();
3936
4062
  let config;
3937
- if (existsSync4(configPath)) {
4063
+ if (existsSync3(configPath)) {
3938
4064
  try {
3939
4065
  const parsed = readJsonFile(configPath);
3940
4066
  config = {
3941
4067
  mcpServers: { ...parsed.mcpServers ?? {}, vhk: vhkEntry }
3942
4068
  };
3943
4069
  } catch {
3944
- console.log(chalk16.yellow("\u26A0\uFE0F \uAE30\uC874 .cursor/mcp.json \uD30C\uC2F1 \uC2E4\uD328 \u2014 \uC0C8 \uD30C\uC77C\uB85C \uB36E\uC5B4\uC501\uB2C8\uB2E4."));
4070
+ console.log(chalk15.yellow("\u26A0\uFE0F \uAE30\uC874 .cursor/mcp.json \uD30C\uC2F1 \uC2E4\uD328 \u2014 \uC0C8 \uD30C\uC77C\uB85C \uB36E\uC5B4\uC501\uB2C8\uB2E4."));
3945
4071
  config = { mcpServers: { vhk: vhkEntry } };
3946
4072
  }
3947
4073
  } else {
3948
4074
  config = { mcpServers: { vhk: vhkEntry } };
3949
4075
  }
3950
- writeFileSync3(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
3951
- console.log(chalk16.green("\n\u2705 Cursor MCP \uC124\uC815 \uC644\uB8CC!"));
3952
- console.log(chalk16.cyan("\u{1F4C1} \uC0DD\uC131\uB41C \uD30C\uC77C:"));
4076
+ writeFileSync2(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
4077
+ console.log(chalk15.green("\n\u2705 Cursor MCP \uC124\uC815 \uC644\uB8CC!"));
4078
+ console.log(chalk15.cyan("\u{1F4C1} \uC0DD\uC131\uB41C \uD30C\uC77C:"));
3953
4079
  console.log(` ${configPath}`);
3954
4080
  printNextStep({
3955
4081
  message: t("mcp.nextMessage"),
@@ -3959,8 +4085,8 @@ async function mcpInit() {
3959
4085
  }
3960
4086
 
3961
4087
  // src/commands/design.ts
3962
- import { existsSync as existsSync5, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
3963
- import chalk17 from "chalk";
4088
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
4089
+ import chalk16 from "chalk";
3964
4090
  import inquirer8 from "inquirer";
3965
4091
  var PALETTES = [
3966
4092
  {
@@ -4013,7 +4139,7 @@ var PALETTES = [
4013
4139
  }
4014
4140
  ];
4015
4141
  function hasTailwind() {
4016
- return existsSync5("tailwind.config.js") || existsSync5("tailwind.config.ts") || existsSync5("tailwind.config.mjs") || existsSync5("tailwind.config.cjs");
4142
+ return existsSync4("tailwind.config.js") || existsSync4("tailwind.config.ts") || existsSync4("tailwind.config.mjs") || existsSync4("tailwind.config.cjs");
4017
4143
  }
4018
4144
  function isTailwindV4Deps(deps) {
4019
4145
  if (deps["@tailwindcss/vite"] || deps["@tailwindcss/postcss"]) return true;
@@ -4063,8 +4189,9 @@ export default vhkColors
4063
4189
  `;
4064
4190
  }
4065
4191
  async function design() {
4066
- console.log(chalk17.bold("\n\u{1F3A8} " + t("design.title")));
4067
- console.log(chalk17.gray("\u2500".repeat(40)));
4192
+ if (!ensureNotHardStopped("design")) return;
4193
+ console.log(chalk16.bold("\n\u{1F3A8} " + t("design.title")));
4194
+ console.log(chalk16.gray("\u2500".repeat(40)));
4068
4195
  if (!ensureInteractive("\uCEEC\uB7EC \uD314\uB808\uD2B8 \uC120\uD0DD\uC740 \uB300\uD654\uD615\uC73C\uB85C\uB9CC \uAC00\uB2A5\uD569\uB2C8\uB2E4.")) return;
4069
4196
  const { paletteIndex } = await inquirer8.prompt([
4070
4197
  {
@@ -4078,12 +4205,12 @@ async function design() {
4078
4205
  }
4079
4206
  ]);
4080
4207
  const palette = PALETTES[paletteIndex];
4081
- console.log(chalk17.cyan(`
4208
+ console.log(chalk16.cyan(`
4082
4209
  \u{1F3A8} \uC120\uD0DD\uB41C \uD314\uB808\uD2B8: ${palette.name}`));
4083
4210
  const v4 = hasTailwindV4();
4084
4211
  const targetPath = v4 ? "src/styles/theme.css" : hasTailwind() ? "src/styles/vhk-colors.ts" : "src/styles/tokens.css";
4085
4212
  const content = v4 ? generateTailwindV4Theme(palette) : hasTailwind() ? generateTailwindExtend(palette) : generateCSSTokens(palette);
4086
- if (existsSync5(targetPath)) {
4213
+ if (existsSync4(targetPath)) {
4087
4214
  const { overwrite } = await inquirer8.prompt([{
4088
4215
  type: "confirm",
4089
4216
  name: "overwrite",
@@ -4091,24 +4218,24 @@ async function design() {
4091
4218
  default: false
4092
4219
  }]);
4093
4220
  if (!overwrite) {
4094
- console.log(chalk17.yellow("\n\u23ED\uFE0F \uC0DD\uC131 \uCDE8\uC18C \u2014 \uAE30\uC874 \uD30C\uC77C \uC720\uC9C0."));
4221
+ console.log(chalk16.yellow("\n\u23ED\uFE0F \uC0DD\uC131 \uCDE8\uC18C \u2014 \uAE30\uC874 \uD30C\uC77C \uC720\uC9C0."));
4095
4222
  return;
4096
4223
  }
4097
4224
  }
4098
- mkdirSync4("src/styles", { recursive: true });
4099
- writeFileSync4(targetPath, content, "utf-8");
4225
+ mkdirSync3("src/styles", { recursive: true });
4226
+ writeFileSync3(targetPath, content, "utf-8");
4100
4227
  if (v4) {
4101
- console.log(chalk17.green("\n\u2705 src/styles/theme.css \uC0DD\uC131 (Tailwind v4 @theme)"));
4102
- console.log(chalk17.gray(' \uC9C4\uC785 CSS(\uC608: src/index.css)\uC5D0 `@import "./styles/theme.css";` \uCD94\uAC00 \u2192 bg-primary \uB4F1 \uC720\uD2F8 \uC0AC\uC6A9.'));
4103
- console.log(chalk17.gray(" \uB2E4\uD06C \uD1A0\uAE00: \uB8E8\uD2B8 <html>/<body> \uC5D0 `.dark` \uD074\uB798\uC2A4 on/off."));
4228
+ console.log(chalk16.green("\n\u2705 src/styles/theme.css \uC0DD\uC131 (Tailwind v4 @theme)"));
4229
+ console.log(chalk16.gray(' \uC9C4\uC785 CSS(\uC608: src/index.css)\uC5D0 `@import "./styles/theme.css";` \uCD94\uAC00 \u2192 bg-primary \uB4F1 \uC720\uD2F8 \uC0AC\uC6A9.'));
4230
+ console.log(chalk16.gray(" \uB2E4\uD06C \uD1A0\uAE00: \uB8E8\uD2B8 <html>/<body> \uC5D0 `.dark` \uD074\uB798\uC2A4 on/off."));
4104
4231
  } else if (hasTailwind()) {
4105
- console.log(chalk17.green("\n\u2705 src/styles/vhk-colors.ts \uC0DD\uC131"));
4106
- console.log(chalk17.gray(" tailwind.config\uC758 extend.colors\uC5D0 import \uD574\uC11C \uC0AC\uC6A9\uD558\uC138\uC694."));
4232
+ console.log(chalk16.green("\n\u2705 src/styles/vhk-colors.ts \uC0DD\uC131"));
4233
+ console.log(chalk16.gray(" tailwind.config\uC758 extend.colors\uC5D0 import \uD574\uC11C \uC0AC\uC6A9\uD558\uC138\uC694."));
4107
4234
  } else {
4108
- console.log(chalk17.green("\n\u2705 src/styles/tokens.css \uC0DD\uC131"));
4109
- console.log(chalk17.gray(" HTML\uC5D0 <link>\uB85C \uCD94\uAC00\uD558\uAC70\uB098 CSS\uC5D0\uC11C @import \uD558\uC138\uC694."));
4235
+ console.log(chalk16.green("\n\u2705 src/styles/tokens.css \uC0DD\uC131"));
4236
+ console.log(chalk16.gray(" HTML\uC5D0 <link>\uB85C \uCD94\uAC00\uD558\uAC70\uB098 CSS\uC5D0\uC11C @import \uD558\uC138\uC694."));
4110
4237
  }
4111
- console.log(chalk17.bold("\n\u{1F308} \uCEEC\uB7EC \uBBF8\uB9AC\uBCF4\uAE30:"));
4238
+ console.log(chalk16.bold("\n\u{1F308} \uCEEC\uB7EC \uBBF8\uB9AC\uBCF4\uAE30:"));
4112
4239
  for (const [key, value] of Object.entries(palette.colors)) {
4113
4240
  console.log(` ${key.padEnd(12)} ${value}`);
4114
4241
  }
@@ -4123,8 +4250,8 @@ async function designPalette() {
4123
4250
  }
4124
4251
 
4125
4252
  // src/commands/theme.ts
4126
- import { existsSync as existsSync6, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "fs";
4127
- import chalk18 from "chalk";
4253
+ import { existsSync as existsSync5, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
4254
+ import chalk17 from "chalk";
4128
4255
  import inquirer9 from "inquirer";
4129
4256
  function generateDarkCSS() {
4130
4257
  return `/* vhk theme \u2014 \uB2E4\uD06C/\uB77C\uC774\uD2B8 \uBAA8\uB4DC CSS \uBCC0\uC218 */
@@ -4181,11 +4308,12 @@ export function initTheme(): void {
4181
4308
  `;
4182
4309
  }
4183
4310
  async function theme(options) {
4184
- console.log(chalk18.bold("\n\u{1F319} " + t("theme.title")));
4185
- console.log(chalk18.gray("\u2500".repeat(40)));
4311
+ if (!ensureNotHardStopped("theme")) return;
4312
+ console.log(chalk17.bold("\n\u{1F319} " + t("theme.title")));
4313
+ console.log(chalk17.gray("\u2500".repeat(40)));
4186
4314
  const cssPath = "src/styles/theme.css";
4187
4315
  const togglePath = "src/lib/theme-toggle.ts";
4188
- const conflicts = [cssPath, togglePath].filter((p) => existsSync6(p));
4316
+ const conflicts = [cssPath, togglePath].filter((p) => existsSync5(p));
4189
4317
  if (conflicts.length > 0) {
4190
4318
  const overwrite = options?.yes === true ? true : await promptOrDefault(
4191
4319
  async () => (await inquirer9.prompt([{
@@ -4198,21 +4326,21 @@ async function theme(options) {
4198
4326
  false
4199
4327
  );
4200
4328
  if (!overwrite) {
4201
- console.log(chalk18.yellow("\n\u23ED\uFE0F \uC0DD\uC131 \uCDE8\uC18C \u2014 \uAE30\uC874 \uD30C\uC77C \uC720\uC9C0. (\uBE44\uB300\uD654\uD615\uC774\uBA74 --yes \uB85C \uB36E\uC5B4\uC4F0\uAE30)"));
4329
+ console.log(chalk17.yellow("\n\u23ED\uFE0F \uC0DD\uC131 \uCDE8\uC18C \u2014 \uAE30\uC874 \uD30C\uC77C \uC720\uC9C0. (\uBE44\uB300\uD654\uD615\uC774\uBA74 --yes \uB85C \uB36E\uC5B4\uC4F0\uAE30)"));
4202
4330
  return;
4203
4331
  }
4204
4332
  }
4205
- mkdirSync5("src/styles", { recursive: true });
4206
- mkdirSync5("src/lib", { recursive: true });
4207
- writeFileSync5(cssPath, generateDarkCSS(), "utf-8");
4208
- console.log(chalk18.green("\n\u2705 src/styles/theme.css \uC0DD\uC131 (\uB2E4\uD06C/\uB77C\uC774\uD2B8 \uBAA8\uB4DC)"));
4209
- writeFileSync5(togglePath, generateToggleUtil(), "utf-8");
4210
- console.log(chalk18.green("\u2705 src/lib/theme-toggle.ts \uC0DD\uC131 (\uD1A0\uAE00 \uC720\uD2F8\uB9AC\uD2F0)"));
4211
- console.log(chalk18.bold("\n\u{1F4D6} \uC0AC\uC6A9\uBC95:"));
4212
- console.log(chalk18.gray(" 1. theme.css\uB97C \uAE00\uB85C\uBC8C \uC2A4\uD0C0\uC77C\uC5D0 \uCD94\uAC00"));
4213
- console.log(chalk18.gray(' 2. import { initTheme, toggleTheme } from "./lib/theme-toggle"'));
4214
- console.log(chalk18.gray(" 3. \uC571 \uC9C4\uC785\uC810\uC5D0\uC11C initTheme() \uD638\uCD9C"));
4215
- console.log(chalk18.gray(" 4. \uD1A0\uAE00 \uBC84\uD2BC\uC5D0\uC11C toggleTheme() \uD638\uCD9C"));
4333
+ mkdirSync4("src/styles", { recursive: true });
4334
+ mkdirSync4("src/lib", { recursive: true });
4335
+ writeFileSync4(cssPath, generateDarkCSS(), "utf-8");
4336
+ console.log(chalk17.green("\n\u2705 src/styles/theme.css \uC0DD\uC131 (\uB2E4\uD06C/\uB77C\uC774\uD2B8 \uBAA8\uB4DC)"));
4337
+ writeFileSync4(togglePath, generateToggleUtil(), "utf-8");
4338
+ console.log(chalk17.green("\u2705 src/lib/theme-toggle.ts \uC0DD\uC131 (\uD1A0\uAE00 \uC720\uD2F8\uB9AC\uD2F0)"));
4339
+ console.log(chalk17.bold("\n\u{1F4D6} \uC0AC\uC6A9\uBC95:"));
4340
+ console.log(chalk17.gray(" 1. theme.css\uB97C \uAE00\uB85C\uBC8C \uC2A4\uD0C0\uC77C\uC5D0 \uCD94\uAC00"));
4341
+ console.log(chalk17.gray(' 2. import { initTheme, toggleTheme } from "./lib/theme-toggle"'));
4342
+ console.log(chalk17.gray(" 3. \uC571 \uC9C4\uC785\uC810\uC5D0\uC11C initTheme() \uD638\uCD9C"));
4343
+ console.log(chalk17.gray(" 4. \uD1A0\uAE00 \uBC84\uD2BC\uC5D0\uC11C toggleTheme() \uD638\uCD9C"));
4216
4344
  printNextStep({
4217
4345
  message: "\uD14C\uB9C8 \uC124\uC815 \uC644\uB8CC!",
4218
4346
  command: "vhk ref list",
@@ -4221,11 +4349,11 @@ async function theme(options) {
4221
4349
  }
4222
4350
 
4223
4351
  // src/commands/ref.ts
4224
- import { existsSync as existsSync7, mkdirSync as mkdirSync6, writeFileSync as writeFileSync6 } from "fs";
4225
- import chalk19 from "chalk";
4352
+ import { existsSync as existsSync6, mkdirSync as mkdirSync5 } from "fs";
4353
+ import chalk18 from "chalk";
4226
4354
  var REFS_PATH = ".vhk/refs.json";
4227
4355
  function loadRefs() {
4228
- if (!existsSync7(REFS_PATH)) return [];
4356
+ if (!existsSync6(REFS_PATH)) return [];
4229
4357
  try {
4230
4358
  const parsed = readJsonFile(REFS_PATH);
4231
4359
  return Array.isArray(parsed) ? parsed : [];
@@ -4234,28 +4362,29 @@ function loadRefs() {
4234
4362
  }
4235
4363
  }
4236
4364
  function saveRefs(refs) {
4237
- mkdirSync6(".vhk", { recursive: true });
4238
- writeFileSync6(REFS_PATH, JSON.stringify(refs, null, 2) + "\n", "utf-8");
4365
+ mkdirSync5(".vhk", { recursive: true });
4366
+ atomicWriteFile(REFS_PATH, JSON.stringify(refs, null, 2) + "\n");
4239
4367
  }
4240
4368
  async function refAdd(url, memo = "") {
4241
- console.log(chalk19.bold("\n\u{1F517} " + t("ref.addTitle")));
4242
- console.log(chalk19.gray("\u2500".repeat(40)));
4369
+ if (!ensureNotHardStopped("ref add")) return;
4370
+ console.log(chalk18.bold("\n\u{1F517} " + t("ref.addTitle")));
4371
+ console.log(chalk18.gray("\u2500".repeat(40)));
4243
4372
  if (!url) {
4244
- console.log(chalk19.red("\u274C URL\uC744 \uC785\uB825\uD574\uC8FC\uC138\uC694."));
4245
- console.log(chalk19.gray(' \uC608: vhk ref add https://example.com --memo "\uCC38\uACE0 \uC0AC\uC774\uD2B8"'));
4373
+ console.log(chalk18.red("\u274C URL\uC744 \uC785\uB825\uD574\uC8FC\uC138\uC694."));
4374
+ console.log(chalk18.gray(' \uC608: vhk ref add https://example.com --memo "\uCC38\uACE0 \uC0AC\uC774\uD2B8"'));
4246
4375
  return;
4247
4376
  }
4248
4377
  const refs = loadRefs();
4249
4378
  if (refs.some((r) => r.url === url)) {
4250
- console.log(chalk19.yellow("\u26A0\uFE0F \uC774\uBBF8 \uC800\uC7A5\uB41C URL\uC785\uB2C8\uB2E4."));
4379
+ console.log(chalk18.yellow("\u26A0\uFE0F \uC774\uBBF8 \uC800\uC7A5\uB41C URL\uC785\uB2C8\uB2E4."));
4251
4380
  return;
4252
4381
  }
4253
4382
  refs.push({ url, memo, addedAt: (/* @__PURE__ */ new Date()).toISOString() });
4254
4383
  saveRefs(refs);
4255
- console.log(chalk19.green(`
4384
+ console.log(chalk18.green(`
4256
4385
  \u2705 \uB808\uD37C\uB7F0\uC2A4 \uCD94\uAC00\uB428 (#${refs.length})`));
4257
- console.log(chalk19.cyan(` ${url}`));
4258
- if (memo) console.log(chalk19.gray(` \u{1F4DD} ${memo}`));
4386
+ console.log(chalk18.cyan(` ${url}`));
4387
+ if (memo) console.log(chalk18.gray(` \u{1F4DD} ${memo}`));
4259
4388
  printNextStep({
4260
4389
  message: "\uB808\uD37C\uB7F0\uC2A4 \uC800\uC7A5 \uC644\uB8CC!",
4261
4390
  command: "vhk ref list",
@@ -4263,22 +4392,22 @@ async function refAdd(url, memo = "") {
4263
4392
  });
4264
4393
  }
4265
4394
  async function refList() {
4266
- console.log(chalk19.bold("\n\u{1F4DA} " + t("ref.listTitle")));
4267
- console.log(chalk19.gray("\u2500".repeat(40)));
4395
+ console.log(chalk18.bold("\n\u{1F4DA} " + t("ref.listTitle")));
4396
+ console.log(chalk18.gray("\u2500".repeat(40)));
4268
4397
  const refs = loadRefs();
4269
4398
  if (refs.length === 0) {
4270
- console.log(chalk19.yellow("\n\u{1F4ED} \uC800\uC7A5\uB41C \uB808\uD37C\uB7F0\uC2A4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4."));
4271
- console.log(chalk19.gray(' vhk ref add <url> --memo "\uBA54\uBAA8"\uB85C \uCD94\uAC00\uD558\uC138\uC694.'));
4399
+ console.log(chalk18.yellow("\n\u{1F4ED} \uC800\uC7A5\uB41C \uB808\uD37C\uB7F0\uC2A4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4."));
4400
+ console.log(chalk18.gray(' vhk ref add <url> --memo "\uBA54\uBAA8"\uB85C \uCD94\uAC00\uD558\uC138\uC694.'));
4272
4401
  return;
4273
4402
  }
4274
- console.log(chalk19.cyan(`
4403
+ console.log(chalk18.cyan(`
4275
4404
  \uCD1D ${refs.length}\uAC1C\uC758 \uB808\uD37C\uB7F0\uC2A4:
4276
4405
  `));
4277
4406
  refs.forEach((ref, index) => {
4278
4407
  const date = new Date(ref.addedAt).toLocaleDateString("ko-KR");
4279
- console.log(chalk19.white(` [${index + 1}] ${ref.url}`));
4280
- if (ref.memo) console.log(chalk19.gray(` \u{1F4DD} ${ref.memo}`));
4281
- console.log(chalk19.gray(` \u{1F4C5} ${date}`));
4408
+ console.log(chalk18.white(` [${index + 1}] ${ref.url}`));
4409
+ if (ref.memo) console.log(chalk18.gray(` \u{1F4DD} ${ref.memo}`));
4410
+ console.log(chalk18.gray(` \u{1F4C5} ${date}`));
4282
4411
  console.log("");
4283
4412
  });
4284
4413
  }
@@ -4286,7 +4415,7 @@ async function refOpen(indexStr) {
4286
4415
  const refs = loadRefs();
4287
4416
  const idx = parseInt(indexStr, 10) - 1;
4288
4417
  if (Number.isNaN(idx) || idx < 0 || idx >= refs.length) {
4289
- console.log(chalk19.red(`\u274C \uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uBC88\uD638\uC785\uB2C8\uB2E4. (1~${refs.length || 0})`));
4418
+ console.log(chalk18.red(`\u274C \uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uBC88\uD638\uC785\uB2C8\uB2E4. (1~${refs.length || 0})`));
4290
4419
  return;
4291
4420
  }
4292
4421
  const ref = refs[idx];
@@ -4294,14 +4423,14 @@ async function refOpen(indexStr) {
4294
4423
  try {
4295
4424
  parsed = new URL(ref.url);
4296
4425
  } catch {
4297
- console.log(chalk19.red(`\u274C \uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 URL: ${ref.url}`));
4426
+ console.log(chalk18.red(`\u274C \uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 URL: ${ref.url}`));
4298
4427
  return;
4299
4428
  }
4300
4429
  if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
4301
- console.log(chalk19.red(`\u274C http(s) URL\uB9CC \uC5F4 \uC218 \uC788\uC2B5\uB2C8\uB2E4 (${parsed.protocol})`));
4430
+ console.log(chalk18.red(`\u274C http(s) URL\uB9CC \uC5F4 \uC218 \uC788\uC2B5\uB2C8\uB2E4 (${parsed.protocol})`));
4302
4431
  return;
4303
4432
  }
4304
- console.log(chalk19.cyan(`
4433
+ console.log(chalk18.cyan(`
4305
4434
  \u{1F310} \uC5F4\uAE30: ${ref.url}`));
4306
4435
  let result;
4307
4436
  if (process.platform === "darwin") {
@@ -4312,19 +4441,19 @@ async function refOpen(indexStr) {
4312
4441
  result = safeExecFile("xdg-open", [ref.url]);
4313
4442
  }
4314
4443
  if (result.ok) {
4315
- console.log(chalk19.green("\u2705 \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uC5F4\uC5C8\uC2B5\uB2C8\uB2E4."));
4444
+ console.log(chalk18.green("\u2705 \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uC5F4\uC5C8\uC2B5\uB2C8\uB2E4."));
4316
4445
  } else {
4317
- console.log(chalk19.yellow("\u26A0\uFE0F \uBE0C\uB77C\uC6B0\uC800\uB97C \uC5F4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. URL\uC744 \uC9C1\uC811 \uBC29\uBB38\uD574\uC8FC\uC138\uC694."));
4446
+ console.log(chalk18.yellow("\u26A0\uFE0F \uBE0C\uB77C\uC6B0\uC800\uB97C \uC5F4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. URL\uC744 \uC9C1\uC811 \uBC29\uBB38\uD574\uC8FC\uC138\uC694."));
4318
4447
  }
4319
4448
  }
4320
4449
 
4321
4450
  // src/commands/harness.ts
4322
- import { existsSync as existsSync8 } from "fs";
4323
- import chalk20 from "chalk";
4451
+ import { existsSync as existsSync7 } from "fs";
4452
+ import chalk19 from "chalk";
4324
4453
  import ora2 from "ora";
4325
4454
  function detectPM() {
4326
- if (existsSync8("pnpm-lock.yaml")) return "pnpm";
4327
- if (existsSync8("yarn.lock")) return "yarn";
4455
+ if (existsSync7("pnpm-lock.yaml")) return "pnpm";
4456
+ if (existsSync7("yarn.lock")) return "yarn";
4328
4457
  return "npm";
4329
4458
  }
4330
4459
  function pmRun(pm, script) {
@@ -4342,14 +4471,14 @@ function detectChecks() {
4342
4471
  const pm = detectPM();
4343
4472
  if (s.lint) {
4344
4473
  checks.push({ name: "lint", bin: pm, args: pmRun(pm, "lint") });
4345
- } else if (existsSync8(".eslintrc.js") || existsSync8(".eslintrc.json") || existsSync8("eslint.config.js")) {
4474
+ } else if (existsSync7(".eslintrc.js") || existsSync7(".eslintrc.json") || existsSync7("eslint.config.js")) {
4346
4475
  checks.push({ name: "lint", bin: "npx", args: ["eslint", ".", "--ext", ".ts,.tsx"] });
4347
4476
  }
4348
4477
  if (s["type-check"]) {
4349
4478
  checks.push({ name: "type-check", bin: pm, args: pmRun(pm, "type-check") });
4350
4479
  } else if (s.typecheck) {
4351
4480
  checks.push({ name: "type-check", bin: pm, args: pmRun(pm, "typecheck") });
4352
- } else if (existsSync8("tsconfig.json")) {
4481
+ } else if (existsSync7("tsconfig.json")) {
4353
4482
  checks.push({ name: "type-check", bin: "npx", args: ["tsc", "--noEmit"] });
4354
4483
  }
4355
4484
  if (s.test) {
@@ -4362,32 +4491,32 @@ function detectChecks() {
4362
4491
  }
4363
4492
  async function harness() {
4364
4493
  if (!ensureNotHardStopped("harness")) return;
4365
- console.log(chalk20.bold("\n\u{1F527} " + t("harness.title")));
4366
- console.log(chalk20.gray("\u2500".repeat(40)));
4494
+ console.log(chalk19.bold("\n\u{1F527} " + t("harness.title")));
4495
+ console.log(chalk19.gray("\u2500".repeat(40)));
4367
4496
  const checks = detectChecks();
4368
4497
  if (checks.length === 0) {
4369
- console.log(chalk20.yellow("\n\u26A0\uFE0F \uC2E4\uD589\uD560 \uC218 \uC788\uB294 \uC2A4\uD06C\uB9BD\uD2B8\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4."));
4370
- console.log(chalk20.gray(" package.json\uC5D0 lint, test, build \uC2A4\uD06C\uB9BD\uD2B8\uB97C \uCD94\uAC00\uD574\uC8FC\uC138\uC694."));
4498
+ console.log(chalk19.yellow("\n\u26A0\uFE0F \uC2E4\uD589\uD560 \uC218 \uC788\uB294 \uC2A4\uD06C\uB9BD\uD2B8\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4."));
4499
+ console.log(chalk19.gray(" package.json\uC5D0 lint, test, build \uC2A4\uD06C\uB9BD\uD2B8\uB97C \uCD94\uAC00\uD574\uC8FC\uC138\uC694."));
4371
4500
  return;
4372
4501
  }
4373
- console.log(chalk20.cyan(`
4502
+ console.log(chalk19.cyan(`
4374
4503
  \u{1F3C3} ${checks.length}\uAC1C \uC810\uAC80 \uC2DC\uC791:
4375
4504
  `));
4376
4505
  const results = [];
4377
- for (const check2 of checks) {
4378
- const display = `${check2.bin} ${check2.args.join(" ")}`;
4379
- const spinner = ora2(`${check2.name} \uC2E4\uD589 \uC911...`).start();
4506
+ for (const check3 of checks) {
4507
+ const display = `${check3.bin} ${check3.args.join(" ")}`;
4508
+ const spinner = ora2(`${check3.name} \uC2E4\uD589 \uC911...`).start();
4380
4509
  const start2 = Date.now();
4381
- const result = safeExecFile(check2.bin, check2.args);
4510
+ const result = safeExecFile(check3.bin, check3.args);
4382
4511
  const duration = Date.now() - start2;
4383
4512
  const sec = (duration / 1e3).toFixed(1);
4384
4513
  if (result.ok) {
4385
- spinner.succeed(`${check2.name} ${chalk20.gray(`(${sec}s)`)}`);
4386
- results.push({ name: check2.name, command: display, passed: true, duration });
4514
+ spinner.succeed(`${check3.name} ${chalk19.gray(`(${sec}s)`)}`);
4515
+ results.push({ name: check3.name, command: display, passed: true, duration });
4387
4516
  } else {
4388
- spinner.fail(`${check2.name} ${chalk20.gray(`(${sec}s)`)}`);
4517
+ spinner.fail(`${check3.name} ${chalk19.gray(`(${sec}s)`)}`);
4389
4518
  results.push({
4390
- name: check2.name,
4519
+ name: check3.name,
4391
4520
  command: display,
4392
4521
  passed: false,
4393
4522
  duration,
@@ -4395,22 +4524,22 @@ async function harness() {
4395
4524
  });
4396
4525
  }
4397
4526
  }
4398
- console.log(chalk20.bold("\n\u{1F4CA} \uD1B5\uD569 \uB9AC\uD3EC\uD2B8:"));
4399
- console.log(chalk20.gray("\u2500".repeat(40)));
4527
+ console.log(chalk19.bold("\n\u{1F4CA} \uD1B5\uD569 \uB9AC\uD3EC\uD2B8:"));
4528
+ console.log(chalk19.gray("\u2500".repeat(40)));
4400
4529
  for (const r of results) {
4401
- const icon = r.passed ? chalk20.green("\u2705") : chalk20.red("\u274C");
4530
+ const icon = r.passed ? chalk19.green("\u2705") : chalk19.red("\u274C");
4402
4531
  const sec = (r.duration / 1e3).toFixed(1);
4403
- console.log(` ${icon} ${r.name.padEnd(15)} ${chalk20.gray(`${sec}s`)}`);
4532
+ console.log(` ${icon} ${r.name.padEnd(15)} ${chalk19.gray(`${sec}s`)}`);
4404
4533
  }
4405
4534
  const passed = results.filter((r) => r.passed).length;
4406
4535
  const all = passed === results.length;
4407
- console.log(chalk20.gray("\u2500".repeat(40)));
4536
+ console.log(chalk19.gray("\u2500".repeat(40)));
4408
4537
  if (all) {
4409
- console.log(chalk20.green.bold(`
4538
+ console.log(chalk19.green.bold(`
4410
4539
  \u{1F389} \uC804\uCCB4 \uD1B5\uACFC! (${passed}/${results.length})`));
4411
4540
  } else {
4412
4541
  console.log(
4413
- chalk20.red.bold(`
4542
+ chalk19.red.bold(`
4414
4543
  \u26A0\uFE0F ${results.length - passed}\uAC1C \uC2E4\uD328 (${passed}/${results.length} \uD1B5\uACFC)`)
4415
4544
  );
4416
4545
  process.exitCode = 1;
@@ -4423,8 +4552,8 @@ async function harness() {
4423
4552
  }
4424
4553
 
4425
4554
  // src/commands/migrate.ts
4426
- import { existsSync as existsSync9, unlinkSync, rmSync as rmSync2 } from "fs";
4427
- import chalk21 from "chalk";
4555
+ import { existsSync as existsSync8, unlinkSync, rmSync } from "fs";
4556
+ import chalk20 from "chalk";
4428
4557
  import inquirer10 from "inquirer";
4429
4558
  import ora3 from "ora";
4430
4559
  var LOCK_FILES = {
@@ -4433,19 +4562,19 @@ var LOCK_FILES = {
4433
4562
  pnpm: "pnpm-lock.yaml"
4434
4563
  };
4435
4564
  function detectCurrentPM() {
4436
- if (existsSync9("pnpm-lock.yaml")) return "pnpm";
4437
- if (existsSync9("yarn.lock")) return "yarn";
4438
- if (existsSync9("package-lock.json")) return "npm";
4565
+ if (existsSync8("pnpm-lock.yaml")) return "pnpm";
4566
+ if (existsSync8("yarn.lock")) return "yarn";
4567
+ if (existsSync8("package-lock.json")) return "npm";
4439
4568
  return null;
4440
4569
  }
4441
4570
  function isCLIAvailable(pm) {
4442
4571
  return safeExecFile(pm, ["--version"]).ok;
4443
4572
  }
4444
4573
  async function migrate(target) {
4445
- console.log(chalk21.bold("\n\u{1F504} " + t("migrate.title")));
4446
- console.log(chalk21.gray("\u2500".repeat(40)));
4574
+ console.log(chalk20.bold("\n\u{1F504} " + t("migrate.title")));
4575
+ console.log(chalk20.gray("\u2500".repeat(40)));
4447
4576
  const current = detectCurrentPM();
4448
- console.log(chalk21.cyan(`
4577
+ console.log(chalk20.cyan(`
4449
4578
  \uD604\uC7AC \uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800: ${current ?? "\uAC10\uC9C0 \uBD88\uAC00"}`));
4450
4579
  let targetPM;
4451
4580
  if (target && ["npm", "yarn", "pnpm"].includes(target)) {
@@ -4463,14 +4592,14 @@ async function migrate(target) {
4463
4592
  targetPM = selected;
4464
4593
  }
4465
4594
  if (targetPM === current) {
4466
- console.log(chalk21.yellow(`
4595
+ console.log(chalk20.yellow(`
4467
4596
  \u26A0\uFE0F \uC774\uBBF8 ${targetPM}\uC744 \uC0AC\uC6A9 \uC911\uC785\uB2C8\uB2E4.`));
4468
4597
  return;
4469
4598
  }
4470
4599
  if (!isCLIAvailable(targetPM)) {
4471
- console.log(chalk21.red(`
4600
+ console.log(chalk20.red(`
4472
4601
  \u274C ${targetPM}\uC774 \uC124\uCE58\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.`));
4473
- console.log(chalk21.yellow(` npm i -g ${targetPM}`));
4602
+ console.log(chalk20.yellow(` npm i -g ${targetPM}`));
4474
4603
  return;
4475
4604
  }
4476
4605
  const { confirm } = await inquirer10.prompt([
@@ -4482,18 +4611,18 @@ async function migrate(target) {
4482
4611
  }
4483
4612
  ]);
4484
4613
  if (!confirm) {
4485
- console.log(chalk21.gray("\uCDE8\uC18C\uB428"));
4614
+ console.log(chalk20.gray("\uCDE8\uC18C\uB428"));
4486
4615
  return;
4487
4616
  }
4488
4617
  const cleanup = ora3("\uAE30\uC874 lock \uD30C\uC77C \uC815\uB9AC \uC911...").start();
4489
4618
  for (const lockFile of Object.values(LOCK_FILES)) {
4490
- if (existsSync9(lockFile)) {
4619
+ if (existsSync8(lockFile)) {
4491
4620
  unlinkSync(lockFile);
4492
4621
  }
4493
4622
  }
4494
- if (existsSync9("node_modules")) {
4623
+ if (existsSync8("node_modules")) {
4495
4624
  cleanup.text = "node_modules \uC0AD\uC81C \uC911...";
4496
- rmSync2("node_modules", { recursive: true, force: true });
4625
+ rmSync("node_modules", { recursive: true, force: true });
4497
4626
  }
4498
4627
  cleanup.succeed("\uAE30\uC874 \uD30C\uC77C \uC815\uB9AC \uC644\uB8CC");
4499
4628
  const install = ora3(`${targetPM} install \uC2E4\uD589 \uC911...`).start();
@@ -4502,10 +4631,10 @@ async function migrate(target) {
4502
4631
  install.succeed(`${targetPM} install \uC644\uB8CC!`);
4503
4632
  } else {
4504
4633
  install.fail(`${targetPM} install \uC2E4\uD328`);
4505
- console.log(chalk21.red(installResult.err.slice(0, 300)));
4634
+ console.log(chalk20.red(installResult.err.slice(0, 300)));
4506
4635
  return;
4507
4636
  }
4508
- console.log(chalk21.green.bold(`
4637
+ console.log(chalk20.green.bold(`
4509
4638
  \u{1F389} ${current ?? "\uC774\uC804"} \u2192 ${targetPM} \uC804\uD658 \uC644\uB8CC!`));
4510
4639
  printNextStep({
4511
4640
  message: "\uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800 \uC804\uD658 \uC644\uB8CC!",
@@ -4515,17 +4644,17 @@ async function migrate(target) {
4515
4644
  }
4516
4645
 
4517
4646
  // src/commands/update.ts
4518
- import { existsSync as existsSync10 } from "fs";
4519
- import { dirname as dirname2, join as join6 } from "path";
4647
+ import { existsSync as existsSync9 } from "fs";
4648
+ import { dirname as dirname2, join as join5 } from "path";
4520
4649
  import { fileURLToPath as fileURLToPath3 } from "url";
4521
- import chalk22 from "chalk";
4650
+ import chalk21 from "chalk";
4522
4651
  import ora4 from "ora";
4523
4652
  var PACKAGE = "@byh3071/vhk";
4524
4653
  function getCurrentVersion() {
4525
4654
  const dir = dirname2(fileURLToPath3(import.meta.url));
4526
- for (const pkgPath of [join6(dir, "../package.json"), join6(dir, "../../package.json")]) {
4655
+ for (const pkgPath of [join5(dir, "../package.json"), join5(dir, "../../package.json")]) {
4527
4656
  try {
4528
- if (existsSync10(pkgPath)) {
4657
+ if (existsSync9(pkgPath)) {
4529
4658
  const pkg = readJsonFile(pkgPath);
4530
4659
  if (pkg.version) return pkg.version;
4531
4660
  }
@@ -4548,33 +4677,33 @@ function isUpToDate(current, latest) {
4548
4677
  return cc >= lc;
4549
4678
  }
4550
4679
  async function update() {
4551
- console.log(chalk22.bold("\n\u2B06\uFE0F " + t("update.title")));
4552
- console.log(chalk22.gray("\u2500".repeat(40)));
4680
+ console.log(chalk21.bold("\n\u2B06\uFE0F " + t("update.title")));
4681
+ console.log(chalk21.gray("\u2500".repeat(40)));
4553
4682
  const current = getCurrentVersion();
4554
- console.log(chalk22.cyan(`
4683
+ console.log(chalk21.cyan(`
4555
4684
  \u{1F4CC} \uD604\uC7AC \uBC84\uC804: v${current}`));
4556
4685
  const spinner = ora4("\uCD5C\uC2E0 \uBC84\uC804 \uD655\uC778 \uC911...").start();
4557
4686
  const latest = getLatestVersion();
4558
4687
  if (latest) recordLatest(latest);
4559
4688
  if (!latest) {
4560
4689
  spinner.fail("\uCD5C\uC2E0 \uBC84\uC804\uC744 \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
4561
- console.log(chalk22.yellow(" \uB124\uD2B8\uC6CC\uD06C\uB97C \uD655\uC778\uD558\uAC70\uB098 \uC218\uB3D9\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694:"));
4562
- console.log(chalk22.gray(` npm update -g ${PACKAGE}`));
4690
+ console.log(chalk21.yellow(" \uB124\uD2B8\uC6CC\uD06C\uB97C \uD655\uC778\uD558\uAC70\uB098 \uC218\uB3D9\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694:"));
4691
+ console.log(chalk21.gray(` npm update -g ${PACKAGE}`));
4563
4692
  return;
4564
4693
  }
4565
4694
  spinner.stop();
4566
- console.log(chalk22.cyan(`\u{1F195} \uCD5C\uC2E0 \uBC84\uC804: v${latest}`));
4695
+ console.log(chalk21.cyan(`\u{1F195} \uCD5C\uC2E0 \uBC84\uC804: v${latest}`));
4567
4696
  if (isUpToDate(current, latest)) {
4568
- console.log(chalk22.green("\n\u2705 \uC774\uBBF8 \uCD5C\uC2E0 \uBC84\uC804\uC785\uB2C8\uB2E4!"));
4697
+ console.log(chalk21.green("\n\u2705 \uC774\uBBF8 \uCD5C\uC2E0 \uBC84\uC804\uC785\uB2C8\uB2E4!"));
4569
4698
  return;
4570
4699
  }
4571
4700
  const updateSpinner = ora4(`v${latest}\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8 \uC911...`).start();
4572
4701
  const upd = safeExecFile("npm", ["update", "-g", PACKAGE]);
4573
4702
  if (upd.ok) {
4574
4703
  updateSpinner.succeed(`v${latest}\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC!`);
4575
- console.log(chalk22.green.bold(`
4704
+ console.log(chalk21.green.bold(`
4576
4705
  \u{1F389} VHK CLI v${latest} \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC!`));
4577
- console.log(chalk22.gray(" \uBCC0\uACBD \uC0AC\uD56D\uC740 GitHub Releases\uB97C \uD655\uC778\uD558\uC138\uC694."));
4706
+ console.log(chalk21.gray(" \uBCC0\uACBD \uC0AC\uD56D\uC740 GitHub Releases\uB97C \uD655\uC778\uD558\uC138\uC694."));
4578
4707
  printNextStep({
4579
4708
  message: t("update.nextOkMessage"),
4580
4709
  command: "vhk --version",
@@ -4582,9 +4711,9 @@ async function update() {
4582
4711
  });
4583
4712
  } else {
4584
4713
  updateSpinner.fail("\uC5C5\uB370\uC774\uD2B8 \uC2E4\uD328");
4585
- console.log(chalk22.red(upd.err.slice(0, 300)));
4586
- console.log(chalk22.yellow("\n\uC218\uB3D9\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694:"));
4587
- console.log(chalk22.gray(` npm update -g ${PACKAGE}`));
4714
+ console.log(chalk21.red(upd.err.slice(0, 300)));
4715
+ console.log(chalk21.yellow("\n\uC218\uB3D9\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694:"));
4716
+ console.log(chalk21.gray(` npm update -g ${PACKAGE}`));
4588
4717
  printNextStep({
4589
4718
  message: t("update.nextFailMessage"),
4590
4719
  command: "vhk doctor",
@@ -4595,21 +4724,21 @@ async function update() {
4595
4724
 
4596
4725
  // src/commands/context.ts
4597
4726
  import {
4598
- existsSync as existsSync12,
4599
- mkdirSync as mkdirSync8,
4600
- readFileSync as readFileSync5,
4727
+ existsSync as existsSync11,
4728
+ mkdirSync as mkdirSync7,
4729
+ readFileSync as readFileSync4,
4601
4730
  readdirSync as readdirSync2,
4602
4731
  statSync as statSync2,
4603
- writeFileSync as writeFileSync8
4732
+ writeFileSync as writeFileSync6
4604
4733
  } from "fs";
4605
- import { join as join8 } from "path";
4606
- import chalk24 from "chalk";
4607
-
4608
- // src/commands/memory.ts
4609
- import { existsSync as existsSync11, mkdirSync as mkdirSync7, writeFileSync as writeFileSync7, copyFileSync, readFileSync as readFileSync4, renameSync, rmSync as rmSync3 } from "fs";
4610
4734
  import { join as join7 } from "path";
4611
4735
  import chalk23 from "chalk";
4612
- var MEMORY_PATH_REL = join7(".vhk", "memory.json");
4736
+
4737
+ // src/commands/memory.ts
4738
+ import { existsSync as existsSync10, mkdirSync as mkdirSync6, writeFileSync as writeFileSync5, copyFileSync, readFileSync as readFileSync3, renameSync, rmSync as rmSync2 } from "fs";
4739
+ import { join as join6 } from "path";
4740
+ import chalk22 from "chalk";
4741
+ var MEMORY_PATH_REL = join6(".vhk", "memory.json");
4613
4742
  var MEMORY_SCHEMA_VERSION = 2;
4614
4743
  function emptyV2() {
4615
4744
  return { schemaVersion: MEMORY_SCHEMA_VERSION, decisions: [], failures: [], successes: [], patterns: [] };
@@ -4670,8 +4799,8 @@ function migrateMemory(rawMemory, rawLearnings) {
4670
4799
  return v2;
4671
4800
  }
4672
4801
  function readRaw(cwd) {
4673
- const p = join7(cwd, MEMORY_PATH_REL);
4674
- if (!existsSync11(p)) return { kind: "missing" };
4802
+ const p = join6(cwd, MEMORY_PATH_REL);
4803
+ if (!existsSync10(p)) return { kind: "missing" };
4675
4804
  try {
4676
4805
  return { kind: "parsed", value: readJsonFile(p) };
4677
4806
  } catch {
@@ -4679,24 +4808,24 @@ function readRaw(cwd) {
4679
4808
  }
4680
4809
  }
4681
4810
  function warnUnreadable(cwd) {
4682
- const p = join7(cwd, MEMORY_PATH_REL);
4683
- console.error(chalk23.red(`
4811
+ const p = join6(cwd, MEMORY_PATH_REL);
4812
+ console.error(chalk22.red(`
4684
4813
  \u26A0\uFE0F ${MEMORY_PATH_REL} \uB97C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uC190\uC0C1/\uBD80\uBD84 \uC4F0\uAE30 \uC758\uC2EC).`));
4685
- console.error(chalk23.yellow(` \uB36E\uC5B4\uC4F0\uC9C0 \uC54A\uACE0 \uBE48 \uBA54\uBAA8\uB9AC\uB85C \uC9C4\uD589\uD569\uB2C8\uB2E4 \u2014 \uC6D0\uBCF8 \uBCF4\uC874\uB428.`));
4686
- console.error(chalk23.dim(` \uD655\uC778/\uBCF5\uAD6C: ${p} (\uBC31\uC5C5: ${p}.bak / ${p}.v1.bak)`));
4814
+ console.error(chalk22.yellow(` \uB36E\uC5B4\uC4F0\uC9C0 \uC54A\uACE0 \uBE48 \uBA54\uBAA8\uB9AC\uB85C \uC9C4\uD589\uD569\uB2C8\uB2E4 \u2014 \uC6D0\uBCF8 \uBCF4\uC874\uB428.`));
4815
+ console.error(chalk22.dim(` \uD655\uC778/\uBCF5\uAD6C: ${p} (\uBC31\uC5C5: ${p}.bak / ${p}.v1.bak)`));
4687
4816
  }
4688
4817
  function warnUnrecognized(cwd) {
4689
- const p = join7(cwd, MEMORY_PATH_REL);
4690
- console.error(chalk23.red(`
4818
+ const p = join6(cwd, MEMORY_PATH_REL);
4819
+ console.error(chalk22.red(`
4691
4820
  \u26A0\uFE0F ${MEMORY_PATH_REL} \uAC00 \uC778\uC2DD \uAC00\uB2A5\uD55C \uD615\uC2DD\uC774 \uC544\uB2D9\uB2C8\uB2E4 (v1 \uBC30\uC5F4/v2 \uAC1D\uCCB4 \uC544\uB2D8).`));
4692
- console.error(chalk23.yellow(` \uBBF8\uB798 \uC2A4\uD0A4\uB9C8/\uC218\uB3D9 \uD3B8\uC9D1 \uC758\uC2EC \u2014 \uB36E\uC5B4\uC4F0\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4(\uC6D0\uBCF8 \uBCF4\uC874). \uD655\uC778 \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4.`));
4693
- console.error(chalk23.dim(` \uD655\uC778: ${p}`));
4821
+ console.error(chalk22.yellow(` \uBBF8\uB798 \uC2A4\uD0A4\uB9C8/\uC218\uB3D9 \uD3B8\uC9D1 \uC758\uC2EC \u2014 \uB36E\uC5B4\uC4F0\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4(\uC6D0\uBCF8 \uBCF4\uC874). \uD655\uC778 \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4.`));
4822
+ console.error(chalk22.dim(` \uD655\uC778: ${p}`));
4694
4823
  }
4695
4824
  function readLearningsRaw(cwd) {
4696
- const p = join7(cwd, "docs", "state", "learnings.md");
4697
- if (!existsSync11(p)) return void 0;
4825
+ const p = join6(cwd, "docs", "state", "learnings.md");
4826
+ if (!existsSync10(p)) return void 0;
4698
4827
  try {
4699
- return stripBom(readFileSync4(p, "utf-8"));
4828
+ return stripBom(readFileSync3(p, "utf-8"));
4700
4829
  } catch {
4701
4830
  return void 0;
4702
4831
  }
@@ -4714,7 +4843,7 @@ function readMemory(cwd = process.cwd()) {
4714
4843
  try {
4715
4844
  writeMemory(cwd, v2);
4716
4845
  } catch {
4717
- console.error(chalk23.yellow(` (v2 \uC601\uAD6C\uD654 \uBCF4\uB958 \u2014 ${MEMORY_PATH_REL} \uC7A0\uAE08 \uC758\uC2EC. \uC774\uBC88\uC5D4 \uBA54\uBAA8\uB9AC\uC0C1\uC73C\uB85C\uB9CC \uC9C4\uD589)`));
4846
+ console.error(chalk22.yellow(` (v2 \uC601\uAD6C\uD654 \uBCF4\uB958 \u2014 ${MEMORY_PATH_REL} \uC7A0\uAE08 \uC758\uC2EC. \uC774\uBC88\uC5D4 \uBA54\uBAA8\uB9AC\uC0C1\uC73C\uB85C\uB9CC \uC9C4\uD589)`));
4718
4847
  }
4719
4848
  return v2;
4720
4849
  }
@@ -4741,12 +4870,12 @@ function isActive(e) {
4741
4870
  return e.status !== "archived" && e.status !== "resolved";
4742
4871
  }
4743
4872
  function writeMemory(cwd, mem) {
4744
- const p = join7(cwd, MEMORY_PATH_REL);
4745
- mkdirSync7(join7(cwd, ".vhk"), { recursive: true });
4746
- if (existsSync11(p)) {
4873
+ const p = join6(cwd, MEMORY_PATH_REL);
4874
+ mkdirSync6(join6(cwd, ".vhk"), { recursive: true });
4875
+ if (existsSync10(p)) {
4747
4876
  const cur = readRaw(cwd);
4748
4877
  const curIsV2 = cur.kind === "parsed" && isV2(cur.value);
4749
- if (cur.kind !== "error" && !curIsV2 && !existsSync11(p + ".v1.bak")) {
4878
+ if (cur.kind !== "error" && !curIsV2 && !existsSync10(p + ".v1.bak")) {
4750
4879
  try {
4751
4880
  copyFileSync(p, p + ".v1.bak");
4752
4881
  } catch {
@@ -4760,12 +4889,12 @@ function writeMemory(cwd, mem) {
4760
4889
  }
4761
4890
  }
4762
4891
  const tmpPath = p + ".tmp";
4763
- writeFileSync7(tmpPath, JSON.stringify(mem, null, 2) + "\n", "utf-8");
4892
+ writeFileSync5(tmpPath, JSON.stringify(mem, null, 2) + "\n", "utf-8");
4764
4893
  try {
4765
4894
  renameSync(tmpPath, p);
4766
4895
  } catch (err) {
4767
4896
  try {
4768
- rmSync3(tmpPath, { force: true });
4897
+ rmSync2(tmpPath, { force: true });
4769
4898
  } catch {
4770
4899
  }
4771
4900
  throw err;
@@ -4791,28 +4920,29 @@ function orderedAll(mem) {
4791
4920
  }
4792
4921
  var VALID_BUCKETS = ["decision", "failure", "success"];
4793
4922
  async function memoryAdd(content, opts = {}) {
4794
- console.log(chalk23.bold("\n\u{1F9E0} " + t("memory.addTitle")));
4795
- console.log(chalk23.gray("\u2500".repeat(40)));
4923
+ if (!ensureNotHardStopped("memory add")) return;
4924
+ console.log(chalk22.bold("\n\u{1F9E0} " + t("memory.addTitle")));
4925
+ console.log(chalk22.gray("\u2500".repeat(40)));
4796
4926
  if (!content || !content.trim()) {
4797
- console.log(chalk23.red("\u274C \uAE30\uC5B5\uD560 \uB0B4\uC6A9\uC744 \uC785\uB825\uD574\uC8FC\uC138\uC694."));
4798
- console.log(chalk23.gray(' \uC608: vhk memory add "API\uB294 tRPC \uC0AC\uC6A9" --type decision'));
4927
+ console.log(chalk22.red("\u274C \uAE30\uC5B5\uD560 \uB0B4\uC6A9\uC744 \uC785\uB825\uD574\uC8FC\uC138\uC694."));
4928
+ console.log(chalk22.gray(' \uC608: vhk memory add "API\uB294 tRPC \uC0AC\uC6A9" --type decision'));
4799
4929
  process.exitCode = 1;
4800
4930
  return;
4801
4931
  }
4802
4932
  const typeRaw = opts.type ?? "decision";
4803
4933
  if (!VALID_BUCKETS.includes(typeRaw)) {
4804
- console.log(chalk23.red(`\u274C --type \uC740 decision|failure|success \uC911 \uD558\uB098\uC5EC\uC57C \uD569\uB2C8\uB2E4 (\uBC1B\uC740 \uAC12: ${typeRaw}).`));
4934
+ console.log(chalk22.red(`\u274C --type \uC740 decision|failure|success \uC911 \uD558\uB098\uC5EC\uC57C \uD569\uB2C8\uB2E4 (\uBC1B\uC740 \uAC12: ${typeRaw}).`));
4805
4935
  process.exitCode = 1;
4806
4936
  return;
4807
4937
  }
4808
4938
  const type = typeRaw;
4809
4939
  if (type === "decision" && (opts.why || opts.lesson)) {
4810
- console.log(chalk23.yellow("\u26A0\uFE0F --why/--lesson \uC740 --type failure|success \uC5D0\uC11C\uB9CC \uC800\uC7A5\uB429\uB2C8\uB2E4 \u2014 decision \uC5D0\uC11C\uB294 \uBB34\uC2DC\uB428."));
4940
+ console.log(chalk22.yellow("\u26A0\uFE0F --why/--lesson \uC740 --type failure|success \uC5D0\uC11C\uB9CC \uC800\uC7A5\uB429\uB2C8\uB2E4 \u2014 decision \uC5D0\uC11C\uB294 \uBB34\uC2DC\uB428."));
4811
4941
  }
4812
4942
  const cwd = process.cwd();
4813
4943
  const loaded = loadForMutation(cwd);
4814
4944
  if (!loaded.ok) {
4815
- console.log(chalk23.red("\u274C memory.json \uC190\uC0C1 \uC758\uC2EC \u2014 \uC800\uC7A5 \uC911\uB2E8 (\uC6D0\uBCF8 \uBCF4\uC874). \uBC31\uC5C5 \uD655\uC778 \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694."));
4945
+ console.log(chalk22.red("\u274C memory.json \uC190\uC0C1 \uC758\uC2EC \u2014 \uC800\uC7A5 \uC911\uB2E8 (\uC6D0\uBCF8 \uBCF4\uC874). \uBC31\uC5C5 \uD655\uC778 \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694."));
4816
4946
  process.exitCode = 1;
4817
4947
  return;
4818
4948
  }
@@ -4828,34 +4958,34 @@ async function memoryAdd(content, opts = {}) {
4828
4958
  else if (type === "success") mem.successes.push({ ...base, why: opts.why });
4829
4959
  else mem.decisions.push(base);
4830
4960
  writeMemory(cwd, mem);
4831
- console.log(chalk23.green(`
4961
+ console.log(chalk22.green(`
4832
4962
  \u2705 \uAE30\uC5B5 \uC800\uC7A5\uB428 (${type} #${base.id})`));
4833
- console.log(chalk23.cyan(` \u{1F4DD} ${base.content}`));
4963
+ console.log(chalk22.cyan(` \u{1F4DD} ${base.content}`));
4834
4964
  printNextStep({ message: "\uAE30\uC5B5 \uC800\uC7A5 \uC644\uB8CC!", command: "vhk memory list", cursorHint: "\uAE30\uC5B5 \uBAA9\uB85D \uBCF4\uC5EC\uC918" });
4835
4965
  }
4836
4966
  var STATUS_ICON2 = { active: "\u{1F7E2}", resolved: "\u2705", archived: "\u{1F4E6}" };
4837
4967
  var BUCKET_LABEL = { decision: "\uACB0\uC815", failure: "\uC2E4\uD328", success: "\uC131\uACF5" };
4838
4968
  async function memoryList(opts = {}) {
4839
- console.log(chalk23.bold("\n\u{1F9E0} " + t("memory.listTitle")));
4840
- console.log(chalk23.gray("\u2500".repeat(40)));
4969
+ console.log(chalk22.bold("\n\u{1F9E0} " + t("memory.listTitle")));
4970
+ console.log(chalk22.gray("\u2500".repeat(40)));
4841
4971
  const mem = readMemory(process.cwd());
4842
4972
  const all = orderedAll(mem);
4843
4973
  const visible = all.map((x, i) => ({ ...x, n: i + 1 })).filter((x) => (opts.all || isActive(x.entry)) && (!opts.type || x.bucket === opts.type));
4844
4974
  if (visible.length === 0) {
4845
- console.log(chalk23.yellow("\n\u{1F4ED} \uD45C\uC2DC\uD560 \uAE30\uC5B5\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
4846
- console.log(chalk23.gray(' vhk memory add "\uB0B4\uC6A9" --type decision|failure|success'));
4975
+ console.log(chalk22.yellow("\n\u{1F4ED} \uD45C\uC2DC\uD560 \uAE30\uC5B5\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
4976
+ console.log(chalk22.gray(' vhk memory add "\uB0B4\uC6A9" --type decision|failure|success'));
4847
4977
  return;
4848
4978
  }
4849
- console.log(chalk23.cyan(`
4979
+ console.log(chalk22.cyan(`
4850
4980
  ${visible.length}\uAC1C${opts.all ? " (\uBCF4\uAD00 \uD3EC\uD568)" : " (\uD65C\uC131)"}:
4851
4981
  `));
4852
4982
  for (const x of visible) {
4853
4983
  const e = x.entry;
4854
4984
  const fail = e;
4855
4985
  console.log(` [${x.n}] ${STATUS_ICON2[e.status] ?? "\u{1F7E2}"} (${BUCKET_LABEL[x.bucket]}) ${e.content || (fail.lesson ? "\u{1F4A1} " + fail.lesson : "(\uB0B4\uC6A9 \uC5C6\uC74C)")}`);
4856
- if (fail.lesson && e.content) console.log(chalk23.dim(` \u{1F4A1} \uAD50\uD6C8: ${fail.lesson}`));
4857
- if (fail.why) console.log(chalk23.dim(` \u21B3 ${fail.why}`));
4858
- if (e.tags.length > 0) console.log(chalk23.blue(` \u{1F3F7}\uFE0F ${e.tags.join(", ")}`));
4986
+ if (fail.lesson && e.content) console.log(chalk22.dim(` \u{1F4A1} \uAD50\uD6C8: ${fail.lesson}`));
4987
+ if (fail.why) console.log(chalk22.dim(` \u21B3 ${fail.why}`));
4988
+ if (e.tags.length > 0) console.log(chalk22.blue(` \u{1F3F7}\uFE0F ${e.tags.join(", ")}`));
4859
4989
  }
4860
4990
  }
4861
4991
  function resolveIndex(indexStr, len) {
@@ -4864,10 +4994,11 @@ function resolveIndex(indexStr, len) {
4864
4994
  return idx;
4865
4995
  }
4866
4996
  async function memoryRemove(indexStr) {
4997
+ if (!ensureNotHardStopped("memory remove")) return;
4867
4998
  const cwd = process.cwd();
4868
4999
  const loaded = loadForMutation(cwd);
4869
5000
  if (!loaded.ok) {
4870
- console.log(chalk23.red("\u274C memory.json \uC190\uC0C1 \uC758\uC2EC \u2014 \uC0AD\uC81C \uC911\uB2E8 (\uC6D0\uBCF8 \uBCF4\uC874)."));
5001
+ console.log(chalk22.red("\u274C memory.json \uC190\uC0C1 \uC758\uC2EC \u2014 \uC0AD\uC81C \uC911\uB2E8 (\uC6D0\uBCF8 \uBCF4\uC874)."));
4871
5002
  process.exitCode = 1;
4872
5003
  return;
4873
5004
  }
@@ -4875,7 +5006,7 @@ async function memoryRemove(indexStr) {
4875
5006
  const all = orderedAll(mem);
4876
5007
  const idx = resolveIndex(indexStr, all.length);
4877
5008
  if (idx === null) {
4878
- console.log(chalk23.red(`\u274C \uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uBC88\uD638\uC785\uB2C8\uB2E4. (1~${all.length || 0})`));
5009
+ console.log(chalk22.red(`\u274C \uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uBC88\uD638\uC785\uB2C8\uB2E4. (1~${all.length || 0})`));
4879
5010
  process.exitCode = 1;
4880
5011
  return;
4881
5012
  }
@@ -4884,14 +5015,14 @@ async function memoryRemove(indexStr) {
4884
5015
  const pos = list.findIndex((e) => e === entry);
4885
5016
  if (pos >= 0) list.splice(pos, 1);
4886
5017
  writeMemory(cwd, mem);
4887
- console.log(chalk23.green("\n\u2705 \uAE30\uC5B5 \uC0AD\uC81C\uB428:"));
4888
- console.log(chalk23.gray(` ${entry.content || entry.lesson || entry.id}`));
5018
+ console.log(chalk22.green("\n\u2705 \uAE30\uC5B5 \uC0AD\uC81C\uB428:"));
5019
+ console.log(chalk22.gray(` ${entry.content || entry.lesson || entry.id}`));
4889
5020
  }
4890
5021
  function resolveEntryForMutation(indexStr) {
4891
5022
  const cwd = process.cwd();
4892
5023
  const loaded = loadForMutation(cwd);
4893
5024
  if (!loaded.ok) {
4894
- console.log(chalk23.red("\u274C memory.json \uC190\uC0C1 \uC758\uC2EC \u2014 \uC791\uC5C5 \uC911\uB2E8 (\uC6D0\uBCF8 \uBCF4\uC874)."));
5025
+ console.log(chalk22.red("\u274C memory.json \uC190\uC0C1 \uC758\uC2EC \u2014 \uC791\uC5C5 \uC911\uB2E8 (\uC6D0\uBCF8 \uBCF4\uC874)."));
4895
5026
  process.exitCode = 1;
4896
5027
  return null;
4897
5028
  }
@@ -4899,7 +5030,7 @@ function resolveEntryForMutation(indexStr) {
4899
5030
  const all = orderedAll(mem);
4900
5031
  const idx = resolveIndex(indexStr, all.length);
4901
5032
  if (idx === null) {
4902
- console.log(chalk23.red(`\u274C \uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uBC88\uD638\uC785\uB2C8\uB2E4. (1~${all.length || 0})`));
5033
+ console.log(chalk22.red(`\u274C \uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uBC88\uD638\uC785\uB2C8\uB2E4. (1~${all.length || 0})`));
4903
5034
  process.exitCode = 1;
4904
5035
  return null;
4905
5036
  }
@@ -4909,39 +5040,42 @@ function entryLabel(entry) {
4909
5040
  return entry.content || entry.lesson || entry.id;
4910
5041
  }
4911
5042
  async function memoryArchive(indexStr) {
5043
+ if (!ensureNotHardStopped("memory archive")) return;
4912
5044
  const r = resolveEntryForMutation(indexStr);
4913
5045
  if (!r) return;
4914
5046
  r.entry.status = "archived";
4915
5047
  r.entry.archivedAt = (/* @__PURE__ */ new Date()).toISOString();
4916
5048
  delete r.entry.resolvedAt;
4917
5049
  writeMemory(r.cwd, r.mem);
4918
- console.log(chalk23.green(`
5050
+ console.log(chalk22.green(`
4919
5051
  \u{1F4E6} \uBCF4\uAD00\uB428: ${entryLabel(r.entry)}`));
4920
- console.log(chalk23.dim(" (\uD328\uD134 \uAC10\uC9C0\xB7\uC9C4\uD654\uC5D0\uC11C \uC81C\uC678\uB429\uB2C8\uB2E4 \u2014 \uC120\uC21C\uD658). \uB418\uB3CC\uB9AC\uAE30: vhk memory unarchive <\uBC88\uD638>"));
5052
+ console.log(chalk22.dim(" (\uD328\uD134 \uAC10\uC9C0\xB7\uC9C4\uD654\uC5D0\uC11C \uC81C\uC678\uB429\uB2C8\uB2E4 \u2014 \uC120\uC21C\uD658). \uB418\uB3CC\uB9AC\uAE30: vhk memory unarchive <\uBC88\uD638>"));
4921
5053
  }
4922
5054
  async function memoryResolve(indexStr) {
5055
+ if (!ensureNotHardStopped("memory resolve")) return;
4923
5056
  const r = resolveEntryForMutation(indexStr);
4924
5057
  if (!r) return;
4925
5058
  r.entry.status = "resolved";
4926
5059
  r.entry.resolvedAt = (/* @__PURE__ */ new Date()).toISOString();
4927
5060
  delete r.entry.archivedAt;
4928
5061
  writeMemory(r.cwd, r.mem);
4929
- console.log(chalk23.green(`
5062
+ console.log(chalk22.green(`
4930
5063
  \u2705 \uD574\uACB0\uB428: ${entryLabel(r.entry)}`));
4931
- console.log(chalk23.dim(" (vhk memory list --all \uB85C \uD655\uC778. \uB418\uB3CC\uB9AC\uAE30: vhk memory unarchive <\uBC88\uD638>)"));
5064
+ console.log(chalk22.dim(" (vhk memory list --all \uB85C \uD655\uC778. \uB418\uB3CC\uB9AC\uAE30: vhk memory unarchive <\uBC88\uD638>)"));
4932
5065
  }
4933
5066
  async function memoryUnarchive(indexStr) {
5067
+ if (!ensureNotHardStopped("memory unarchive")) return;
4934
5068
  const r = resolveEntryForMutation(indexStr);
4935
5069
  if (!r) return;
4936
5070
  if (isActive(r.entry)) {
4937
- console.log(chalk23.dim(` \uC774\uBBF8 \uD65C\uC131 \uD56D\uBAA9\uC785\uB2C8\uB2E4 \u2014 \uBCC0\uACBD \uC5C6\uC74C: ${entryLabel(r.entry)}`));
5071
+ console.log(chalk22.dim(` \uC774\uBBF8 \uD65C\uC131 \uD56D\uBAA9\uC785\uB2C8\uB2E4 \u2014 \uBCC0\uACBD \uC5C6\uC74C: ${entryLabel(r.entry)}`));
4938
5072
  return;
4939
5073
  }
4940
5074
  r.entry.status = "active";
4941
5075
  delete r.entry.archivedAt;
4942
5076
  delete r.entry.resolvedAt;
4943
5077
  writeMemory(r.cwd, r.mem);
4944
- console.log(chalk23.green(`
5078
+ console.log(chalk22.green(`
4945
5079
  \u{1F7E2} \uD65C\uC131\uC73C\uB85C \uBCF5\uAD6C\uB428: ${entryLabel(r.entry)}`));
4946
5080
  }
4947
5081
  async function memoryMigrate() {
@@ -4949,33 +5083,33 @@ async function memoryMigrate() {
4949
5083
  const raw = readRaw(cwd);
4950
5084
  if (raw.kind === "error") {
4951
5085
  warnUnreadable(cwd);
4952
- console.log(chalk23.red(" \u274C \uB9C8\uC774\uADF8\uB808\uC774\uC158 \uC911\uB2E8 (\uC190\uC0C1 \uC758\uC2EC). \uC6D0\uBCF8 \uD655\uC778 \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694."));
5086
+ console.log(chalk22.red(" \u274C \uB9C8\uC774\uADF8\uB808\uC774\uC158 \uC911\uB2E8 (\uC190\uC0C1 \uC758\uC2EC). \uC6D0\uBCF8 \uD655\uC778 \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694."));
4953
5087
  process.exitCode = 1;
4954
5088
  return;
4955
5089
  }
4956
5090
  if (raw.kind === "parsed" && isV2(raw.value)) {
4957
- console.log(chalk23.dim(" \uC774\uBBF8 memory schema v2 \uC785\uB2C8\uB2E4 \u2014 \uBCC0\uACBD \uC5C6\uC74C(\uBA71\uB4F1)."));
5091
+ console.log(chalk22.dim(" \uC774\uBBF8 memory schema v2 \uC785\uB2C8\uB2E4 \u2014 \uBCC0\uACBD \uC5C6\uC74C(\uBA71\uB4F1)."));
4958
5092
  return;
4959
5093
  }
4960
5094
  if (raw.kind === "parsed" && !Array.isArray(raw.value)) {
4961
5095
  warnUnrecognized(cwd);
4962
- console.log(chalk23.red(" \u274C v1(\uD3C9\uBA74 \uBC30\uC5F4) \uD615\uC2DD\uC774 \uC544\uB2C8\uB77C \uB9C8\uC774\uADF8\uB808\uC774\uC158 \uB300\uC0C1\uC774 \uC544\uB2D9\uB2C8\uB2E4 \u2014 \uC911\uB2E8(\uC6D0\uBCF8 \uBCF4\uC874)."));
5096
+ console.log(chalk22.red(" \u274C v1(\uD3C9\uBA74 \uBC30\uC5F4) \uD615\uC2DD\uC774 \uC544\uB2C8\uB77C \uB9C8\uC774\uADF8\uB808\uC774\uC158 \uB300\uC0C1\uC774 \uC544\uB2D9\uB2C8\uB2E4 \u2014 \uC911\uB2E8(\uC6D0\uBCF8 \uBCF4\uC874)."));
4963
5097
  process.exitCode = 1;
4964
5098
  return;
4965
5099
  }
4966
5100
  const learnings = readLearningsRaw(cwd);
4967
5101
  const hadFile = raw.kind === "parsed";
4968
5102
  if (raw.kind === "missing" && !learnings) {
4969
- console.log(chalk23.yellow(" \u2139\uFE0F \uB9C8\uC774\uADF8\uB808\uC774\uC158\uD560 v1 memory.json / learnings.md \uAC00 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uBCC0\uACBD \uC5C6\uC74C."));
5103
+ console.log(chalk22.yellow(" \u2139\uFE0F \uB9C8\uC774\uADF8\uB808\uC774\uC158\uD560 v1 memory.json / learnings.md \uAC00 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uBCC0\uACBD \uC5C6\uC74C."));
4970
5104
  return;
4971
5105
  }
4972
5106
  const v2 = migrateMemory(raw.kind === "parsed" ? raw.value : null, learnings);
4973
5107
  writeMemory(cwd, v2);
4974
5108
  const backupNote = hadFile ? " (.v1.bak \uC6D0\uBCF8 \uC601\uAD6C \uBC31\uC5C5)" : " (\uC2E0\uADDC \uC0DD\uC131 \u2014 \uC6D0\uBCF8 \uC5C6\uC74C, \uBC31\uC5C5 \uC5C6\uC74C)";
4975
- console.log(chalk23.green(`
5109
+ console.log(chalk22.green(`
4976
5110
  \u2705 memory.json \u2192 v2 \uB9C8\uC774\uADF8\uB808\uC774\uC158 \uC644\uB8CC${backupNote}`));
4977
5111
  console.log(
4978
- chalk23.dim(
5112
+ chalk22.dim(
4979
5113
  ` decisions ${v2.decisions.length} \xB7 failures ${v2.failures.length} \xB7 successes ${v2.successes.length}` + (learnings ? " (learnings.md \uAD50\uD6C8 \uD761\uC218 \u2014 \uC774\uD6C4 vhk learn \uC740 memory \uC5D0 \uAE30\uB85D)" : "")
4980
5114
  )
4981
5115
  );
@@ -5045,7 +5179,7 @@ function buildTree(dir, prefix = "", maxDepth = 3, depth = 0) {
5045
5179
  filtered.forEach((entry, index) => {
5046
5180
  const isLast = index === filtered.length - 1;
5047
5181
  const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
5048
- const fullPath = join8(dir, entry);
5182
+ const fullPath = join7(dir, entry);
5049
5183
  const stat = statSync2(fullPath);
5050
5184
  const isDir = stat.isDirectory();
5051
5185
  lines.push(`${prefix}${connector}${entry}${isDir ? "/" : ""}`);
@@ -5077,8 +5211,8 @@ function extractTechStack() {
5077
5211
  else if (all.jest) stack["\uD14C\uC2A4\uD2B8"] = "jest";
5078
5212
  if (all.commander) stack["CLI"] = "commander";
5079
5213
  if (all.inquirer) stack["\uC778\uD130\uB799\uD2F0\uBE0C"] = "inquirer";
5080
- if (existsSync12("pnpm-lock.yaml")) stack["\uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800"] = "pnpm";
5081
- else if (existsSync12("yarn.lock")) stack["\uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800"] = "yarn";
5214
+ if (existsSync11("pnpm-lock.yaml")) stack["\uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800"] = "pnpm";
5215
+ else if (existsSync11("yarn.lock")) stack["\uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800"] = "yarn";
5082
5216
  else stack["\uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800"] = "npm";
5083
5217
  if (pkg.name) stack["\uD328\uD0A4\uC9C0 \uC774\uB984"] = pkg.name;
5084
5218
  if (pkg.version) stack["\uBC84\uC804"] = pkg.version;
@@ -5091,8 +5225,8 @@ function getVhkCommands() {
5091
5225
  }
5092
5226
  async function context(opts = {}) {
5093
5227
  const compact = opts.compact === true;
5094
- console.log(chalk24.bold("\n\u{1F9E0} " + t("context.title")));
5095
- console.log(chalk24.gray("\u2500".repeat(40)));
5228
+ console.log(chalk23.bold("\n\u{1F9E0} " + t("context.title")));
5229
+ console.log(chalk23.gray("\u2500".repeat(40)));
5096
5230
  const stack = extractTechStack();
5097
5231
  const tree = buildTree(".", "", compact ? 2 : 3).join("\n");
5098
5232
  const commands = getVhkCommands();
@@ -5196,12 +5330,12 @@ async function context(opts = {}) {
5196
5330
  } catch {
5197
5331
  }
5198
5332
  lines.push("");
5199
- mkdirSync8(".vhk", { recursive: true });
5200
- writeFileSync8(CONTEXT_PATH, lines.join("\n"), "utf-8");
5201
- console.log(chalk24.green(`
5333
+ mkdirSync7(".vhk", { recursive: true });
5334
+ writeFileSync6(CONTEXT_PATH, lines.join("\n"), "utf-8");
5335
+ console.log(chalk23.green(`
5202
5336
  \u2705 ${CONTEXT_PATH} \uC0DD\uC131 \uC644\uB8CC!`));
5203
- console.log(chalk24.gray(` \uAE30\uC220 \uC2A4\uD0DD ${Object.keys(stack).length}\uAC1C \uAC10\uC9C0`));
5204
- console.log(chalk24.gray(" AI \uC5B4\uC2DC\uC2A4\uD134\uD2B8\uC5D0\uAC8C \uC774 \uD30C\uC77C\uC744 \uCC38\uC870\uD558\uAC8C \uD558\uC138\uC694."));
5337
+ console.log(chalk23.gray(` \uAE30\uC220 \uC2A4\uD0DD ${Object.keys(stack).length}\uAC1C \uAC10\uC9C0`));
5338
+ console.log(chalk23.gray(" AI \uC5B4\uC2DC\uC2A4\uD134\uD2B8\uC5D0\uAC8C \uC774 \uD30C\uC77C\uC744 \uCC38\uC870\uD558\uAC8C \uD558\uC138\uC694."));
5205
5339
  printNextStep({
5206
5340
  message: "\uCEE8\uD14D\uC2A4\uD2B8 \uD30C\uC77C \uC0DD\uC131 \uC644\uB8CC!",
5207
5341
  command: "vhk context-show",
@@ -5209,33 +5343,33 @@ async function context(opts = {}) {
5209
5343
  });
5210
5344
  }
5211
5345
  async function contextShow() {
5212
- console.log(chalk24.bold("\n\u{1F4C4} " + t("context.showTitle")));
5213
- console.log(chalk24.gray("\u2500".repeat(40)));
5214
- if (!existsSync12(CONTEXT_PATH)) {
5215
- console.log(chalk24.yellow("\n\u26A0\uFE0F \uCEE8\uD14D\uC2A4\uD2B8 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
5216
- console.log(chalk24.gray(" vhk context\uB97C \uBA3C\uC800 \uC2E4\uD589\uD558\uC138\uC694."));
5346
+ console.log(chalk23.bold("\n\u{1F4C4} " + t("context.showTitle")));
5347
+ console.log(chalk23.gray("\u2500".repeat(40)));
5348
+ if (!existsSync11(CONTEXT_PATH)) {
5349
+ console.log(chalk23.yellow("\n\u26A0\uFE0F \uCEE8\uD14D\uC2A4\uD2B8 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
5350
+ console.log(chalk23.gray(" vhk context\uB97C \uBA3C\uC800 \uC2E4\uD589\uD558\uC138\uC694."));
5217
5351
  return;
5218
5352
  }
5219
- const content = readFileSync5(CONTEXT_PATH, "utf-8");
5353
+ const content = readFileSync4(CONTEXT_PATH, "utf-8");
5220
5354
  console.log("\n" + content);
5221
5355
  }
5222
5356
 
5223
5357
  // src/commands/brief.ts
5224
- import { existsSync as existsSync13, mkdirSync as mkdirSync9, writeFileSync as writeFileSync9, readFileSync as readFileSync6 } from "fs";
5225
- import chalk25 from "chalk";
5358
+ import { existsSync as existsSync12, mkdirSync as mkdirSync8, writeFileSync as writeFileSync7, readFileSync as readFileSync5 } from "fs";
5359
+ import chalk24 from "chalk";
5226
5360
  var BRIEF_PATH = ".vhk/brief.md";
5227
5361
  function readProjectIdentity() {
5228
5362
  const out = {};
5229
5363
  try {
5230
- if (existsSync13("RULES.md")) {
5231
- const r = readFileSync6("RULES.md", "utf-8");
5364
+ if (existsSync12("RULES.md")) {
5365
+ const r = readFileSync5("RULES.md", "utf-8");
5232
5366
  const m = r.split("\n")[0].match(/^#\s*(.+?)(?:\s*—.*)?$/);
5233
5367
  if (m) out.name = m[1].trim();
5234
5368
  const d = r.match(/한 줄 설명:\s*(.+)/);
5235
5369
  if (d) out.description = d[1].trim();
5236
5370
  }
5237
- if (!out.name && existsSync13("CLAUDE.md")) {
5238
- const m = readFileSync6("CLAUDE.md", "utf-8").match(/#\s*기록 규칙\s*\((.+?)\)/);
5371
+ if (!out.name && existsSync12("CLAUDE.md")) {
5372
+ const m = readFileSync5("CLAUDE.md", "utf-8").match(/#\s*기록 규칙\s*\((.+?)\)/);
5239
5373
  if (m) out.name = m[1].trim();
5240
5374
  }
5241
5375
  } catch {
@@ -5247,8 +5381,8 @@ function git2(args) {
5247
5381
  return result.ok ? result.out : "";
5248
5382
  }
5249
5383
  async function brief() {
5250
- console.log(chalk25.bold("\n\u{1F4CB} " + t("brief.title")));
5251
- console.log(chalk25.gray("\u2500".repeat(40)));
5384
+ console.log(chalk24.bold("\n\u{1F4CB} " + t("brief.title")));
5385
+ console.log(chalk24.gray("\u2500".repeat(40)));
5252
5386
  const lines = [];
5253
5387
  lines.push("# \uD504\uB85C\uC81D\uD2B8 \uBE0C\uB9AC\uD551");
5254
5388
  lines.push("");
@@ -5294,7 +5428,7 @@ async function brief() {
5294
5428
  }
5295
5429
  } catch {
5296
5430
  }
5297
- if (existsSync13(".vhk/refs.json")) {
5431
+ if (existsSync12(".vhk/refs.json")) {
5298
5432
  try {
5299
5433
  const refs = readJsonFile(".vhk/refs.json");
5300
5434
  if (Array.isArray(refs) && refs.length > 0) {
@@ -5322,10 +5456,10 @@ async function brief() {
5322
5456
  lines.push("");
5323
5457
  lines.push("_VHK CLI \uBE0C\uB9AC\uD551_");
5324
5458
  lines.push("");
5325
- mkdirSync9(".vhk", { recursive: true });
5326
- writeFileSync9(BRIEF_PATH, lines.join("\n"), "utf-8");
5459
+ mkdirSync8(".vhk", { recursive: true });
5460
+ writeFileSync7(BRIEF_PATH, lines.join("\n"), "utf-8");
5327
5461
  console.log("\n" + lines.join("\n"));
5328
- console.log(chalk25.green(`
5462
+ console.log(chalk24.green(`
5329
5463
  \u2705 ${BRIEF_PATH} \uC800\uC7A5 \uC644\uB8CC`));
5330
5464
  printNextStep({
5331
5465
  message: "\uBE0C\uB9AC\uD551 \uC0DD\uC131 \uC644\uB8CC!",
@@ -5335,9 +5469,9 @@ async function brief() {
5335
5469
  }
5336
5470
 
5337
5471
  // src/commands/work.ts
5338
- import { existsSync as existsSync14, mkdirSync as mkdirSync10, writeFileSync as writeFileSync10, readFileSync as readFileSync7 } from "fs";
5339
- import { join as join9 } from "path";
5340
- import chalk26 from "chalk";
5472
+ import { existsSync as existsSync13, mkdirSync as mkdirSync9, writeFileSync as writeFileSync8, readFileSync as readFileSync6 } from "fs";
5473
+ import { join as join8 } from "path";
5474
+ import chalk25 from "chalk";
5341
5475
 
5342
5476
  // src/lib/clipboard.ts
5343
5477
  import { spawnSync } from "child_process";
@@ -5366,26 +5500,26 @@ function runWithInput(cmd, args, input) {
5366
5500
  }
5367
5501
 
5368
5502
  // src/commands/work.ts
5369
- var VHK_DIR2 = ".vhk";
5503
+ var VHK_DIR = ".vhk";
5370
5504
  function gitShort() {
5371
5505
  const r = safeExecFile("git", ["status", "--short"]);
5372
5506
  return r.ok ? r.out.trim() : "";
5373
5507
  }
5374
5508
  function ensureVhkProject() {
5375
- if (existsSync14("CLAUDE.md") || existsSync14(VHK_DIR2) || existsSync14("goals")) return true;
5376
- console.log(chalk26.yellow(" \u26A0\uFE0F \uC5EC\uAE30\uB294 VHK \uD504\uB85C\uC81D\uD2B8 \uD3F4\uB354\uAC00 \uC544\uB2CC \uAC83 \uAC19\uC544\uC694."));
5377
- console.log(chalk26.dim(" VHK \uD504\uB85C\uC81D\uD2B8 \uD3F4\uB354\uC5D0\uC11C \uC2E4\uD589\uD558\uAC70\uB098, vhk start \uB85C \uC0C8\uB85C \uC2DC\uC791\uD558\uC138\uC694."));
5509
+ if (existsSync13("CLAUDE.md") || existsSync13(VHK_DIR) || existsSync13("goals")) return true;
5510
+ console.log(chalk25.yellow(" \u26A0\uFE0F \uC5EC\uAE30\uB294 VHK \uD504\uB85C\uC81D\uD2B8 \uD3F4\uB354\uAC00 \uC544\uB2CC \uAC83 \uAC19\uC544\uC694."));
5511
+ console.log(chalk25.dim(" VHK \uD504\uB85C\uC81D\uD2B8 \uD3F4\uB354\uC5D0\uC11C \uC2E4\uD589\uD558\uAC70\uB098, vhk start \uB85C \uC0C8\uB85C \uC2DC\uC791\uD558\uC138\uC694."));
5378
5512
  return false;
5379
5513
  }
5380
5514
  function passHardStop() {
5381
5515
  if (!isHardStopActive()) {
5382
- console.log(chalk26.green(" \u2705 HARD_STOP \uC5C6\uC74C"));
5516
+ console.log(chalk25.green(" \u2705 HARD_STOP \uC5C6\uC74C"));
5383
5517
  return true;
5384
5518
  }
5385
- console.log(chalk26.red.bold("\n\u{1F6D1} HARD STOP \uD65C\uC131 (.vhk/HARD_STOP) \u2014 \uC790\uB3D9\uD654\uB97C \uC911\uB2E8\uD569\uB2C8\uB2E4."));
5519
+ console.log(chalk25.red.bold("\n\u{1F6D1} HARD STOP \uD65C\uC131 (.vhk/HARD_STOP) \u2014 \uC790\uB3D9\uD654\uB97C \uC911\uB2E8\uD569\uB2C8\uB2E4."));
5386
5520
  const reason = readHardStopReason();
5387
- if (reason) console.log(chalk26.red(` \uC0AC\uC720: ${reason.replace(/\s*\n\s*/g, " ")}`));
5388
- console.log(chalk26.yellow(" \uD574\uC81C\uB294 \uC0AC\uB78C\uC774 \uC9C1\uC811: vhk resume --confirm"));
5521
+ if (reason) console.log(chalk25.red(` \uC0AC\uC720: ${reason.replace(/\s*\n\s*/g, " ")}`));
5522
+ console.log(chalk25.yellow(" \uD574\uC81C\uB294 \uC0AC\uB78C\uC774 \uC9C1\uC811: vhk resume --confirm"));
5389
5523
  return false;
5390
5524
  }
5391
5525
  function activeGoalLine() {
@@ -5399,7 +5533,7 @@ function activeGoalLine() {
5399
5533
  }
5400
5534
  function printGit(git3) {
5401
5535
  if (!git3) {
5402
- console.log(chalk26.dim(" (\uBCC0\uACBD\uB41C \uD30C\uC77C \uC5C6\uC74C \u2014 \uAE68\uB057\uD55C \uC0C1\uD0DC)"));
5536
+ console.log(chalk25.dim(" (\uBCC0\uACBD\uB41C \uD30C\uC77C \uC5C6\uC74C \u2014 \uAE68\uB057\uD55C \uC0C1\uD0DC)"));
5403
5537
  return;
5404
5538
  }
5405
5539
  for (const line of git3.split(/\r?\n/)) console.log(` ${line}`);
@@ -5460,24 +5594,24 @@ function buildHandoffPrompt(git3) {
5460
5594
  function emitPrompt(prompt, fileName, label) {
5461
5595
  let savedPath = "";
5462
5596
  try {
5463
- mkdirSync10(VHK_DIR2, { recursive: true });
5464
- savedPath = join9(VHK_DIR2, fileName);
5465
- writeFileSync10(savedPath, prompt, "utf-8");
5597
+ mkdirSync9(VHK_DIR, { recursive: true });
5598
+ savedPath = join8(VHK_DIR, fileName);
5599
+ writeFileSync8(savedPath, prompt, "utf-8");
5466
5600
  } catch {
5467
5601
  savedPath = "";
5468
5602
  }
5469
5603
  const copied = copyToClipboard(prompt);
5470
5604
  if (copied) {
5471
- console.log(chalk26.green(`
5605
+ console.log(chalk25.green(`
5472
5606
  \u{1F4CB} Claude\uC5D0\uAC8C \uC904 '${label}'\uC744 \uD074\uB9BD\uBCF4\uB4DC\uC5D0 \uBCF5\uC0AC\uD588\uC2B5\uB2C8\uB2E4! \u2705`));
5473
- if (savedPath) console.log(chalk26.dim(` (\uC0AC\uBCF8 \uC800\uC7A5: ${savedPath})`));
5607
+ if (savedPath) console.log(chalk25.dim(` (\uC0AC\uBCF8 \uC800\uC7A5: ${savedPath})`));
5474
5608
  } else {
5475
- console.log(chalk26.yellow(`
5609
+ console.log(chalk25.yellow(`
5476
5610
  \u26A0\uFE0F \uD074\uB9BD\uBCF4\uB4DC \uBCF5\uC0AC\uC5D0 \uC2E4\uD328\uD588\uC5B4\uC694 \u2014 \uC544\uB798 \uD504\uB86C\uD504\uD2B8\uB97C \uC9C1\uC811 \uBCF5\uC0AC\uD558\uC138\uC694:`));
5477
- if (savedPath) console.log(chalk26.dim(` (\uD30C\uC77C\uB85C\uB3C4 \uC800\uC7A5\uB428: ${savedPath} \u2014 \uC5F4\uC5B4\uC11C \uBCF5\uC0AC \uAC00\uB2A5)`));
5478
- console.log(chalk26.gray("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
5611
+ if (savedPath) console.log(chalk25.dim(` (\uD30C\uC77C\uB85C\uB3C4 \uC800\uC7A5\uB428: ${savedPath} \u2014 \uC5F4\uC5B4\uC11C \uBCF5\uC0AC \uAC00\uB2A5)`));
5612
+ console.log(chalk25.gray("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
5479
5613
  console.log(prompt);
5480
- console.log(chalk26.gray("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
5614
+ console.log(chalk25.gray("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
5481
5615
  }
5482
5616
  }
5483
5617
  async function refreshContextQuietly() {
@@ -5494,31 +5628,31 @@ async function refreshContextQuietly() {
5494
5628
  }
5495
5629
  }
5496
5630
  async function work() {
5497
- console.log(chalk26.bold(`
5631
+ console.log(chalk25.bold(`
5498
5632
  ${ko.work.workTitle}`));
5499
- console.log(chalk26.gray("\u2500".repeat(40)));
5633
+ console.log(chalk25.gray("\u2500".repeat(40)));
5500
5634
  if (!ensureVhkProject()) return;
5501
5635
  if (!passHardStop()) return;
5502
5636
  const git3 = gitShort();
5503
5637
  console.log("");
5504
- console.log(chalk26.cyan("\u{1F4CB} \uBCC0\uACBD\uB41C \uD30C\uC77C"));
5638
+ console.log(chalk25.cyan("\u{1F4CB} \uBCC0\uACBD\uB41C \uD30C\uC77C"));
5505
5639
  printGit(git3);
5506
5640
  console.log("");
5507
- console.log(chalk26.cyan("\u{1F4D6} \uADDC\uCE59"));
5641
+ console.log(chalk25.cyan("\u{1F4D6} \uADDC\uCE59"));
5508
5642
  console.log(" \xB7 CLAUDE.md \uAC00 1\uC21C\uC704 \uADDC\uCE59\uC785\uB2C8\uB2E4.");
5509
- console.log(chalk26.dim(" \xB7 AGENTS.md \uB294 Codex/\uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8\uC6A9 \uCC38\uACE0 \uADDC\uCE59\uC785\uB2C8\uB2E4."));
5643
+ console.log(chalk25.dim(" \xB7 AGENTS.md \uB294 Codex/\uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8\uC6A9 \uCC38\uACE0 \uADDC\uCE59\uC785\uB2C8\uB2E4."));
5510
5644
  const goalLine = activeGoalLine();
5511
5645
  console.log("");
5512
- console.log(chalk26.cyan(`\u{1F3AF} \uD604\uC7AC \uBAA9\uD45C: ${goalLine}`));
5646
+ console.log(chalk25.cyan(`\u{1F3AF} \uD604\uC7AC \uBAA9\uD45C: ${goalLine}`));
5513
5647
  const refreshed = await refreshContextQuietly();
5514
- console.log(chalk26.dim(refreshed ? "\u2699\uFE0F .vhk/context.md \uAC31\uC2E0\uB428" : "\u2699\uFE0F .vhk/context.md \uAC31\uC2E0 \uC0DD\uB7B5(\uAC74\uB108\uB700)"));
5515
- const nextTaskPath = join9("docs", "state", "next-task.md");
5516
- if (existsSync14(nextTaskPath)) {
5648
+ console.log(chalk25.dim(refreshed ? "\u2699\uFE0F .vhk/context.md \uAC31\uC2E0\uB428" : "\u2699\uFE0F .vhk/context.md \uAC31\uC2E0 \uC0DD\uB7B5(\uAC74\uB108\uB700)"));
5649
+ const nextTaskPath = join8("docs", "state", "next-task.md");
5650
+ if (existsSync13(nextTaskPath)) {
5517
5651
  try {
5518
- const lines = readFileSync7(nextTaskPath, "utf-8").split(/\r?\n/).slice(0, 12);
5652
+ const lines = readFileSync6(nextTaskPath, "utf-8").split(/\r?\n/).slice(0, 12);
5519
5653
  console.log("");
5520
- console.log(chalk26.cyan("\u{1F4DD} \uB2E4\uC74C \uD560 \uC77C (next-task.md)"));
5521
- for (const l of lines) console.log(chalk26.dim(` ${l}`));
5654
+ console.log(chalk25.cyan("\u{1F4DD} \uB2E4\uC74C \uD560 \uC77C (next-task.md)"));
5655
+ for (const l of lines) console.log(chalk25.dim(` ${l}`));
5522
5656
  } catch {
5523
5657
  }
5524
5658
  }
@@ -5530,19 +5664,19 @@ ${ko.work.workTitle}`));
5530
5664
  });
5531
5665
  }
5532
5666
  async function workHandoff() {
5533
- console.log(chalk26.bold(`
5667
+ console.log(chalk25.bold(`
5534
5668
  ${ko.work.handoffTitle}`));
5535
- console.log(chalk26.gray("\u2500".repeat(40)));
5669
+ console.log(chalk25.gray("\u2500".repeat(40)));
5536
5670
  if (!ensureVhkProject()) return;
5537
5671
  if (!passHardStop()) return;
5538
5672
  const git3 = gitShort();
5539
5673
  console.log("");
5540
- console.log(chalk26.cyan("\u{1F4CB} \uBC14\uB010 \uD30C\uC77C"));
5674
+ console.log(chalk25.cyan("\u{1F4CB} \uBC14\uB010 \uD30C\uC77C"));
5541
5675
  printGit(git3);
5542
5676
  const prompt = buildHandoffPrompt(git3);
5543
5677
  emitPrompt(prompt, "handoff-prompt.md", "\uC911\uB2E8 \uC815\uB9AC \uD504\uB86C\uD504\uD2B8");
5544
5678
  console.log("");
5545
- console.log(chalk26.dim("\u{1F4A1} \uC774 \uBA85\uB839\uC740 \uCEE4\uBC0B\xB7\uC0AD\uC81C\uB97C \uC808\uB300 \uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uC815\uB9AC\xB7\uD310\uB2E8\uC740 Claude \uAC00 \uD569\uB2C8\uB2E4."));
5679
+ console.log(chalk25.dim("\u{1F4A1} \uC774 \uBA85\uB839\uC740 \uCEE4\uBC0B\xB7\uC0AD\uC81C\uB97C \uC808\uB300 \uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uC815\uB9AC\xB7\uD310\uB2E8\uC740 Claude \uAC00 \uD569\uB2C8\uB2E4."));
5546
5680
  printNextStep({
5547
5681
  message: "\uD130\uBBF8\uB110\uC5D0\uC11C claude \uB97C \uC2E4\uD589\uD55C \uB4A4, Ctrl+V \uB85C \uBD99\uC5EC\uB123\uACE0 Enter \uD558\uC138\uC694.",
5548
5682
  command: "claude"
@@ -5550,11 +5684,11 @@ ${ko.work.handoffTitle}`));
5550
5684
  }
5551
5685
 
5552
5686
  // src/commands/start.ts
5553
- import chalk27 from "chalk";
5687
+ import chalk26 from "chalk";
5554
5688
  import inquirer11 from "inquirer";
5555
5689
  import { simpleGit as simpleGit2 } from "simple-git";
5556
- import { existsSync as existsSync15 } from "fs";
5557
- import { join as join10 } from "path";
5690
+ import { existsSync as existsSync14 } from "fs";
5691
+ import { join as join9 } from "path";
5558
5692
  var VHK_FOOTPRINT_FILES = [
5559
5693
  "CLAUDE.md",
5560
5694
  ".cursorrules",
@@ -5563,7 +5697,7 @@ var VHK_FOOTPRINT_FILES = [
5563
5697
  "docs/PRD.md"
5564
5698
  ];
5565
5699
  function detectExistingFootprint(cwd) {
5566
- return VHK_FOOTPRINT_FILES.filter((rel) => existsSync15(join10(cwd, rel)));
5700
+ return VHK_FOOTPRINT_FILES.filter((rel) => existsSync14(join9(cwd, rel)));
5567
5701
  }
5568
5702
  async function runGitInit(cwd) {
5569
5703
  try {
@@ -5592,21 +5726,21 @@ async function runStep(label, fn) {
5592
5726
  }
5593
5727
  }
5594
5728
  async function start(options = {}) {
5595
- console.log(chalk27.bold(`
5729
+ console.log(chalk26.bold(`
5596
5730
  ${ko.start.title}
5597
5731
  `));
5598
- console.log(chalk27.dim(ko.start.intro));
5599
- console.log(chalk27.dim(` ${ko.start.step1}`));
5600
- console.log(chalk27.dim(` ${ko.start.step2}`));
5601
- console.log(chalk27.dim(` ${ko.start.step3}`));
5602
- console.log(chalk27.dim(` ${ko.start.step4}`));
5732
+ console.log(chalk26.dim(ko.start.intro));
5733
+ console.log(chalk26.dim(` ${ko.start.step1}`));
5734
+ console.log(chalk26.dim(` ${ko.start.step2}`));
5735
+ console.log(chalk26.dim(` ${ko.start.step3}`));
5736
+ console.log(chalk26.dim(` ${ko.start.step4}`));
5603
5737
  console.log();
5604
5738
  const cwd = process.cwd();
5605
5739
  const footprint = detectExistingFootprint(cwd);
5606
5740
  if (footprint.length > 0 && !options.yes) {
5607
- console.log(chalk27.yellow("\u26A0\uFE0F \uC774\uBBF8 VHK \uC124\uCE58 \uD754\uC801\uC774 \uAC10\uC9C0\uB410\uC5B4\uC694:"));
5608
- for (const f of footprint) console.log(chalk27.dim(` - ${f}`));
5609
- console.log(chalk27.dim(" \uACC4\uC18D \uC9C4\uD589\uD558\uBA74 \uC77C\uBD80 \uD30C\uC77C(`.cursor/mcp.json`, `.vhk/context.md`)\uC740 \uAC31\uC2E0\xB7\uB36E\uC5B4\uC4F0\uAE30\uB429\uB2C8\uB2E4."));
5741
+ console.log(chalk26.yellow("\u26A0\uFE0F \uC774\uBBF8 VHK \uC124\uCE58 \uD754\uC801\uC774 \uAC10\uC9C0\uB410\uC5B4\uC694:"));
5742
+ for (const f of footprint) console.log(chalk26.dim(` - ${f}`));
5743
+ console.log(chalk26.dim(" \uACC4\uC18D \uC9C4\uD589\uD558\uBA74 \uC77C\uBD80 \uD30C\uC77C(`.cursor/mcp.json`, `.vhk/context.md`)\uC740 \uAC31\uC2E0\xB7\uB36E\uC5B4\uC4F0\uAE30\uB429\uB2C8\uB2E4."));
5610
5744
  const { proceedExisting } = await inquirer11.prompt([{
5611
5745
  type: "confirm",
5612
5746
  name: "proceedExisting",
@@ -5644,7 +5778,7 @@ ${ko.start.title}
5644
5778
  await runStep("[3/4] vhk mcp-init", () => mcpInit());
5645
5779
  log.step(ko.start.step4Header);
5646
5780
  await runStep("[4/4] vhk context", () => context());
5647
- console.log(chalk27.bold.green(`
5781
+ console.log(chalk26.bold.green(`
5648
5782
  ${ko.start.allDone}
5649
5783
  `));
5650
5784
  printNextStep({
@@ -5655,9 +5789,9 @@ ${ko.start.allDone}
5655
5789
 
5656
5790
  // src/commands/cloud.ts
5657
5791
  import fs13 from "fs";
5658
- import os2 from "os";
5792
+ import os3 from "os";
5659
5793
  import path14 from "path";
5660
- import chalk28 from "chalk";
5794
+ import chalk27 from "chalk";
5661
5795
 
5662
5796
  // src/lib/vhk-cloud.ts
5663
5797
  var import_ignore = __toESM(require_ignore(), 1);
@@ -5675,7 +5809,7 @@ var DEFAULT_CLOUD_EXCLUDES = [
5675
5809
  ".gitignore"
5676
5810
  // .vhk/ 내부 gitignore
5677
5811
  ];
5678
- var VHK_DIR3 = ".vhk";
5812
+ var VHK_DIR2 = ".vhk";
5679
5813
  var CLOUD_CONFIG_FILE = "cloud.json";
5680
5814
  function loadVhkignore(rootDir) {
5681
5815
  const ig = (0, import_ignore.default)();
@@ -5687,7 +5821,7 @@ function loadVhkignore(rootDir) {
5687
5821
  return ig;
5688
5822
  }
5689
5823
  function collectVhkFiles(rootDir, ig = loadVhkignore(rootDir)) {
5690
- const vhkDir = path13.join(rootDir, VHK_DIR3);
5824
+ const vhkDir = path13.join(rootDir, VHK_DIR2);
5691
5825
  let entries;
5692
5826
  try {
5693
5827
  entries = fs12.readdirSync(vhkDir, { withFileTypes: true });
@@ -5706,7 +5840,7 @@ function partitionGistFiles(gistFiles, ig) {
5706
5840
  return { keep, excluded };
5707
5841
  }
5708
5842
  function readCloudConfig(rootDir) {
5709
- const p = path13.join(rootDir, VHK_DIR3, CLOUD_CONFIG_FILE);
5843
+ const p = path13.join(rootDir, VHK_DIR2, CLOUD_CONFIG_FILE);
5710
5844
  if (!fs12.existsSync(p)) return null;
5711
5845
  try {
5712
5846
  const parsed = readJsonFile(p);
@@ -5719,7 +5853,7 @@ function readCloudConfig(rootDir) {
5719
5853
  }
5720
5854
  }
5721
5855
  function writeCloudConfig(rootDir, config) {
5722
- const vhkDir = path13.join(rootDir, VHK_DIR3);
5856
+ const vhkDir = path13.join(rootDir, VHK_DIR2);
5723
5857
  fs12.mkdirSync(vhkDir, { recursive: true });
5724
5858
  const p = path13.join(vhkDir, CLOUD_CONFIG_FILE);
5725
5859
  fs12.writeFileSync(p, JSON.stringify(config, null, 2) + "\n", "utf-8");
@@ -5749,14 +5883,14 @@ ${CLOUD_CONFIG_FILE}
5749
5883
  function ensureGhReady() {
5750
5884
  const ver = safeExecFile("gh", ["--version"]);
5751
5885
  if (!ver.ok) {
5752
- console.log(chalk28.red(` ${ko.cloud.noGh}`));
5753
- console.log(chalk28.dim(" \uC124\uCE58: https://cli.github.com/ (\uC124\uCE58 \uD6C4 `gh auth login`)"));
5886
+ console.log(chalk27.red(` ${ko.cloud.noGh}`));
5887
+ console.log(chalk27.dim(" \uC124\uCE58: https://cli.github.com/ (\uC124\uCE58 \uD6C4 `gh auth login`)"));
5754
5888
  return false;
5755
5889
  }
5756
5890
  const auth = safeExecFile("gh", ["auth", "status"]);
5757
5891
  if (!auth.ok) {
5758
- console.log(chalk28.red(` ${ko.cloud.noAuth}`));
5759
- console.log(chalk28.dim(" \uC2E4\uD589: gh auth login (gist \uAD8C\uD55C \uD544\uC694)"));
5892
+ console.log(chalk27.red(` ${ko.cloud.noAuth}`));
5893
+ console.log(chalk27.dim(" \uC2E4\uD589: gh auth login (gist \uAD8C\uD55C \uD544\uC694)"));
5760
5894
  return false;
5761
5895
  }
5762
5896
  return true;
@@ -5769,26 +5903,27 @@ function parseGistId(output) {
5769
5903
  return null;
5770
5904
  }
5771
5905
  async function cloudPush() {
5772
- console.log(chalk28.bold(`
5906
+ if (!ensureNotHardStopped("cloud push")) return;
5907
+ console.log(chalk27.bold(`
5773
5908
  ${ko.cloud.pushTitle}
5774
5909
  `));
5775
5910
  const cwd = process.cwd();
5776
- if (!fs13.existsSync(path14.join(cwd, VHK_DIR3))) {
5777
- console.log(chalk28.yellow(` ${ko.cloud.noVhkDir}`));
5911
+ if (!fs13.existsSync(path14.join(cwd, VHK_DIR2))) {
5912
+ console.log(chalk27.yellow(` ${ko.cloud.noVhkDir}`));
5778
5913
  return;
5779
5914
  }
5780
5915
  const ig = loadVhkignore(cwd);
5781
5916
  const files = collectVhkFiles(cwd, ig);
5782
5917
  if (files.length === 0) {
5783
- console.log(chalk28.yellow(` ${ko.cloud.nothingToSync}`));
5918
+ console.log(chalk27.yellow(` ${ko.cloud.nothingToSync}`));
5784
5919
  return;
5785
5920
  }
5786
5921
  if (!ensureGhReady()) {
5787
5922
  process.exitCode = 1;
5788
5923
  return;
5789
5924
  }
5790
- const filePaths = files.map((f) => path14.join(cwd, VHK_DIR3, f));
5791
- console.log(chalk28.dim(` \u{1F4E6} \uBC31\uC5C5 \uB300\uC0C1 ${files.length}\uAC1C: ${files.join(", ")}
5925
+ const filePaths = files.map((f) => path14.join(cwd, VHK_DIR2, f));
5926
+ console.log(chalk27.dim(` \u{1F4E6} \uBC31\uC5C5 \uB300\uC0C1 ${files.length}\uAC1C: ${files.join(", ")}
5792
5927
  `));
5793
5928
  const existing = readCloudConfig(cwd);
5794
5929
  const desc = `vhk .vhk backup \u2014 ${path14.basename(cwd)}`;
@@ -5800,8 +5935,8 @@ ${ko.cloud.pushTitle}
5800
5935
  const args = gistFiles.includes(name) ? ["gist", "edit", existing.gistId, "-f", name, src] : ["gist", "edit", existing.gistId, "-a", src];
5801
5936
  const res2 = safeExecFile("gh", args);
5802
5937
  if (!res2.ok) {
5803
- console.log(chalk28.red(` ${ko.cloud.pushFail}: ${name}`));
5804
- console.log(chalk28.dim(` ${res2.err}`));
5938
+ console.log(chalk27.red(` ${ko.cloud.pushFail}: ${name}`));
5939
+ console.log(chalk27.dim(` ${res2.err}`));
5805
5940
  process.exitCode = 1;
5806
5941
  return;
5807
5942
  }
@@ -5816,15 +5951,15 @@ ${ko.cloud.pushTitle}
5816
5951
  if (!purgeFailed.includes(name)) purgeFailed.push(name);
5817
5952
  }
5818
5953
  }
5819
- console.log(chalk28.green.bold(` ${ko.cloud.pushDone}`));
5820
- console.log(chalk28.dim(` gist: ${existing.gistId} (\uAC31\uC2E0)`));
5954
+ console.log(chalk27.green.bold(` ${ko.cloud.pushDone}`));
5955
+ console.log(chalk27.dim(` gist: ${existing.gistId} (\uAC31\uC2E0)`));
5821
5956
  if (excluded.length > 0) {
5822
5957
  const purged = excluded.filter((n) => !purgeFailed.includes(n));
5823
5958
  if (purged.length > 0) {
5824
- console.log(chalk28.dim(` \u{1F512} \uC81C\uC678 \uB300\uC0C1 ${purged.length}\uAC1C gist \uC5D0\uC11C \uC81C\uAC70: ${purged.join(", ")}`));
5959
+ console.log(chalk27.dim(` \u{1F512} \uC81C\uC678 \uB300\uC0C1 ${purged.length}\uAC1C gist \uC5D0\uC11C \uC81C\uAC70: ${purged.join(", ")}`));
5825
5960
  }
5826
5961
  if (purgeFailed.length > 0) {
5827
- console.log(chalk28.yellow(` \u26A0\uFE0F \uC81C\uC678 \uB300\uC0C1 \uC81C\uAC70 \uC2E4\uD328: ${purgeFailed.join(", ")} (\uC218\uB3D9 \uC81C\uAC70 \uAD8C\uC7A5 \u2014 pull \uC2DC\uC5D4 \uBCF5\uC6D0 \uC548 \uB428)`));
5962
+ console.log(chalk27.yellow(` \u26A0\uFE0F \uC81C\uC678 \uB300\uC0C1 \uC81C\uAC70 \uC2E4\uD328: ${purgeFailed.join(", ")} (\uC218\uB3D9 \uC81C\uAC70 \uAD8C\uC7A5 \u2014 pull \uC2DC\uC5D4 \uBCF5\uC6D0 \uC548 \uB428)`));
5828
5963
  }
5829
5964
  }
5830
5965
  printPushNext();
@@ -5832,32 +5967,32 @@ ${ko.cloud.pushTitle}
5832
5967
  }
5833
5968
  const res = safeExecFile("gh", ["gist", "create", "--desc", desc, ...filePaths]);
5834
5969
  if (!res.ok) {
5835
- console.log(chalk28.red(` ${ko.cloud.pushFail}`));
5836
- console.log(chalk28.dim(` ${res.err || res.out}`));
5970
+ console.log(chalk27.red(` ${ko.cloud.pushFail}`));
5971
+ console.log(chalk27.dim(` ${res.err || res.out}`));
5837
5972
  process.exitCode = 1;
5838
5973
  return;
5839
5974
  }
5840
5975
  const gistId = parseGistId(res.out);
5841
5976
  if (!gistId) {
5842
- console.log(chalk28.red(` ${ko.cloud.pushFail} \u2014 gist id \uD30C\uC2F1 \uC2E4\uD328`));
5843
- console.log(chalk28.dim(` \uCD9C\uB825: ${res.out}`));
5977
+ console.log(chalk27.red(` ${ko.cloud.pushFail} \u2014 gist id \uD30C\uC2F1 \uC2E4\uD328`));
5978
+ console.log(chalk27.dim(` \uCD9C\uB825: ${res.out}`));
5844
5979
  process.exitCode = 1;
5845
5980
  return;
5846
5981
  }
5847
5982
  writeCloudConfig(cwd, { gistId });
5848
- console.log(chalk28.green.bold(` ${ko.cloud.pushDone}`));
5849
- console.log(chalk28.dim(` gist: ${gistId} (\uC2E0\uADDC, secret) \u2192 .vhk/cloud.json \uC800\uC7A5`));
5983
+ console.log(chalk27.green.bold(` ${ko.cloud.pushDone}`));
5984
+ console.log(chalk27.dim(` gist: ${gistId} (\uC2E0\uADDC, secret) \u2192 .vhk/cloud.json \uC800\uC7A5`));
5850
5985
  printPushNext();
5851
5986
  }
5852
5987
  async function cloudPull(gistIdArg) {
5853
- console.log(chalk28.bold(`
5988
+ console.log(chalk27.bold(`
5854
5989
  ${ko.cloud.pullTitle}
5855
5990
  `));
5856
5991
  const cwd = process.cwd();
5857
5992
  const gistId = gistIdArg || readCloudConfig(cwd)?.gistId;
5858
5993
  if (!gistId) {
5859
- console.log(chalk28.yellow(` ${ko.cloud.noGistId}`));
5860
- console.log(chalk28.dim(" \uC0AC\uC6A9\uBC95: vhk cloud pull <gistId> (\uB610\uB294 cloud.json \uC774 \uC788\uB294 \uACF3\uC5D0\uC11C \uC2E4\uD589)"));
5994
+ console.log(chalk27.yellow(` ${ko.cloud.noGistId}`));
5995
+ console.log(chalk27.dim(" \uC0AC\uC6A9\uBC95: vhk cloud pull <gistId> (\uB610\uB294 cloud.json \uC774 \uC788\uB294 \uACF3\uC5D0\uC11C \uC2E4\uD589)"));
5861
5996
  return;
5862
5997
  }
5863
5998
  if (!ensureGhReady()) {
@@ -5866,34 +6001,34 @@ ${ko.cloud.pullTitle}
5866
6001
  }
5867
6002
  const allNames = listGistFiles(gistId);
5868
6003
  if (allNames.length === 0) {
5869
- console.log(chalk28.red(` ${ko.cloud.pullFail} \u2014 gist \uBE44\uC5C8\uAC70\uB098 \uC811\uADFC \uBD88\uAC00: ${gistId}`));
6004
+ console.log(chalk27.red(` ${ko.cloud.pullFail} \u2014 gist \uBE44\uC5C8\uAC70\uB098 \uC811\uADFC \uBD88\uAC00: ${gistId}`));
5870
6005
  process.exitCode = 1;
5871
6006
  return;
5872
6007
  }
5873
6008
  const { keep: names, excluded: skipped } = partitionGistFiles(allNames, loadVhkignore(cwd));
5874
6009
  if (skipped.length > 0) {
5875
- console.log(chalk28.dim(` \u{1F512} \uC81C\uC678 \uB300\uC0C1 ${skipped.length}\uAC1C \uBCF5\uC6D0 \uC2A4\uD0B5: ${skipped.join(", ")}`));
6010
+ console.log(chalk27.dim(` \u{1F512} \uC81C\uC678 \uB300\uC0C1 ${skipped.length}\uAC1C \uBCF5\uC6D0 \uC2A4\uD0B5: ${skipped.join(", ")}`));
5876
6011
  }
5877
6012
  if (names.length === 0) {
5878
- console.log(chalk28.yellow(` \uBCF5\uC6D0 \uB300\uC0C1\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 (gist \uD30C\uC77C\uC774 \uBAA8\uB450 \uC81C\uC678 \uADDC\uCE59\uC5D0 \uD574\uB2F9).`));
6013
+ console.log(chalk27.yellow(` \uBCF5\uC6D0 \uB300\uC0C1\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 (gist \uD30C\uC77C\uC774 \uBAA8\uB450 \uC81C\uC678 \uADDC\uCE59\uC5D0 \uD574\uB2F9).`));
5879
6014
  return;
5880
6015
  }
5881
- const vhkDir = path14.join(cwd, VHK_DIR3);
6016
+ const vhkDir = path14.join(cwd, VHK_DIR2);
5882
6017
  fs13.mkdirSync(vhkDir, { recursive: true });
5883
6018
  let restored = 0;
5884
6019
  for (const name of names) {
5885
6020
  const res = safeExecFile("gh", ["gist", "view", gistId, "-f", name, "--raw"]);
5886
6021
  if (!res.ok) {
5887
- console.log(chalk28.red(` ${ko.cloud.pullFail}: ${name}`));
5888
- console.log(chalk28.dim(` ${res.err}`));
6022
+ console.log(chalk27.red(` ${ko.cloud.pullFail}: ${name}`));
6023
+ console.log(chalk27.dim(` ${res.err}`));
5889
6024
  continue;
5890
6025
  }
5891
6026
  fs13.writeFileSync(path14.join(vhkDir, name), ensureTrailingNewline(res.out), "utf-8");
5892
6027
  restored++;
5893
6028
  }
5894
6029
  writeCloudConfig(cwd, { gistId });
5895
- console.log(chalk28.green.bold(` ${ko.cloud.pullDone}`));
5896
- console.log(chalk28.dim(` ${restored}\uAC1C \uD30C\uC77C \uBCF5\uC6D0 (gist: ${gistId})`));
6030
+ console.log(chalk27.green.bold(` ${ko.cloud.pullDone}`));
6031
+ console.log(chalk27.dim(` ${restored}\uAC1C \uD30C\uC77C \uBCF5\uC6D0 (gist: ${gistId})`));
5897
6032
  printNextStep({
5898
6033
  message: "\uD074\uB77C\uC6B0\uB4DC\uC5D0\uC11C .vhk/ \uBCF5\uC6D0 \uC644\uB8CC!",
5899
6034
  command: "vhk \uB9E5\uB77D",
@@ -5905,7 +6040,7 @@ function purgeExcludedFromGist(gistId, names) {
5905
6040
  const body = JSON.stringify({
5906
6041
  files: Object.fromEntries(names.map((n) => [n, null]))
5907
6042
  });
5908
- const tmp = path14.join(os2.tmpdir(), `vhk-gist-purge-${process.pid}.json`);
6043
+ const tmp = path14.join(os3.tmpdir(), `vhk-gist-purge-${process.pid}.json`);
5909
6044
  try {
5910
6045
  fs13.writeFileSync(tmp, body, "utf-8");
5911
6046
  for (let attempt = 0; attempt < 2; attempt++) {
@@ -5941,7 +6076,7 @@ function printPushNext() {
5941
6076
  }
5942
6077
 
5943
6078
  // src/commands/help.ts
5944
- import chalk29 from "chalk";
6079
+ import chalk28 from "chalk";
5945
6080
  var QUICK_ACTIONS = [
5946
6081
  { say: "\uC0C1\uD0DC \uC54C\uB824\uC918", does: "vhk status" },
5947
6082
  { say: "\uBB50 \uBC14\uB00C\uC5C8\uC5B4?", does: "vhk diff" },
@@ -5955,21 +6090,21 @@ var QUICK_ACTIONS = [
5955
6090
  { say: "\uC804\uCCB4 \uBA85\uB839\uC5B4 \uBCF4\uAE30", does: "vhk --help" }
5956
6091
  ];
5957
6092
  function quickActions() {
5958
- console.log(chalk29.bold("\n\u{1F9ED} VHK \u2014 \uC774\uB807\uAC8C \uB9D0\uD558\uBA74 \uB429\uB2C8\uB2E4 (quick actions)"));
5959
- console.log(chalk29.gray("\u2500".repeat(40)));
6093
+ console.log(chalk28.bold("\n\u{1F9ED} VHK \u2014 \uC774\uB807\uAC8C \uB9D0\uD558\uBA74 \uB429\uB2C8\uB2E4 (quick actions)"));
6094
+ console.log(chalk28.gray("\u2500".repeat(40)));
5960
6095
  for (const a of QUICK_ACTIONS) {
5961
- console.log(` "${chalk29.cyan(a.say)}" \u2192 ${chalk29.dim(a.does)}`);
6096
+ console.log(` "${chalk28.cyan(a.say)}" \u2192 ${chalk28.dim(a.does)}`);
5962
6097
  }
5963
- console.log(chalk29.gray("\n \uC804\uCCB4 \uBA85\uB839\uC740 `vhk --help` \uB610\uB294 COMMANDS.md \uB97C \uBCF4\uC138\uC694."));
6098
+ console.log(chalk28.gray("\n \uC804\uCCB4 \uBA85\uB839\uC740 `vhk --help` \uB610\uB294 COMMANDS.md \uB97C \uBCF4\uC138\uC694."));
5964
6099
  console.log("");
5965
6100
  }
5966
6101
 
5967
6102
  // src/commands/mode.ts
5968
- import chalk30 from "chalk";
6103
+ import chalk29 from "chalk";
5969
6104
 
5970
6105
  // src/lib/config.ts
5971
- import { existsSync as existsSync16, mkdirSync as mkdirSync11, writeFileSync as writeFileSync11 } from "fs";
5972
- import { join as join11 } from "path";
6106
+ import { existsSync as existsSync15, mkdirSync as mkdirSync10, writeFileSync as writeFileSync9 } from "fs";
6107
+ import { join as join10 } from "path";
5973
6108
 
5974
6109
  // src/lib/safety-mode.ts
5975
6110
  var SAFETY_MODES = ["lite", "standard", "strict"];
@@ -5985,11 +6120,11 @@ function isSafetyMode(value) {
5985
6120
 
5986
6121
  // src/lib/config.ts
5987
6122
  var CONFIG_DIR = ".vhk";
5988
- var CONFIG_PATH = join11(CONFIG_DIR, "config.json");
6123
+ var CONFIG_PATH = join10(CONFIG_DIR, "config.json");
5989
6124
  var DEFAULT_CONFIG = { safetyMode: DEFAULT_SAFETY_MODE };
5990
6125
  function readConfig(rootDir = process.cwd()) {
5991
- const full = join11(rootDir, CONFIG_PATH);
5992
- if (!existsSync16(full)) return { ...DEFAULT_CONFIG };
6126
+ const full = join10(rootDir, CONFIG_PATH);
6127
+ if (!existsSync15(full)) return { ...DEFAULT_CONFIG };
5993
6128
  try {
5994
6129
  const raw = readJsonFile(full);
5995
6130
  return {
@@ -6000,23 +6135,23 @@ function readConfig(rootDir = process.cwd()) {
6000
6135
  }
6001
6136
  }
6002
6137
  function writeConfig(config, rootDir = process.cwd()) {
6003
- mkdirSync11(join11(rootDir, CONFIG_DIR), { recursive: true });
6004
- writeFileSync11(join11(rootDir, CONFIG_PATH), JSON.stringify(config, null, 2) + "\n", "utf-8");
6138
+ mkdirSync10(join10(rootDir, CONFIG_DIR), { recursive: true });
6139
+ writeFileSync9(join10(rootDir, CONFIG_PATH), JSON.stringify(config, null, 2) + "\n", "utf-8");
6005
6140
  }
6006
6141
 
6007
6142
  // src/commands/mode.ts
6008
6143
  async function mode(target) {
6009
- console.log(chalk30.bold("\n\u{1F6E1}\uFE0F Safety Mode"));
6010
- console.log(chalk30.gray("\u2500".repeat(40)));
6144
+ console.log(chalk29.bold("\n\u{1F6E1}\uFE0F Safety Mode"));
6145
+ console.log(chalk29.gray("\u2500".repeat(40)));
6011
6146
  const current = readConfig().safetyMode;
6012
6147
  if (!target) {
6013
- console.log(chalk30.cyan(`
6014
- \uD604\uC7AC \uBAA8\uB4DC: ${chalk30.bold(current)}`));
6015
- console.log(chalk30.dim(` ${SAFETY_MODE_DESC[current]}`));
6148
+ console.log(chalk29.cyan(`
6149
+ \uD604\uC7AC \uBAA8\uB4DC: ${chalk29.bold(current)}`));
6150
+ console.log(chalk29.dim(` ${SAFETY_MODE_DESC[current]}`));
6016
6151
  console.log("");
6017
6152
  for (const m of SAFETY_MODES) {
6018
6153
  const mark = m === current ? "\u25CF" : "\u25CB";
6019
- console.log(` ${mark} ${m.padEnd(9)} ${chalk30.dim(SAFETY_MODE_DESC[m])}`);
6154
+ console.log(` ${mark} ${m.padEnd(9)} ${chalk29.dim(SAFETY_MODE_DESC[m])}`);
6020
6155
  }
6021
6156
  printNextStep({
6022
6157
  message: "\uBAA8\uB4DC\uB97C \uBC14\uAFB8\uB824\uBA74:",
@@ -6026,23 +6161,23 @@ async function mode(target) {
6026
6161
  return;
6027
6162
  }
6028
6163
  if (!isSafetyMode(target)) {
6029
- console.log(chalk30.red(`
6164
+ console.log(chalk29.red(`
6030
6165
  \u274C \uC54C \uC218 \uC5C6\uB294 \uBAA8\uB4DC: ${target}`));
6031
- console.log(chalk30.dim(` \uAC00\uB2A5: ${SAFETY_MODES.join(" | ")}`));
6166
+ console.log(chalk29.dim(` \uAC00\uB2A5: ${SAFETY_MODES.join(" | ")}`));
6032
6167
  process.exitCode = 1;
6033
6168
  return;
6034
6169
  }
6035
6170
  writeConfig({ ...readConfig(), safetyMode: target });
6036
- console.log(chalk30.green(`
6037
- \u2705 Safety Mode \u2192 ${chalk30.bold(target)}`));
6038
- console.log(chalk30.dim(` ${SAFETY_MODE_DESC[target]}`));
6171
+ console.log(chalk29.green(`
6172
+ \u2705 Safety Mode \u2192 ${chalk29.bold(target)}`));
6173
+ console.log(chalk29.dim(` ${SAFETY_MODE_DESC[target]}`));
6039
6174
  }
6040
6175
 
6041
6176
  // src/commands/verify.ts
6042
6177
  import { execFileSync as execFileSync4 } from "child_process";
6043
- import { existsSync as existsSync17, mkdirSync as mkdirSync12, writeFileSync as writeFileSync12 } from "fs";
6044
- import { join as join12 } from "path";
6045
- import chalk31 from "chalk";
6178
+ import { existsSync as existsSync16, mkdirSync as mkdirSync11 } from "fs";
6179
+ import { join as join11 } from "path";
6180
+ import chalk30 from "chalk";
6046
6181
 
6047
6182
  // src/commands/verify-report.ts
6048
6183
  function escapeHtml(text) {
@@ -6150,13 +6285,13 @@ ${actions}
6150
6285
 
6151
6286
  // src/commands/verify.ts
6152
6287
  var REPORT_SCHEMA_VERSION = 1;
6153
- var REPORT_DIR_REL = join12(".vhk", "reports");
6154
- var REPORT_PATH_REL = join12(REPORT_DIR_REL, "latest.json");
6155
- var REPORT_HTML_PATH_REL = join12(REPORT_DIR_REL, "latest.html");
6288
+ var REPORT_DIR_REL = join11(".vhk", "reports");
6289
+ var REPORT_PATH_REL = join11(REPORT_DIR_REL, "latest.json");
6290
+ var REPORT_HTML_PATH_REL = join11(REPORT_DIR_REL, "latest.html");
6156
6291
  var SHIM = /* @__PURE__ */ new Set(["pnpm", "npm", "npx", "yarn"]);
6157
6292
  function detectPm(cwd) {
6158
- if (existsSync17(join12(cwd, "pnpm-lock.yaml"))) return "pnpm";
6159
- if (existsSync17(join12(cwd, "yarn.lock"))) return "yarn";
6293
+ if (existsSync16(join11(cwd, "pnpm-lock.yaml"))) return "pnpm";
6294
+ if (existsSync16(join11(cwd, "yarn.lock"))) return "yarn";
6160
6295
  return "npm";
6161
6296
  }
6162
6297
  function execGate(cmd, args, cwd) {
@@ -6199,8 +6334,8 @@ function runScriptGate(id, label, cwd, pm, argvFor) {
6199
6334
  };
6200
6335
  }
6201
6336
  function readPackageScripts(cwd) {
6202
- const pkgPath = join12(cwd, "package.json");
6203
- if (!existsSync17(pkgPath)) return {};
6337
+ const pkgPath = join11(cwd, "package.json");
6338
+ if (!existsSync16(pkgPath)) return {};
6204
6339
  try {
6205
6340
  const pkg = readJsonFile(pkgPath);
6206
6341
  return pkg.scripts ?? {};
@@ -6215,7 +6350,7 @@ function runGates(cwd) {
6215
6350
  gates.push(
6216
6351
  runScriptGate("typecheck", "tsc --noEmit", cwd, pm, () => {
6217
6352
  if (scripts.typecheck) return ["run", "typecheck"];
6218
- if (existsSync17(join12(cwd, "tsconfig.json"))) return pm === "npm" ? ["exec", "--", "tsc", "--noEmit"] : ["exec", "tsc", "--noEmit"];
6353
+ if (existsSync16(join11(cwd, "tsconfig.json"))) return pm === "npm" ? ["exec", "--", "tsc", "--noEmit"] : ["exec", "tsc", "--noEmit"];
6219
6354
  return null;
6220
6355
  })
6221
6356
  );
@@ -6294,10 +6429,10 @@ function buildReport(gates, generatedAt, date) {
6294
6429
  function verifyEvidence(cwd = process.cwd()) {
6295
6430
  const gates = runGates(cwd);
6296
6431
  const report = buildReport(gates, (/* @__PURE__ */ new Date()).toISOString(), localDate());
6297
- const dir = join12(cwd, REPORT_DIR_REL);
6298
- mkdirSync12(dir, { recursive: true });
6299
- const path15 = join12(cwd, REPORT_PATH_REL);
6300
- writeFileSync12(path15, JSON.stringify(report, null, 2) + "\n", "utf-8");
6432
+ const dir = join11(cwd, REPORT_DIR_REL);
6433
+ mkdirSync11(dir, { recursive: true });
6434
+ const path15 = join11(cwd, REPORT_PATH_REL);
6435
+ atomicWriteFile(path15, JSON.stringify(report, null, 2) + "\n");
6301
6436
  try {
6302
6437
  ensureVhkIgnored(cwd, "reports/");
6303
6438
  } catch {
@@ -6305,44 +6440,44 @@ function verifyEvidence(cwd = process.cwd()) {
6305
6440
  return { report, path: REPORT_PATH_REL };
6306
6441
  }
6307
6442
  var STATUS_BADGE = {
6308
- PASS: chalk31.green.bold("PASS"),
6309
- WARN: chalk31.yellow.bold("WARN"),
6310
- FAIL: chalk31.red.bold("FAIL")
6443
+ PASS: chalk30.green.bold("PASS"),
6444
+ WARN: chalk30.yellow.bold("WARN"),
6445
+ FAIL: chalk30.red.bold("FAIL")
6311
6446
  };
6312
6447
  async function renderVerifyReport(cwd, opts) {
6313
- const jsonPath = join12(cwd, REPORT_PATH_REL);
6448
+ const jsonPath = join11(cwd, REPORT_PATH_REL);
6314
6449
  let report;
6315
- if (existsSync17(jsonPath)) {
6450
+ if (existsSync16(jsonPath)) {
6316
6451
  try {
6317
6452
  report = readJsonFile(jsonPath);
6318
6453
  } catch {
6319
- console.log(chalk31.yellow(" \u26A0\uFE0F \uAE30\uC874 latest.json \uC190\uC0C1 \u2014 verify \uC7AC\uC2E4\uD589\uC73C\uB85C \uC99D\uAC70\uB97C \uB2E4\uC2DC \uB9CC\uB4ED\uB2C8\uB2E4."));
6454
+ console.log(chalk30.yellow(" \u26A0\uFE0F \uAE30\uC874 latest.json \uC190\uC0C1 \u2014 verify \uC7AC\uC2E4\uD589\uC73C\uB85C \uC99D\uAC70\uB97C \uB2E4\uC2DC \uB9CC\uB4ED\uB2C8\uB2E4."));
6320
6455
  report = verifyEvidence(cwd).report;
6321
6456
  }
6322
6457
  } else {
6323
- console.log(chalk31.dim(" latest.json \uC5C6\uC74C \u2014 verify 1\uD68C \uC120\uC2E4\uD589\uC73C\uB85C \uC99D\uAC70\uB97C \uB9CC\uB4ED\uB2C8\uB2E4."));
6458
+ console.log(chalk30.dim(" latest.json \uC5C6\uC74C \u2014 verify 1\uD68C \uC120\uC2E4\uD589\uC73C\uB85C \uC99D\uAC70\uB97C \uB9CC\uB4ED\uB2C8\uB2E4."));
6324
6459
  report = verifyEvidence(cwd).report;
6325
6460
  }
6326
6461
  const html = renderReportHtml(report);
6327
- const htmlPath = join12(cwd, REPORT_HTML_PATH_REL);
6462
+ const htmlPath = join11(cwd, REPORT_HTML_PATH_REL);
6328
6463
  try {
6329
- mkdirSync12(join12(cwd, REPORT_DIR_REL), { recursive: true });
6330
- writeFileSync12(htmlPath, html, "utf-8");
6464
+ mkdirSync11(join11(cwd, REPORT_DIR_REL), { recursive: true });
6465
+ atomicWriteFile(htmlPath, html);
6331
6466
  } catch (e) {
6332
6467
  console.error(
6333
- chalk31.red(` \u274C \uB9AC\uD3EC\uD2B8 HTML \uC744 \uC4F8 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (${REPORT_HTML_PATH_REL}): ${e instanceof Error ? e.message : String(e)}`)
6468
+ chalk30.red(` \u274C \uB9AC\uD3EC\uD2B8 HTML \uC744 \uC4F8 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (${REPORT_HTML_PATH_REL}): ${e instanceof Error ? e.message : String(e)}`)
6334
6469
  );
6335
- console.error(chalk31.dim(" \uD574\uB2F9 \uACBD\uB85C\uC758 \uC4F0\uAE30 \uAD8C\uD55C\uC744 \uD655\uC778\uD558\uC138\uC694."));
6470
+ console.error(chalk30.dim(" \uD574\uB2F9 \uACBD\uB85C\uC758 \uC4F0\uAE30 \uAD8C\uD55C\uC744 \uD655\uC778\uD558\uC138\uC694."));
6336
6471
  process.exitCode = 1;
6337
6472
  return;
6338
6473
  }
6339
- console.log(chalk31.bold("\n\u{1F50E} \uAC80\uC99D \uB9AC\uD3EC\uD2B8 (verify --report)"));
6474
+ console.log(chalk30.bold("\n\u{1F50E} \uAC80\uC99D \uB9AC\uD3EC\uD2B8 (verify --report)"));
6340
6475
  console.log(` \uACB0\uACFC: ${STATUS_BADGE[report.status]}`);
6341
- console.log(chalk31.dim(` \u{1F4C4} HTML: ${REPORT_HTML_PATH_REL}`));
6476
+ console.log(chalk30.dim(` \u{1F4C4} HTML: ${REPORT_HTML_PATH_REL}`));
6342
6477
  process.exitCode = report.status === "FAIL" ? 1 : 0;
6343
6478
  if (opts.open) {
6344
6479
  if (isInteractive()) openReportInBrowser(htmlPath);
6345
- else console.log(chalk31.dim(" (\uBE44\uB300\uD654\uD615/CI/MCP \u2014 --open \uC790\uB3D9 \uC2A4\uD0B5)"));
6480
+ else console.log(chalk30.dim(" (\uBE44\uB300\uD654\uD615/CI/MCP \u2014 --open \uC790\uB3D9 \uC2A4\uD0B5)"));
6346
6481
  return;
6347
6482
  }
6348
6483
  printNextStep({
@@ -6360,8 +6495,8 @@ function openReportInBrowser(filePath) {
6360
6495
  } else {
6361
6496
  result = safeExecFile("xdg-open", [filePath]);
6362
6497
  }
6363
- if (result.ok) console.log(chalk31.green(" \u2705 \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uC5F4\uC5C8\uC2B5\uB2C8\uB2E4."));
6364
- else console.log(chalk31.yellow(" \u26A0\uFE0F \uBE0C\uB77C\uC6B0\uC800\uB97C \uC5F4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC704 \uD30C\uC77C\uC744 \uC9C1\uC811 \uC5EC\uC138\uC694."));
6498
+ if (result.ok) console.log(chalk30.green(" \u2705 \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uC5F4\uC5C8\uC2B5\uB2C8\uB2E4."));
6499
+ else console.log(chalk30.yellow(" \u26A0\uFE0F \uBE0C\uB77C\uC6B0\uC800\uB97C \uC5F4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC704 \uD30C\uC77C\uC744 \uC9C1\uC811 \uC5EC\uC138\uC694."));
6365
6500
  }
6366
6501
  async function verify(opts = {}) {
6367
6502
  if (!ensureNotHardStopped("verify")) return;
@@ -6376,21 +6511,21 @@ async function verify(opts = {}) {
6376
6511
  process.exitCode = report.status === "FAIL" ? 1 : 0;
6377
6512
  return;
6378
6513
  }
6379
- console.log(chalk31.bold("\n\u{1F50E} \uAC80\uC99D \uBB36\uC74C (verify)"));
6380
- console.log(chalk31.gray("\u2500".repeat(40)));
6514
+ console.log(chalk30.bold("\n\u{1F50E} \uAC80\uC99D \uBB36\uC74C (verify)"));
6515
+ console.log(chalk30.gray("\u2500".repeat(40)));
6381
6516
  const mode2 = readConfig().safetyMode;
6382
- console.log(chalk31.dim(` \uD604\uC7AC Safety Mode: ${mode2} \u2014 ${SAFETY_MODE_DESC[mode2]}`));
6383
- const icon = (s2) => s2 === "pass" ? chalk31.green("\u2713") : s2 === "fail" ? chalk31.red("\u2717") : chalk31.yellow("\u2298");
6517
+ console.log(chalk30.dim(` \uD604\uC7AC Safety Mode: ${mode2} \u2014 ${SAFETY_MODE_DESC[mode2]}`));
6518
+ const icon = (s2) => s2 === "pass" ? chalk30.green("\u2713") : s2 === "fail" ? chalk30.red("\u2717") : chalk30.yellow("\u2298");
6384
6519
  for (const g of report.gates) {
6385
- const tail = g.detail ? chalk31.dim(` \u2014 ${g.detail}`) : "";
6520
+ const tail = g.detail ? chalk30.dim(` \u2014 ${g.detail}`) : "";
6386
6521
  console.log(` ${icon(g.status)} ${g.label}${tail}`);
6387
6522
  }
6388
6523
  const s = report.summary;
6389
6524
  console.log(
6390
6525
  `
6391
- \uACB0\uACFC: ${STATUS_BADGE[report.status]} ` + chalk31.dim(`(pass ${s.pass} / fail ${s.fail} / skip ${s.skip}, \uCD1D ${s.total})`)
6526
+ \uACB0\uACFC: ${STATUS_BADGE[report.status]} ` + chalk30.dim(`(pass ${s.pass} / fail ${s.fail} / skip ${s.skip}, \uCD1D ${s.total})`)
6392
6527
  );
6393
- console.log(chalk31.dim(` \u{1F4C4} \uC99D\uAC70: ${path15}`));
6528
+ console.log(chalk30.dim(` \u{1F4C4} \uC99D\uAC70: ${path15}`));
6394
6529
  process.exitCode = report.status === "FAIL" ? 1 : 0;
6395
6530
  if (report.status === "FAIL") {
6396
6531
  printNextStep({
@@ -6409,9 +6544,9 @@ async function verify(opts = {}) {
6409
6544
  }
6410
6545
 
6411
6546
  // src/commands/review.ts
6412
- import { existsSync as existsSync18, writeFileSync as writeFileSync13 } from "fs";
6413
- import { join as join13 } from "path";
6414
- import chalk32 from "chalk";
6547
+ import { existsSync as existsSync17 } from "fs";
6548
+ import { join as join12 } from "path";
6549
+ import chalk31 from "chalk";
6415
6550
  var GOALS_DIR2 = "goals";
6416
6551
  var COVERAGE_MIN = 0.5;
6417
6552
  var STALE_AGE_MS = 6 * 60 * 60 * 1e3;
@@ -6543,22 +6678,22 @@ function resolveGoal(optId, goals) {
6543
6678
  return goals.find((g) => g.frontmatter.id === id) ?? null;
6544
6679
  }
6545
6680
  var CONFIDENCE_LABEL = {
6546
- low: chalk32.red.bold("\uB0AE\uC74C (\uAC70\uC9D3\uC644\uB8CC \uC758\uC2EC \uB610\uB294 \uC644\uB8CC \uC8FC\uC7A5 \uC5C6\uC74C)"),
6547
- medium: chalk32.yellow.bold("\uC911\uAC04 (\uCEE4\uBC84\uB9AC\uC9C0/\uC2E0\uC120\uB3C4 \uBD80\uC871 \u2014 \uC99D\uAC70 \uC5C6\uC74C \u2260 \uD1B5\uACFC)"),
6548
- high: chalk32.green.bold("\uB192\uC74C (\uC758\uC2EC 0 + \uCEE4\uBC84\uB9AC\uC9C0\xB7\uC2E0\uC120\uB3C4 \uCDA9\uBD84 \u2014 \uB2E8 \uBCF4\uC7A5 \uC544\uB2D8)")
6681
+ low: chalk31.red.bold("\uB0AE\uC74C (\uAC70\uC9D3\uC644\uB8CC \uC758\uC2EC \uB610\uB294 \uC644\uB8CC \uC8FC\uC7A5 \uC5C6\uC74C)"),
6682
+ medium: chalk31.yellow.bold("\uC911\uAC04 (\uCEE4\uBC84\uB9AC\uC9C0/\uC2E0\uC120\uB3C4 \uBD80\uC871 \u2014 \uC99D\uAC70 \uC5C6\uC74C \u2260 \uD1B5\uACFC)"),
6683
+ high: chalk31.green.bold("\uB192\uC74C (\uC758\uC2EC 0 + \uCEE4\uBC84\uB9AC\uC9C0\xB7\uC2E0\uC120\uB3C4 \uCDA9\uBD84 \u2014 \uB2E8 \uBCF4\uC7A5 \uC544\uB2D8)")
6549
6684
  };
6550
6685
  async function review(opts = {}) {
6551
6686
  if (!ensureNotHardStopped("review")) return;
6552
6687
  const cwd = process.cwd();
6553
6688
  const goals = listGoals(GOALS_DIR2);
6554
6689
  if (goals.length === 0) {
6555
- console.error(chalk32.yellow(" \u26A0\uFE0F goals/ \uC5D0 goal \uC774 \uC5C6\uC2B5\uB2C8\uB2E4. vhk goal init \uC73C\uB85C \uC2DC\uC791\uD558\uC138\uC694."));
6690
+ console.error(chalk31.yellow(" \u26A0\uFE0F goals/ \uC5D0 goal \uC774 \uC5C6\uC2B5\uB2C8\uB2E4. vhk goal init \uC73C\uB85C \uC2DC\uC791\uD558\uC138\uC694."));
6556
6691
  process.exitCode = 1;
6557
6692
  return;
6558
6693
  }
6559
6694
  const goal = resolveGoal(opts.id, goals);
6560
6695
  if (!goal || typeof goal.frontmatter.id !== "number") {
6561
- console.error(chalk32.red(` \u274C \uB300\uC0C1 goal \uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4${opts.id ? ` (--id ${opts.id})` : " (active goal \uC5C6\uC74C)"}.`));
6696
+ console.error(chalk31.red(` \u274C \uB300\uC0C1 goal \uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4${opts.id ? ` (--id ${opts.id})` : " (active goal \uC5C6\uC74C)"}.`));
6562
6697
  process.exitCode = 1;
6563
6698
  return;
6564
6699
  }
@@ -6566,11 +6701,11 @@ async function review(opts = {}) {
6566
6701
  const goalStatus = goal.frontmatter.status ?? "NOT_STARTED";
6567
6702
  const checks = parseCompletionChecks(goal.body);
6568
6703
  if (opts.id === void 0 && goalStatus === "NOT_STARTED") {
6569
- console.error(chalk32.yellow(` \u26A0\uFE0F active goal ${goalId} \uAC00 NOT_STARTED \u2014 \uC644\uB8CC \uC8FC\uC7A5\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uAC80\uC99D \uB300\uC0C1\uC740 --id \uB85C \uC9C0\uC815\uD558\uC138\uC694.`));
6704
+ console.error(chalk31.yellow(` \u26A0\uFE0F active goal ${goalId} \uAC00 NOT_STARTED \u2014 \uC644\uB8CC \uC8FC\uC7A5\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uAC80\uC99D \uB300\uC0C1\uC740 --id \uB85C \uC9C0\uC815\uD558\uC138\uC694.`));
6570
6705
  }
6571
- const jsonPath = join13(cwd, REPORT_PATH_REL);
6572
- if (!existsSync18(jsonPath)) {
6573
- console.error(chalk32.yellow(` \u26A0\uFE0F \uC99D\uAC70 \uBD80\uC7AC \u2014 ${REPORT_PATH_REL} \uC5C6\uC74C. review \uB97C \uC911\uB2E8\uD569\uB2C8\uB2E4(\uC0C8 \uC99D\uAC70 \uC548 \uB9CC\uB4E6).`));
6706
+ const jsonPath = join12(cwd, REPORT_PATH_REL);
6707
+ if (!existsSync17(jsonPath)) {
6708
+ console.error(chalk31.yellow(` \u26A0\uFE0F \uC99D\uAC70 \uBD80\uC7AC \u2014 ${REPORT_PATH_REL} \uC5C6\uC74C. review \uB97C \uC911\uB2E8\uD569\uB2C8\uB2E4(\uC0C8 \uC99D\uAC70 \uC548 \uB9CC\uB4E6).`));
6574
6709
  printNextStep({
6575
6710
  message: "\uC99D\uAC70(latest.json)\uAC00 \uC788\uC5B4\uC57C review \uAC00 \uAD50\uCC28\uAC80\uC99D\uD569\uB2C8\uB2E4:",
6576
6711
  command: "vhk verify",
@@ -6583,7 +6718,7 @@ async function review(opts = {}) {
6583
6718
  try {
6584
6719
  report = readJsonFile(jsonPath);
6585
6720
  } catch {
6586
- console.error(chalk32.red(` \u274C ${REPORT_PATH_REL} \uB97C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4(\uC190\uC0C1). vhk verify \uB85C \uC7AC\uC0DD\uC131\uD558\uC138\uC694.`));
6721
+ console.error(chalk31.red(` \u274C ${REPORT_PATH_REL} \uB97C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4(\uC190\uC0C1). vhk verify \uB85C \uC7AC\uC0DD\uC131\uD558\uC138\uC694.`));
6587
6722
  process.exitCode = 1;
6588
6723
  return;
6589
6724
  }
@@ -6595,44 +6730,44 @@ async function review(opts = {}) {
6595
6730
  goalStatus,
6596
6731
  reportStatus: report.status
6597
6732
  };
6598
- console.log(chalk32.bold(`
6733
+ console.log(chalk31.bold(`
6599
6734
  \u{1F52C} \uC801\uB300\uC801 \uC790\uAE30\uAC80\uC99D (review) \u2014 Goal ${goalId}`));
6600
- console.log(chalk32.gray("\u2500".repeat(44)));
6735
+ console.log(chalk31.gray("\u2500".repeat(44)));
6601
6736
  console.log(
6602
- chalk32.dim(
6737
+ chalk31.dim(
6603
6738
  ` goal status: ${goalStatus} \xB7 verify: ${report.status} \xB7 \uCCB4\uD06C\uB41C \uC644\uB8CC\uC870\uAC74 ${result.checkedCount}\uAC1C (\uB9E4\uD551 ${result.mappedCount} / \uBBF8\uAC80\uC99D ${result.unmappedCount}, coverage ${result.coverage * 100 | 0}%)`
6604
6739
  )
6605
6740
  );
6606
- console.log(chalk32.dim(` \uC99D\uAC70 \uC2E0\uC120\uB3C4: ${result.freshness.note}`));
6741
+ console.log(chalk31.dim(` \uC99D\uAC70 \uC2E0\uC120\uB3C4: ${result.freshness.note}`));
6607
6742
  if (result.checkedCount === 0) {
6608
- console.log(chalk32.yellow("\n \u26AA \uC644\uB8CC \uC8FC\uC7A5 \uC5C6\uC74C(\uCCB4\uD06C\uB41C \uC644\uB8CC\uC870\uAC74 0\uAC1C) \u2014 \uC2EC\uBB38\uD560 \uB300\uC0C1\uC774 \uC5C6\uC2B5\uB2C8\uB2E4(vacuous)."));
6743
+ console.log(chalk31.yellow("\n \u26AA \uC644\uB8CC \uC8FC\uC7A5 \uC5C6\uC74C(\uCCB4\uD06C\uB41C \uC644\uB8CC\uC870\uAC74 0\uAC1C) \u2014 \uC2EC\uBB38\uD560 \uB300\uC0C1\uC774 \uC5C6\uC2B5\uB2C8\uB2E4(vacuous)."));
6609
6744
  }
6610
6745
  if (result.suspicions.length > 0) {
6611
- console.log(chalk32.red.bold(`
6746
+ console.log(chalk31.red.bold(`
6612
6747
  \u{1F6A9} \uAC70\uC9D3\uC644\uB8CC \uC758\uC2EC ${result.suspicions.length}\uAC74`));
6613
- for (const s of result.suspicions) console.log(chalk32.red(` \u2717 ${s.check}
6748
+ for (const s of result.suspicions) console.log(chalk31.red(` \u2717 ${s.check}
6614
6749
  \u21B3 ${s.reason}`));
6615
6750
  }
6616
6751
  if (result.gaps.length > 0) {
6617
- console.log(chalk32.yellow.bold(`
6752
+ console.log(chalk31.yellow.bold(`
6618
6753
  \u26A0\uFE0F \uBBF8\uAC80\uC99D(unmapped) ${result.gaps.length}\uAC74 \u2014 \uAC8C\uC774\uD2B8\uB85C \uC790\uB3D9 \uD655\uC778 \uBD88\uAC00`));
6619
- for (const g of result.gaps) console.log(chalk32.yellow(` ? ${g.check}`));
6754
+ for (const g of result.gaps) console.log(chalk31.yellow(` ? ${g.check}`));
6620
6755
  }
6621
6756
  if (result.checkedCount > 0 && result.suspicions.length === 0 && result.gaps.length === 0) {
6622
- console.log(chalk32.green("\n \u2713 \uCCB4\uD06C\uB41C \uC644\uB8CC\uC870\uAC74\uC774 \uBAA8\uB450 \uAC8C\uC774\uD2B8 \uC99D\uAC70\uB85C \uB4B7\uBC1B\uCE68\uB428."));
6757
+ console.log(chalk31.green("\n \u2713 \uCCB4\uD06C\uB41C \uC644\uB8CC\uC870\uAC74\uC774 \uBAA8\uB450 \uAC8C\uC774\uD2B8 \uC99D\uAC70\uB85C \uB4B7\uBC1B\uCE68\uB428."));
6623
6758
  }
6624
6759
  console.log(`
6625
6760
  \uC2E0\uB8B0\uB3C4: ${CONFIDENCE_LABEL[result.confidence]}`);
6626
- console.log(chalk32.yellow(`
6761
+ console.log(chalk31.yellow(`
6627
6762
  ${result.disclaimer}`));
6628
6763
  let mergeOk = false;
6629
6764
  try {
6630
6765
  const merged = { ...report, review: result };
6631
- writeFileSync13(jsonPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
6766
+ atomicWriteFile(jsonPath, JSON.stringify(merged, null, 2) + "\n");
6632
6767
  mergeOk = true;
6633
- console.log(chalk32.dim(` \u{1F4C4} \uD310\uC815 \uBCD1\uD569: ${REPORT_PATH_REL} (review \uC139\uC158)`));
6768
+ console.log(chalk31.dim(` \u{1F4C4} \uD310\uC815 \uBCD1\uD569: ${REPORT_PATH_REL} (review \uC139\uC158)`));
6634
6769
  } catch (e) {
6635
- console.error(chalk32.red(` \u274C review \uD310\uC815 \uAE30\uB85D \uC2E4\uD328: ${e instanceof Error ? e.message : String(e)}`));
6770
+ console.error(chalk31.red(` \u274C review \uD310\uC815 \uAE30\uB85D \uC2E4\uD328: ${e instanceof Error ? e.message : String(e)}`));
6636
6771
  }
6637
6772
  if (!mergeOk) {
6638
6773
  process.exitCode = 1;
@@ -6653,8 +6788,8 @@ ${result.disclaimer}`));
6653
6788
  cursorHint: "\uC644\uB8CC\uC870\uAC74 \uCC44\uC6CC\uC918"
6654
6789
  });
6655
6790
  } else if (result.suspicions.length > 0) {
6656
- console.log(chalk32.dim("\n AI \uC7AC\uC9C8\uBB38 \uD504\uB86C\uD504\uD2B8:"));
6657
- console.log(chalk32.cyan(result.reprompt.split("\n").map((l) => ` ${l}`).join("\n")));
6791
+ console.log(chalk31.dim("\n AI \uC7AC\uC9C8\uBB38 \uD504\uB86C\uD504\uD2B8:"));
6792
+ console.log(chalk31.cyan(result.reprompt.split("\n").map((l) => ` ${l}`).join("\n")));
6658
6793
  printNextStep({
6659
6794
  message: "\uAC70\uC9D3\uC644\uB8CC \uC758\uC2EC\uC73C\uB85C \uC2E4\uD328(exit 1) \u2014 \uC99D\uAC70 \uBCF4\uAC15 \uD6C4 \uB2E4\uC2DC \uAC80\uC99D\uD558\uC138\uC694:",
6660
6795
  command: "vhk verify",
@@ -6668,8 +6803,8 @@ ${result.disclaimer}`));
6668
6803
  cursorHint: "goal \uC644\uB8CC \uCC98\uB9AC\uD574\uC918"
6669
6804
  });
6670
6805
  } else {
6671
- console.log(chalk32.dim("\n AI \uC7AC\uC9C8\uBB38 \uD504\uB86C\uD504\uD2B8:"));
6672
- console.log(chalk32.cyan(result.reprompt.split("\n").map((l) => ` ${l}`).join("\n")));
6806
+ console.log(chalk31.dim("\n AI \uC7AC\uC9C8\uBB38 \uD504\uB86C\uD504\uD2B8:"));
6807
+ console.log(chalk31.cyan(result.reprompt.split("\n").map((l) => ` ${l}`).join("\n")));
6673
6808
  printNextStep({
6674
6809
  message: `\uC99D\uAC70 \uBD88\uCDA9\uBD84(\uC2E0\uB8B0\uB3C4 ${result.confidence}) \u2014 goal done \uAE08\uC9C0. \uCEE4\uBC84\uB9AC\uC9C0/\uC2E0\uC120\uB3C4 \uBCF4\uAC15 \uD6C4 \uC7AC\uAC80\uC99D:`,
6675
6810
  command: "vhk verify",
@@ -6679,12 +6814,12 @@ ${result.disclaimer}`));
6679
6814
  }
6680
6815
 
6681
6816
  // src/commands/mission.ts
6682
- import { existsSync as existsSync19, mkdirSync as mkdirSync13, writeFileSync as writeFileSync14, rmSync as rmSync4 } from "fs";
6683
- import { join as join14 } from "path";
6684
- import chalk33 from "chalk";
6817
+ import { existsSync as existsSync18, mkdirSync as mkdirSync12, rmSync as rmSync3 } from "fs";
6818
+ import { join as join13 } from "path";
6819
+ import chalk32 from "chalk";
6685
6820
  import inquirer12 from "inquirer";
6686
6821
  import { simpleGit as simpleGit3 } from "simple-git";
6687
- var MISSION_PATH_REL = join14(".vhk", "mission.json");
6822
+ var MISSION_PATH_REL = join13(".vhk", "mission.json");
6688
6823
  var MISSION_SCHEMA_VERSION = 1;
6689
6824
  var MISSION_DISCLAIMER = "\u26A0\uFE0F mission check \uB294 \uACBD\uB85C glob \uAE30\uC900\uC785\uB2C8\uB2E4 \u2014 objective \uC758\uBBF8 \uBD80\uD569\uC740 \uAC80\uC99D\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4(\uC2E0\uB8B0\uB3C4 \uC2E0\uD638, \uBCF4\uC7A5 \uC544\uB2D8).";
6690
6825
  function globToRegExp(glob) {
@@ -6732,8 +6867,8 @@ function checkMission(changedFiles, mission) {
6732
6867
  return { violations, warnings, disclaimer: MISSION_DISCLAIMER };
6733
6868
  }
6734
6869
  function readMission(cwd = process.cwd()) {
6735
- const p = join14(cwd, MISSION_PATH_REL);
6736
- if (!existsSync19(p)) return null;
6870
+ const p = join13(cwd, MISSION_PATH_REL);
6871
+ if (!existsSync18(p)) return null;
6737
6872
  try {
6738
6873
  const m = readJsonFile(p);
6739
6874
  if (m && typeof m.objective === "string") return m;
@@ -6743,8 +6878,8 @@ function readMission(cwd = process.cwd()) {
6743
6878
  }
6744
6879
  }
6745
6880
  function writeMission(cwd, mission) {
6746
- mkdirSync13(join14(cwd, ".vhk"), { recursive: true });
6747
- writeFileSync14(join14(cwd, MISSION_PATH_REL), JSON.stringify(mission, null, 2) + "\n", "utf-8");
6881
+ mkdirSync12(join13(cwd, ".vhk"), { recursive: true });
6882
+ atomicWriteFile(join13(cwd, MISSION_PATH_REL), JSON.stringify(mission, null, 2) + "\n");
6748
6883
  }
6749
6884
  async function collectChangedFiles(cwd) {
6750
6885
  const status2 = await simpleGit3(cwd).status();
@@ -6765,7 +6900,7 @@ async function missionSet(opts = {}) {
6765
6900
  objective = ans.obj.trim();
6766
6901
  }
6767
6902
  if (!objective) {
6768
- console.error(chalk33.red(' \u274C objective \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. --objective "..." \uB85C \uC9C0\uC815\uD558\uC138\uC694(\uBE44\uB300\uD654\uD615).'));
6903
+ console.error(chalk32.red(' \u274C objective \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. --objective "..." \uB85C \uC9C0\uC815\uD558\uC138\uC694(\uBE44\uB300\uD654\uD615).'));
6769
6904
  process.exitCode = 1;
6770
6905
  return;
6771
6906
  }
@@ -6784,12 +6919,12 @@ async function missionSet(opts = {}) {
6784
6919
  try {
6785
6920
  writeMission(cwd, mission);
6786
6921
  } catch (e) {
6787
- console.error(chalk33.red(` \u274C mission.json \uAE30\uB85D \uC2E4\uD328: ${e instanceof Error ? e.message : String(e)}`));
6922
+ console.error(chalk32.red(` \u274C mission.json \uAE30\uB85D \uC2E4\uD328: ${e instanceof Error ? e.message : String(e)}`));
6788
6923
  process.exitCode = 1;
6789
6924
  return;
6790
6925
  }
6791
- console.log(chalk33.bold("\n\u{1F3AF} \uBBF8\uC158 \uACC4\uC57D \uC800\uC7A5"));
6792
- console.log(chalk33.dim(` \u{1F4C4} ${MISSION_PATH_REL}`));
6926
+ console.log(chalk32.bold("\n\u{1F3AF} \uBBF8\uC158 \uACC4\uC57D \uC800\uC7A5"));
6927
+ console.log(chalk32.dim(` \u{1F4C4} ${MISSION_PATH_REL}`));
6793
6928
  console.log(` objective: ${mission.objective}`);
6794
6929
  console.log(` scope: ${mission.scope.length ? mission.scope.join(", ") : "(\uC81C\uD55C \uC5C6\uC74C)"}`);
6795
6930
  console.log(` forbidden: ${mission.forbidden.length ? mission.forbidden.join(", ") : "(\uC5C6\uC74C)"}`);
@@ -6799,64 +6934,65 @@ async function missionShow() {
6799
6934
  const cwd = process.cwd();
6800
6935
  const mission = readMission(cwd);
6801
6936
  if (!mission) {
6802
- console.error(chalk33.yellow(" \u26A0\uFE0F \uBBF8\uC158 \uACC4\uC57D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 (.vhk/mission.json)."));
6937
+ console.error(chalk32.yellow(" \u26A0\uFE0F \uBBF8\uC158 \uACC4\uC57D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 (.vhk/mission.json)."));
6803
6938
  printNextStep({ message: "\uBA3C\uC800 \uBBF8\uC158\uC744 \uC120\uC5B8\uD558\uC138\uC694:", command: 'vhk mission set --objective "..."', cursorHint: "\uBBF8\uC158 \uC815\uD574\uC918" });
6804
6939
  process.exitCode = 1;
6805
6940
  return;
6806
6941
  }
6807
- console.log(chalk33.bold("\n\u{1F3AF} \uD604\uC7AC \uBBF8\uC158 \uACC4\uC57D"));
6942
+ console.log(chalk32.bold("\n\u{1F3AF} \uD604\uC7AC \uBBF8\uC158 \uACC4\uC57D"));
6808
6943
  console.log(` objective: ${mission.objective}`);
6809
6944
  console.log(` scope: ${mission.scope.length ? mission.scope.join(", ") : "(\uC81C\uD55C \uC5C6\uC74C)"}`);
6810
6945
  console.log(` forbidden: ${mission.forbidden.length ? mission.forbidden.join(", ") : "(\uC5C6\uC74C)"}`);
6811
- console.log(chalk33.dim(` \uC0DD\uC131 ${mission.createdAt} \xB7 \uAC31\uC2E0 ${mission.updatedAt}`));
6946
+ console.log(chalk32.dim(` \uC0DD\uC131 ${mission.createdAt} \xB7 \uAC31\uC2E0 ${mission.updatedAt}`));
6812
6947
  }
6813
6948
  async function missionCheck() {
6814
6949
  const cwd = process.cwd();
6815
6950
  const mission = readMission(cwd);
6816
6951
  if (!mission) {
6817
- console.error(chalk33.yellow(" \u26A0\uFE0F \uBBF8\uC158 \uACC4\uC57D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uBA3C\uC800 vhk mission set \uC73C\uB85C \uC120\uC5B8\uD558\uC138\uC694."));
6952
+ console.error(chalk32.yellow(" \u26A0\uFE0F \uBBF8\uC158 \uACC4\uC57D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uBA3C\uC800 vhk mission set \uC73C\uB85C \uC120\uC5B8\uD558\uC138\uC694."));
6818
6953
  process.exitCode = 1;
6819
6954
  return;
6820
6955
  }
6821
6956
  const changed = await collectChangedFiles(cwd);
6822
6957
  const result = checkMission(changed, mission);
6823
- console.log(chalk33.bold("\n\u{1F3AF} \uBBF8\uC158 \uACC4\uC57D \uAC80\uC99D (mission check)"));
6824
- console.log(chalk33.dim(` objective: ${mission.objective} \xB7 \uBCC0\uACBD \uD30C\uC77C ${changed.length}\uAC1C`));
6958
+ console.log(chalk32.bold("\n\u{1F3AF} \uBBF8\uC158 \uACC4\uC57D \uAC80\uC99D (mission check)"));
6959
+ console.log(chalk32.dim(` objective: ${mission.objective} \xB7 \uBCC0\uACBD \uD30C\uC77C ${changed.length}\uAC1C`));
6825
6960
  if (result.violations.length > 0) {
6826
- console.log(chalk33.red.bold(`
6961
+ console.log(chalk32.red.bold(`
6827
6962
  \u{1F6AB} forbidden \uC704\uBC18 ${result.violations.length}\uAC74`));
6828
- for (const v of result.violations) console.log(chalk33.red(` \u2717 ${v.file} (\uAE08\uC9C0: ${v.pattern})`));
6963
+ for (const v of result.violations) console.log(chalk32.red(` \u2717 ${v.file} (\uAE08\uC9C0: ${v.pattern})`));
6829
6964
  }
6830
6965
  if (result.warnings.length > 0) {
6831
- console.log(chalk33.yellow.bold(`
6966
+ console.log(chalk32.yellow.bold(`
6832
6967
  \u26A0\uFE0F scope \uBC16 \uBCC0\uACBD ${result.warnings.length}\uAC74 (\uACBD\uACE0)`));
6833
- for (const w of result.warnings) console.log(chalk33.yellow(` ? ${w.file}`));
6968
+ for (const w of result.warnings) console.log(chalk32.yellow(` ? ${w.file}`));
6834
6969
  }
6835
6970
  if (result.violations.length === 0 && result.warnings.length === 0) {
6836
- console.log(chalk33.green("\n \u2713 \uBCC0\uACBD\uC774 \uACC4\uC57D(scope/forbidden) \uC548\uC785\uB2C8\uB2E4."));
6971
+ console.log(chalk32.green("\n \u2713 \uBCC0\uACBD\uC774 \uACC4\uC57D(scope/forbidden) \uC548\uC785\uB2C8\uB2E4."));
6837
6972
  }
6838
- console.log(chalk33.yellow(`
6973
+ console.log(chalk32.yellow(`
6839
6974
  ${result.disclaimer}`));
6840
6975
  process.exitCode = result.violations.length > 0 ? 1 : 0;
6841
6976
  }
6842
6977
  async function missionClear() {
6978
+ if (!ensureNotHardStopped("mission clear")) return;
6843
6979
  const cwd = process.cwd();
6844
- const p = join14(cwd, MISSION_PATH_REL);
6845
- if (!existsSync19(p)) {
6846
- console.log(chalk33.dim(" \uBBF8\uC158 \uACC4\uC57D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uC9C0\uC6B8 \uAC83 \uC5C6\uC74C."));
6980
+ const p = join13(cwd, MISSION_PATH_REL);
6981
+ if (!existsSync18(p)) {
6982
+ console.log(chalk32.dim(" \uBBF8\uC158 \uACC4\uC57D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uC9C0\uC6B8 \uAC83 \uC5C6\uC74C."));
6847
6983
  return;
6848
6984
  }
6849
6985
  try {
6850
- rmSync4(p);
6851
- console.log(chalk33.green(" \u2705 \uBBF8\uC158 \uACC4\uC57D \uC0AD\uC81C\uB428 (.vhk/mission.json)."));
6986
+ rmSync3(p);
6987
+ console.log(chalk32.green(" \u2705 \uBBF8\uC158 \uACC4\uC57D \uC0AD\uC81C\uB428 (.vhk/mission.json)."));
6852
6988
  } catch (e) {
6853
- console.error(chalk33.red(` \u274C \uC0AD\uC81C \uC2E4\uD328: ${e instanceof Error ? e.message : String(e)}`));
6989
+ console.error(chalk32.red(` \u274C \uC0AD\uC81C \uC2E4\uD328: ${e instanceof Error ? e.message : String(e)}`));
6854
6990
  process.exitCode = 1;
6855
6991
  }
6856
6992
  }
6857
6993
 
6858
6994
  // src/commands/pattern.ts
6859
- import chalk34 from "chalk";
6995
+ import chalk33 from "chalk";
6860
6996
  var MIN_TAG_FREQ = 3;
6861
6997
  var STOPWORDS = /* @__PURE__ */ new Set(["the", "a", "an", "is", "are", "and", "or", "in", "on", "at", "to", "for", "of", "with", "it", "was", "be"]);
6862
6998
  function tokenize(text) {
@@ -6974,14 +7110,14 @@ function reconcilePatterns(patterns, candidates, now) {
6974
7110
  async function patternDetect(opts = {}) {
6975
7111
  const minFreq = opts.min !== void 0 ? parseInt(opts.min, 10) : MIN_TAG_FREQ;
6976
7112
  if (!Number.isFinite(minFreq) || minFreq < 1) {
6977
- console.log(chalk34.red("\u274C --min \uC740 1 \uC774\uC0C1\uC758 \uC815\uC218\uC5EC\uC57C \uD569\uB2C8\uB2E4."));
7113
+ console.log(chalk33.red("\u274C --min \uC740 1 \uC774\uC0C1\uC758 \uC815\uC218\uC5EC\uC57C \uD569\uB2C8\uB2E4."));
6978
7114
  process.exitCode = 1;
6979
7115
  return;
6980
7116
  }
6981
7117
  const cwd = process.cwd();
6982
7118
  const loaded = loadForMutation(cwd);
6983
7119
  if (!loaded.ok) {
6984
- console.log(chalk34.red("\u274C memory.json \uC190\uC0C1 \uC758\uC2EC \u2014 \uAC10\uC9C0 \uC911\uB2E8 (\uC6D0\uBCF8 \uBCF4\uC874). \uBC31\uC5C5 \uD655\uC778 \uD6C4 \uC7AC\uC2DC\uB3C4\uD558\uC138\uC694."));
7120
+ console.log(chalk33.red("\u274C memory.json \uC190\uC0C1 \uC758\uC2EC \u2014 \uAC10\uC9C0 \uC911\uB2E8 (\uC6D0\uBCF8 \uBCF4\uC874). \uBC31\uC5C5 \uD655\uC778 \uD6C4 \uC7AC\uC2DC\uB3C4\uD558\uC138\uC694."));
6985
7121
  process.exitCode = 1;
6986
7122
  return;
6987
7123
  }
@@ -6997,26 +7133,26 @@ async function patternDetect(opts = {}) {
6997
7133
  console.log(JSON.stringify(active2, null, 2));
6998
7134
  return;
6999
7135
  }
7000
- console.log(chalk34.bold("\n\u{1F50D} " + t("pattern.detectTitle")));
7001
- console.log(chalk34.gray("\u2500".repeat(40)));
7002
- console.log(chalk34.dim(` \uC784\uACC4: ${minFreq}\uD68C \uC774\uC0C1 \xB7 active failures+successes \uC785\uB825`));
7003
- console.log(chalk34.dim(` \uD6C4\uBCF4: ${candidates.length}\uAC1C \uAC10\uC9C0 (\uCD94\uAC00 ${added} / \uAC31\uC2E0 ${updated})`));
7136
+ console.log(chalk33.bold("\n\u{1F50D} " + t("pattern.detectTitle")));
7137
+ console.log(chalk33.gray("\u2500".repeat(40)));
7138
+ console.log(chalk33.dim(` \uC784\uACC4: ${minFreq}\uD68C \uC774\uC0C1 \xB7 active failures+successes \uC785\uB825`));
7139
+ console.log(chalk33.dim(` \uD6C4\uBCF4: ${candidates.length}\uAC1C \uAC10\uC9C0 (\uCD94\uAC00 ${added} / \uAC31\uC2E0 ${updated})`));
7004
7140
  const active = mem.patterns.filter((p) => p.status !== "archived");
7005
7141
  if (active.length === 0) {
7006
- console.log(chalk34.yellow("\n\u{1F4ED} \uC784\uACC4 \uC774\uC0C1 \uBC18\uBCF5 \uD328\uD134\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
7007
- console.log(chalk34.gray(` failures/successes \uAC00 ${minFreq}\uAC1C \uC774\uC0C1 \uC313\uC774\uBA74 \uAC10\uC9C0\uB429\uB2C8\uB2E4.`));
7008
- console.log(chalk34.gray(" --min N \uC73C\uB85C \uC784\uACC4\uB97C \uB0AE\uCD9C \uC218 \uC788\uC2B5\uB2C8\uB2E4."));
7142
+ console.log(chalk33.yellow("\n\u{1F4ED} \uC784\uACC4 \uC774\uC0C1 \uBC18\uBCF5 \uD328\uD134\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
7143
+ console.log(chalk33.gray(` failures/successes \uAC00 ${minFreq}\uAC1C \uC774\uC0C1 \uC313\uC774\uBA74 \uAC10\uC9C0\uB429\uB2C8\uB2E4.`));
7144
+ console.log(chalk33.gray(" --min N \uC73C\uB85C \uC784\uACC4\uB97C \uB0AE\uCD9C \uC218 \uC788\uC2B5\uB2C8\uB2E4."));
7009
7145
  return;
7010
7146
  }
7011
- console.log(chalk34.cyan(`
7147
+ console.log(chalk33.cyan(`
7012
7148
  \uD328\uD134 \uD6C4\uBCF4 ${active.length}\uAC1C:
7013
7149
  `));
7014
7150
  for (const p of active.slice(0, 20)) {
7015
7151
  const icon = p.kind === "avoid" ? "\u26A0\uFE0F " : "\u2705";
7016
7152
  console.log(` [${p.id}] ${icon} (${p.kind}/${p.axis}) "${p.signal}" \u2014 ${p.count}\uAC74`);
7017
7153
  const preview = p.sources.slice(0, 5).join(", ") + (p.sources.length > 5 ? ` \uC678 ${p.sources.length - 5}\uAC74` : "");
7018
- console.log(chalk34.dim(` \uADFC\uAC70: ${preview}`));
7019
- console.log(chalk34.dim(` ${p.summary}`));
7154
+ console.log(chalk33.dim(` \uADFC\uAC70: ${preview}`));
7155
+ console.log(chalk33.dim(` ${p.summary}`));
7020
7156
  }
7021
7157
  printNextStep({
7022
7158
  message: `\uD328\uD134 \uAC10\uC9C0 \uC644\uB8CC! ${active.length}\uAC1C \uD6C4\uBCF4.`,
@@ -7026,8 +7162,8 @@ async function patternDetect(opts = {}) {
7026
7162
  });
7027
7163
  }
7028
7164
  async function patternList(opts = {}) {
7029
- console.log(chalk34.bold("\n\u{1F50D} " + t("pattern.listTitle")));
7030
- console.log(chalk34.gray("\u2500".repeat(40)));
7165
+ console.log(chalk33.bold("\n\u{1F50D} " + t("pattern.listTitle")));
7166
+ console.log(chalk33.gray("\u2500".repeat(40)));
7031
7167
  const mem = readMemory(process.cwd());
7032
7168
  let patterns = mem.patterns;
7033
7169
  if (!opts.all) patterns = patterns.filter((p) => p.status !== "archived");
@@ -7039,51 +7175,52 @@ async function patternList(opts = {}) {
7039
7175
  return;
7040
7176
  }
7041
7177
  if (patterns.length === 0) {
7042
- console.log(chalk34.yellow("\n\u{1F4ED} \uD45C\uC2DC\uD560 \uD328\uD134\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
7043
- console.log(chalk34.gray(" vhk pattern detect \uB85C \uAC10\uC9C0 \uBA3C\uC800 \uC2E4\uD589\uD558\uC138\uC694."));
7178
+ console.log(chalk33.yellow("\n\u{1F4ED} \uD45C\uC2DC\uD560 \uD328\uD134\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
7179
+ console.log(chalk33.gray(" vhk pattern detect \uB85C \uAC10\uC9C0 \uBA3C\uC800 \uC2E4\uD589\uD558\uC138\uC694."));
7044
7180
  return;
7045
7181
  }
7046
- console.log(chalk34.cyan(`
7182
+ console.log(chalk33.cyan(`
7047
7183
  ${patterns.length}\uAC1C${opts.all ? " (\uBCF4\uAD00 \uD3EC\uD568)" : " (\uD65C\uC131)"}:
7048
7184
  `));
7049
7185
  for (const p of patterns) {
7050
7186
  const icon = p.kind === "avoid" ? "\u26A0\uFE0F " : "\u2705";
7051
7187
  const archived = p.status === "archived" ? "\u{1F4E6} " : "";
7052
7188
  console.log(` [${p.id}] ${archived}${icon} (${p.kind}/${p.axis}) "${p.signal}" \u2014 ${p.count}\uAC74`);
7053
- if (p.summary) console.log(chalk34.dim(` ${p.summary}`));
7054
- if (p.tags?.length) console.log(chalk34.blue(` \u{1F3F7}\uFE0F ${p.tags.join(", ")}`));
7189
+ if (p.summary) console.log(chalk33.dim(` ${p.summary}`));
7190
+ if (p.tags?.length) console.log(chalk33.blue(` \u{1F3F7}\uFE0F ${p.tags.join(", ")}`));
7055
7191
  }
7056
7192
  }
7057
7193
  async function patternDismiss(idStr) {
7194
+ if (!ensureNotHardStopped("pattern dismiss")) return;
7058
7195
  if (!idStr?.trim()) {
7059
- console.log(chalk34.red("\u274C \uD328\uD134 id \uB97C \uC785\uB825\uD574\uC8FC\uC138\uC694. \uC608: vhk pattern dismiss p1"));
7196
+ console.log(chalk33.red("\u274C \uD328\uD134 id \uB97C \uC785\uB825\uD574\uC8FC\uC138\uC694. \uC608: vhk pattern dismiss p1"));
7060
7197
  process.exitCode = 1;
7061
7198
  return;
7062
7199
  }
7063
7200
  const cwd = process.cwd();
7064
7201
  const loaded = loadForMutation(cwd);
7065
7202
  if (!loaded.ok) {
7066
- console.log(chalk34.red("\u274C memory.json \uC190\uC0C1 \uC758\uC2EC \u2014 dismiss \uC911\uB2E8 (\uC6D0\uBCF8 \uBCF4\uC874)."));
7203
+ console.log(chalk33.red("\u274C memory.json \uC190\uC0C1 \uC758\uC2EC \u2014 dismiss \uC911\uB2E8 (\uC6D0\uBCF8 \uBCF4\uC874)."));
7067
7204
  process.exitCode = 1;
7068
7205
  return;
7069
7206
  }
7070
7207
  const mem = loaded.mem;
7071
7208
  const pattern = mem.patterns.find((p) => p.id === idStr.trim());
7072
7209
  if (!pattern) {
7073
- console.log(chalk34.red(`\u274C \uD328\uD134 '${idStr}' \uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`));
7074
- console.log(chalk34.gray(" vhk pattern list --all \uB85C \uBAA9\uB85D \uD655\uC778."));
7210
+ console.log(chalk33.red(`\u274C \uD328\uD134 '${idStr}' \uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`));
7211
+ console.log(chalk33.gray(" vhk pattern list --all \uB85C \uBAA9\uB85D \uD655\uC778."));
7075
7212
  process.exitCode = 1;
7076
7213
  return;
7077
7214
  }
7078
7215
  if (pattern.status === "archived") {
7079
- console.log(chalk34.dim(` \uC774\uBBF8 \uBCF4\uAD00\uB41C \uD328\uD134\uC785\uB2C8\uB2E4 \u2014 \uBCC0\uACBD \uC5C6\uC74C: ${pattern.id}`));
7216
+ console.log(chalk33.dim(` \uC774\uBBF8 \uBCF4\uAD00\uB41C \uD328\uD134\uC785\uB2C8\uB2E4 \u2014 \uBCC0\uACBD \uC5C6\uC74C: ${pattern.id}`));
7080
7217
  return;
7081
7218
  }
7082
7219
  pattern.status = "archived";
7083
7220
  writeMemory(cwd, mem);
7084
- console.log(chalk34.green(`
7221
+ console.log(chalk33.green(`
7085
7222
  \u{1F4E6} \uD328\uD134 dismiss(\uBCF4\uAD00)\uB428: [${pattern.id}] ${pattern.summary ?? pattern.signal}`));
7086
- console.log(chalk34.dim(" \uC624\uD0D0\uC73C\uB85C \uD310\uB2E8. detect \uC7AC\uC2E4\uD589 \uC2DC \uC7AC\uC81C\uC548 \uC548 \uB428."));
7223
+ console.log(chalk33.dim(" \uC624\uD0D0\uC73C\uB85C \uD310\uB2E8. detect \uC7AC\uC2E4\uD589 \uC2DC \uC7AC\uC81C\uC548 \uC548 \uB428."));
7087
7224
  printNextStep({
7088
7225
  message: "\uD328\uD134 dismiss \uC644\uB8CC!",
7089
7226
  command: "vhk pattern list",
@@ -7092,11 +7229,11 @@ async function patternDismiss(idStr) {
7092
7229
  }
7093
7230
 
7094
7231
  // src/commands/evolve.ts
7095
- import { existsSync as existsSync20, mkdirSync as mkdirSync14, writeFileSync as writeFileSync15, readFileSync as readFileSync8, copyFileSync as copyFileSync2, renameSync as renameSync2, rmSync as rmSync5 } from "fs";
7096
- import { join as join15 } from "path";
7097
- import chalk35 from "chalk";
7232
+ import { existsSync as existsSync19, mkdirSync as mkdirSync13, writeFileSync as writeFileSync10, readFileSync as readFileSync7, copyFileSync as copyFileSync2, renameSync as renameSync2, rmSync as rmSync4 } from "fs";
7233
+ import { join as join14 } from "path";
7234
+ import chalk34 from "chalk";
7098
7235
  import inquirer13 from "inquirer";
7099
- var QUEUE_PATH_REL = join15(".vhk", "evolve", "queue.json");
7236
+ var QUEUE_PATH_REL = join14(".vhk", "evolve", "queue.json");
7100
7237
  function buildDraft(p) {
7101
7238
  const axisLabel = p.axis === "tag" ? `\uD0DC\uADF8 '${p.signal}'` : `\uD0A4\uC6CC\uB4DC '${p.signal}'`;
7102
7239
  const countDesc = `${p.count}\uAC74 \uBC18\uBCF5`;
@@ -7140,10 +7277,10 @@ function stripBomStr(s) {
7140
7277
  return s.charCodeAt(0) === 65279 ? s.slice(1) : s;
7141
7278
  }
7142
7279
  function readQueue(cwd) {
7143
- const p = join15(cwd, QUEUE_PATH_REL);
7144
- if (!existsSync20(p)) return { version: 1, items: [] };
7280
+ const p = join14(cwd, QUEUE_PATH_REL);
7281
+ if (!existsSync19(p)) return { version: 1, items: [] };
7145
7282
  try {
7146
- const raw = stripBomStr(readFileSync8(p, "utf-8"));
7283
+ const raw = stripBomStr(readFileSync7(p, "utf-8"));
7147
7284
  const parsed = JSON.parse(raw);
7148
7285
  if (!parsed || !Array.isArray(parsed.items)) return { version: 1, items: [] };
7149
7286
  return parsed;
@@ -7152,15 +7289,15 @@ function readQueue(cwd) {
7152
7289
  }
7153
7290
  }
7154
7291
  function writeQueue(cwd, queue) {
7155
- const p = join15(cwd, QUEUE_PATH_REL);
7156
- mkdirSync14(join15(cwd, ".vhk", "evolve"), { recursive: true });
7292
+ const p = join14(cwd, QUEUE_PATH_REL);
7293
+ mkdirSync13(join14(cwd, ".vhk", "evolve"), { recursive: true });
7157
7294
  const tmpPath = p + ".tmp";
7158
- writeFileSync15(tmpPath, JSON.stringify(queue, null, 2) + "\n", "utf-8");
7295
+ writeFileSync10(tmpPath, JSON.stringify(queue, null, 2) + "\n", "utf-8");
7159
7296
  try {
7160
7297
  renameSync2(tmpPath, p);
7161
7298
  } catch (err) {
7162
7299
  try {
7163
- rmSync5(tmpPath, { force: true });
7300
+ rmSync4(tmpPath, { force: true });
7164
7301
  } catch {
7165
7302
  }
7166
7303
  throw err;
@@ -7184,8 +7321,8 @@ function checkApplyRef(pattern, queueItems) {
7184
7321
  }
7185
7322
  async function evolveSuggest(opts = {}) {
7186
7323
  const cwd = process.cwd();
7187
- if (!existsSync20(join15(cwd, "RULES.md"))) {
7188
- console.log(chalk35.yellow("\n\u26A0\uFE0F " + t("evolve.noRules")));
7324
+ if (!existsSync19(join14(cwd, "RULES.md"))) {
7325
+ console.log(chalk34.yellow("\n\u26A0\uFE0F " + t("evolve.noRules")));
7189
7326
  process.exitCode = 1;
7190
7327
  return;
7191
7328
  }
@@ -7196,10 +7333,10 @@ async function evolveSuggest(opts = {}) {
7196
7333
  if (newItems.length === 0 && !opts.json) {
7197
7334
  const activeAvoid = patterns.filter((p) => p.kind === "avoid" && p.status === "active");
7198
7335
  if (activeAvoid.length === 0) {
7199
- console.log(chalk35.yellow("\n\u{1F4ED} " + t("evolve.noPatterns")));
7336
+ console.log(chalk34.yellow("\n\u{1F4ED} " + t("evolve.noPatterns")));
7200
7337
  return;
7201
7338
  }
7202
- console.log(chalk35.dim("\n " + t("evolve.allSuggested")));
7339
+ console.log(chalk34.dim("\n " + t("evolve.allSuggested")));
7203
7340
  return;
7204
7341
  }
7205
7342
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -7212,16 +7349,16 @@ async function evolveSuggest(opts = {}) {
7212
7349
  console.log(JSON.stringify(pending2, null, 2));
7213
7350
  return;
7214
7351
  }
7215
- console.log(chalk35.bold("\n\u{1F504} " + t("evolve.suggestTitle")));
7216
- console.log(chalk35.gray("\u2500".repeat(40)));
7217
- console.log(chalk35.dim(" " + t("evolve.newCandidates", newItems.length)));
7352
+ console.log(chalk34.bold("\n\u{1F504} " + t("evolve.suggestTitle")));
7353
+ console.log(chalk34.gray("\u2500".repeat(40)));
7354
+ console.log(chalk34.dim(" " + t("evolve.newCandidates", newItems.length)));
7218
7355
  const pending = queue.items.filter((i) => i.status === "pending");
7219
- console.log(chalk35.cyan(`
7356
+ console.log(chalk34.cyan(`
7220
7357
  \uD6C4\uBCF4 ${pending.length}\uAC1C:
7221
7358
  `));
7222
7359
  for (const item of pending) {
7223
7360
  console.log(` [${item.id}] (${item.status}) \uD328\uD134 ${item.patternId} \u2192 rule`);
7224
- console.log(chalk35.dim(` \uCD08\uC548: ${item.draft}`));
7361
+ console.log(chalk34.dim(` \uCD08\uC548: ${item.draft}`));
7225
7362
  }
7226
7363
  printNextStep({
7227
7364
  message: `\uC9C4\uD654 \uD6C4\uBCF4 ${pending.length}\uAC1C \uC0DD\uC131\uB428!`,
@@ -7242,11 +7379,11 @@ async function evolveList(opts = {}) {
7242
7379
  console.log(JSON.stringify(items, null, 2));
7243
7380
  return;
7244
7381
  }
7245
- console.log(chalk35.bold("\n\u{1F504} " + t("evolve.listTitle")));
7246
- console.log(chalk35.gray("\u2500".repeat(40)));
7382
+ console.log(chalk34.bold("\n\u{1F504} " + t("evolve.listTitle")));
7383
+ console.log(chalk34.gray("\u2500".repeat(40)));
7247
7384
  if (items.length === 0) {
7248
- console.log(chalk35.yellow("\n\u{1F4ED} " + t("evolve.noQueue")));
7249
- console.log(chalk35.gray(" " + t("evolve.suggestHint")));
7385
+ console.log(chalk34.yellow("\n\u{1F4ED} " + t("evolve.noQueue")));
7386
+ console.log(chalk34.gray(" " + t("evolve.suggestHint")));
7250
7387
  return;
7251
7388
  }
7252
7389
  const STATUS_ICON3 = {
@@ -7254,68 +7391,69 @@ async function evolveList(opts = {}) {
7254
7391
  rejected: "\u274C",
7255
7392
  applied: "\u2705"
7256
7393
  };
7257
- console.log(chalk35.cyan(`
7394
+ console.log(chalk34.cyan(`
7258
7395
  ${items.length}\uAC1C:
7259
7396
  `));
7260
7397
  for (const item of items) {
7261
7398
  console.log(` [${item.id}] ${STATUS_ICON3[item.status]} (${item.status}) \u2192 ${item.draft}`);
7262
- if (item.appliedAt) console.log(chalk35.dim(` \uBC18\uC601: ${item.appliedAt}`));
7399
+ if (item.appliedAt) console.log(chalk34.dim(` \uBC18\uC601: ${item.appliedAt}`));
7263
7400
  }
7264
7401
  }
7265
7402
  async function evolveApply(idStr) {
7403
+ if (!ensureNotHardStopped("evolve apply")) return;
7266
7404
  if (!ensureInteractive("apply\uB294 TTY \uD655\uC778\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. \uD130\uBBF8\uB110\uC5D0\uC11C \uC9C1\uC811 \uC2E4\uD589\uD558\uC138\uC694.")) return;
7267
7405
  const cwd = process.cwd();
7268
- const rulesPath = join15(cwd, "RULES.md");
7269
- if (!existsSync20(rulesPath)) {
7270
- console.log(chalk35.red("\n\u274C " + t("evolve.noRules")));
7406
+ const rulesPath = join14(cwd, "RULES.md");
7407
+ if (!existsSync19(rulesPath)) {
7408
+ console.log(chalk34.red("\n\u274C " + t("evolve.noRules")));
7271
7409
  process.exitCode = 1;
7272
7410
  return;
7273
7411
  }
7274
7412
  const queue = readQueue(cwd);
7275
7413
  const item = queue.items.find((i) => i.id === idStr?.trim());
7276
7414
  if (!item) {
7277
- console.log(chalk35.red("\n\u274C " + t("evolve.notFound", idStr ?? "")));
7415
+ console.log(chalk34.red("\n\u274C " + t("evolve.notFound", idStr ?? "")));
7278
7416
  process.exitCode = 1;
7279
7417
  return;
7280
7418
  }
7281
7419
  if (item.status === "applied") {
7282
- console.log(chalk35.yellow("\n\u26A0\uFE0F " + t("evolve.alreadyApplied")));
7420
+ console.log(chalk34.yellow("\n\u26A0\uFE0F " + t("evolve.alreadyApplied")));
7283
7421
  process.exitCode = 1;
7284
7422
  return;
7285
7423
  }
7286
7424
  const hasUnresolved = queue.items.some((i) => i.status === "applied");
7287
7425
  if (hasUnresolved) {
7288
- console.log(chalk35.red("\n\u274C " + t("evolve.pendingApplyExists")));
7426
+ console.log(chalk34.red("\n\u274C " + t("evolve.pendingApplyExists")));
7289
7427
  process.exitCode = 1;
7290
7428
  return;
7291
7429
  }
7292
7430
  const memLoaded = loadForMutation(cwd);
7293
7431
  if (!memLoaded.ok) {
7294
- console.log(chalk35.red("\n\u274C memory.json \uC190\uC0C1 \uC758\uC2EC \u2014 apply \uC911\uB2E8 (\uC6D0\uBCF8 \uBCF4\uC874)."));
7432
+ console.log(chalk34.red("\n\u274C memory.json \uC190\uC0C1 \uC758\uC2EC \u2014 apply \uC911\uB2E8 (\uC6D0\uBCF8 \uBCF4\uC874)."));
7295
7433
  process.exitCode = 1;
7296
7434
  return;
7297
7435
  }
7298
7436
  const srcPattern = memLoaded.mem.patterns.find((p) => p.id === item.patternId);
7299
7437
  const refResult = checkApplyRef(srcPattern, queue.items);
7300
7438
  if (refResult === "dismissed") {
7301
- console.log(chalk35.red("\n\u274C " + t("evolve.dismissed")));
7439
+ console.log(chalk34.red("\n\u274C " + t("evolve.dismissed")));
7302
7440
  process.exitCode = 1;
7303
7441
  return;
7304
7442
  }
7305
7443
  if (refResult === "already-applied") {
7306
- console.log(chalk35.red("\n\u274C " + t("evolve.alreadyAppliedPattern")));
7444
+ console.log(chalk34.red("\n\u274C " + t("evolve.alreadyAppliedPattern")));
7307
7445
  process.exitCode = 1;
7308
7446
  return;
7309
7447
  }
7310
- const rulesContent = readFileSync8(rulesPath, "utf-8");
7448
+ const rulesContent = readFileSync7(rulesPath, "utf-8");
7311
7449
  if (isDuplicateRule(rulesContent, item.draft)) {
7312
- console.log(chalk35.yellow("\n\u26A0\uFE0F " + t("evolve.duplicateRule", item.draft)));
7450
+ console.log(chalk34.yellow("\n\u26A0\uFE0F " + t("evolve.duplicateRule", item.draft)));
7313
7451
  return;
7314
7452
  }
7315
- console.log(chalk35.bold("\n\u{1F504} " + t("evolve.applyTitle")));
7316
- console.log(chalk35.gray("\u2500".repeat(40)));
7317
- console.log(chalk35.cyan("\n\uCD94\uAC00\uB420 \uB8F0 \uCD08\uC548:"));
7318
- console.log(chalk35.white(` ${item.draft}`));
7453
+ console.log(chalk34.bold("\n\u{1F504} " + t("evolve.applyTitle")));
7454
+ console.log(chalk34.gray("\u2500".repeat(40)));
7455
+ console.log(chalk34.cyan("\n\uCD94\uAC00\uB420 \uB8F0 \uCD08\uC548:"));
7456
+ console.log(chalk34.white(` ${item.draft}`));
7319
7457
  const { editedDraft } = await inquirer13.prompt([{
7320
7458
  type: "input",
7321
7459
  name: "editedDraft",
@@ -7330,22 +7468,22 @@ async function evolveApply(idStr) {
7330
7468
  default: false
7331
7469
  }]);
7332
7470
  if (!confirmed) {
7333
- console.log(chalk35.dim(" \uCDE8\uC18C\uB428."));
7471
+ console.log(chalk34.dim(" \uCDE8\uC18C\uB428."));
7334
7472
  return;
7335
7473
  }
7336
7474
  const backupPath = rulesPath + ".bak";
7337
7475
  copyFileSync2(rulesPath, backupPath);
7338
7476
  const appendContent = "\n" + editedDraft + "\n";
7339
7477
  try {
7340
- writeFileSync15(rulesPath, rulesContent + appendContent, "utf-8");
7478
+ writeFileSync10(rulesPath, rulesContent + appendContent, "utf-8");
7341
7479
  await sync({ yes: true });
7342
7480
  } catch (err) {
7343
7481
  try {
7344
7482
  copyFileSync2(backupPath, rulesPath);
7345
7483
  } catch {
7346
7484
  }
7347
- console.error(chalk35.red("\n\u274C RULES.md \uBC18\uC601 \uC911 \uC624\uB958 \u2014 \uC6D0\uBCF8 \uBCF5\uC6D0\uB428. \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694."));
7348
- console.error(chalk35.dim(` ${err instanceof Error ? err.message : String(err)}`));
7485
+ console.error(chalk34.red("\n\u274C RULES.md \uBC18\uC601 \uC911 \uC624\uB958 \u2014 \uC6D0\uBCF8 \uBCF5\uC6D0\uB428. \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694."));
7486
+ console.error(chalk34.dim(` ${err instanceof Error ? err.message : String(err)}`));
7349
7487
  process.exitCode = 1;
7350
7488
  return;
7351
7489
  }
@@ -7361,12 +7499,12 @@ async function evolveApply(idStr) {
7361
7499
  p.status = "archived";
7362
7500
  writeMemory(cwd, memLoaded.mem);
7363
7501
  } else {
7364
- console.error(chalk35.yellow(" \u26A0\uFE0F \uC18C\uC2A4 \uD328\uD134 archived \uCC98\uB9AC \uC2E4\uD328 (memory.json \uC190\uC0C1 \uC758\uC2EC). \uB8F0\uC740 \uBC18\uC601\uB410\uC2B5\uB2C8\uB2E4."));
7502
+ console.error(chalk34.yellow(" \u26A0\uFE0F \uC18C\uC2A4 \uD328\uD134 archived \uCC98\uB9AC \uC2E4\uD328 (memory.json \uC190\uC0C1 \uC758\uC2EC). \uB8F0\uC740 \uBC18\uC601\uB410\uC2B5\uB2C8\uB2E4."));
7365
7503
  }
7366
7504
  }
7367
- console.log(chalk35.green(`
7505
+ console.log(chalk34.green(`
7368
7506
  \u2705 \uB8F0 \uBC18\uC601 \uC644\uB8CC! [${item.id}]`));
7369
- console.log(chalk35.dim(" RULES.md\uC5D0 \uCD94\uAC00 + vhk sync \uC7AC\uC0DD\uC131\uB428"));
7507
+ console.log(chalk34.dim(" RULES.md\uC5D0 \uCD94\uAC00 + vhk sync \uC7AC\uC0DD\uC131\uB428"));
7370
7508
  printNextStep({
7371
7509
  message: "\uB8F0 \uBC18\uC601 \uC644\uB8CC!",
7372
7510
  command: "vhk evolve list --status applied",
@@ -7375,29 +7513,30 @@ async function evolveApply(idStr) {
7375
7513
  });
7376
7514
  }
7377
7515
  async function evolveReject(idStr) {
7516
+ if (!ensureNotHardStopped("evolve reject")) return;
7378
7517
  const cwd = process.cwd();
7379
7518
  const queue = readQueue(cwd);
7380
7519
  const item = queue.items.find((i) => i.id === idStr?.trim());
7381
7520
  if (!item) {
7382
- console.log(chalk35.red("\n\u274C " + t("evolve.notFound", idStr ?? "")));
7521
+ console.log(chalk34.red("\n\u274C " + t("evolve.notFound", idStr ?? "")));
7383
7522
  process.exitCode = 1;
7384
7523
  return;
7385
7524
  }
7386
7525
  if (item.status === "rejected") {
7387
- console.log(chalk35.dim(` \uC774\uBBF8 \uAE30\uAC01\uB41C \uD6C4\uBCF4\uC785\uB2C8\uB2E4 \u2014 \uBCC0\uACBD \uC5C6\uC74C: ${item.id}`));
7526
+ console.log(chalk34.dim(` \uC774\uBBF8 \uAE30\uAC01\uB41C \uD6C4\uBCF4\uC785\uB2C8\uB2E4 \u2014 \uBCC0\uACBD \uC5C6\uC74C: ${item.id}`));
7388
7527
  return;
7389
7528
  }
7390
7529
  if (item.status === "applied") {
7391
- console.log(chalk35.red(`
7530
+ console.log(chalk34.red(`
7392
7531
  \u274C \uC774\uBBF8 \uBC18\uC601\uB41C \uD56D\uBAA9\uC740 \uAE30\uAC01\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 vhk evolve undo \uB85C \uB418\uB3CC\uB9AC\uC138\uC694: ${item.id}`));
7393
7532
  process.exitCode = 1;
7394
7533
  return;
7395
7534
  }
7396
7535
  item.status = "rejected";
7397
7536
  writeQueue(cwd, queue);
7398
- console.log(chalk35.green(`
7537
+ console.log(chalk34.green(`
7399
7538
  \u274C \uD6C4\uBCF4 \uAE30\uAC01\uB428: [${item.id}] ${item.draft}`));
7400
- console.log(chalk35.dim(" (A1: \uB2E4\uC74C suggest\uC5D0\uC11C \uC7AC\uC81C\uC548 \uC548 \uB428)"));
7539
+ console.log(chalk34.dim(" (A1: \uB2E4\uC74C suggest\uC5D0\uC11C \uC7AC\uC81C\uC548 \uC548 \uB428)"));
7401
7540
  printNextStep({
7402
7541
  message: "\uAE30\uAC01 \uC644\uB8CC!",
7403
7542
  command: "vhk evolve list",
@@ -7405,24 +7544,25 @@ async function evolveReject(idStr) {
7405
7544
  });
7406
7545
  }
7407
7546
  async function evolveUndo() {
7547
+ if (!ensureNotHardStopped("evolve undo")) return;
7408
7548
  if (!ensureInteractive("undo\uB294 TTY \uD655\uC778\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. \uD130\uBBF8\uB110\uC5D0\uC11C \uC9C1\uC811 \uC2E4\uD589\uD558\uC138\uC694.")) return;
7409
7549
  const cwd = process.cwd();
7410
7550
  const queue = readQueue(cwd);
7411
7551
  const applied = queue.items.filter((i) => i.status === "applied");
7412
7552
  if (applied.length === 0) {
7413
- console.log(chalk35.yellow("\n\u{1F4ED} " + t("evolve.noAppliedToUndo")));
7553
+ console.log(chalk34.yellow("\n\u{1F4ED} " + t("evolve.noAppliedToUndo")));
7414
7554
  return;
7415
7555
  }
7416
7556
  const last = applied.sort(
7417
7557
  (a, b) => (b.appliedAt ?? "").localeCompare(a.appliedAt ?? "")
7418
7558
  )[0];
7419
- if (!last.rulesBackupPath || !existsSync20(last.rulesBackupPath)) {
7420
- console.log(chalk35.red("\n\u274C " + t("evolve.noBackup")));
7559
+ if (!last.rulesBackupPath || !existsSync19(last.rulesBackupPath)) {
7560
+ console.log(chalk34.red("\n\u274C " + t("evolve.noBackup")));
7421
7561
  process.exitCode = 1;
7422
7562
  return;
7423
7563
  }
7424
- console.log(chalk35.bold("\n\u{1F504} " + t("evolve.undoTitle")));
7425
- console.log(chalk35.dim(` \uB418\uB3CC\uB9B4 \uD56D\uBAA9: [${last.id}] ${last.draft}`));
7564
+ console.log(chalk34.bold("\n\u{1F504} " + t("evolve.undoTitle")));
7565
+ console.log(chalk34.dim(` \uB418\uB3CC\uB9B4 \uD56D\uBAA9: [${last.id}] ${last.draft}`));
7426
7566
  const { confirmed } = await inquirer13.prompt([{
7427
7567
  type: "confirm",
7428
7568
  name: "confirmed",
@@ -7430,16 +7570,16 @@ async function evolveUndo() {
7430
7570
  default: false
7431
7571
  }]);
7432
7572
  if (!confirmed) {
7433
- console.log(chalk35.dim(" \uCDE8\uC18C\uB428."));
7573
+ console.log(chalk34.dim(" \uCDE8\uC18C\uB428."));
7434
7574
  return;
7435
7575
  }
7436
- copyFileSync2(last.rulesBackupPath, join15(cwd, "RULES.md"));
7576
+ copyFileSync2(last.rulesBackupPath, join14(cwd, "RULES.md"));
7437
7577
  try {
7438
7578
  await sync({ yes: true });
7439
7579
  } catch (err) {
7440
- console.error(chalk35.red("\n\u274C sync \uC7AC\uC2E4\uD589 \uC911 \uC624\uB958. RULES.md\uB294 \uBCF5\uC6D0\uB410\uC73C\uB098 .cursorrules \uB4F1 \uC7AC\uC0DD\uC131 \uC2E4\uD328."));
7441
- console.error(chalk35.dim(` ${err instanceof Error ? err.message : String(err)}`));
7442
- console.error(chalk35.dim(" \uC218\uB3D9\uC73C\uB85C `vhk sync` \uC2E4\uD589\uD558\uC138\uC694."));
7580
+ console.error(chalk34.red("\n\u274C sync \uC7AC\uC2E4\uD589 \uC911 \uC624\uB958. RULES.md\uB294 \uBCF5\uC6D0\uB410\uC73C\uB098 .cursorrules \uB4F1 \uC7AC\uC0DD\uC131 \uC2E4\uD328."));
7581
+ console.error(chalk34.dim(` ${err instanceof Error ? err.message : String(err)}`));
7582
+ console.error(chalk34.dim(" \uC218\uB3D9\uC73C\uB85C `vhk sync` \uC2E4\uD589\uD558\uC138\uC694."));
7443
7583
  }
7444
7584
  last.status = "pending";
7445
7585
  delete last.appliedAt;
@@ -7453,7 +7593,7 @@ async function evolveUndo() {
7453
7593
  writeMemory(cwd, memLoaded.mem);
7454
7594
  }
7455
7595
  }
7456
- console.log(chalk35.green("\n\u2705 \uB418\uB3CC\uB9AC\uAE30 \uC644\uB8CC! RULES.md \uBCF5\uC6D0 + sync \uC7AC\uC2E4\uD589\uB428"));
7596
+ console.log(chalk34.green("\n\u2705 \uB418\uB3CC\uB9AC\uAE30 \uC644\uB8CC! RULES.md \uBCF5\uC6D0 + sync \uC7AC\uC2E4\uD589\uB428"));
7457
7597
  printNextStep({
7458
7598
  message: "\uB418\uB3CC\uB9AC\uAE30 \uC644\uB8CC!",
7459
7599
  command: "vhk evolve list",
@@ -7652,14 +7792,14 @@ function requiresConfirmation(route) {
7652
7792
  async function runNaturalLanguageRoute(input) {
7653
7793
  const route = routeNaturalLanguage(input);
7654
7794
  if (!route) {
7655
- console.log(chalk36.yellow(`
7795
+ console.log(chalk35.yellow(`
7656
7796
  \u2753 "${input}" \u2014 ${ko.nlp.notMatched}
7657
7797
  `));
7658
7798
  return;
7659
7799
  }
7660
7800
  console.log("");
7661
- console.log(chalk36.cyan(` \u{1F4AC} "${input}"`));
7662
- console.log(chalk36.cyan(` \u2192 ${route.explanation}`));
7801
+ console.log(chalk35.cyan(` \u{1F4AC} "${input}"`));
7802
+ console.log(chalk35.cyan(` \u2192 ${route.explanation}`));
7663
7803
  if (requiresConfirmation(route)) {
7664
7804
  const { confirm } = await inquirer14.prompt([{
7665
7805
  type: "confirm",
@@ -7668,7 +7808,7 @@ async function runNaturalLanguageRoute(input) {
7668
7808
  default: true
7669
7809
  }]);
7670
7810
  if (!confirm) {
7671
- console.log(chalk36.dim(` ${ko.nlp.menuHint}`));
7811
+ console.log(chalk35.dim(` ${ko.nlp.menuHint}`));
7672
7812
  return;
7673
7813
  }
7674
7814
  }
@@ -7677,7 +7817,7 @@ async function runNaturalLanguageRoute(input) {
7677
7817
  if (riskAction) {
7678
7818
  await runGuarded(
7679
7819
  riskAction,
7680
- { channel: "nl", approved: false, log: (m) => console.log(chalk36.yellow(` ${m}`)) },
7820
+ { channel: "nl", approved: false, log: (m) => console.log(chalk35.yellow(` ${m}`)) },
7681
7821
  () => dispatchNlpRoute(route, input)
7682
7822
  );
7683
7823
  return;
@@ -7685,81 +7825,341 @@ async function runNaturalLanguageRoute(input) {
7685
7825
  await dispatchNlpRoute(route, input);
7686
7826
  }
7687
7827
 
7688
- // src/commands/agent.ts
7828
+ // src/commands/preflight.ts
7829
+ import chalk36 from "chalk";
7830
+ import { existsSync as existsSync21 } from "fs";
7831
+ import { join as join16 } from "path";
7832
+
7833
+ // src/lib/worktree-env.ts
7834
+ import { existsSync as existsSync20, readFileSync as readFileSync8 } from "fs";
7835
+ import { join as join15 } from "path";
7836
+ function parseEnvKeys(content) {
7837
+ const keys = [];
7838
+ for (const raw of content.split(/\r?\n/)) {
7839
+ let line = raw.trim();
7840
+ if (!line || line.startsWith("#")) continue;
7841
+ if (line.startsWith("export ")) line = line.slice("export ".length).trim();
7842
+ const idx = line.indexOf("=");
7843
+ if (idx <= 0) continue;
7844
+ const key = line.slice(0, idx).trim();
7845
+ if (key) keys.push(key);
7846
+ }
7847
+ return keys;
7848
+ }
7849
+ function missingEnvKeys(required, present) {
7850
+ const have = new Set(present);
7851
+ return required.filter((k) => !have.has(k));
7852
+ }
7853
+ function checkWorktreeEnv(input) {
7854
+ const name = "worktree env";
7855
+ const severity = "critical";
7856
+ if (input.exampleContent === null) {
7857
+ return { name, status: "skip", detail: ".env.example \uC5C6\uC74C \u2014 \uD544\uC218 \uD0A4 \uBA85\uC138 \uC5C6\uC74C", severity };
7858
+ }
7859
+ const required = parseEnvKeys(input.exampleContent);
7860
+ if (required.length === 0) {
7861
+ return { name, status: "skip", detail: ".env.example \uC5D0 \uD544\uC218 \uD0A4 \uC5C6\uC74C", severity };
7862
+ }
7863
+ const present = input.envContent === null ? [] : parseEnvKeys(input.envContent);
7864
+ const missing = missingEnvKeys(required, present);
7865
+ if (missing.length === 0) {
7866
+ return { name, status: "pass", detail: `${required.length}/${required.length} keys present`, severity };
7867
+ }
7868
+ return {
7869
+ name,
7870
+ status: "fail",
7871
+ detail: `\uB204\uB77D ${missing.length}\uAC1C: ${missing.join(", ")}`,
7872
+ severity
7873
+ };
7874
+ }
7875
+ function checkWorktreeEnvDir(cwd) {
7876
+ const read = (p) => {
7877
+ const fp = join15(cwd, p);
7878
+ return existsSync20(fp) ? readFileSync8(fp, "utf-8") : null;
7879
+ };
7880
+ const exampleContent = read(".env.example");
7881
+ const envContent = read(".env.local") ?? read(".env");
7882
+ return checkWorktreeEnv({ exampleContent, envContent });
7883
+ }
7884
+
7885
+ // src/commands/preflight.ts
7886
+ var ESLINT_CONFIGS = [
7887
+ ".eslintrc.js",
7888
+ ".eslintrc.cjs",
7889
+ ".eslintrc.json",
7890
+ ".eslintrc.yml",
7891
+ "eslint.config.js",
7892
+ "eslint.config.mjs"
7893
+ ];
7894
+ async function preflight(opts = {}) {
7895
+ if (!ensureNotHardStopped("preflight")) return;
7896
+ console.log(chalk36.bold(`
7897
+ ${ko.preflight.title}
7898
+ `));
7899
+ const cwd = process.cwd();
7900
+ const run = (cmd, args) => {
7901
+ const r = safeExecFile(cmd, args);
7902
+ return r.ok ? { ok: true, out: r.out } : { ok: false, out: r.out, err: r.err };
7903
+ };
7904
+ let hasLintScript = false;
7905
+ try {
7906
+ const pkg = readJsonFile(join16(cwd, "package.json"));
7907
+ hasLintScript = !!pkg.scripts?.lint;
7908
+ } catch {
7909
+ }
7910
+ const hasEslintConfig = ESLINT_CONFIGS.some((f) => existsSync21(join16(cwd, f)));
7911
+ const hasLinter = detectHasLinter({ hasLintScript, hasEslintConfig });
7912
+ const mode2 = opts.publish ? "publish" : opts.pr ? "pr" : "default";
7913
+ const checks = runPreflight(
7914
+ { full: opts.full, mode: mode2 },
7915
+ {
7916
+ run,
7917
+ nodeVersion: process.version,
7918
+ hasLinter,
7919
+ worktreeEnv: () => checkWorktreeEnvDir(cwd)
7920
+ }
7921
+ );
7922
+ for (const c of checks) {
7923
+ console.log(` ${statusIcon(c)} ${c.name.padEnd(14)} ${chalk36.dim(c.detail)}`);
7924
+ }
7925
+ const s = summarizePreflight(checks);
7926
+ console.log("");
7927
+ if (s.blocked) {
7928
+ console.log(chalk36.red.bold(` ${ko.preflight.resultBlocked(s.failed)}`));
7929
+ printNextStep({
7930
+ message: ko.preflight.nextBlocked,
7931
+ command: "vhk preflight",
7932
+ cursorHint: "preflight \uB2E4\uC2DC \uB3CC\uB824\uC918"
7933
+ });
7934
+ process.exitCode = 1;
7935
+ } else {
7936
+ console.log(chalk36.green.bold(` ${ko.preflight.resultPass(s.warned)}`));
7937
+ printNextStep({
7938
+ message: ko.preflight.nextPass,
7939
+ command: opts.publish ? "vhk publish" : "vhk save",
7940
+ cursorHint: "\uB2E4\uC74C \uB2E8\uACC4\uB85C"
7941
+ });
7942
+ }
7943
+ }
7944
+
7945
+ // src/commands/standup.ts
7689
7946
  import chalk37 from "chalk";
7947
+
7948
+ // src/daily/lastActiveDay.ts
7949
+ function lastActiveDay(activityDates2, today) {
7950
+ let best = null;
7951
+ for (const d of activityDates2) {
7952
+ if (d < today && (best === null || d > best)) best = d;
7953
+ }
7954
+ return best;
7955
+ }
7956
+
7957
+ // src/daily/gitlog.ts
7958
+ function normalizeCommitDate(raw) {
7959
+ const m = raw.match(/(\d{4})-(\d{2})-(\d{2})/);
7960
+ return m ? `${m[1]}-${m[2]}-${m[3]}` : raw.slice(0, 10);
7961
+ }
7962
+ function commitsInRange(commits, range) {
7963
+ return commits.filter((c) => c.date >= range.start && c.date <= range.end);
7964
+ }
7965
+ function activityDates(commits) {
7966
+ return [...new Set(commits.map((c) => c.date))];
7967
+ }
7968
+ async function fetchRecentCommitSummaries(since, count = 200) {
7969
+ const raw = await getRecentCommits(count, since);
7970
+ return raw.map((c) => ({ hash: c.hash, message: c.message, date: normalizeCommitDate(c.date) }));
7971
+ }
7972
+
7973
+ // src/daily/goals.ts
7974
+ function slugFromPath(filePath) {
7975
+ const base = filePath.split(/[\\/]/).pop() ?? "";
7976
+ return base.replace(/\.md$/, "").replace(/^\d+-/, "");
7977
+ }
7978
+ function toState(g) {
7979
+ return {
7980
+ id: g.frontmatter.id,
7981
+ slug: slugFromPath(g.filePath),
7982
+ status: g.frontmatter.status ?? "NOT_STARTED"
7983
+ };
7984
+ }
7985
+ function toGoalStates(goals) {
7986
+ return goals.filter((g) => typeof g.frontmatter.id === "number").map(toState);
7987
+ }
7988
+ function recommendGoals(states) {
7989
+ return states.filter((s) => s.status === "NOT_STARTED" || s.status === "IN_PROGRESS");
7990
+ }
7991
+ function doneGoalsOnDay(goals, day) {
7992
+ return goals.filter((g) => g.frontmatter.status === "DONE" && g.frontmatter.completed === day).filter((g) => typeof g.frontmatter.id === "number").map(toState);
7993
+ }
7994
+ function unresolvedGoals(states) {
7995
+ return states.filter((s) => s.status === "BLOCKED").map((s) => `Goal ${s.id} (${s.slug}) \u2014 BLOCKED`);
7996
+ }
7997
+
7998
+ // src/daily/standup.ts
7999
+ function buildStandupReport(input) {
8000
+ const dates = [...activityDates(input.commits), ...input.devlogs.map((d) => d.date)];
8001
+ const last = lastActiveDay(dates, input.asOf);
8002
+ return {
8003
+ asOf: input.asOf,
8004
+ lastActiveDay: last,
8005
+ yesterday: {
8006
+ commits: last ? commitsInRange(input.commits, { start: last, end: last }) : [],
8007
+ devlogs: last ? input.devlogs.filter((d) => d.date === last) : [],
8008
+ doneGoals: last ? input.doneGoalsByDay(last) : []
8009
+ },
8010
+ todayRecommend: recommendGoals(input.goalStates),
8011
+ unresolved: unresolvedGoals(input.goalStates)
8012
+ };
8013
+ }
8014
+ async function runStandup(deps) {
8015
+ const asOf = deps?.asOf ?? localDate();
8016
+ let commits = [];
8017
+ try {
8018
+ commits = await fetchRecentCommitSummaries();
8019
+ } catch {
8020
+ commits = [];
8021
+ }
8022
+ let goals = [];
8023
+ try {
8024
+ goals = listGoals(deps?.goalsDir ?? "goals");
8025
+ } catch {
8026
+ goals = [];
8027
+ }
8028
+ const goalStates = toGoalStates(goals);
8029
+ return buildStandupReport({
8030
+ asOf,
8031
+ commits,
8032
+ goalStates,
8033
+ doneGoalsByDay: (day) => doneGoalsOnDay(goals, day),
8034
+ devlogs: []
8035
+ // Phase 2: Dev Log 노션 데이터소스 연동
8036
+ });
8037
+ }
8038
+
8039
+ // src/commands/standup.ts
8040
+ var WEEKDAYS = ["\uC77C", "\uC6D4", "\uD654", "\uC218", "\uBAA9", "\uAE08", "\uD1A0"];
8041
+ function withWeekday(ymd) {
8042
+ const m = ymd.match(/^(\d{4})-(\d{2})-(\d{2})$/);
8043
+ if (!m) return ymd;
8044
+ const d = new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
8045
+ return `${ymd} (${WEEKDAYS[d.getDay()]})`;
8046
+ }
8047
+ async function standup() {
8048
+ const r = await runStandup();
8049
+ console.log(chalk37.bold(`
8050
+ ${ko.standup.title(withWeekday(r.asOf))}
8051
+ `));
8052
+ console.log(chalk37.bold(` ${ko.standup.yesterday}`));
8053
+ if (r.lastActiveDay === null) {
8054
+ console.log(chalk37.dim(` ${ko.standup.noHistory}`));
8055
+ } else {
8056
+ console.log(chalk37.dim(` (\uB9C8\uC9C0\uB9C9 \uD65C\uB3D9\uC77C: ${r.lastActiveDay})`));
8057
+ const top = r.yesterday.commits.slice(0, 5);
8058
+ for (const c of top) console.log(` \u2022 ${c.message}`);
8059
+ if (r.yesterday.commits.length > top.length) {
8060
+ console.log(chalk37.dim(` \u2022 \u2026 \uC678 ${r.yesterday.commits.length - top.length}\uAC1C`));
8061
+ }
8062
+ console.log(chalk37.dim(` \u2022 ${ko.standup.commitsLine(r.yesterday.commits.length)}`));
8063
+ for (const dg of r.yesterday.doneGoals) console.log(` \u2705 Goal ${dg.id} (${dg.slug}) \uC644\uB8CC`);
8064
+ for (const dl of r.yesterday.devlogs) console.log(` \u{1F4DD} ${dl.title}`);
8065
+ }
8066
+ console.log("");
8067
+ console.log(chalk37.bold(` ${ko.standup.todayRecommend}`));
8068
+ if (r.todayRecommend.length === 0) {
8069
+ console.log(chalk37.green(" \u{1F389} \uBAA8\uB4E0 goal \uC644\uB8CC!"));
8070
+ } else {
8071
+ for (const g of r.todayRecommend) {
8072
+ const icon = g.status === "IN_PROGRESS" ? "\u{1F7E1}" : "\u26AA";
8073
+ console.log(` ${icon} Goal ${g.id} (${g.slug}) \u2014 ${g.status}`);
8074
+ }
8075
+ }
8076
+ if (r.unresolved.length > 0) {
8077
+ console.log("");
8078
+ console.log(chalk37.bold(` ${ko.standup.unresolved}`));
8079
+ for (const u of r.unresolved) console.log(chalk37.yellow(` \u2022 ${u}`));
8080
+ }
8081
+ printNextStep({
8082
+ message: "\uC624\uB298 \uC791\uC5C5 \uC2DC\uC791:",
8083
+ command: "vhk work",
8084
+ cursorHint: "\uC791\uC5C5 \uC2DC\uC791\uD560\uAC8C"
8085
+ });
8086
+ }
8087
+
8088
+ // src/commands/agent.ts
8089
+ import chalk38 from "chalk";
7690
8090
  function activeGoalId() {
7691
8091
  const goals = listGoals("goals");
7692
8092
  const id = selectActiveId(goals);
7693
8093
  return id ?? void 0;
7694
8094
  }
7695
8095
  async function blocker(description) {
7696
- console.log(chalk37.bold(`
8096
+ console.log(chalk38.bold(`
7697
8097
  ${ko.agent.blockerTitle}
7698
8098
  `));
7699
8099
  if (!description || !description.trim()) {
7700
- console.log(chalk37.red(" \u274C \uBE14\uB85C\uCEE4 \uC124\uBA85\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694."));
7701
- console.log(chalk37.dim(' \uC608: vhk blocker "tsc \uC5D0\uB7EC \u2014 simple-git \uD0C0\uC785 \uD638\uD658"'));
8100
+ console.log(chalk38.red(" \u274C \uBE14\uB85C\uCEE4 \uC124\uBA85\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694."));
8101
+ console.log(chalk38.dim(' \uC608: vhk blocker "tsc \uC5D0\uB7EC \u2014 simple-git \uD0C0\uC785 \uD638\uD658"'));
7702
8102
  process.exitCode = 1;
7703
8103
  return;
7704
8104
  }
7705
8105
  const goalId = activeGoalId();
7706
8106
  const r = appendBlocker(description, goalId);
7707
- console.log(chalk37.green(` \u2705 blocker \uAE30\uB85D (\uD604\uC7AC \uD65C\uC131 ${r.count}\uAC74)`));
8107
+ console.log(chalk38.green(` \u2705 blocker \uAE30\uB85D (\uD604\uC7AC \uD65C\uC131 ${r.count}\uAC74)`));
7708
8108
  if (r.hardStopTripped) {
7709
- console.log(chalk37.red.bold(" \u{1F6D1} HARD_STOP \uC790\uB3D9 \uC0DD\uC131 \u2014 \uBAA8\uB4E0 \uC790\uB3D9\uD654 \uC911\uB2E8."));
7710
- console.log(chalk37.yellow(" \uC0AC\uB78C \uAC80\uD1A0 \uD6C4 `vhk resume --confirm` \uC73C\uB85C\uB9CC \uD574\uC81C."));
8109
+ console.log(chalk38.red.bold(" \u{1F6D1} HARD_STOP \uC790\uB3D9 \uC0DD\uC131 \u2014 \uBAA8\uB4E0 \uC790\uB3D9\uD654 \uC911\uB2E8."));
8110
+ console.log(chalk38.yellow(" \uC0AC\uB78C \uAC80\uD1A0 \uD6C4 `vhk resume --confirm` \uC73C\uB85C\uB9CC \uD574\uC81C."));
7711
8111
  process.exitCode = 2;
7712
8112
  }
7713
8113
  }
7714
8114
  async function learn(lesson) {
7715
- console.log(chalk37.bold(`
8115
+ console.log(chalk38.bold(`
7716
8116
  ${ko.agent.learnTitle}
7717
8117
  `));
7718
8118
  if (!lesson || !lesson.trim()) {
7719
- console.log(chalk37.red(" \u274C \uAD50\uD6C8 \uB0B4\uC6A9\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694."));
7720
- console.log(chalk37.dim(' \uC608: vhk learn "PowerShell \uC5D0\uC11C\uB294 ; \uC0AC\uC6A9 (&& \uBBF8\uC9C0\uC6D0)"'));
8119
+ console.log(chalk38.red(" \u274C \uAD50\uD6C8 \uB0B4\uC6A9\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694."));
8120
+ console.log(chalk38.dim(' \uC608: vhk learn "PowerShell \uC5D0\uC11C\uB294 ; \uC0AC\uC6A9 (&& \uBBF8\uC9C0\uC6D0)"'));
7721
8121
  process.exitCode = 1;
7722
8122
  return;
7723
8123
  }
7724
8124
  const goalId = activeGoalId();
7725
8125
  const entry = recordLesson(process.cwd(), lesson, goalId);
7726
8126
  if (!entry) {
7727
- console.log(chalk37.red(" \u274C memory.json \uC190\uC0C1 \uC758\uC2EC \u2014 \uAD50\uD6C8 \uAE30\uB85D \uC911\uB2E8. \uC6D0\uBCF8/\uBC31\uC5C5 \uD655\uC778 \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694."));
8127
+ console.log(chalk38.red(" \u274C memory.json \uC190\uC0C1 \uC758\uC2EC \u2014 \uAD50\uD6C8 \uAE30\uB85D \uC911\uB2E8. \uC6D0\uBCF8/\uBC31\uC5C5 \uD655\uC778 \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694."));
7728
8128
  process.exitCode = 1;
7729
8129
  return;
7730
8130
  }
7731
- console.log(chalk37.green(` \u2705 \uAD50\uD6C8 \uAE30\uB85D \u2192 memory failures.lesson (${entry.id})`));
7732
- console.log(chalk37.dim(" \uAD50\uD6C8\xB7\uACB0\uC815\xB7\uC2E4\uD328\xB7\uC131\uACF5 \uBAA8\uB450 vhk memory (\uB2E8\uC77C SoT). vhk memory list \uB85C \uD655\uC778."));
8131
+ console.log(chalk38.green(` \u2705 \uAD50\uD6C8 \uAE30\uB85D \u2192 memory failures.lesson (${entry.id})`));
8132
+ console.log(chalk38.dim(" \uAD50\uD6C8\xB7\uACB0\uC815\xB7\uC2E4\uD328\xB7\uC131\uACF5 \uBAA8\uB450 vhk memory (\uB2E8\uC77C SoT). vhk memory list \uB85C \uD655\uC778."));
7733
8133
  }
7734
8134
  async function resume(opts = {}) {
7735
- console.log(chalk37.bold(`
8135
+ console.log(chalk38.bold(`
7736
8136
  ${ko.agent.resumeTitle}
7737
8137
  `));
7738
8138
  if (!isHardStopActive()) {
7739
- console.log(chalk37.dim(" HARD_STOP \uD65C\uC131 \uC544\uB2D8 \u2014 \uD560 \uC77C \uC5C6\uC74C."));
8139
+ console.log(chalk38.dim(" HARD_STOP \uD65C\uC131 \uC544\uB2D8 \u2014 \uD560 \uC77C \uC5C6\uC74C."));
7740
8140
  return;
7741
8141
  }
7742
8142
  const reason = readHardStopReason();
7743
8143
  if (reason) {
7744
- console.log(chalk37.yellow(" \u{1F4CB} HARD_STOP \uC0AC\uC720:"));
7745
- console.log(chalk37.dim(` ${reason.split("\n").join("\n ")}`));
8144
+ console.log(chalk38.yellow(" \u{1F4CB} HARD_STOP \uC0AC\uC720:"));
8145
+ console.log(chalk38.dim(` ${reason.split("\n").join("\n ")}`));
7746
8146
  console.log("");
7747
8147
  }
7748
8148
  if (!opts.confirm) {
7749
8149
  console.log(
7750
- chalk37.red(
8150
+ chalk38.red(
7751
8151
  " \u274C --confirm \uD50C\uB798\uADF8 \uC5C6\uC774\uB294 \uD574\uC81C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uC790\uB3D9 \uD638\uCD9C \uAE08\uC9C0)."
7752
8152
  )
7753
8153
  );
7754
- console.log(chalk37.yellow(" \uC0AC\uC720\uB97C \uD655\uC778\uD55C \uD6C4 \uB2E4\uC2DC: vhk resume --confirm"));
8154
+ console.log(chalk38.yellow(" \uC0AC\uC720\uB97C \uD655\uC778\uD55C \uD6C4 \uB2E4\uC2DC: vhk resume --confirm"));
7755
8155
  process.exitCode = 1;
7756
8156
  return;
7757
8157
  }
7758
8158
  const removed = clearHardStop();
7759
8159
  if (removed) {
7760
- console.log(chalk37.green(" \u2705 HARD_STOP \uD574\uC81C. \uC790\uB3D9\uD654 \uC7AC\uAC1C \uAC00\uB2A5."));
8160
+ console.log(chalk38.green(" \u2705 HARD_STOP \uD574\uC81C. \uC790\uB3D9\uD654 \uC7AC\uAC1C \uAC00\uB2A5."));
7761
8161
  } else {
7762
- console.log(chalk37.dim(" \uD30C\uC77C\uC774 \uC774\uBBF8 \uC5C6\uC74C \u2014 no-op."));
8162
+ console.log(chalk38.dim(" \uD30C\uC77C\uC774 \uC774\uBBF8 \uC5C6\uC74C \u2014 no-op."));
7763
8163
  }
7764
8164
  }
7765
8165
 
@@ -7780,7 +8180,7 @@ async function guardCli(action, approved, run) {
7780
8180
  }]);
7781
8181
  return ok;
7782
8182
  },
7783
- log: (m) => console.log(chalk38.yellow(` ${m}`))
8183
+ log: (m) => console.log(chalk39.yellow(` ${m}`))
7784
8184
  },
7785
8185
  run
7786
8186
  );
@@ -7793,7 +8193,7 @@ async function guardCliDefer(action, approved, run) {
7793
8193
  approved,
7794
8194
  // TTY 면 통과(명령이 자체 확인), 비대화형은 confirm 불가 → 가드가 차단.
7795
8195
  confirm: async () => !!process.stdout.isTTY,
7796
- log: (m) => console.log(chalk38.yellow(` ${m}`))
8196
+ log: (m) => console.log(chalk39.yellow(` ${m}`))
7797
8197
  },
7798
8198
  run
7799
8199
  );
@@ -7832,6 +8232,8 @@ var KO_ALIASES = {
7832
8232
  memory: "\uAE30\uC5B5",
7833
8233
  brief: "\uBE0C\uB9AC\uD551",
7834
8234
  goal: "\uBAA9\uD45C",
8235
+ preflight: "\uCD9C\uACE0\uC810\uAC80",
8236
+ standup: "\uC544\uCE68",
7835
8237
  review: "\uAC80\uD1A0",
7836
8238
  mission: "\uBBF8\uC158",
7837
8239
  blocker: "\uBE14\uB85C\uCEE4",
@@ -7963,6 +8365,12 @@ program.command("mode [target]").alias("\uBAA8\uB4DC").description("Safety Mode
7963
8365
  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)").option("--report", "latest.json \uC744 \uC0AC\uB78C\uC6A9 \uC815\uC801 HTML(.vhk/reports/latest.html) \uB85C \uB80C\uB354 (\uC678\uBD80 \uC758\uC874 0)").option("--open", "\uB9AC\uD3EC\uD2B8 \uC0DD\uC131 \uD6C4 \uAE30\uBCF8 \uBE0C\uB77C\uC6B0\uC800\uB85C \uC5F4\uAE30 (\uBE44\uB300\uD654\uD615/CI/MCP \uC790\uB3D9 \uC2A4\uD0B5)").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) => {
7964
8366
  await verify(opts);
7965
8367
  });
8368
+ program.command("preflight").alias("\uCD9C\uACE0\uC810\uAC80").option("--publish", "publish \uC9C1\uC804 \uC810\uAC80 (2FA\xB7\uBC84\uC804 \uAC15\uC870)").option("--pr", "PR \uC9C1\uC804 \uC810\uAC80 (lint\xB7\uD14C\uC2A4\uD2B8\xB7\uBE0C\uB79C\uCE58 \uAC15\uC870)").option("--full", "\uD14C\uC2A4\uD2B8 \uC804\uCCB4 \uC2E4\uD589 (--changed \uCE90\uC2DC \uBBF8\uC0AC\uC6A9)").description("\uCD9C\uACE0 \uC804 \uC548\uC804\uC810\uAC80 \u2014 2FA\xB7shim\xB7env\xB7lint\xB7\uD0C0\uC785\xB7\uD14C\uC2A4\uD2B8\xB7git 8\uAC1C \uD56D\uBAA9, \uCE58\uBA85 \uC2E4\uD328 \uC2DC \uCC28\uB2E8").action(async (opts) => {
8369
+ await preflight(opts);
8370
+ });
8371
+ program.command("standup").alias("\uC544\uCE68").description("\uC544\uCE68 \uBE0C\uB9AC\uD551 \u2014 \uC5B4\uC81C \uD55C \uC77C(\uB9C8\uC9C0\uB9C9 \uD65C\uB3D9\uC77C \uCEE4\uBC0B\xB7\uC644\uB8CC goal) + \uC624\uB298 \uCD94\uCC9C + \uBBF8\uD574\uACB0 (\uC77D\uAE30 \uC804\uC6A9)").action(async () => {
8372
+ await standup();
8373
+ });
7966
8374
  program.command("review").alias("\uAC80\uD1A0").option("--id <id>", "\uB300\uC0C1 goal id (\uC5C6\uC73C\uBA74 active goal)").description("\uC801\uB300\uC801 \uC790\uAE30\uAC80\uC99D \u2014 latest.json \u2194 goal \uC644\uB8CC\uC870\uAC74 \uAD50\uCC28\uAC80\uC99D (\uAC70\uC9D3\uC644\uB8CC \uC758\uC2EC \uD0D0\uC9C0, \uBCF4\uC7A5 \uC544\uB2D8)").action(async (opts) => {
7967
8375
  await review(opts);
7968
8376
  });
@@ -8085,13 +8493,13 @@ program.on("command:*", async (operands) => {
8085
8493
  });
8086
8494
  program.action(async () => {
8087
8495
  const info = getUpdateInfo();
8088
- console.log("\n\u{1F3AF} VHK \u2014 \uBC14\uC774\uBE0C\uCF54\uB529 \uD504\uB85C\uC81D\uD2B8 \uCF54\uCE58 " + chalk38.dim(`v${info.current}`));
8496
+ console.log("\n\u{1F3AF} VHK \u2014 \uBC14\uC774\uBE0C\uCF54\uB529 \uD504\uB85C\uC81D\uD2B8 \uCF54\uCE58 " + chalk39.dim(`v${info.current}`));
8089
8497
  if (info.updateAvailable && info.latest) {
8090
- console.log(chalk38.yellow(`\u{1F195} \uC5C5\uB370\uC774\uD2B8 \uAC00\uB2A5: v${info.latest}`) + chalk38.dim(" \u2192 vhk update"));
8498
+ console.log(chalk39.yellow(`\u{1F195} \uC5C5\uB370\uC774\uD2B8 \uAC00\uB2A5: v${info.latest}`) + chalk39.dim(" \u2192 vhk update"));
8091
8499
  }
8092
8500
  const sample = QUICK_ACTIONS[0]?.say ?? "\uC0C1\uD0DC \uC54C\uB824\uC918";
8093
8501
  console.log(
8094
- chalk38.dim("\u{1F4AC} \uBA85\uB839 \uC9C1\uC811 \uC785\uB825\uB3C4 \uB3FC\uC694 \u2014 \uC608: ") + chalk38.cyan("vhk status") + chalk38.dim(" \xB7 \uC790\uC5F0\uC5B4 OK: ") + chalk38.cyan(`"${sample}"`)
8502
+ chalk39.dim("\u{1F4AC} \uBA85\uB839 \uC9C1\uC811 \uC785\uB825\uB3C4 \uB3FC\uC694 \u2014 \uC608: ") + chalk39.cyan("vhk status") + chalk39.dim(" \xB7 \uC790\uC5F0\uC5B4 OK: ") + chalk39.cyan(`"${sample}"`)
8095
8503
  );
8096
8504
  console.log("");
8097
8505
  const choices = [
@@ -8171,9 +8579,9 @@ if (isMainModule) {
8171
8579
  }
8172
8580
  } catch (err) {
8173
8581
  if (isPromptAbortError(err)) {
8174
- console.error(chalk38.yellow("\n \u26A0\uFE0F \uB300\uD654\uD615 \uC785\uB825\uC774 \uCDE8\uC18C/\uC885\uB8CC\uB410\uC2B5\uB2C8\uB2E4. (\uBE44\uB300\uD654\uD615 \uD658\uACBD\uC5D0\uC11C\uB294 \uD574\uB2F9 \uBA85\uB839\uC744 \uC4F8 \uC218 \uC5C6\uC5B4\uC694)"));
8582
+ console.error(chalk39.yellow("\n \u26A0\uFE0F \uB300\uD654\uD615 \uC785\uB825\uC774 \uCDE8\uC18C/\uC885\uB8CC\uB410\uC2B5\uB2C8\uB2E4. (\uBE44\uB300\uD654\uD615 \uD658\uACBD\uC5D0\uC11C\uB294 \uD574\uB2F9 \uBA85\uB839\uC744 \uC4F8 \uC218 \uC5C6\uC5B4\uC694)"));
8175
8583
  } else {
8176
- console.error(chalk38.red(`
8584
+ console.error(chalk39.red(`
8177
8585
  \u274C ${err instanceof Error ? err.message : String(err)}`));
8178
8586
  }
8179
8587
  process.exitCode = 1;