@axis-ds/cli 1.1.3 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +21 -0
  2. package/dist/index.js +994 -108
  3. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -15,10 +15,11 @@ import {
15
15
 
16
16
  // src/index.ts
17
17
  import { Command } from "commander";
18
- import chalk8 from "chalk";
18
+ import chalk9 from "chalk";
19
19
  import ora6 from "ora";
20
- import fs9 from "fs-extra";
21
- import path9 from "path";
20
+ import fs10 from "fs-extra";
21
+ import path11 from "path";
22
+ import { createRequire as createRequire2 } from "module";
22
23
  import prompts2 from "prompts";
23
24
 
24
25
  // src/library/base-collector.ts
@@ -483,11 +484,11 @@ var ShadcnCollector = class extends BaseCollector {
483
484
  /**
484
485
  * 파일 타입 추론
485
486
  */
486
- getFileType(path10) {
487
- if (path10.includes("hook") || path10.startsWith("use")) return "hook";
488
- if (path10.includes("util") || path10.includes("lib")) return "util";
489
- if (path10.endsWith(".css")) return "style";
490
- if (path10.endsWith(".d.ts") || path10.includes("types")) return "type";
487
+ getFileType(path12) {
488
+ if (path12.includes("hook") || path12.startsWith("use")) return "hook";
489
+ if (path12.includes("util") || path12.includes("lib")) return "util";
490
+ if (path12.endsWith(".css")) return "style";
491
+ if (path12.endsWith(".d.ts") || path12.includes("types")) return "type";
491
492
  return "component";
492
493
  }
493
494
  };
@@ -1167,8 +1168,8 @@ var Publisher = class {
1167
1168
  };
1168
1169
  } catch (error) {
1169
1170
  spinner.fail("\uB77C\uC774\uBE0C\uB7EC\uB9AC \uBC30\uCE58 \uC2E4\uD328");
1170
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
1171
- errors.push(errorMessage);
1171
+ const errorMessage2 = error instanceof Error ? error.message : "Unknown error";
1172
+ errors.push(errorMessage2);
1172
1173
  return {
1173
1174
  success: false,
1174
1175
  outputDir,
@@ -1503,7 +1504,7 @@ import chalk7 from "chalk";
1503
1504
  import ora5 from "ora";
1504
1505
 
1505
1506
  // src/template/template-apply.ts
1506
- import fs6 from "fs-extra";
1507
+ import fs7 from "fs-extra";
1507
1508
  import path6 from "path";
1508
1509
  import chalk4 from "chalk";
1509
1510
  import ora3 from "ora";
@@ -1571,6 +1572,29 @@ function deepMerge(target, source) {
1571
1572
  }
1572
1573
  }
1573
1574
 
1575
+ // src/config/axis-config.ts
1576
+ import fs6 from "fs-extra";
1577
+ var AXIS_CONFIG_SCHEMA_URL = "https://axis.minu.best/schema.json";
1578
+ var AXIS_CONFIG_FILE = "axis.config.json";
1579
+ function createInitConfig(answers, registry) {
1580
+ return {
1581
+ $schema: AXIS_CONFIG_SCHEMA_URL,
1582
+ componentsDir: answers.componentsDir,
1583
+ tailwindConfig: answers.tailwindConfig,
1584
+ globalCss: answers.globalCss,
1585
+ registry
1586
+ };
1587
+ }
1588
+ function createTemplateConfig(template) {
1589
+ return {
1590
+ $schema: AXIS_CONFIG_SCHEMA_URL,
1591
+ template,
1592
+ componentsDir: "./components/ui",
1593
+ tailwindConfig: "./tailwind.config.ts",
1594
+ globalCss: "./globals.css"
1595
+ };
1596
+ }
1597
+
1574
1598
  // src/template/template-apply.ts
1575
1599
  async function applyTemplate(slug, options) {
1576
1600
  const spinner = ora3(`'${slug}' \uD15C\uD50C\uB9BF \uAC00\uC838\uC624\uB294 \uC911...`).start();
@@ -1586,7 +1610,7 @@ async function applyTemplate(slug, options) {
1586
1610
  const conflicts = [];
1587
1611
  for (const file of detail.files) {
1588
1612
  const dest = path6.join(baseDir, file.path);
1589
- if (await fs6.pathExists(dest)) {
1613
+ if (await fs7.pathExists(dest)) {
1590
1614
  conflicts.push(file.path);
1591
1615
  }
1592
1616
  }
@@ -1626,8 +1650,8 @@ async function applyTemplate(slug, options) {
1626
1650
  const writeSpinner = ora3("\uD30C\uC77C \uC0DD\uC131 \uC911...").start();
1627
1651
  for (const file of detail.files) {
1628
1652
  const dest = path6.join(baseDir, file.path);
1629
- await fs6.ensureDir(path6.dirname(dest));
1630
- await fs6.writeFile(dest, file.content);
1653
+ await fs7.ensureDir(path6.dirname(dest));
1654
+ await fs7.writeFile(dest, file.content);
1631
1655
  }
1632
1656
  writeSpinner.succeed(`${detail.files.length}\uAC1C \uD30C\uC77C \uC0DD\uC131 \uC644\uB8CC`);
1633
1657
  if (detail.postInstall?.length) {
@@ -1688,8 +1712,8 @@ async function initTemplate(slug, options) {
1688
1712
  targetDir = dir;
1689
1713
  }
1690
1714
  const resolved = path6.resolve(targetDir);
1691
- if (await fs6.pathExists(resolved)) {
1692
- const entries = await fs6.readdir(resolved);
1715
+ if (await fs7.pathExists(resolved)) {
1716
+ const entries = await fs7.readdir(resolved);
1693
1717
  if (entries.length > 0) {
1694
1718
  const { proceed } = await prompts({
1695
1719
  type: "confirm",
@@ -1700,19 +1724,13 @@ async function initTemplate(slug, options) {
1700
1724
  if (!proceed) return;
1701
1725
  }
1702
1726
  } else {
1703
- await fs6.ensureDir(resolved);
1727
+ await fs7.ensureDir(resolved);
1704
1728
  }
1705
1729
  await applyTemplate(selectedSlug, { dir: targetDir, yes: true });
1706
1730
  const configPath = path6.join(resolved, "axis.config.json");
1707
- if (!await fs6.pathExists(configPath)) {
1708
- const axisConfig = {
1709
- $schema: "https://axis.minu.best/schema.json",
1710
- template: selectedSlug,
1711
- componentsDir: "./components/ui",
1712
- tailwindConfig: "./tailwind.config.ts",
1713
- globalCss: "./globals.css"
1714
- };
1715
- await fs6.writeJSON(configPath, axisConfig, { spaces: 2 });
1731
+ if (!await fs7.pathExists(configPath)) {
1732
+ const axisConfig = createTemplateConfig(selectedSlug);
1733
+ await fs7.writeJSON(configPath, axisConfig, { spaces: 2 });
1716
1734
  console.log(chalk4.green("axis.config.json \uC0DD\uC131 \uC644\uB8CC"));
1717
1735
  }
1718
1736
  console.log(chalk4.blue(`
@@ -1723,7 +1741,7 @@ async function initTemplate(slug, options) {
1723
1741
  }
1724
1742
 
1725
1743
  // src/template/template-diff.ts
1726
- import fs7 from "fs-extra";
1744
+ import fs8 from "fs-extra";
1727
1745
  import path7 from "path";
1728
1746
  import chalk5 from "chalk";
1729
1747
  import ora4 from "ora";
@@ -1741,11 +1759,11 @@ async function diffTemplate(slug, options) {
1741
1759
  const entries = [];
1742
1760
  for (const file of detail.files) {
1743
1761
  const localPath = path7.join(baseDir, file.path);
1744
- if (!await fs7.pathExists(localPath)) {
1762
+ if (!await fs8.pathExists(localPath)) {
1745
1763
  entries.push({ path: file.path, status: "added" });
1746
1764
  continue;
1747
1765
  }
1748
- const localContent = await fs7.readFile(localPath, "utf-8");
1766
+ const localContent = await fs8.readFile(localPath, "utf-8");
1749
1767
  if (localContent === file.content) {
1750
1768
  entries.push({ path: file.path, status: "unchanged" });
1751
1769
  } else {
@@ -1815,7 +1833,7 @@ async function collectLocalFiles(dir, base) {
1815
1833
  const results = [];
1816
1834
  let entries;
1817
1835
  try {
1818
- entries = await fs7.readdir(dir, { withFileTypes: true });
1836
+ entries = await fs8.readdir(dir, { withFileTypes: true });
1819
1837
  } catch {
1820
1838
  return results;
1821
1839
  }
@@ -1833,15 +1851,261 @@ async function collectLocalFiles(dir, base) {
1833
1851
  }
1834
1852
 
1835
1853
  // src/template/template-check.ts
1836
- import fs8 from "fs-extra";
1854
+ import fs9 from "fs-extra";
1837
1855
  import path8 from "path";
1838
1856
  import chalk6 from "chalk";
1857
+
1858
+ // src/theme/brand-core.ts
1859
+ import { existsSync, readFileSync, statSync } from "fs";
1860
+ import { createRequire } from "module";
1861
+ import { dirname, join, relative, resolve, sep } from "path";
1862
+ var TOKENS_PACKAGE = "@axis-ds/tokens";
1863
+ var BRAND_NAME_PATTERN = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
1864
+ var MIN_BRAND_TOKENS_VERSION = "1.3.0";
1865
+ var KEY_TOKENS = [
1866
+ "--primary",
1867
+ "--primary-foreground",
1868
+ "--background",
1869
+ "--foreground",
1870
+ "--ring",
1871
+ "--radius",
1872
+ "--axis-surface-default",
1873
+ "--axis-text-primary",
1874
+ "--axis-text-brand",
1875
+ "--axis-button-bg-default",
1876
+ "--axis-border-focus",
1877
+ "--axis-font-family-sans"
1878
+ ];
1879
+ var BRAND_ADJUSTMENT_REFERENCE = "WI-0025 PRD \uBD80\uB85D B (docs/workitems/WI-0025-brand-theme/prd.md)";
1880
+ function readJsonFile(file) {
1881
+ return JSON.parse(readFileSync(file, "utf8"));
1882
+ }
1883
+ function errorCode(err) {
1884
+ return typeof err === "object" && err !== null && "code" in err ? String(err.code) : void 0;
1885
+ }
1886
+ function findTokensPackageJson(baseDir) {
1887
+ let dir = resolve(baseDir);
1888
+ for (; ; ) {
1889
+ const candidate = join(dir, "node_modules", TOKENS_PACKAGE, "package.json");
1890
+ if (existsSync(candidate)) return candidate;
1891
+ const parent = dirname(dir);
1892
+ if (parent === dir) return void 0;
1893
+ dir = parent;
1894
+ }
1895
+ }
1896
+ function projectRequire(baseDir) {
1897
+ return createRequire(join(resolve(baseDir), "package.json"));
1898
+ }
1899
+ function tokensSpecifier(subpath) {
1900
+ return `${TOKENS_PACKAGE}/${subpath.replace(/^\.\//, "")}`;
1901
+ }
1902
+ function resolveTokens(baseDir) {
1903
+ const pkgJson = findTokensPackageJson(baseDir);
1904
+ if (!pkgJson) return { status: "not-installed" };
1905
+ const pkg = readJsonFile(pkgJson);
1906
+ const version = typeof pkg.version === "string" ? pkg.version : void 0;
1907
+ try {
1908
+ const indexPath = projectRequire(baseDir).resolve(tokensSpecifier("./brands"));
1909
+ return { status: "ok", version, indexPath };
1910
+ } catch (err) {
1911
+ const code = errorCode(err);
1912
+ if (code === "ERR_PACKAGE_PATH_NOT_EXPORTED") return { status: "unsupported", version };
1913
+ if (code === "MODULE_NOT_FOUND") return { status: "not-built", version };
1914
+ throw err;
1915
+ }
1916
+ }
1917
+ function brandImportPath(entry) {
1918
+ return tokensSpecifier(entry.css);
1919
+ }
1920
+ var GLOBAL_CSS_CANDIDATES = ["app/globals.css", "src/app/globals.css", "globals.css"];
1921
+ function isFile(file) {
1922
+ try {
1923
+ return statSync(file).isFile();
1924
+ } catch {
1925
+ return false;
1926
+ }
1927
+ }
1928
+ function findGlobalCss(baseDir, config) {
1929
+ const root = resolve(baseDir);
1930
+ const result = {};
1931
+ const configured = config?.globalCss;
1932
+ if (typeof configured === "string" && configured.trim() !== "") {
1933
+ const file = resolve(root, configured);
1934
+ if (isFile(file)) return { path: file };
1935
+ result.configuredMissing = configured;
1936
+ }
1937
+ for (const candidate of GLOBAL_CSS_CANDIDATES) {
1938
+ const file = join(root, candidate);
1939
+ if (isFile(file)) {
1940
+ result.path = file;
1941
+ break;
1942
+ }
1943
+ }
1944
+ return result;
1945
+ }
1946
+ function findBrandImports(css) {
1947
+ const names = [];
1948
+ const pattern = /@import\s+(?:url\(\s*)?["']?@axis-ds\/tokens\/css\/brands\/([a-z][a-z0-9-]*)(?:\.css)?["']?\s*\)?\s*(?=[;\s]|$)/g;
1949
+ for (const m of stripComments(css).matchAll(pattern)) {
1950
+ names.push(m[1]);
1951
+ }
1952
+ return names;
1953
+ }
1954
+ var AXIS_CONFIG_FILE_NAME = "axis.config.json";
1955
+ function readBrandConfig(baseDir) {
1956
+ let parsed;
1957
+ try {
1958
+ parsed = JSON.parse(readFileSync(join(resolve(baseDir), AXIS_CONFIG_FILE_NAME), "utf8"));
1959
+ } catch {
1960
+ return null;
1961
+ }
1962
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
1963
+ return parsed;
1964
+ }
1965
+ function inspectBrandImport(baseDir) {
1966
+ const config = readBrandConfig(baseDir);
1967
+ const brand = config?.brand;
1968
+ if (typeof brand !== "string" || brand.trim() === "") return null;
1969
+ const root = resolve(baseDir);
1970
+ const location = findGlobalCss(root, config);
1971
+ const base = { brand };
1972
+ if (location.configuredMissing !== void 0) base.configuredMissing = location.configuredMissing;
1973
+ if (!location.path) {
1974
+ return { ...base, state: "css-missing", imports: [] };
1975
+ }
1976
+ const cssFile = relative(root, location.path).split(sep).join("/");
1977
+ const imports = [...new Set(findBrandImports(readFileSync(location.path, "utf8")))];
1978
+ let state;
1979
+ if (imports.includes(brand)) state = "match";
1980
+ else if (imports.length === 0) state = "import-missing";
1981
+ else state = "other-brand";
1982
+ return { ...base, state, cssFile, imports };
1983
+ }
1984
+ function isMissingBuildFile(err) {
1985
+ const code = errorCode(err);
1986
+ return code === "MODULE_NOT_FOUND" || code === "ENOENT";
1987
+ }
1988
+ function readBrandIndex(indexPath) {
1989
+ const data = readJsonFile(indexPath);
1990
+ if (!Array.isArray(data.brands)) {
1991
+ throw new Error(`${indexPath}: "brands" \uBC30\uC5F4\uC774 \uC5C6\uC2B5\uB2C8\uB2E4`);
1992
+ }
1993
+ return data.brands;
1994
+ }
1995
+ function readSummary(baseDir, entry) {
1996
+ const metaPath = projectRequire(baseDir).resolve(tokensSpecifier(entry.meta));
1997
+ const meta = readJsonFile(metaPath);
1998
+ return {
1999
+ name: entry.name,
2000
+ description: typeof meta.description === "string" ? meta.description : "",
2001
+ themes: Array.isArray(meta.themes) ? meta.themes.map(String) : [],
2002
+ a11y: typeof meta.a11y === "object" && meta.a11y !== null ? meta.a11y : {},
2003
+ importPath: brandImportPath(entry)
2004
+ };
2005
+ }
2006
+ function listBrands(baseDir) {
2007
+ const tokens = resolveTokens(baseDir);
2008
+ if (tokens.status !== "ok" || !tokens.indexPath) {
2009
+ return { status: tokens.status, version: tokens.version, brands: [] };
2010
+ }
2011
+ const entries = readBrandIndex(tokens.indexPath);
2012
+ let brands;
2013
+ try {
2014
+ brands = entries.map((entry) => readSummary(baseDir, entry));
2015
+ } catch (err) {
2016
+ if (isMissingBuildFile(err)) return { status: "not-built", version: tokens.version, brands: [] };
2017
+ throw err;
2018
+ }
2019
+ return { status: "ok", version: tokens.version, brands };
2020
+ }
2021
+ function stripComments(css) {
2022
+ return css.replace(/\/\*[\s\S]*?\*\//g, "");
2023
+ }
2024
+ function parseDeclarations(body) {
2025
+ const out = {};
2026
+ for (const m of body.matchAll(/(--[A-Za-z0-9_-]+)\s*:\s*([^;]+);/g)) {
2027
+ out[m[1]] = m[2].trim();
2028
+ }
2029
+ return out;
2030
+ }
2031
+ function isDarkSelector(selector) {
2032
+ return /\.dark\b/.test(selector) || /\[data-theme=["']?dark["']?\]/.test(selector);
2033
+ }
2034
+ function parseBrandCss(css, name) {
2035
+ const result = { light: {}, dark: {} };
2036
+ let lightFound = false;
2037
+ let darkFound = false;
2038
+ const brandAttr = new RegExp(`\\[data-brand=["']?${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}["']?\\]`);
2039
+ for (const m of stripComments(css).matchAll(/([^{}]+)\{([^{}]*)\}/g)) {
2040
+ const selectors = m[1].split(",").map((s) => s.trim());
2041
+ if (!selectors.some((s) => brandAttr.test(s))) continue;
2042
+ if (selectors.some(isDarkSelector)) {
2043
+ if (!darkFound) {
2044
+ result.dark = parseDeclarations(m[2]);
2045
+ darkFound = true;
2046
+ }
2047
+ } else if (!lightFound) {
2048
+ result.light = parseDeclarations(m[2]);
2049
+ lightFound = true;
2050
+ }
2051
+ }
2052
+ return result;
2053
+ }
2054
+ function pickKeyTokens(decls) {
2055
+ const out = {};
2056
+ for (const key of KEY_TOKENS) {
2057
+ if (Object.prototype.hasOwnProperty.call(decls, key)) out[key] = decls[key];
2058
+ }
2059
+ return out;
2060
+ }
2061
+ function providerExample(name) {
2062
+ return [
2063
+ 'import { ThemeProvider } from "@axis-ds/theme";',
2064
+ "",
2065
+ `<ThemeProvider defaultBrand="${name}">{children}</ThemeProvider>`
2066
+ ].join("\n");
2067
+ }
2068
+ function getBrand(baseDir, name) {
2069
+ const tokens = resolveTokens(baseDir);
2070
+ if (tokens.status !== "ok" || !tokens.indexPath) {
2071
+ return { status: tokens.status, version: tokens.version, brand: null, available: [] };
2072
+ }
2073
+ const entries = readBrandIndex(tokens.indexPath);
2074
+ const available = entries.map((e) => e.name);
2075
+ const entry = entries.find((e) => e.name === name);
2076
+ if (!entry) {
2077
+ return { status: "ok", version: tokens.version, brand: null, available };
2078
+ }
2079
+ let summary;
2080
+ let parsed;
2081
+ try {
2082
+ summary = readSummary(baseDir, entry);
2083
+ const cssPath = projectRequire(baseDir).resolve(summary.importPath);
2084
+ parsed = parseBrandCss(readFileSync(cssPath, "utf8"), name);
2085
+ } catch (err) {
2086
+ if (isMissingBuildFile(err)) return { status: "not-built", version: tokens.version, brand: null, available: [] };
2087
+ throw err;
2088
+ }
2089
+ return {
2090
+ status: "ok",
2091
+ version: tokens.version,
2092
+ available,
2093
+ brand: {
2094
+ ...summary,
2095
+ tokens: { light: pickKeyTokens(parsed.light), dark: pickKeyTokens(parsed.dark) },
2096
+ providerExample: providerExample(name),
2097
+ adjustments: { a11y: summary.a11y, reference: BRAND_ADJUSTMENT_REFERENCE }
2098
+ }
2099
+ };
2100
+ }
2101
+
2102
+ // src/template/template-check.ts
1839
2103
  var SEVERITY_ICON = {
1840
2104
  pass: chalk6.green("PASS"),
1841
2105
  warn: chalk6.yellow("WARN"),
1842
2106
  fail: chalk6.red("FAIL")
1843
2107
  };
1844
- async function runCheck(dir) {
2108
+ async function collectCheckResults(dir) {
1845
2109
  const baseDir = path8.resolve(dir || ".");
1846
2110
  const results = [];
1847
2111
  results.push(await checkFileExists(baseDir, "axis.config.json", "axis.config.json"));
@@ -1849,6 +2113,12 @@ async function runCheck(dir) {
1849
2113
  results.push(await checkGlobalsCss(baseDir));
1850
2114
  results.push(await checkUtils(baseDir));
1851
2115
  results.push(await checkDependencies(baseDir));
2116
+ const brand = checkBrand(baseDir);
2117
+ if (brand) results.push(brand);
2118
+ return results;
2119
+ }
2120
+ async function runCheck(dir) {
2121
+ const results = await collectCheckResults(dir);
1852
2122
  console.log(chalk6.blue("\nAXIS \uD504\uB85C\uC81D\uD2B8 \uAC80\uC99D \uACB0\uACFC:\n"));
1853
2123
  let passCount = 0;
1854
2124
  let warnCount = 0;
@@ -1880,7 +2150,7 @@ async function runCheck(dir) {
1880
2150
  }
1881
2151
  }
1882
2152
  async function checkFileExists(baseDir, fileName, label) {
1883
- const exists = await fs8.pathExists(path8.join(baseDir, fileName));
2153
+ const exists = await fs9.pathExists(path8.join(baseDir, fileName));
1884
2154
  return {
1885
2155
  name: label,
1886
2156
  severity: exists ? "pass" : "fail",
@@ -1890,8 +2160,8 @@ async function checkFileExists(baseDir, fileName, label) {
1890
2160
  async function checkTailwindConfig(baseDir) {
1891
2161
  const tsPath = path8.join(baseDir, "tailwind.config.ts");
1892
2162
  const jsPath = path8.join(baseDir, "tailwind.config.js");
1893
- if (await fs8.pathExists(tsPath)) {
1894
- const content = await fs8.readFile(tsPath, "utf-8");
2163
+ if (await fs9.pathExists(tsPath)) {
2164
+ const content = await fs9.readFile(tsPath, "utf-8");
1895
2165
  const hasTokens = content.includes("--background") || content.includes("hsl(var(");
1896
2166
  return {
1897
2167
  name: "tailwind.config",
@@ -1899,7 +2169,7 @@ async function checkTailwindConfig(baseDir) {
1899
2169
  message: hasTokens ? "tailwind.config.ts\uC5D0 AXIS \uD1A0\uD070\uC774 \uC124\uC815\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4" : "tailwind.config.ts\uC5D0 AXIS \uD1A0\uD070 \uC124\uC815\uC774 \uC5C6\uC2B5\uB2C8\uB2E4"
1900
2170
  };
1901
2171
  }
1902
- if (await fs8.pathExists(jsPath)) {
2172
+ if (await fs9.pathExists(jsPath)) {
1903
2173
  return {
1904
2174
  name: "tailwind.config",
1905
2175
  severity: "warn",
@@ -1922,8 +2192,8 @@ async function checkGlobalsCss(baseDir) {
1922
2192
  ];
1923
2193
  for (const candidate of candidates) {
1924
2194
  const fullPath = path8.join(baseDir, candidate);
1925
- if (await fs8.pathExists(fullPath)) {
1926
- const content = await fs8.readFile(fullPath, "utf-8");
2195
+ if (await fs9.pathExists(fullPath)) {
2196
+ const content = await fs9.readFile(fullPath, "utf-8");
1927
2197
  const hasTokens = content.includes("--background") && content.includes("--primary");
1928
2198
  return {
1929
2199
  name: "globals.css",
@@ -1948,8 +2218,8 @@ async function checkUtils(baseDir) {
1948
2218
  ];
1949
2219
  for (const candidate of candidates) {
1950
2220
  const fullPath = path8.join(baseDir, candidate);
1951
- if (await fs8.pathExists(fullPath)) {
1952
- const content = await fs8.readFile(fullPath, "utf-8");
2221
+ if (await fs9.pathExists(fullPath)) {
2222
+ const content = await fs9.readFile(fullPath, "utf-8");
1953
2223
  const hasCn = content.includes("function cn");
1954
2224
  return {
1955
2225
  name: "utils.ts",
@@ -1966,14 +2236,14 @@ async function checkUtils(baseDir) {
1966
2236
  }
1967
2237
  async function checkDependencies(baseDir) {
1968
2238
  const pkgPath = path8.join(baseDir, "package.json");
1969
- if (!await fs8.pathExists(pkgPath)) {
2239
+ if (!await fs9.pathExists(pkgPath)) {
1970
2240
  return {
1971
2241
  name: "package.json",
1972
2242
  severity: "fail",
1973
2243
  message: "package.json \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4"
1974
2244
  };
1975
2245
  }
1976
- const pkg = await fs8.readJSON(pkgPath);
2246
+ const pkg = await fs9.readJSON(pkgPath);
1977
2247
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
1978
2248
  const required = ["tailwindcss"];
1979
2249
  const recommended = ["clsx", "tailwind-merge"];
@@ -1999,6 +2269,40 @@ async function checkDependencies(baseDir) {
1999
2269
  message: "\uD544\uC218 \uBC0F \uAD8C\uC7A5 \uC758\uC874\uC131\uC774 \uBAA8\uB450 \uC124\uCE58\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4"
2000
2270
  };
2001
2271
  }
2272
+ function checkBrand(baseDir) {
2273
+ const check = inspectBrandImport(baseDir);
2274
+ return check ? brandCheckResult(check) : null;
2275
+ }
2276
+ function brandCheckResult(check) {
2277
+ const { brand } = check;
2278
+ const suffix = check.configuredMissing !== void 0 ? ` (globalCss ${check.configuredMissing} \uC5C6\uC74C)` : "";
2279
+ switch (check.state) {
2280
+ case "match":
2281
+ return {
2282
+ name: "brand",
2283
+ severity: "pass",
2284
+ message: `brand "${brand}" import \uAC00 ${check.cssFile}\uC5D0 \uC788\uC2B5\uB2C8\uB2E4${suffix}`
2285
+ };
2286
+ case "css-missing":
2287
+ return {
2288
+ name: "brand",
2289
+ severity: "warn",
2290
+ message: `brand "${brand}" \uAC00 \uC124\uC815\uB410\uC9C0\uB9CC CSS \uD30C\uC77C\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4${suffix}`
2291
+ };
2292
+ case "import-missing":
2293
+ return {
2294
+ name: "brand",
2295
+ severity: "warn",
2296
+ message: `brand "${brand}" import \uAC00 ${check.cssFile}\uC5D0 \uC5C6\uC2B5\uB2C8\uB2E4 \u2192 'axis theme add ${brand}'${suffix}`
2297
+ };
2298
+ case "other-brand":
2299
+ return {
2300
+ name: "brand",
2301
+ severity: "warn",
2302
+ message: `brand "${brand}" \uC640 import(${check.imports.join(", ")})\uAC00 \uB2E4\uB985\uB2C8\uB2E4${suffix}`
2303
+ };
2304
+ }
2305
+ }
2002
2306
 
2003
2307
  // src/template/index.ts
2004
2308
  function registerTemplateCommand(program2) {
@@ -2102,12 +2406,592 @@ function registerCheckCommand(program2) {
2102
2406
  });
2103
2407
  }
2104
2408
 
2409
+ // src/theme/index.ts
2410
+ import chalk8 from "chalk";
2411
+ import path10 from "path";
2412
+
2413
+ // src/theme/theme-add.ts
2414
+ import { accessSync, constants, existsSync as existsSync2, readFileSync as readFileSync2, realpathSync, unlinkSync, writeFileSync } from "fs";
2415
+ import path9 from "path";
2416
+ var TOKENS_DEPENDENCY_RANGE = `^${MIN_BRAND_TOKENS_VERSION}`;
2417
+ var THEME_PACKAGE = "@axis-ds/theme";
2418
+ function readText(file) {
2419
+ return existsSync2(file) ? readFileSync2(file, "utf8") : null;
2420
+ }
2421
+ function eolOf(text) {
2422
+ return text.includes("\r\n") ? "\r\n" : "\n";
2423
+ }
2424
+ function indentOf(text) {
2425
+ const m = /\n([ \t]+)\S/.exec(text);
2426
+ if (!m) return 2;
2427
+ return m[1].includes(" ") ? " " : m[1].length;
2428
+ }
2429
+ function formatJsonLike(original, value) {
2430
+ const eol = eolOf(original);
2431
+ const endsWithNewline = /\r?\n$/.test(original);
2432
+ let out = JSON.stringify(value, null, indentOf(original));
2433
+ if (eol === "\r\n") out = out.replace(/\n/g, "\r\n");
2434
+ return endsWithNewline ? out + eol : out;
2435
+ }
2436
+ function insertImportLine(css, line) {
2437
+ const bom = css.startsWith("\uFEFF") ? "\uFEFF" : "";
2438
+ const body = bom ? css.slice(1) : css;
2439
+ const eol = eolOf(body);
2440
+ const lines = body.split(/\r?\n/);
2441
+ let insertAt = 0;
2442
+ let inComment = false;
2443
+ for (let i = 0; i < lines.length; i++) {
2444
+ let rest = lines[i].trim();
2445
+ if (inComment) {
2446
+ const end = rest.indexOf("*/");
2447
+ if (end === -1) continue;
2448
+ inComment = false;
2449
+ rest = rest.slice(end + 2).trim();
2450
+ }
2451
+ while (rest.startsWith("/*")) {
2452
+ const end = rest.indexOf("*/", 2);
2453
+ if (end === -1) {
2454
+ inComment = true;
2455
+ rest = "";
2456
+ break;
2457
+ }
2458
+ rest = rest.slice(end + 2).trim();
2459
+ }
2460
+ if (rest === "") continue;
2461
+ if (/^@(charset|import)\b/i.test(rest)) {
2462
+ insertAt = i + 1;
2463
+ continue;
2464
+ }
2465
+ break;
2466
+ }
2467
+ lines.splice(insertAt, 0, line);
2468
+ return bom + lines.join(eol);
2469
+ }
2470
+ function compareVersions(a, b) {
2471
+ const parse = (v) => {
2472
+ const m = /^(\d+)\.(\d+)\.(\d+)(-.+)?$/.exec(v.trim());
2473
+ return m ? { nums: [Number(m[1]), Number(m[2]), Number(m[3])], pre: Boolean(m[4]) } : void 0;
2474
+ };
2475
+ const pa = parse(a);
2476
+ const pb = parse(b);
2477
+ if (!pa || !pb) return void 0;
2478
+ for (let i = 0; i < 3; i++) {
2479
+ if (pa.nums[i] !== pb.nums[i]) return pa.nums[i] < pb.nums[i] ? -1 : 1;
2480
+ }
2481
+ if (pa.pre !== pb.pre) return pa.pre ? -1 : 1;
2482
+ return 0;
2483
+ }
2484
+ function realPathInside(baseDir, file) {
2485
+ const realBase = realpathSync(baseDir);
2486
+ const realPath = realpathSync(file);
2487
+ const relPath = path9.relative(realBase, realPath);
2488
+ const inside = relPath !== "" && !relPath.startsWith("..") && !path9.isAbsolute(relPath);
2489
+ return { inside, realPath };
2490
+ }
2491
+ function isStylesheet(realPath) {
2492
+ return path9.extname(realPath).toLowerCase() === ".css";
2493
+ }
2494
+ function detectInstallCommand(baseDir) {
2495
+ if (existsSync2(path9.join(baseDir, "pnpm-lock.yaml"))) return "pnpm install";
2496
+ if (existsSync2(path9.join(baseDir, "yarn.lock"))) return "yarn install";
2497
+ if (existsSync2(path9.join(baseDir, "package-lock.json"))) return "npm install";
2498
+ return "pnpm install";
2499
+ }
2500
+ function hasDependency(pkg, name) {
2501
+ for (const field of ["dependencies", "devDependencies"]) {
2502
+ const deps = pkg[field];
2503
+ if (typeof deps === "object" && deps !== null && name in deps) {
2504
+ return String(deps[name]);
2505
+ }
2506
+ }
2507
+ return void 0;
2508
+ }
2509
+ function isPlainObject(value) {
2510
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2511
+ }
2512
+ function planThemeAdd(baseDirInput, brand) {
2513
+ const baseDir = path9.resolve(baseDirInput);
2514
+ const plan = {
2515
+ baseDir,
2516
+ brand,
2517
+ importPath: brandImportPath({ css: `./css/brands/${brand}` }),
2518
+ tokens: { status: "not-installed" },
2519
+ errors: [],
2520
+ unverifiedBrand: false,
2521
+ configChanged: false,
2522
+ installCommand: detectInstallCommand(baseDir),
2523
+ writes: []
2524
+ };
2525
+ const configFile = path9.join(baseDir, AXIS_CONFIG_FILE);
2526
+ const configText = readText(configFile);
2527
+ if (configText === null) {
2528
+ plan.errors.push({ kind: "no-config" });
2529
+ return plan;
2530
+ }
2531
+ let config;
2532
+ try {
2533
+ const parsed = JSON.parse(configText);
2534
+ if (!isPlainObject(parsed)) throw new Error("\uCD5C\uC0C1\uC704\uAC00 \uAC1D\uCCB4\uAC00 \uC544\uB2D9\uB2C8\uB2E4");
2535
+ config = parsed;
2536
+ } catch (err) {
2537
+ plan.errors.push({ kind: "invalid-config", message: err.message });
2538
+ return plan;
2539
+ }
2540
+ if (!BRAND_NAME_PATTERN.test(brand)) {
2541
+ plan.errors.push({ kind: "invalid-name", brand });
2542
+ return plan;
2543
+ }
2544
+ plan.tokens = resolveTokens(baseDir);
2545
+ if (plan.tokens.status === "ok" && plan.tokens.indexPath) {
2546
+ const data = JSON.parse(readFileSync2(plan.tokens.indexPath, "utf8"));
2547
+ const entries = Array.isArray(data.brands) ? data.brands : [];
2548
+ const entry = entries.find((e) => e.name === brand);
2549
+ if (!entry) {
2550
+ plan.errors.push({
2551
+ kind: "unknown-brand",
2552
+ brand,
2553
+ available: entries.map((e) => e.name),
2554
+ version: plan.tokens.version
2555
+ });
2556
+ return plan;
2557
+ }
2558
+ plan.importPath = brandImportPath(entry);
2559
+ } else {
2560
+ plan.unverifiedBrand = true;
2561
+ }
2562
+ const location = findGlobalCss(baseDir, config);
2563
+ if (location.path) {
2564
+ const where = realPathInside(baseDir, location.path);
2565
+ if (!where.inside) {
2566
+ plan.errors.push({ kind: "css-outside-project", file: location.path, realPath: where.realPath });
2567
+ plan.writes = [];
2568
+ return plan;
2569
+ }
2570
+ if (!isStylesheet(where.realPath)) {
2571
+ plan.errors.push({ kind: "css-not-stylesheet", file: location.path, realPath: where.realPath });
2572
+ plan.writes = [];
2573
+ return plan;
2574
+ }
2575
+ const before = readFileSync2(location.path, "utf8");
2576
+ const imported = findBrandImports(before);
2577
+ const otherBrands = [...new Set(imported.filter((n) => n !== brand))];
2578
+ if (imported.includes(brand)) {
2579
+ plan.css = { file: location.path, configuredMissing: location.configuredMissing, action: "exists", otherBrands };
2580
+ } else {
2581
+ plan.css = { file: location.path, configuredMissing: location.configuredMissing, action: "add", otherBrands };
2582
+ plan.writes.push({
2583
+ file: location.path,
2584
+ before,
2585
+ after: insertImportLine(before, `@import "${plan.importPath}";`)
2586
+ });
2587
+ }
2588
+ } else {
2589
+ plan.css = { configuredMissing: location.configuredMissing, action: "manual", otherBrands: [] };
2590
+ }
2591
+ const pkgFile = path9.join(baseDir, "package.json");
2592
+ const pkgText = readText(pkgFile);
2593
+ const installed = plan.tokens.version;
2594
+ const upgrade = installed !== void 0 && (compareVersions(installed, MIN_BRAND_TOKENS_VERSION) ?? 0) < 0;
2595
+ if (pkgText === null) {
2596
+ plan.dependency = { action: "no-package-json", upgrade, themeMissing: false };
2597
+ } else {
2598
+ let pkg;
2599
+ try {
2600
+ const parsed = JSON.parse(pkgText);
2601
+ if (!isPlainObject(parsed)) throw new Error("\uCD5C\uC0C1\uC704\uAC00 \uAC1D\uCCB4\uAC00 \uC544\uB2D9\uB2C8\uB2E4");
2602
+ pkg = parsed;
2603
+ } catch (err) {
2604
+ plan.errors.push({ kind: "invalid-package-json", message: err.message });
2605
+ plan.writes = [];
2606
+ return plan;
2607
+ }
2608
+ const themeMissing = hasDependency(pkg, THEME_PACKAGE) === void 0;
2609
+ const declared = hasDependency(pkg, TOKENS_PACKAGE);
2610
+ if (declared !== void 0) {
2611
+ plan.dependency = { action: "present", declaredRange: declared, upgrade, themeMissing };
2612
+ } else {
2613
+ const deps = isPlainObject(pkg.dependencies) ? { ...pkg.dependencies } : {};
2614
+ deps[TOKENS_PACKAGE] = TOKENS_DEPENDENCY_RANGE;
2615
+ const next = { ...pkg, dependencies: deps };
2616
+ plan.dependency = { action: "add", upgrade, themeMissing };
2617
+ plan.writes.push({ file: pkgFile, before: pkgText, after: formatJsonLike(pkgText, next) });
2618
+ }
2619
+ }
2620
+ if (config.brand !== brand) {
2621
+ plan.configChanged = true;
2622
+ plan.writes.push({
2623
+ file: configFile,
2624
+ before: configText,
2625
+ after: formatJsonLike(configText, { ...config, brand })
2626
+ });
2627
+ }
2628
+ return plan;
2629
+ }
2630
+ var ThemeApplyError = class extends Error {
2631
+ constructor(message, failedFile, restored, restoreFailed) {
2632
+ super(message);
2633
+ this.failedFile = failedFile;
2634
+ this.restored = restored;
2635
+ this.restoreFailed = restoreFailed;
2636
+ this.name = "ThemeApplyError";
2637
+ }
2638
+ };
2639
+ function errorMessage(err) {
2640
+ return err instanceof Error ? err.message : String(err);
2641
+ }
2642
+ function applyThemeAdd(plan) {
2643
+ if (plan.errors.length > 0) {
2644
+ throw new Error("\uC624\uB958\uAC00 \uC788\uB294 \uACC4\uD68D\uC740 \uC801\uC6A9\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4");
2645
+ }
2646
+ for (const w of plan.writes) {
2647
+ const current = readText(w.file);
2648
+ if (current !== w.before) {
2649
+ throw new Error(`${w.file} \uC774 \uACC4\uD68D \uB4A4\uC5D0 \uBC14\uB00C\uC5C8\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.`);
2650
+ }
2651
+ const where = realPathInside(plan.baseDir, w.before === null ? path9.dirname(w.file) : w.file);
2652
+ if (!where.inside && !(w.before === null && path9.dirname(w.file) === plan.baseDir)) {
2653
+ throw new Error(`${w.file} \uC758 \uC2E4\uC81C \uC704\uCE58(${where.realPath})\uAC00 \uD504\uB85C\uC81D\uD2B8 \uB514\uB809\uD130\uB9AC \uBC16\uC785\uB2C8\uB2E4. \uC544\uBB34\uAC83\uB3C4 \uC4F0\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.`);
2654
+ }
2655
+ if (!w.file.endsWith(".json") && !isStylesheet(where.realPath)) {
2656
+ throw new Error(`${w.file} \uC758 \uC2E4\uC81C \uB300\uC0C1(${where.realPath})\uC774 CSS \uD30C\uC77C\uC774 \uC544\uB2D9\uB2C8\uB2E4. \uC544\uBB34\uAC83\uB3C4 \uC4F0\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.`);
2657
+ }
2658
+ if (w.file.endsWith(".json")) {
2659
+ JSON.parse(w.after);
2660
+ } else if (findBrandImports(w.after).filter((n) => n === plan.brand).length !== 1) {
2661
+ throw new Error(`${w.file}: \uBE0C\uB79C\uB4DC import \uAC00 \uC815\uD655\uD788 1\uC904\uC774 \uC544\uB2D9\uB2C8\uB2E4`);
2662
+ }
2663
+ try {
2664
+ accessSync(w.before === null ? path9.dirname(w.file) : w.file, constants.W_OK);
2665
+ } catch (err) {
2666
+ throw new Error(`${w.file} \uC5D0 \uC4F8 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4(${errorMessage(err)}).`);
2667
+ }
2668
+ }
2669
+ const done = [];
2670
+ for (const w of plan.writes) {
2671
+ try {
2672
+ writeFileSync(w.file, w.after);
2673
+ done.push(w);
2674
+ } catch (err) {
2675
+ const restored = [];
2676
+ const restoreFailed = [];
2677
+ for (const d of done.reverse()) {
2678
+ try {
2679
+ if (d.before === null) unlinkSync(d.file);
2680
+ else writeFileSync(d.file, d.before);
2681
+ restored.push(d.file);
2682
+ } catch {
2683
+ restoreFailed.push(d.file);
2684
+ }
2685
+ }
2686
+ throw new ThemeApplyError(
2687
+ `${w.file} \uB97C \uC4F0\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4(${errorMessage(err)}).`,
2688
+ w.file,
2689
+ restored,
2690
+ restoreFailed
2691
+ );
2692
+ }
2693
+ }
2694
+ return done.map((w) => w.file);
2695
+ }
2696
+
2697
+ // src/theme/index.ts
2698
+ var TOKENS_INSTALL_COMMAND = `pnpm add ${TOKENS_PACKAGE}@^${MIN_BRAND_TOKENS_VERSION}`;
2699
+ function tokensStatusGuide(status, version) {
2700
+ switch (status) {
2701
+ case "not-installed":
2702
+ return [
2703
+ chalk8.yellow(`${TOKENS_PACKAGE} \uAC00 \uC124\uCE58\uB3FC \uC788\uC9C0 \uC54A\uC544 \uBE0C\uB79C\uB4DC\uB97C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`),
2704
+ ` \uC124\uCE58: ${chalk8.cyan(TOKENS_INSTALL_COMMAND)}`
2705
+ ];
2706
+ case "unsupported":
2707
+ return [
2708
+ chalk8.yellow(
2709
+ `${TOKENS_PACKAGE} ${version ?? "(\uBC84\uC804 \uBAA8\uB984)"} \uC740 \uBE0C\uB79C\uB4DC\uB97C \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4(${MIN_BRAND_TOKENS_VERSION} \uC774\uC0C1 \uD544\uC694).`
2710
+ ),
2711
+ ` \uC5C5\uADF8\uB808\uC774\uB4DC: ${chalk8.cyan(TOKENS_INSTALL_COMMAND)}`
2712
+ ];
2713
+ case "not-built":
2714
+ return [
2715
+ chalk8.yellow(
2716
+ `${TOKENS_PACKAGE}${version ? ` ${version}` : ""} \uB294 \uC788\uC9C0\uB9CC \uBE4C\uB4DC \uC0B0\uCD9C\uBB3C\uC774 \uC5C6\uC5B4 \uBE0C\uB79C\uB4DC\uB97C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`
2717
+ ),
2718
+ ` \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4\uBA74 ${chalk8.cyan(`pnpm --filter ${TOKENS_PACKAGE} build`)}, \uC544\uB2C8\uBA74 \uC7AC\uC124\uCE58: ${chalk8.cyan(TOKENS_INSTALL_COMMAND)}`
2719
+ ];
2720
+ }
2721
+ }
2722
+ function renderThemeList(result) {
2723
+ if (result.status !== "ok") {
2724
+ return { lines: ["", ...tokensStatusGuide(result.status, result.version), ""], exitCode: 1 };
2725
+ }
2726
+ if (result.brands.length === 0) {
2727
+ return {
2728
+ lines: ["", chalk8.yellow(`\uC124\uCE58\uB41C ${TOKENS_PACKAGE} ${result.version ?? ""} \uC5D0 \uBE0C\uB79C\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.`), ""],
2729
+ exitCode: 0
2730
+ };
2731
+ }
2732
+ const width = Math.max(...result.brands.map((b) => b.name.length));
2733
+ const lines = [
2734
+ "",
2735
+ chalk8.blue(`AXIS \uBE0C\uB79C\uB4DC (${result.brands.length}\uAC1C, ${TOKENS_PACKAGE} ${result.version ?? ""})`),
2736
+ ""
2737
+ ];
2738
+ for (const b of result.brands) {
2739
+ lines.push(
2740
+ ` ${chalk8.cyan(b.name.padEnd(width))} ${b.description} ${chalk8.gray(`(${b.themes.join(" \xB7 ")})`)}`
2741
+ );
2742
+ }
2743
+ lines.push("", chalk8.gray("\uC0AC\uC6A9\uBC95: axis theme info <brand>"), "");
2744
+ return { lines, exitCode: 0 };
2745
+ }
2746
+ function a11yValue(value) {
2747
+ return value === null || value === void 0 || value === "" ? "-" : String(value);
2748
+ }
2749
+ function tokenTable(brand) {
2750
+ const rows = KEY_TOKENS.filter(
2751
+ (k) => k in brand.tokens.light || k in brand.tokens.dark
2752
+ ).map((k) => [k, brand.tokens.light[k] ?? "-", brand.tokens.dark[k] ?? "-"]);
2753
+ const missing = KEY_TOKENS.filter((k) => !(k in brand.tokens.light) && !(k in brand.tokens.dark));
2754
+ const lines = [];
2755
+ if (rows.length > 0) {
2756
+ const header = ["\uD1A0\uD070", "\uB77C\uC774\uD2B8", "\uB2E4\uD06C"];
2757
+ const w0 = Math.max(header[0].length, ...rows.map((r) => r[0].length));
2758
+ const w1 = Math.max(header[1].length, ...rows.map((r) => r[1].length));
2759
+ lines.push(chalk8.gray(` ${header[0].padEnd(w0)} ${header[1].padEnd(w1)} ${header[2]}`));
2760
+ for (const [k, l, d] of rows) {
2761
+ lines.push(` ${k.padEnd(w0)} ${l.padEnd(w1)} ${d}`);
2762
+ }
2763
+ }
2764
+ if (missing.length > 0) {
2765
+ lines.push(chalk8.gray(` \uBE0C\uB79C\uB4DC\uAC00 \uB36E\uC5B4\uC4F0\uC9C0 \uC54A\uC74C: ${missing.join(", ")}`));
2766
+ }
2767
+ return lines;
2768
+ }
2769
+ function renderThemeInfo(result, name) {
2770
+ if (result.status !== "ok") {
2771
+ return { lines: ["", ...tokensStatusGuide(result.status, result.version), ""], exitCode: 1 };
2772
+ }
2773
+ const brand = result.brand;
2774
+ if (!brand) {
2775
+ return {
2776
+ lines: [
2777
+ "",
2778
+ chalk8.red(`\uBE0C\uB79C\uB4DC '${name}' \uAC00 \uC124\uCE58\uB41C ${TOKENS_PACKAGE} ${result.version ?? ""} \uC5D0 \uC5C6\uC2B5\uB2C8\uB2E4.`),
2779
+ ` \uC124\uCE58\uB41C \uBE0C\uB79C\uB4DC: ${result.available.length > 0 ? result.available.join(", ") : "(\uC5C6\uC74C)"}`,
2780
+ chalk8.gray(" \uBAA9\uB85D: axis theme list"),
2781
+ ""
2782
+ ],
2783
+ exitCode: 1
2784
+ };
2785
+ }
2786
+ const lines = [
2787
+ "",
2788
+ chalk8.blue(`\uBE0C\uB79C\uB4DC ${brand.name}`),
2789
+ ` \uC124\uBA85: ${brand.description}`,
2790
+ ` \uD14C\uB9C8: ${brand.themes.join(" \xB7 ")}`,
2791
+ "",
2792
+ chalk8.bold("Import (globals.css, @charset \xB7 \uB2E4\uB978 @import \uB2E4\uC74C)"),
2793
+ ` @import "${brand.importPath}";`,
2794
+ "",
2795
+ chalk8.bold("Provider (@axis-ds/theme 1.3.0 \uC774\uC0C1)"),
2796
+ ...brand.providerExample.split("\n").map((l) => l ? ` ${l}` : ""),
2797
+ chalk8.gray(
2798
+ ` ThemeProvider \uB098 <html data-brand="${brand.name}"> \uAC00 \uC5C6\uC73C\uBA74 import \uB9CC\uC73C\uB85C\uB294 \uD654\uBA74\uC774 \uBC14\uB00C\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.`
2799
+ ),
2800
+ "",
2801
+ chalk8.bold("\uC8FC\uC694 \uD1A0\uD070"),
2802
+ ...tokenTable(brand),
2803
+ "",
2804
+ chalk8.bold("\uBCF4\uC815 \uB0B4\uC5ED"),
2805
+ ` a11y \uC0C1\uD0DC: ${a11yValue(brand.adjustments.a11y.status)}`,
2806
+ ` \uC810\uAC80\uC77C: ${a11yValue(brand.adjustments.a11y.checkedAt)}`,
2807
+ ` \uAC8C\uC774\uD2B8: ${a11yValue(brand.adjustments.a11y.gate)}`,
2808
+ ` \uADFC\uAC70 \uBB38\uC11C: ${brand.adjustments.reference}`,
2809
+ chalk8.gray(" \uBCF4\uC815 \uC804 \xB7 \uD6C4 \uAC12\uC740 \uC774 \uBC84\uC804\uC5D0\uC11C \uCD9C\uB825\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4."),
2810
+ ""
2811
+ ];
2812
+ return { lines, exitCode: 0 };
2813
+ }
2814
+ function rel(plan, file) {
2815
+ return path10.relative(plan.baseDir, file) || file;
2816
+ }
2817
+ function renderThemeAdd(plan, written) {
2818
+ const error = plan.errors[0];
2819
+ if (error) {
2820
+ const lines2 = [""];
2821
+ switch (error.kind) {
2822
+ case "no-config":
2823
+ lines2.push(
2824
+ chalk8.red("axis.config.json \uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uC544\uBB34\uAC83\uB3C4 \uBC14\uAFB8\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4."),
2825
+ ` \uBA3C\uC800 ${chalk8.cyan("axis init")} \uC744 \uC2E4\uD589\uD558\uC138\uC694.`
2826
+ );
2827
+ break;
2828
+ case "invalid-config":
2829
+ lines2.push(chalk8.red(`axis.config.json \uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4(${error.message}). \uC544\uBB34\uAC83\uB3C4 \uBC14\uAFB8\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.`));
2830
+ break;
2831
+ case "invalid-package-json":
2832
+ lines2.push(chalk8.red(`package.json \uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4(${error.message}). \uC544\uBB34\uAC83\uB3C4 \uBC14\uAFB8\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.`));
2833
+ break;
2834
+ case "invalid-name":
2835
+ lines2.push(
2836
+ chalk8.red(`\uBE0C\uB79C\uB4DC \uC774\uB984 '${error.brand}' \uAC00 \uADDC\uCE59(\uC18C\uBB38\uC790 \xB7 \uC22B\uC790 \xB7 \uD558\uC774\uD508, \uC608: ontology)\uC5D0 \uB9DE\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.`)
2837
+ );
2838
+ break;
2839
+ case "css-outside-project":
2840
+ lines2.push(
2841
+ chalk8.red(
2842
+ `CSS \uD30C\uC77C ${error.file} \uC758 \uC2E4\uC81C \uC704\uCE58(${error.realPath})\uAC00 \uD504\uB85C\uC81D\uD2B8 \uB514\uB809\uD130\uB9AC \uBC16\uC785\uB2C8\uB2E4. \uC544\uBB34\uAC83\uB3C4 \uBC14\uAFB8\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.`
2843
+ ),
2844
+ ` axis.config.json \uC758 globalCss \uB97C \uD504\uB85C\uC81D\uD2B8 \uC548 \uD30C\uC77C\uB85C \uACE0\uCE58\uAC70\uB098, \uADF8 \uD30C\uC77C\uC5D0 \uC9C1\uC811 \uB123\uC73C\uC138\uC694: @import "${plan.importPath}";`
2845
+ );
2846
+ break;
2847
+ case "css-not-stylesheet":
2848
+ lines2.push(
2849
+ chalk8.red(`${error.file} \uC758 \uC2E4\uC81C \uB300\uC0C1(${error.realPath})\uC774 CSS \uD30C\uC77C(.css)\uC774 \uC544\uB2D9\uB2C8\uB2E4. \uC544\uBB34\uAC83\uB3C4 \uBC14\uAFB8\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.`),
2850
+ ` axis.config.json \uC758 globalCss \uB97C \uC804\uC5ED CSS \uD30C\uC77C\uB85C \uACE0\uCE58\uAC70\uB098, \uADF8 \uD30C\uC77C\uC5D0 \uC9C1\uC811 \uB123\uC73C\uC138\uC694: @import "${plan.importPath}";`
2851
+ );
2852
+ break;
2853
+ case "unknown-brand":
2854
+ lines2.push(
2855
+ chalk8.red(`\uBE0C\uB79C\uB4DC '${error.brand}' \uAC00 \uC124\uCE58\uB41C ${TOKENS_PACKAGE} ${error.version ?? ""} \uC5D0 \uC5C6\uC2B5\uB2C8\uB2E4.`),
2856
+ ` \uC124\uCE58\uB41C \uBE0C\uB79C\uB4DC: ${error.available.length > 0 ? error.available.join(", ") : "(\uC5C6\uC74C)"}`,
2857
+ chalk8.gray(" \uBAA9\uB85D: axis theme list")
2858
+ );
2859
+ break;
2860
+ }
2861
+ lines2.push("");
2862
+ return { lines: lines2, exitCode: 1 };
2863
+ }
2864
+ const lines = ["", chalk8.blue(`\uBE0C\uB79C\uB4DC ${plan.brand} \uC801\uC6A9`), ""];
2865
+ if (plan.unverifiedBrand) {
2866
+ lines.push(
2867
+ chalk8.yellow(
2868
+ ` ! ${TOKENS_PACKAGE} \uB97C \uC77D\uC744 \uC218 \uC5C6\uC5B4(${plan.tokens.status}) \uC774\uB984 \uADDC\uCE59\uB9CC \uD655\uC778\uD588\uC2B5\uB2C8\uB2E4. \uC124\uCE58 \uD6C4 axis theme list \uB85C '${plan.brand}' \uAC00 \uC788\uB294\uC9C0 \uD655\uC778\uD558\uC138\uC694.`
2869
+ )
2870
+ );
2871
+ }
2872
+ const css = plan.css;
2873
+ if (css?.configuredMissing) {
2874
+ lines.push(
2875
+ chalk8.yellow(
2876
+ css.file ? ` ! axis.config.json \uC758 globalCss(${css.configuredMissing})\uAC00 \uC5C6\uC5B4 ${rel(plan, css.file)} \uC5D0 \uCD94\uAC00\uD569\uB2C8\uB2E4.` : ` ! axis.config.json \uC758 globalCss(${css.configuredMissing})\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.`
2877
+ )
2878
+ );
2879
+ }
2880
+ if (css?.action === "add" && css.file) {
2881
+ lines.push(` ${chalk8.green("\u2713")} ${rel(plan, css.file)}: @import "${plan.importPath}"; \uCD94\uAC00`);
2882
+ } else if (css?.action === "exists" && css.file) {
2883
+ lines.push(` ${chalk8.gray("-")} ${rel(plan, css.file)}: import \uAC00 \uC774\uBBF8 \uC788\uC5B4 \uAC74\uB108\uB700`);
2884
+ } else {
2885
+ lines.push(
2886
+ chalk8.yellow(" ! CSS \uC9C4\uC785\uC810(globalCss \xB7 app/globals.css \xB7 src/app/globals.css \xB7 globals.css)\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4."),
2887
+ ` \uC804\uC5ED CSS \uB9E8 \uC55E(@charset \xB7 \uB2E4\uB978 @import \uB2E4\uC74C)\uC5D0 \uC9C1\uC811 \uB123\uC73C\uC138\uC694: @import "${plan.importPath}";`
2888
+ );
2889
+ }
2890
+ if (css && css.otherBrands.length > 0) {
2891
+ lines.push(
2892
+ chalk8.yellow(
2893
+ ` ! \uB2E4\uB978 \uBE0C\uB79C\uB4DC import(${css.otherBrands.join(", ")})\uB294 \uADF8\uB300\uB85C \uB450\uC5C8\uC2B5\uB2C8\uB2E4. \uC4F0\uC9C0 \uC54A\uC73C\uBA74 \uC774\uC804 \uBE0C\uB79C\uB4DC import \uB97C \uC9C0\uC6CC\uB3C4 \uB429\uB2C8\uB2E4.`
2894
+ )
2895
+ );
2896
+ }
2897
+ const dep = plan.dependency;
2898
+ if (dep?.action === "add") {
2899
+ lines.push(
2900
+ ` ${chalk8.green("\u2713")} package.json: dependencies \uC5D0 "${TOKENS_PACKAGE}": "${TOKENS_DEPENDENCY_RANGE}" \uCD94\uAC00`,
2901
+ ` \uC124\uCE58\uB294 \uC2E4\uD589\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uC124\uCE58: ${chalk8.cyan(plan.installCommand)}`
2902
+ );
2903
+ } else if (dep?.action === "present") {
2904
+ lines.push(` ${chalk8.gray("-")} package.json: ${TOKENS_PACKAGE} ${dep.declaredRange} \uAC00 \uC774\uBBF8 \uC788\uC5B4 \uBC94\uC704\uB97C \uBC14\uAFB8\uC9C0 \uC54A\uC74C`);
2905
+ } else if (dep?.action === "no-package-json") {
2906
+ lines.push(
2907
+ chalk8.yellow(` ! package.json \uC774 \uC5C6\uC5B4 \uC758\uC874\uC131\uC744 \uCD94\uAC00\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uC124\uCE58: ${TOKENS_INSTALL_COMMAND}`)
2908
+ );
2909
+ }
2910
+ if (dep?.upgrade) {
2911
+ lines.push(
2912
+ chalk8.yellow(
2913
+ ` ! \uC124\uCE58\uB41C ${TOKENS_PACKAGE} ${plan.tokens.version} \uC740 \uBE0C\uB79C\uB4DC\uB97C \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4(${MIN_BRAND_TOKENS_VERSION} \uC774\uC0C1 \uD544\uC694).`
2914
+ ),
2915
+ ` \uC5C5\uADF8\uB808\uC774\uB4DC: ${chalk8.cyan(TOKENS_INSTALL_COMMAND)}`
2916
+ );
2917
+ }
2918
+ if (plan.configChanged) {
2919
+ lines.push(` ${chalk8.green("\u2713")} axis.config.json: "brand": "${plan.brand}" \uAE30\uB85D`);
2920
+ } else {
2921
+ lines.push(` ${chalk8.gray("-")} axis.config.json: brand \uAC00 \uC774\uBBF8 "${plan.brand}"`);
2922
+ }
2923
+ lines.push(
2924
+ "",
2925
+ chalk8.bold(`Provider (${THEME_PACKAGE} 1.3.0 \uC774\uC0C1)`),
2926
+ ...providerExample(plan.brand).split("\n").map((l) => l ? ` ${l}` : ""),
2927
+ chalk8.gray(
2928
+ ` ThemeProvider \uB098 <html data-brand="${plan.brand}"> \uAC00 \uC5C6\uC73C\uBA74 import \uB9CC\uC73C\uB85C\uB294 \uD654\uBA74\uC774 \uBC14\uB00C\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.`
2929
+ )
2930
+ );
2931
+ if (dep?.themeMissing) {
2932
+ lines.push(` ${THEME_PACKAGE} \uC124\uCE58: ${chalk8.cyan(`pnpm add ${THEME_PACKAGE}@^1.3.0`)}`);
2933
+ }
2934
+ lines.push("", chalk8.gray(written.length > 0 ? `\uD30C\uC77C ${written.length}\uAC1C\uB97C \uBC14\uAFE8\uC2B5\uB2C8\uB2E4.` : "\uBC14\uAFBC \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."), "");
2935
+ return { lines, exitCode: 0 };
2936
+ }
2937
+ function renderThemeAddFailure(plan, err) {
2938
+ const lines = [""];
2939
+ if (err instanceof ThemeApplyError) {
2940
+ lines.push(chalk8.red(err.message));
2941
+ if (err.restored.length > 0) {
2942
+ lines.push(` \uBA3C\uC800 \uC4F4 \uD30C\uC77C\uC740 \uC6D0\uB798 \uB0B4\uC6A9\uC73C\uB85C \uB418\uB3CC\uB838\uC2B5\uB2C8\uB2E4: ${err.restored.map((f) => rel(plan, f)).join(", ")}`);
2943
+ }
2944
+ if (err.restoreFailed.length > 0) {
2945
+ lines.push(
2946
+ chalk8.red(` \uB418\uB3CC\uB9AC\uC9C0 \uBABB\uD574 \uBC14\uB010 \uCC44 \uB0A8\uC740 \uD30C\uC77C: ${err.restoreFailed.map((f) => rel(plan, f)).join(", ")}`)
2947
+ );
2948
+ }
2949
+ if (err.restored.length === 0 && err.restoreFailed.length === 0) {
2950
+ lines.push(" \uBC14\uB010 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
2951
+ }
2952
+ } else {
2953
+ lines.push(chalk8.red(err instanceof Error ? err.message : String(err)), " \uBC14\uB010 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
2954
+ }
2955
+ lines.push("");
2956
+ return { lines, exitCode: 1 };
2957
+ }
2958
+ function print(output) {
2959
+ for (const line of output.lines) console.log(line);
2960
+ if (output.exitCode !== 0) process.exitCode = output.exitCode;
2961
+ }
2962
+ function registerThemeCommand(program2) {
2963
+ const themeCmd = program2.command("theme").description("\uBE0C\uB79C\uB4DC \uD14C\uB9C8 (@axis-ds/tokens \uBE0C\uB79C\uB4DC)");
2964
+ themeCmd.command("list").alias("ls").description("\uC124\uCE58\uB41C @axis-ds/tokens \uC758 \uBE0C\uB79C\uB4DC \uBAA9\uB85D").option("-d, --dir <path>", "\uB300\uC0C1 \uB514\uB809\uD1A0\uB9AC").action((options) => {
2965
+ const baseDir = path10.resolve(options.dir || ".");
2966
+ print(renderThemeList(listBrands(baseDir)));
2967
+ });
2968
+ themeCmd.command("info <brand>").description("\uBE0C\uB79C\uB4DC \uC0C1\uC138: import \uACBD\uB85C \xB7 Provider \uC608\uC2DC \xB7 \uC8FC\uC694 \uD1A0\uD070 \xB7 \uBCF4\uC815 \uB0B4\uC5ED").option("-d, --dir <path>", "\uB300\uC0C1 \uB514\uB809\uD1A0\uB9AC").action((name, options) => {
2969
+ const baseDir = path10.resolve(options.dir || ".");
2970
+ print(renderThemeInfo(getBrand(baseDir, name), name));
2971
+ });
2972
+ themeCmd.command("add <brand>").description("\uBE0C\uB79C\uB4DC \uC801\uC6A9: CSS import \xB7 package.json \uC758\uC874\uC131 \xB7 axis.config.json brand (\uC124\uCE58\uB294 \uC548\uB0B4\uB9CC)").option("-d, --dir <path>", "\uB300\uC0C1 \uB514\uB809\uD1A0\uB9AC").action((name, options) => {
2973
+ const baseDir = path10.resolve(options.dir || ".");
2974
+ const plan = planThemeAdd(baseDir, name);
2975
+ let written = [];
2976
+ if (plan.errors.length === 0) {
2977
+ try {
2978
+ written = applyThemeAdd(plan);
2979
+ } catch (err) {
2980
+ print(renderThemeAddFailure(plan, err));
2981
+ return;
2982
+ }
2983
+ }
2984
+ print(renderThemeAdd(plan, written));
2985
+ });
2986
+ }
2987
+
2105
2988
  // src/index.ts
2989
+ var { version: CLI_VERSION } = createRequire2(import.meta.url)("../package.json");
2106
2990
  var REGISTRY_URL = process.env.AXIS_REGISTRY_URL || "https://axis.minu.best/r";
2107
2991
  var program = new Command();
2108
- program.name("axis").description("AXIS Design System CLI - shadcn \uD638\uD658 \uCEF4\uD3EC\uB10C\uD2B8 \uC124\uCE58 \uB3C4\uAD6C").version("0.1.0");
2992
+ program.name("axis").description("AXIS Design System CLI - shadcn \uD638\uD658 \uCEF4\uD3EC\uB10C\uD2B8 \uC124\uCE58 \uB3C4\uAD6C").version(CLI_VERSION);
2109
2993
  program.command("init").description("\uD504\uB85C\uC81D\uD2B8\uC5D0 AXIS Design System \uCD08\uAE30\uD654").option("-y, --yes", "\uAE30\uBCF8\uAC12\uC73C\uB85C \uC790\uB3D9 \uC124\uC815").action(async (options) => {
2110
- console.log(chalk8.blue("\n\u{1F3A8} AXIS Design System \uCD08\uAE30\uD654\n"));
2994
+ console.log(chalk9.blue("\n\u{1F3A8} AXIS Design System \uCD08\uAE30\uD654\n"));
2111
2995
  const config = options.yes ? {
2112
2996
  componentsDir: "./src/components/ui",
2113
2997
  tailwindConfig: "./tailwind.config.ts",
@@ -2132,18 +3016,19 @@ program.command("init").description("\uD504\uB85C\uC81D\uD2B8\uC5D0 AXIS Design
2132
3016
  initial: "./src/app/globals.css"
2133
3017
  }
2134
3018
  ]);
2135
- const axisConfig = {
2136
- $schema: "https://axis.minu.best/schema.json",
2137
- componentsDir: config.componentsDir,
2138
- tailwindConfig: config.tailwindConfig,
2139
- globalCss: config.globalCss,
2140
- registry: REGISTRY_URL
2141
- };
2142
- await fs9.writeJSON("axis.config.json", axisConfig, { spaces: 2 });
2143
- console.log(chalk8.green("\u2713 axis.config.json \uC0DD\uC131 \uC644\uB8CC"));
2144
- const utilsPath = path9.join(config.componentsDir, "utils.ts");
2145
- await fs9.ensureDir(path9.dirname(utilsPath));
2146
- await fs9.writeFile(
3019
+ const axisConfig = createInitConfig(
3020
+ {
3021
+ componentsDir: config.componentsDir,
3022
+ tailwindConfig: config.tailwindConfig,
3023
+ globalCss: config.globalCss
3024
+ },
3025
+ REGISTRY_URL
3026
+ );
3027
+ await fs10.writeJSON("axis.config.json", axisConfig, { spaces: 2 });
3028
+ console.log(chalk9.green("\u2713 axis.config.json \uC0DD\uC131 \uC644\uB8CC"));
3029
+ const utilsPath = path11.join(config.componentsDir, "utils.ts");
3030
+ await fs10.ensureDir(path11.dirname(utilsPath));
3031
+ await fs10.writeFile(
2147
3032
  utilsPath,
2148
3033
  `import { type ClassValue, clsx } from "clsx";
2149
3034
  import { twMerge } from "tailwind-merge";
@@ -2153,21 +3038,21 @@ export function cn(...inputs: ClassValue[]) {
2153
3038
  }
2154
3039
  `
2155
3040
  );
2156
- console.log(chalk8.green("\u2713 utils.ts \uC0DD\uC131 \uC644\uB8CC"));
2157
- console.log(chalk8.blue("\n\uCD08\uAE30\uD654 \uC644\uB8CC! \uC774\uC81C \uB2E4\uC74C \uBA85\uB839\uC5B4\uB85C \uCEF4\uD3EC\uB10C\uD2B8\uB97C \uCD94\uAC00\uD558\uC138\uC694:"));
2158
- console.log(chalk8.cyan(" npx axis-cli add button\n"));
3041
+ console.log(chalk9.green("\u2713 utils.ts \uC0DD\uC131 \uC644\uB8CC"));
3042
+ console.log(chalk9.blue("\n\uCD08\uAE30\uD654 \uC644\uB8CC! \uC774\uC81C \uB2E4\uC74C \uBA85\uB839\uC5B4\uB85C \uCEF4\uD3EC\uB10C\uD2B8\uB97C \uCD94\uAC00\uD558\uC138\uC694:"));
3043
+ console.log(chalk9.cyan(" npx axis-cli add button\n"));
2159
3044
  });
2160
3045
  program.command("add <component>").description("\uCEF4\uD3EC\uB10C\uD2B8 \uCD94\uAC00").option("--agentic", "Agentic UI \uCEF4\uD3EC\uB10C\uD2B8 \uCD94\uAC00").option("-y, --yes", "\uD655\uC778 \uC5C6\uC774 \uB36E\uC5B4\uC4F0\uAE30").action(async (component, options) => {
2161
3046
  const spinner = ora6(`${component} \uCEF4\uD3EC\uB10C\uD2B8 \uAC00\uC838\uC624\uB294 \uC911...`).start();
2162
3047
  try {
2163
- const configPath = path9.resolve("axis.config.json");
2164
- if (!await fs9.pathExists(configPath)) {
3048
+ const configPath = path11.resolve("axis.config.json");
3049
+ if (!await fs10.pathExists(configPath)) {
2165
3050
  spinner.fail(
2166
3051
  "axis.config.json\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.\n \uD574\uACB0: \uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8\uC5D0\uC11C 'npx @axis-ds/cli init'\uC744 \uC2E4\uD589\uD558\uC138\uC694.\n \uCC38\uACE0: https://axis.minu.best/docs/guides/quick-start"
2167
3052
  );
2168
3053
  process.exit(1);
2169
3054
  }
2170
- const config = await fs9.readJSON(configPath);
3055
+ const config = await fs10.readJSON(configPath);
2171
3056
  const category = options.agentic ? "agentic" : "ui";
2172
3057
  const componentInfo = await getComponentInfo(component, category);
2173
3058
  if (!componentInfo) {
@@ -2180,8 +3065,8 @@ program.command("add <component>").description("\uCEF4\uD3EC\uB10C\uD2B8 \uCD94\
2180
3065
  }
2181
3066
  spinner.text = `${component} \uCEF4\uD3EC\uB10C\uD2B8 \uC124\uCE58 \uC911...`;
2182
3067
  for (const file of componentInfo.files) {
2183
- const filePath = path9.join(config.componentsDir, file.path);
2184
- if (await fs9.pathExists(filePath)) {
3068
+ const filePath = path11.join(config.componentsDir, file.path);
3069
+ if (await fs10.pathExists(filePath)) {
2185
3070
  if (!options.yes) {
2186
3071
  const { overwrite } = await prompts2({
2187
3072
  type: "confirm",
@@ -2192,13 +3077,13 @@ program.command("add <component>").description("\uCEF4\uD3EC\uB10C\uD2B8 \uCD94\
2192
3077
  if (!overwrite) continue;
2193
3078
  }
2194
3079
  }
2195
- await fs9.ensureDir(path9.dirname(filePath));
2196
- await fs9.writeFile(filePath, file.content);
3080
+ await fs10.ensureDir(path11.dirname(filePath));
3081
+ await fs10.writeFile(filePath, file.content);
2197
3082
  }
2198
- spinner.succeed(chalk8.green(`${component} \uCEF4\uD3EC\uB10C\uD2B8 \uCD94\uAC00 \uC644\uB8CC`));
3083
+ spinner.succeed(chalk9.green(`${component} \uCEF4\uD3EC\uB10C\uD2B8 \uCD94\uAC00 \uC644\uB8CC`));
2199
3084
  if (componentInfo.dependencies.length > 0) {
2200
- console.log(chalk8.yellow("\n\uD544\uC694\uD55C \uC758\uC874\uC131:"));
2201
- console.log(chalk8.cyan(` pnpm add ${componentInfo.dependencies.join(" ")}`));
3085
+ console.log(chalk9.yellow("\n\uD544\uC694\uD55C \uC758\uC874\uC131:"));
3086
+ console.log(chalk9.cyan(` pnpm add ${componentInfo.dependencies.join(" ")}`));
2202
3087
  }
2203
3088
  } catch (error) {
2204
3089
  spinner.fail(
@@ -2210,7 +3095,7 @@ program.command("add <component>").description("\uCEF4\uD3EC\uB10C\uD2B8 \uCD94\
2210
3095
  }
2211
3096
  });
2212
3097
  program.command("list").description("\uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uCEF4\uD3EC\uB10C\uD2B8 \uBAA9\uB85D").option("--category <type>", "\uCE74\uD14C\uACE0\uB9AC \uD544\uD130 (ui, agentic)").action(async (options) => {
2213
- console.log(chalk8.blue("\n\u{1F4E6} AXIS Design System \uCEF4\uD3EC\uB10C\uD2B8\n"));
3098
+ console.log(chalk9.blue("\n\u{1F4E6} AXIS Design System \uCEF4\uD3EC\uB10C\uD2B8\n"));
2214
3099
  const components = {
2215
3100
  ui: [
2216
3101
  { name: "button", description: "\uAE30\uBCF8 \uBC84\uD2BC \uCEF4\uD3EC\uB10C\uD2B8" },
@@ -2238,9 +3123,9 @@ program.command("list").description("\uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uCEF4\uD3E
2238
3123
  ]
2239
3124
  };
2240
3125
  const showCategory = (category, items) => {
2241
- console.log(chalk8.bold(category === "ui" ? "Core UI" : "Agentic UI"));
3126
+ console.log(chalk9.bold(category === "ui" ? "Core UI" : "Agentic UI"));
2242
3127
  items.forEach((c) => {
2243
- console.log(` ${chalk8.cyan(c.name.padEnd(20))} ${chalk8.gray(c.description)}`);
3128
+ console.log(` ${chalk9.cyan(c.name.padEnd(20))} ${chalk9.gray(c.description)}`);
2244
3129
  });
2245
3130
  console.log();
2246
3131
  };
@@ -2250,8 +3135,8 @@ program.command("list").description("\uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uCEF4\uD3E
2250
3135
  if (!options.category || options.category === "agentic") {
2251
3136
  showCategory("agentic", components.agentic);
2252
3137
  }
2253
- console.log(chalk8.gray("\uC0AC\uC6A9\uBC95: npx axis-cli add <component-name>"));
2254
- console.log(chalk8.gray("Agentic: npx axis-cli add <component-name> --agentic\n"));
3138
+ console.log(chalk9.gray("\uC0AC\uC6A9\uBC95: npx axis-cli add <component-name>"));
3139
+ console.log(chalk9.gray("Agentic: npx axis-cli add <component-name> --agentic\n"));
2255
3140
  });
2256
3141
  var monetCmd = program.command("monet").description("Monet Design \uCEF4\uD3EC\uB10C\uD2B8 \uAD00\uB9AC (https://monet.design)");
2257
3142
  monetCmd.command("list").alias("ls").description("Monet \uCE74\uD14C\uACE0\uB9AC \uBAA9\uB85D").action(async () => {
@@ -2271,24 +3156,24 @@ monetCmd.command("import").alias("i").description("\uD074\uB9BD\uBCF4\uB4DC\uC5D
2271
3156
  await client.importFromClipboard();
2272
3157
  });
2273
3158
  monetCmd.command("setup").description("Monet MCP \uC11C\uBC84 \uC124\uC815 \uC548\uB0B4").action(() => {
2274
- console.log(chalk8.blue("\n\u{1F527} Monet MCP \uC11C\uBC84 \uC124\uC815\n"));
2275
- console.log(chalk8.bold("1. API \uD0A4 \uBC1C\uAE09:"));
2276
- console.log(chalk8.cyan(" https://monet.design/mcp\n"));
2277
- console.log(chalk8.bold("2. \uD658\uACBD \uBCC0\uC218 \uC124\uC815:"));
2278
- console.log(chalk8.gray(" .env \uD30C\uC77C\uC5D0 \uCD94\uAC00:"));
2279
- console.log(chalk8.cyan(" MONET_API_KEY=your-api-key-here\n"));
2280
- console.log(chalk8.bold("3. Claude Code MCP \uC124\uC815:"));
2281
- console.log(chalk8.gray(" .claude/mcp.json\uC5D0\uC11C monet \uC11C\uBC84 \uD65C\uC131\uD654:\n"));
3159
+ console.log(chalk9.blue("\n\u{1F527} Monet MCP \uC11C\uBC84 \uC124\uC815\n"));
3160
+ console.log(chalk9.bold("1. API \uD0A4 \uBC1C\uAE09:"));
3161
+ console.log(chalk9.cyan(" https://monet.design/mcp\n"));
3162
+ console.log(chalk9.bold("2. \uD658\uACBD \uBCC0\uC218 \uC124\uC815:"));
3163
+ console.log(chalk9.gray(" .env \uD30C\uC77C\uC5D0 \uCD94\uAC00:"));
3164
+ console.log(chalk9.cyan(" MONET_API_KEY=your-api-key-here\n"));
3165
+ console.log(chalk9.bold("3. Claude Code MCP \uC124\uC815:"));
3166
+ console.log(chalk9.gray(" .claude/mcp.json\uC5D0\uC11C monet \uC11C\uBC84 \uD65C\uC131\uD654:\n"));
2282
3167
  console.log(
2283
- chalk8.cyan(` "monet": {
3168
+ chalk9.cyan(` "monet": {
2284
3169
  "url": "https://www.monet.design/api/remote/mcp",
2285
3170
  "headers": { "Authorization": "Bearer \${MONET_API_KEY}" },
2286
3171
  "disabled": false
2287
3172
  }
2288
3173
  `)
2289
3174
  );
2290
- console.log(chalk8.bold("4. Claude Code \uC7AC\uC2DC\uC791 \uD6C4 \uC0AC\uC6A9:"));
2291
- console.log(chalk8.gray(' "monet\uC5D0\uC11C hero \uCEF4\uD3EC\uB10C\uD2B8 \uCC3E\uC544\uC918"\n'));
3175
+ console.log(chalk9.bold("4. Claude Code \uC7AC\uC2DC\uC791 \uD6C4 \uC0AC\uC6A9:"));
3176
+ console.log(chalk9.gray(' "monet\uC5D0\uC11C hero \uCEF4\uD3EC\uB10C\uD2B8 \uCC3E\uC544\uC918"\n'));
2292
3177
  });
2293
3178
  var v0Cmd = program.command("v0").description("V0 (v0.app) \uCF54\uB4DC \uBCC0\uD658 \uBC0F \uD1B5\uD569");
2294
3179
  v0Cmd.command("convert <file>").alias("c").description("V0 \uCF54\uB4DC\uB97C AXIS \uC2A4\uD0C0\uC77C\uB85C \uBCC0\uD658").option("-o, --output <path>", "\uCD9C\uB825 \uD30C\uC77C \uACBD\uB85C").action(async (file, options) => {
@@ -2325,18 +3210,18 @@ libraryCmd.command("collect").alias("c").description("\uC678\uBD80 \uC18C\uC2A4\
2325
3210
  total += result.collected;
2326
3211
  failed += result.failed;
2327
3212
  }
2328
- console.log(chalk8.blue(`
3213
+ console.log(chalk9.blue(`
2329
3214
  \u{1F4CA} \uC218\uC9D1 \uC644\uB8CC: ${total}\uAC1C \uCEF4\uD3EC\uB10C\uD2B8`));
2330
3215
  if (failed > 0) {
2331
- console.log(chalk8.yellow(` \uC2E4\uD328: ${failed}\uAC1C`));
3216
+ console.log(chalk9.yellow(` \uC2E4\uD328: ${failed}\uAC1C`));
2332
3217
  }
2333
3218
  });
2334
3219
  libraryCmd.command("list").alias("ls").description("\uB77C\uC774\uBE0C\uB7EC\uB9AC \uCEF4\uD3EC\uB10C\uD2B8 \uBAA9\uB85D").option("-c, --category <category>", "\uCE74\uD14C\uACE0\uB9AC \uD544\uD130").option("-s, --source <source>", "\uC18C\uC2A4 \uD544\uD130").action(async (options) => {
2335
3220
  const curator = new LibraryCurator();
2336
3221
  const index = await curator.loadIndex();
2337
3222
  if (!index) {
2338
- console.log(chalk8.yellow("\n\u26A0\uFE0F \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC778\uB371\uC2A4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4."));
2339
- console.log(chalk8.gray(" \uD574\uACB0: npx @axis-ds/cli library collect \uBA85\uB839\uC5B4\uB85C \uC218\uC9D1\uC744 \uBA3C\uC800 \uC2E4\uD589\uD558\uC138\uC694.\n"));
3223
+ console.log(chalk9.yellow("\n\u26A0\uFE0F \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC778\uB371\uC2A4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4."));
3224
+ console.log(chalk9.gray(" \uD574\uACB0: npx @axis-ds/cli library collect \uBA85\uB839\uC5B4\uB85C \uC218\uC9D1\uC744 \uBA3C\uC800 \uC2E4\uD589\uD558\uC138\uC694.\n"));
2340
3225
  return;
2341
3226
  }
2342
3227
  let components = index.components;
@@ -2346,7 +3231,7 @@ libraryCmd.command("list").alias("ls").description("\uB77C\uC774\uBE0C\uB7EC\uB9
2346
3231
  if (options.source) {
2347
3232
  components = components.filter((c) => c.source.type === options.source);
2348
3233
  }
2349
- console.log(chalk8.blue(`
3234
+ console.log(chalk9.blue(`
2350
3235
  \u{1F4E6} Library Components (${components.length}\uAC1C)
2351
3236
  `));
2352
3237
  const byCategory = /* @__PURE__ */ new Map();
@@ -2358,14 +3243,14 @@ libraryCmd.command("list").alias("ls").description("\uB77C\uC774\uBE0C\uB7EC\uB9
2358
3243
  byCategory.get(cat).push(comp);
2359
3244
  }
2360
3245
  for (const [category, items] of byCategory) {
2361
- console.log(chalk8.bold(`${category} (${items.length})`));
3246
+ console.log(chalk9.bold(`${category} (${items.length})`));
2362
3247
  for (const item of items.slice(0, 10)) {
2363
3248
  console.log(
2364
- ` ${chalk8.cyan(item.name.padEnd(25))} ${chalk8.gray(item.source.type.padEnd(8))} ${chalk8.gray(item.description.slice(0, 40))}`
3249
+ ` ${chalk9.cyan(item.name.padEnd(25))} ${chalk9.gray(item.source.type.padEnd(8))} ${chalk9.gray(item.description.slice(0, 40))}`
2365
3250
  );
2366
3251
  }
2367
3252
  if (items.length > 10) {
2368
- console.log(chalk8.gray(` ... \uC678 ${items.length - 10}\uAC1C`));
3253
+ console.log(chalk9.gray(` ... \uC678 ${items.length - 10}\uAC1C`));
2369
3254
  }
2370
3255
  console.log();
2371
3256
  }
@@ -2377,21 +3262,21 @@ libraryCmd.command("search <query>").alias("s").description("\uCEF4\uD3EC\uB10C\
2377
3262
  source: options.source
2378
3263
  });
2379
3264
  if (results.length === 0) {
2380
- console.log(chalk8.yellow(`
3265
+ console.log(chalk9.yellow(`
2381
3266
  '${query}'\uC5D0 \uB300\uD55C \uAC80\uC0C9 \uACB0\uACFC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.
2382
3267
  `));
2383
3268
  return;
2384
3269
  }
2385
- console.log(chalk8.blue(`
3270
+ console.log(chalk9.blue(`
2386
3271
  \u{1F50D} '${query}' \uAC80\uC0C9 \uACB0\uACFC (${results.length}\uAC1C)
2387
3272
  `));
2388
3273
  for (const comp of results.slice(0, 20)) {
2389
3274
  console.log(
2390
- ` ${chalk8.cyan(comp.name.padEnd(25))} ${chalk8.gray(comp.source.type.padEnd(8))} ${chalk8.gray(comp.category.padEnd(12))} ${chalk8.gray(comp.description.slice(0, 30))}`
3275
+ ` ${chalk9.cyan(comp.name.padEnd(25))} ${chalk9.gray(comp.source.type.padEnd(8))} ${chalk9.gray(comp.category.padEnd(12))} ${chalk9.gray(comp.description.slice(0, 30))}`
2391
3276
  );
2392
3277
  }
2393
3278
  if (results.length > 20) {
2394
- console.log(chalk8.gray(`
3279
+ console.log(chalk9.gray(`
2395
3280
  ... \uC678 ${results.length - 20}\uAC1C`));
2396
3281
  }
2397
3282
  console.log();
@@ -2405,12 +3290,12 @@ libraryCmd.command("publish").description("\uC0AC\uC774\uD2B8\uC5D0 \uB77C\uC774
2405
3290
  const publisher2 = new Publisher();
2406
3291
  const index = await curator.loadIndex();
2407
3292
  if (!index) {
2408
- console.log(chalk8.yellow("\n\u26A0\uFE0F \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC778\uB371\uC2A4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4."));
2409
- console.log(chalk8.gray(" \uD574\uACB0: npx @axis-ds/cli library collect \uBA85\uB839\uC5B4\uB85C \uC218\uC9D1\uC744 \uBA3C\uC800 \uC2E4\uD589\uD558\uC138\uC694.\n"));
3293
+ console.log(chalk9.yellow("\n\u26A0\uFE0F \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC778\uB371\uC2A4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4."));
3294
+ console.log(chalk9.gray(" \uD574\uACB0: npx @axis-ds/cli library collect \uBA85\uB839\uC5B4\uB85C \uC218\uC9D1\uC744 \uBA3C\uC800 \uC2E4\uD589\uD558\uC138\uC694.\n"));
2410
3295
  return;
2411
3296
  }
2412
- console.log(chalk8.blue("\n\u{1F4E4} Library \uBC30\uCE58\n"));
2413
- console.log(chalk8.gray(`\uCD1D ${index.stats.total}\uAC1C \uCEF4\uD3EC\uB10C\uD2B8
3297
+ console.log(chalk9.blue("\n\u{1F4E4} Library \uBC30\uCE58\n"));
3298
+ console.log(chalk9.gray(`\uCD1D ${index.stats.total}\uAC1C \uCEF4\uD3EC\uB10C\uD2B8
2414
3299
  `));
2415
3300
  if (options.clean) {
2416
3301
  await publisher2.clean(options.output);
@@ -2421,17 +3306,17 @@ libraryCmd.command("publish").description("\uC0AC\uC774\uD2B8\uC5D0 \uB77C\uC774
2421
3306
  generateSearchIndex: true
2422
3307
  });
2423
3308
  if (result.success) {
2424
- console.log(chalk8.green(`
3309
+ console.log(chalk9.green(`
2425
3310
  \u2713 \uBC30\uCE58 \uC644\uB8CC`));
2426
- console.log(chalk8.gray(` - \uCD9C\uB825 \uB514\uB809\uD1A0\uB9AC: ${result.outputDir}`));
2427
- console.log(chalk8.gray(` - \uC0DD\uC131\uB41C \uD30C\uC77C: ${result.files.length}\uAC1C`));
2428
- console.log(chalk8.gray(` - \uCE74\uD14C\uACE0\uB9AC: ${result.stats.categoriesPublished}\uAC1C`));
2429
- console.log(chalk8.gray(` - \uAC80\uC0C9 \uC778\uB371\uC2A4: ${result.stats.searchIndexSize}\uAC1C \uD56D\uBAA9`));
3311
+ console.log(chalk9.gray(` - \uCD9C\uB825 \uB514\uB809\uD1A0\uB9AC: ${result.outputDir}`));
3312
+ console.log(chalk9.gray(` - \uC0DD\uC131\uB41C \uD30C\uC77C: ${result.files.length}\uAC1C`));
3313
+ console.log(chalk9.gray(` - \uCE74\uD14C\uACE0\uB9AC: ${result.stats.categoriesPublished}\uAC1C`));
3314
+ console.log(chalk9.gray(` - \uAC80\uC0C9 \uC778\uB371\uC2A4: ${result.stats.searchIndexSize}\uAC1C \uD56D\uBAA9`));
2430
3315
  } else {
2431
- console.log(chalk8.red(`
3316
+ console.log(chalk9.red(`
2432
3317
  \u2717 \uBC30\uCE58 \uC2E4\uD328`));
2433
3318
  for (const error of result.errors) {
2434
- console.log(chalk8.red(` - ${error}`));
3319
+ console.log(chalk9.red(` - ${error}`));
2435
3320
  }
2436
3321
  }
2437
3322
  console.log();
@@ -2439,6 +3324,7 @@ libraryCmd.command("publish").description("\uC0AC\uC774\uD2B8\uC5D0 \uB77C\uC774
2439
3324
  registerSyncCommand(program);
2440
3325
  registerTemplateCommand(program);
2441
3326
  registerCheckCommand(program);
3327
+ registerThemeCommand(program);
2442
3328
  async function getComponentInfo(name, category) {
2443
3329
  const localComponents = {
2444
3330
  button: {