@axis-ds/mcp 1.4.3 → 1.4.5

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 +1 -0
  2. package/dist/index.js +209 -44
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -66,6 +66,7 @@ Claude Desktop 또는 기타 MCP 호환 클라이언트에서 설정:
66
66
 
67
67
  - `targetDir` 를 주지 않으면 서버의 현재 디렉터리를 봅니다.
68
68
  - `@axis-ds/tokens` 가 없거나 1.3.0 미만이거나 빌드 산출물이 없으면 오류(`isError`)가 아니라 설치 · 업그레이드 안내를 돌려줍니다. 빈 목록으로 답하지 않습니다.
69
+ - 1.4.5 부터 안내는 CLI `axis theme list` 와 같게 선언 출처를 따릅니다(앱 · pnpm · yarn 루트 선언은 `<pm> install`, npm 루트 선언은 「워크스페이스 루트에서 `npm install`」, Yarn PnP 앱 선언은 판정하지 못했다는 한 줄, 선언이 없으면 종전 추가 명령).
69
70
  - 프로젝트에 적용하는 일(import 추가 · `axis.config.json` 의 `brand`)은 CLI `axis theme add <brand>` 가 맡습니다.
70
71
 
71
72
  ## 문서
package/dist/index.js CHANGED
@@ -970,14 +970,56 @@ function workspaceRootPackage(baseDirInput) {
970
970
  return void 0;
971
971
  }
972
972
  }
973
- function declaredDependency(baseDir, pkg, name, rootPkg) {
973
+ function declaredDependency(baseDir, pkg, name, rootPkg, options = {}) {
974
974
  if (pkg === void 0) return void 0;
975
975
  const own = hasDependency(pkg, name);
976
976
  if (own !== void 0) return { range: own, source: "app" };
977
+ if (options.appOnlyInPnp && isPnpProject(baseDir)) return void 0;
977
978
  const root = rootPkg === void 0 ? workspaceRootPackage(baseDir) : rootPkg ?? void 0;
978
979
  const fromRoot = root !== void 0 ? hasDependency(root, name) : void 0;
979
980
  return fromRoot !== void 0 ? { range: fromRoot, source: "root" } : void 0;
980
981
  }
982
+ function pnpRootOnlyDependency(baseDir, pkg, name, rootPkg) {
983
+ if (pkg === void 0 || hasDependency(pkg, name) !== void 0 || !isPnpProject(baseDir)) return void 0;
984
+ const found = declaredDependency(baseDir, pkg, name, rootPkg);
985
+ return found?.source === "root" ? found : void 0;
986
+ }
987
+ var PNP_ROOT_ONLY_NOTE = "\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8\uC5D0\uB9CC \uC120\uC5B8\uB3FC Plug'n'Play \uC5D0\uC11C \uC571\uC774 \uD574\uC11D\uD558\uC9C0 \uBABB\uD569\uB2C8\uB2E4";
988
+ var APP_LOCKFILES = ["package-lock.json", "pnpm-lock.yaml", "yarn.lock", "bun.lockb", "bun.lock"];
989
+ function hasAppLockfile(baseDirInput) {
990
+ const baseDir = path3.resolve(baseDirInput);
991
+ return APP_LOCKFILES.some((f) => existsSync2(path3.join(baseDir, f)));
992
+ }
993
+ function rootDeclarationsAllowed(baseDir, pkg) {
994
+ return pkg !== void 0 && !isPnpProject(baseDir) && !hasAppLockfile(baseDir);
995
+ }
996
+ function readPackageJsonAt(dir) {
997
+ try {
998
+ const parsed = JSON.parse(readFileSync3(path3.join(dir, "package.json"), "utf8"));
999
+ return isPlainObject(parsed) ? parsed : void 0;
1000
+ } catch {
1001
+ return void 0;
1002
+ }
1003
+ }
1004
+ function dependencyInstallGuide(baseDirInput, name, range) {
1005
+ const baseDir = resolveBaseDir(baseDirInput);
1006
+ const installCommand = detectInstallCommand(baseDir);
1007
+ const base = { installCommand, addCommand: addCommandFor(installCommand, { [name]: range }) };
1008
+ const pkg = readPackageJsonAt(baseDir);
1009
+ if (pkg === void 0) return { kind: "add", ...base };
1010
+ const own = hasDependency(pkg, name);
1011
+ if (isPnpProject(baseDir)) return own !== void 0 ? { kind: "pnp", source: "app", range: own, ...base } : { kind: "add", ...base };
1012
+ if (own !== void 0) return { kind: "install", source: "app", range: own, ...base };
1013
+ if (!rootDeclarationsAllowed(baseDir, pkg)) return { kind: "add", ...base };
1014
+ const declared = declaredDependency(baseDir, pkg, name);
1015
+ if (declared?.source !== "root") return { kind: "add", ...base };
1016
+ return {
1017
+ kind: packageManagerOf(installCommand) === "npm" ? "install-at-root" : "install",
1018
+ source: "root",
1019
+ range: declared.range,
1020
+ ...base
1021
+ };
1022
+ }
981
1023
  function isPlainObject(value) {
982
1024
  return typeof value === "object" && value !== null && !Array.isArray(value);
983
1025
  }
@@ -1066,6 +1108,7 @@ function isPnpProject(baseDirInput) {
1066
1108
  let dir = resolveBaseDir(baseDirInput);
1067
1109
  for (; ; ) {
1068
1110
  if (isFile(path3.join(dir, ".pnp.cjs")) || isFile(path3.join(dir, ".pnp.js"))) return true;
1111
+ if (existsSync2(path3.join(dir, ".git"))) return false;
1069
1112
  const parent = path3.dirname(dir);
1070
1113
  if (parent === dir) return false;
1071
1114
  dir = parent;
@@ -1089,6 +1132,9 @@ import { basename, dirname as dirname2, extname, isAbsolute, join as join2, rela
1089
1132
  function declaredRange(baseDir, pkg, name) {
1090
1133
  return declaredDependency(baseDir, pkg, name)?.range;
1091
1134
  }
1135
+ function declaredRangeForCheck(baseDir, pkg, name) {
1136
+ return declaredDependency(baseDir, pkg, name, void 0, { appOnlyInPnp: true })?.range;
1137
+ }
1092
1138
  function isPlainObject2(value) {
1093
1139
  return typeof value === "object" && value !== null && !Array.isArray(value);
1094
1140
  }
@@ -1463,7 +1509,7 @@ function hasBareCjsPresetRequire(code) {
1463
1509
  function isCommonJsConfig(file, source) {
1464
1510
  return extname(file) === ".cjs" || /\bmodule\.exports\b/.test(source);
1465
1511
  }
1466
- function packageVersionGate(baseDirInput, pkg, name, min) {
1512
+ function packageVersionGate(baseDirInput, pkg, name, min, options = {}) {
1467
1513
  const baseDir = resolveBaseDir(baseDirInput);
1468
1514
  if (!isPnpProject(baseDir)) {
1469
1515
  const installed = findInstalledPackage(baseDir, name)?.version;
@@ -1472,7 +1518,11 @@ function packageVersionGate(baseDirInput, pkg, name, min) {
1472
1518
  if (cmp !== void 0) return { status: cmp >= 0 ? "ok" : "below", source: "installed", value: installed };
1473
1519
  }
1474
1520
  }
1475
- const declared = pkg ? declaredRange(baseDir, pkg, name) : void 0;
1521
+ const declared = pkg ? declaredDependency(baseDir, pkg, name, void 0, options)?.range : void 0;
1522
+ if (declared === void 0 && options.appOnlyInPnp) {
1523
+ const rootOnly = pnpRootOnlyDependency(baseDir, pkg, name);
1524
+ if (rootOnly !== void 0) return { status: "unresolved", source: "declared", value: rootOnly.range };
1525
+ }
1476
1526
  if (declared !== void 0) {
1477
1527
  const atLeast = rangeLowerAtLeast(declared, min);
1478
1528
  if (atLeast !== void 0) return { status: atLeast ? "ok" : "below", source: "declared", value: declared };
@@ -1481,6 +1531,7 @@ function packageVersionGate(baseDirInput, pkg, name, min) {
1481
1531
  return { status: "unknown", source: "none" };
1482
1532
  }
1483
1533
  function versionGateLabel(gate) {
1534
+ if (gate.status === "unresolved") return `\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8 \uC120\uC5B8 ${gate.value}`;
1484
1535
  if (gate.source === "installed") return `\uC124\uCE58\uBCF8 ${gate.value}`;
1485
1536
  if (gate.source === "declared") return `\uC120\uC5B8 ${gate.value}`;
1486
1537
  return "\uC124\uCE58\uBCF8 \xB7 \uC120\uC5B8 \uC5C6\uC74C";
@@ -1562,7 +1613,14 @@ function checkTailwindSetup(baseDirInput) {
1562
1613
  if (hasTokensPresetReference(content)) {
1563
1614
  const code = stripJsComments(content);
1564
1615
  if (isCommonJsConfig(file, code) && hasBareCjsPresetRequire(code)) {
1565
- const gate = packageVersionGate(baseDir, pkg, "@axis-ds/tokens", TOKENS_CJS_PRESET_FIXED_VERSION);
1616
+ const gate = packageVersionGate(baseDir, pkg, "@axis-ds/tokens", TOKENS_CJS_PRESET_FIXED_VERSION, { appOnlyInPnp: true });
1617
+ if (gate.status === "unresolved") {
1618
+ return {
1619
+ name: "tailwind.config",
1620
+ severity: "warn",
1621
+ message: `${file}\uC5D0 AXIS preset \uCC38\uC870\uAC00 \uC788\uC9C0\uB9CC @axis-ds/tokens \uAC00 ${PNP_ROOT_ONLY_NOTE}(${versionGateLabel(gate)}) \u2192 \uC571 package.json \uC5D0 @axis-ds/tokens ${TOKENS_CJS_PRESET_FIXED_VERSION} \uC774\uC0C1\uC744 \uC120\uC5B8\uD558\uC138\uC694`
1622
+ };
1623
+ }
1566
1624
  if (gate.status !== "ok") {
1567
1625
  const why = gate.status === "below" ? `@axis-ds/tokens ${versionGateLabel(gate)} \uC5D0\uC11C\uB294 CommonJS \uC758 require("${TOKENS_TAILWIND3_PRESET}") \uAC00 preset \uC774 \uC544\uB2C8\uB77C { default } \uB97C \uB3CC\uB824\uC918 \uC0C9 \xB7 keyframes \uAC00 \uC801\uC6A9\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4` : `@axis-ds/tokens \uBC84\uC804\uC744 \uD655\uC778\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4(${versionGateLabel(gate)}). ${TOKENS_CJS_PRESET_FIXED_VERSION} \uBBF8\uB9CC\uC774\uBA74 CommonJS \uC758 require("${TOKENS_TAILWIND3_PRESET}") \uB85C\uB294 preset \uC774 \uC801\uC6A9\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4`;
1568
1626
  return {
@@ -1700,13 +1758,61 @@ function contentGlobCovers(glob, sample, baseDirInput) {
1700
1758
  return branches.some((b) => contentGlobBranchCovers(b, sample, baseDir));
1701
1759
  }
1702
1760
  var CONTENT_PARENT_PATH_NOTE = "\uC0C1\uC704 \uACBD\uB85C\uB294 Tailwind 3 \uAC00 \uBE4C\uB4DC\uB97C \uC2E4\uD589\uD55C \uB514\uB809\uD130\uB9AC \uAE30\uC900\uC73C\uB85C \uD480\uB824\uC694. \uBAA8\uB178\uB808\uD3EC \uB8E8\uD2B8 \uB4F1 \uC571 \uB514\uB809\uD130\uB9AC \uBC16\uC5D0\uC11C \uBE4C\uB4DC\uD558\uBA74 content: { relative: true, files: [...] } \uB85C \uC124\uC815 \uD30C\uC77C \uAE30\uC900\uC73C\uB85C \uBC14\uAFB8\uC138\uC694";
1761
+ function relDirOf(baseDir, dir) {
1762
+ const rel2 = relative(baseDir, dir);
1763
+ return rel2 === "" || isAbsolute(rel2) ? void 0 : rel2.split(sep).join("/");
1764
+ }
1765
+ function realDir(dir) {
1766
+ try {
1767
+ return realpathSync2(dir);
1768
+ } catch {
1769
+ return void 0;
1770
+ }
1771
+ }
1772
+ function packageVersion(dir) {
1773
+ try {
1774
+ const parsed = JSON.parse(readFileSync4(join2(dir, "package.json"), "utf8"));
1775
+ return isPlainObject2(parsed) && typeof parsed.version === "string" ? parsed.version : void 0;
1776
+ } catch {
1777
+ return void 0;
1778
+ }
1779
+ }
1703
1780
  function packageContentTarget(baseDir, name) {
1704
1781
  const found = isPnpProject(baseDir) ? void 0 : findInstalledPackage(baseDir, name);
1705
- const rel2 = found === void 0 ? void 0 : relative(baseDir, found.dir);
1706
- const relDir = rel2 === void 0 || rel2 === "" || isAbsolute(rel2) ? `node_modules/${name}` : rel2.split(sep).join("/");
1782
+ const relDir = (found === void 0 ? void 0 : relDirOf(baseDir, found.dir)) ?? `node_modules/${name}`;
1783
+ const alternates = [];
1784
+ const others = [];
1785
+ const real = found === void 0 ? void 0 : realDir(found.dir);
1786
+ if (found !== void 0 && real !== void 0) {
1787
+ const relReal = relDirOf(baseDir, real);
1788
+ if (relReal !== void 0 && relReal !== relDir) alternates.push(`${relReal}/dist/index.js`);
1789
+ let dir = dirname2(resolve(found.dir, ...name.split("/").map(() => ".."), ".."));
1790
+ for (; ; ) {
1791
+ const candidate = join2(dir, "node_modules", ...name.split("/"));
1792
+ const rel2 = existsSync3(join2(candidate, "package.json")) ? relDirOf(baseDir, candidate) : void 0;
1793
+ if (rel2 !== void 0) {
1794
+ const candidateReal = realDir(candidate);
1795
+ if (candidateReal === real) alternates.push(`${rel2}/dist/index.js`);
1796
+ else {
1797
+ const theirs = packageVersion(candidate);
1798
+ const ours = found.version ?? packageVersion(found.dir);
1799
+ const versions = theirs === void 0 || ours === void 0 ? "" : theirs === ours ? `(\uBC84\uC804\uC740 ${ours} \uB85C \uAC19\uC74C)` : `(${theirs}, \uC571 ${ours})`;
1800
+ others.push({
1801
+ sample: `${rel2}/dist/index.js`,
1802
+ note: `\uC0C1\uC704 \uACBD\uB85C ${rel2} \uB294 \uC571\uC774 \uC4F0\uB294 \uC124\uCE58\uBCF8\uACFC \uB2E4\uB978 \uC124\uCE58\uBCF8${versions}\uC774\uB77C \uB36E\uC74C\uC73C\uB85C \uBCF4\uC9C0 \uC54A\uC544\uC694`
1803
+ });
1804
+ }
1805
+ }
1806
+ const parent = dirname2(dir);
1807
+ if (parent === dir) break;
1808
+ dir = parent;
1809
+ }
1810
+ }
1707
1811
  return {
1708
1812
  sample: `${relDir}/dist/index.js`,
1709
- suggest: `${relDir.startsWith("../") ? "" : "./"}${relDir}/dist/**/*.{js,mjs}`
1813
+ suggest: `${relDir.startsWith("../") ? "" : "./"}${relDir}/dist/**/*.{js,mjs}`,
1814
+ alternates,
1815
+ others
1710
1816
  };
1711
1817
  }
1712
1818
  function balancedSlice(src, start) {
@@ -1780,7 +1886,12 @@ function inspectTailwindContent(baseDirInput) {
1780
1886
  configFile,
1781
1887
  unreadable: read === void 0,
1782
1888
  dynamic: read?.dynamic ?? false,
1783
- targets: targets.map((t) => ({ ...t, covered: globs.some((g) => contentGlobCovers(g, t.sample, baseDir)) }))
1889
+ targets: targets.map(({ alternates = [], others = [], ...t }) => {
1890
+ const covers = (sample) => globs.some((g) => contentGlobCovers(g, sample, baseDir));
1891
+ const covered = [t.sample, ...alternates].some(covers);
1892
+ const note = covered ? void 0 : others.find((o) => covers(o.sample))?.note;
1893
+ return note === void 0 ? { ...t, covered } : { ...t, covered, note };
1894
+ })
1784
1895
  };
1785
1896
  }
1786
1897
  function checkTailwindContent(baseDir) {
@@ -1794,7 +1905,7 @@ function checkTailwindContent(baseDir) {
1794
1905
  message: `${r.configFile}\uC758 content \uAC00 ${r.targets.map((t) => t.label).join(" \xB7 ")} \uB97C \uB36E\uC2B5\uB2C8\uB2E4`
1795
1906
  };
1796
1907
  }
1797
- const list = missing.map((t) => `${t.label} \u2192 "${t.suggest}"`).join(" \xB7 ");
1908
+ const list = missing.map((t) => `${t.label} \u2192 "${t.suggest}"${t.note === void 0 ? "" : ` (${t.note})`}`).join(" \xB7 ");
1798
1909
  const note = missing.some((t) => t.suggest.startsWith("../")) ? ` (${CONTENT_PARENT_PATH_NOTE})` : "";
1799
1910
  if (r.unreadable || r.dynamic) {
1800
1911
  return {
@@ -1855,30 +1966,56 @@ function checkAnimationPlugin(baseDirInput) {
1855
1966
  if (!declared && !copied) return null;
1856
1967
  const by = declared ? "@axis-ds/ui-react \uC120\uC5B8" : "componentsDir \uC758 animate-in \uB958 \uD074\uB798\uC2A4";
1857
1968
  const tailwind = detectTailwind(baseDir, pkg);
1969
+ const pnp = isPnpProject(baseDir);
1970
+ const pluginProblem = (name) => {
1971
+ if (declaredRangeForCheck(baseDir, pkg, name) === void 0) {
1972
+ return pnpRootOnlyDependency(baseDir, pkg, name) !== void 0 ? `${name} \uAC00 ${PNP_ROOT_ONLY_NOTE}` : `${name} \uC120\uC5B8 \uC5C6\uC74C`;
1973
+ }
1974
+ if (!pnp && findInstalledPackage(baseDir, name) === void 0) return `${name} \uBBF8\uC124\uCE58(\uC120\uC5B8\uB9CC \uC788\uC74C)`;
1975
+ return void 0;
1976
+ };
1858
1977
  if (tailwind.major !== void 0 && tailwind.major >= 4) {
1859
1978
  const file = TAILWIND_CSS_CANDIDATES.find((c) => existsSync3(join2(baseDir, c)));
1860
1979
  const css = file !== void 0 ? stripCssComments(readFileSync4(join2(baseDir, file), "utf8")) : "";
1861
- const ok = new RegExp(`@import\\s+(?:url\\(\\s*)?["']${ANIMATION_PLUGIN_TAILWIND4}["']`).test(css) || new RegExp(`@plugin\\s+["']${ANIMATION_PLUGIN_TAILWIND3}["']`).test(css);
1862
- return ok ? { name: "animation", severity: "pass", message: `${file}\uC5D0 \uC560\uB2C8\uBA54\uC774\uC158 \uD50C\uB7EC\uADF8\uC778\uC774 \uC788\uC2B5\uB2C8\uB2E4` } : {
1980
+ const lines = [];
1981
+ if (new RegExp(`@import\\s+(?:url\\(\\s*)?["']${ANIMATION_PLUGIN_TAILWIND4}["']`).test(css)) lines.push(ANIMATION_PLUGIN_TAILWIND4);
1982
+ if (new RegExp(`@plugin\\s+["']${ANIMATION_PLUGIN_TAILWIND3}["']`).test(css)) lines.push(ANIMATION_PLUGIN_TAILWIND3);
1983
+ if (lines.length === 0) {
1984
+ return {
1985
+ name: "animation",
1986
+ severity: "warn",
1987
+ message: `\uC560\uB2C8\uBA54\uC774\uC158 \uD50C\uB7EC\uADF8\uC778\uC774 \uC5C6\uC2B5\uB2C8\uB2E4(${by}): Dialog \xB7 Select \uB4F1\uC758 \uC5F4\uACE0 \uB2EB\uB294 \uC560\uB2C8\uBA54\uC774\uC158\uC774 \uB3D9\uC791\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2192 ${ANIMATION_PLUGIN_TAILWIND4} \uC124\uCE58 \uB4A4 @import "${ANIMATION_PLUGIN_TAILWIND4}";`
1988
+ };
1989
+ }
1990
+ const problems = lines.map((name) => ({ name, why: pluginProblem(name) })).filter((x) => x.why !== void 0);
1991
+ if (problems.length === 0) return { name: "animation", severity: "pass", message: `${file}\uC5D0 \uC560\uB2C8\uBA54\uC774\uC158 \uD50C\uB7EC\uADF8\uC778\uC774 \uC788\uC2B5\uB2C8\uB2E4` };
1992
+ return {
1863
1993
  name: "animation",
1864
1994
  severity: "warn",
1865
- message: `\uC560\uB2C8\uBA54\uC774\uC158 \uD50C\uB7EC\uADF8\uC778\uC774 \uC5C6\uC2B5\uB2C8\uB2E4(${by}): Dialog \xB7 Select \uB4F1\uC758 \uC5F4\uACE0 \uB2EB\uB294 \uC560\uB2C8\uBA54\uC774\uC158\uC774 \uB3D9\uC791\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2192 ${ANIMATION_PLUGIN_TAILWIND4} \uC124\uCE58 \uB4A4 @import "${ANIMATION_PLUGIN_TAILWIND4}";`
1995
+ message: `${file}\uC758 \uC560\uB2C8\uBA54\uC774\uC158 \uD50C\uB7EC\uADF8\uC778\uC744 \uC4F8 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4(${by}, ${problems.map((x) => x.why).join(" \xB7 ")}): Dialog \xB7 Select \uB4F1\uC758 \uC5F4\uACE0 \uB2EB\uB294 \uC560\uB2C8\uBA54\uC774\uC158\uC774 \uB3D9\uC791\uD558\uC9C0 \uC54A\uACE0 \uBE4C\uB4DC\uAC00 \uC2E4\uD328\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4 \u2192 ${problems.map((x) => x.name).join(" \xB7 ")} \uB97C package.json \uC5D0 \uC120\uC5B8 \xB7 \uC124\uCE58\uD558\uC138\uC694`
1866
1996
  };
1867
1997
  }
1868
1998
  const configFile = TAILWIND_CONFIG_FILES.find((f) => existsSync3(join2(baseDir, f)));
1869
1999
  const source = configFile !== void 0 ? stripJsComments(readFileSync4(join2(baseDir, configFile), "utf8")) : "";
1870
2000
  const plugins = configFile !== void 0 ? readAnimationPluginUse(source) : "absent";
1871
- const pluginDeclared = declaredRange(baseDir, pkg, ANIMATION_PLUGIN_TAILWIND3) !== void 0;
1872
- const pluginInstalled = pluginDeclared && (isPnpProject(baseDir) || findInstalledPackage(baseDir, ANIMATION_PLUGIN_TAILWIND3) !== void 0);
1873
- if (plugins === "found" && pluginDeclared && pluginInstalled) {
2001
+ const pluginReason = pluginProblem(ANIMATION_PLUGIN_TAILWIND3);
2002
+ if (plugins === "found" && pluginReason === void 0) {
1874
2003
  return { name: "animation", severity: "pass", message: `${configFile}\uC758 plugins \uC5D0 ${ANIMATION_PLUGIN_TAILWIND3} \uAC00 \uC788\uC2B5\uB2C8\uB2E4` };
1875
2004
  }
1876
2005
  const cfg = configFile ?? "tailwind.config";
2006
+ const configReason = plugins === "dynamic" ? `${cfg} plugins \uB97C \uC815\uC801\uC73C\uB85C \uB2E4 \uC77D\uC9C0 \uBABB\uD574 \uD655\uC778\uD558\uC9C0 \uBABB\uD568` : plugins !== "found" ? `${cfg} plugins \uC5D0 \uC5C6\uC74C` : void 0;
2007
+ if (pluginReason !== void 0 && pnpRootOnlyDependency(baseDir, pkg, ANIMATION_PLUGIN_TAILWIND3) !== void 0) {
2008
+ const extra = configReason !== void 0 ? `, ${configReason}` : "";
2009
+ const plug = configReason !== void 0 ? ` \xB7 plugins \uC5D0 require("${ANIMATION_PLUGIN_TAILWIND3}")` : "";
2010
+ return {
2011
+ name: "animation",
2012
+ severity: "warn",
2013
+ message: `\uC560\uB2C8\uBA54\uC774\uC158 \uD50C\uB7EC\uADF8\uC778 ${ANIMATION_PLUGIN_TAILWIND3} \uAC00 ${PNP_ROOT_ONLY_NOTE}(${by}${extra}): Dialog \xB7 Select \uB4F1\uC758 \uC5F4\uACE0 \uB2EB\uB294 \uC560\uB2C8\uBA54\uC774\uC158\uC774 \uB3D9\uC791\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2192 \uC571 package.json \uC5D0 ${ANIMATION_PLUGIN_TAILWIND3} \uB97C \uC120\uC5B8\uD558\uC138\uC694${plug}`
2014
+ };
2015
+ }
1877
2016
  const reasons = [];
1878
- if (!pluginDeclared) reasons.push(`${ANIMATION_PLUGIN_TAILWIND3} \uC120\uC5B8 \uC5C6\uC74C`);
1879
- else if (!pluginInstalled) reasons.push(`${ANIMATION_PLUGIN_TAILWIND3} \uBBF8\uC124\uCE58(\uC120\uC5B8\uB9CC \uC788\uC74C)`);
1880
- if (plugins === "dynamic") reasons.push(`${cfg} plugins \uB97C \uC815\uC801\uC73C\uB85C \uB2E4 \uC77D\uC9C0 \uBABB\uD574 \uD655\uC778\uD558\uC9C0 \uBABB\uD568`);
1881
- else if (plugins !== "found") reasons.push(`${cfg} plugins \uC5D0 \uC5C6\uC74C`);
2017
+ if (pluginReason !== void 0) reasons.push(pluginReason);
2018
+ if (configReason !== void 0) reasons.push(configReason);
1882
2019
  return {
1883
2020
  name: "animation",
1884
2021
  severity: "warn",
@@ -4871,7 +5008,11 @@ function inspectBrandActivation(baseDir) {
4871
5008
  if (typeof brand !== "string" || brand.trim() === "") return null;
4872
5009
  const location = findBrandActivation(baseDir);
4873
5010
  if (!location.markupWithProvider) return { brand, ...location };
4874
- return { brand, ...location, theme: packageVersionGate(baseDir, readProjectPackageJson(baseDir), THEME_PACKAGE, THEME_MARKUP_BRAND_MIN_VERSION) };
5011
+ return {
5012
+ brand,
5013
+ ...location,
5014
+ theme: packageVersionGate(baseDir, readProjectPackageJson(baseDir), THEME_PACKAGE, THEME_MARKUP_BRAND_MIN_VERSION, { appOnlyInPnp: true })
5015
+ };
4875
5016
  }
4876
5017
  var THEME_PACKAGE = "@axis-ds/theme";
4877
5018
  function readProjectPackageJson(baseDir) {
@@ -4944,25 +5085,43 @@ function checkDependencies(baseDirInput) {
4944
5085
  return { name: "dependencies", severity: "fail", message: "package.json \uC744 \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4(JSON \uD655\uC778)" };
4945
5086
  }
4946
5087
  const rootPkg = workspaceRootPackage(baseDir) ?? null;
4947
- const declared = (d) => declaredDependency(baseDir, pkg, d, rootPkg);
5088
+ const pnp = isPnpProject(baseDir);
5089
+ const declared = (d) => declaredDependency(baseDir, pkg, d, rootPkg, { appOnlyInPnp: true });
5090
+ const pnpRootOnly = (d) => pnpRootOnlyDependency(baseDir, pkg, d, rootPkg) !== void 0;
4948
5091
  const fromRoot = (d) => declared(d)?.source === "root";
4949
- const missingRequired = REQUIRED_DEPENDENCIES.filter((d) => declared(d) === void 0);
4950
- if (missingRequired.length > 0) {
4951
- return { name: "dependencies", severity: "fail", message: `\uD544\uC218 \uC758\uC874\uC131 \uB204\uB77D: ${missingRequired.join(", ")}` };
4952
- }
4953
- const missingRecommended = RECOMMENDED_DEPENDENCIES.filter((d) => declared(d) === void 0);
4954
- const rootDeclared = [...REQUIRED_DEPENDENCIES, ...RECOMMENDED_DEPENDENCIES].filter((d) => fromRoot(d));
5092
+ const missingRequired = REQUIRED_DEPENDENCIES.filter((d) => declared(d) === void 0 && !pnpRootOnly(d));
5093
+ const pnpRootRequired = REQUIRED_DEPENDENCIES.filter((d) => pnpRootOnly(d));
5094
+ if (missingRequired.length > 0 || pnpRootRequired.length > 0) {
5095
+ const fails = [];
5096
+ if (missingRequired.length > 0) fails.push(`\uD544\uC218 \uC758\uC874\uC131 \uB204\uB77D: ${missingRequired.join(", ")}`);
5097
+ if (pnpRootRequired.length > 0) fails.push(`\uD544\uC218 \uC758\uC874\uC131 ${pnpRootRequired.join(", ")} \uAC00 ${PNP_ROOT_ONLY_NOTE} \u2192 \uC571 package.json \uC5D0 \uC120\uC5B8\uD558\uC138\uC694`);
5098
+ return { name: "dependencies", severity: "fail", message: fails.join(" / ") };
5099
+ }
5100
+ const missingRecommended = RECOMMENDED_DEPENDENCIES.filter((d) => declared(d) === void 0 && !pnpRootOnly(d));
5101
+ const pnpRootRecommended = RECOMMENDED_DEPENDENCIES.filter((d) => pnpRootOnly(d));
5102
+ const appAxis = declaredAxisPackages(pkg);
5103
+ const rootOnlyAxis = rootPkg !== null ? declaredAxisPackages(rootPkg).filter((d) => !appAxis.includes(d)) : [];
5104
+ const rootAxis = pnp ? [] : rootOnlyAxis;
5105
+ const pnpRootAxis = pnp ? rootOnlyAxis : [];
5106
+ const rootDeclared = [...[...REQUIRED_DEPENDENCIES, ...RECOMMENDED_DEPENDENCIES].filter((d) => fromRoot(d)), ...rootAxis];
4955
5107
  const rootNote = rootDeclared.length > 0 ? `\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8\uC5D0 \uC120\uC5B8: ${rootDeclared.join(", ")}` : void 0;
4956
5108
  const targets = [
4957
5109
  ...[...REQUIRED_DEPENDENCIES, ...RECOMMENDED_DEPENDENCIES].filter((d) => declared(d) !== void 0),
4958
- ...declaredAxisPackages(pkg)
5110
+ ...appAxis,
5111
+ ...rootAxis
4959
5112
  ];
4960
- const pnp = isPnpProject(baseDir);
4961
5113
  const notInstalled = pnp ? [] : targets.filter((d) => findInstalledPackage(baseDir, d) === void 0);
5114
+ const notInstalledRoot = notInstalled.filter((d) => rootAxis.includes(d));
5115
+ const notInstalledOther = notInstalled.filter((d) => !rootAxis.includes(d));
4962
5116
  const problems = [];
4963
5117
  if (missingRecommended.length > 0) problems.push(`\uAD8C\uC7A5 \uC758\uC874\uC131 \uB204\uB77D: ${missingRecommended.join(", ")}`);
4964
- if (notInstalled.length > 0) {
4965
- problems.push(`\uC120\uC5B8\uB410\uC9C0\uB9CC \uC124\uCE58\uB418\uC9C0 \uC54A\uC740 \uC758\uC874\uC131: ${notInstalled.join(", ")} \u2192 ${detectInstallCommand(baseDir)}`);
5118
+ if (pnpRootRecommended.length > 0) problems.push(`\uAD8C\uC7A5 \uC758\uC874\uC131 ${pnpRootRecommended.join(", ")} \uAC00 ${PNP_ROOT_ONLY_NOTE} \u2192 \uC571 package.json \uC5D0 \uC120\uC5B8\uD558\uC138\uC694`);
5119
+ if (pnpRootAxis.length > 0) problems.push(`${pnpRootAxis.join(", ")} \uAC00 ${PNP_ROOT_ONLY_NOTE} \u2192 \uC571 package.json \uC5D0 \uC120\uC5B8\uD558\uC138\uC694`);
5120
+ if (notInstalledOther.length > 0) {
5121
+ problems.push(`\uC120\uC5B8\uB410\uC9C0\uB9CC \uC124\uCE58\uB418\uC9C0 \uC54A\uC740 \uC758\uC874\uC131: ${notInstalledOther.join(", ")} \u2192 ${detectInstallCommand(baseDir)}`);
5122
+ }
5123
+ if (notInstalledRoot.length > 0) {
5124
+ problems.push(`\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8\uC5D0 \uC120\uC5B8\uB3FC \uC788\uC9C0\uB9CC \uC124\uCE58\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4: ${notInstalledRoot.join(", ")} \u2192 ${detectInstallCommand(baseDir)}`);
4966
5125
  }
4967
5126
  if (problems.length > 0) {
4968
5127
  return { name: "dependencies", severity: "warn", message: problems.join(" / ") + (rootNote ? ` (${rootNote})` : "") };
@@ -5132,6 +5291,13 @@ function brandCheckResult(check) {
5132
5291
  }
5133
5292
  }
5134
5293
  function brandActivationResult(check) {
5294
+ if (check.found && check.theme?.status === "unresolved") {
5295
+ return {
5296
+ name: "brand.activation",
5297
+ severity: "warn",
5298
+ message: `${check.found}\uC758 <html data-brand> \uB97C ThemeProvider \uC640 \uD568\uAED8 \uC4F0\uB294\uB370 @axis-ds/theme \uAC00 ${PNP_ROOT_ONLY_NOTE}(${versionGateLabel(check.theme)}) \u2192 \uC571 package.json \uC5D0 @axis-ds/theme ${THEME_MARKUP_BRAND_MIN_VERSION} \uC774\uC0C1\uC744 \uC120\uC5B8\uD558\uAC70\uB098 setBrand("${check.brand}")`
5299
+ };
5300
+ }
5135
5301
  if (check.found && check.theme && check.theme.status !== "ok") {
5136
5302
  const label = versionGateLabel(check.theme);
5137
5303
  const why = check.theme.status === "below" ? `@axis-ds/theme ${label} \uC758 ThemeProvider \uAC00 \uB9C8\uC6B4\uD2B8 \uB54C \uC9C0\uC6C1\uB2C8\uB2E4` : `@axis-ds/theme \uBC84\uC804\uC744 \uD655\uC778\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4(${label}). ${THEME_MARKUP_BRAND_MIN_VERSION} \uBBF8\uB9CC\uC774\uBA74 ThemeProvider \uAC00 \uB9C8\uC6B4\uD2B8 \uB54C \uC9C0\uC6C1\uB2C8\uB2E4`;
@@ -5325,10 +5491,8 @@ var TOKENS_INSTALL_COMMAND = addCommandFor("pnpm install", { [TOKENS_PACKAGE]: `
5325
5491
  function baseDirOf(targetDir) {
5326
5492
  return path10.resolve(targetDir || process.cwd());
5327
5493
  }
5328
- function tokensInstallCommandFor(targetDir) {
5329
- return addCommandFor(detectInstallCommand(baseDirOf(targetDir)), {
5330
- [TOKENS_PACKAGE]: `^${TOKENS_DEPENDENCY_MIN_VERSION}`
5331
- });
5494
+ function tokensInstallGuideFor(targetDir) {
5495
+ return dependencyInstallGuide(baseDirOf(targetDir), TOKENS_PACKAGE, `^${TOKENS_DEPENDENCY_MIN_VERSION}`);
5332
5496
  }
5333
5497
  async function handleListBrands(params) {
5334
5498
  return listBrands(baseDirOf(params.targetDir));
@@ -5341,24 +5505,25 @@ async function handleGetBrand(params) {
5341
5505
  return getBrand(baseDirOf(params.targetDir), name);
5342
5506
  }
5343
5507
  function formatTokensStatusGuide(status, version, installCommand = TOKENS_INSTALL_COMMAND) {
5508
+ const g = typeof installCommand === "string" ? { kind: "add", installCommand, addCommand: installCommand } : installCommand;
5509
+ if (g.kind === "pnp") {
5510
+ return `${TOKENS_PACKAGE}${g.range ? ` ${g.range}` : ""} \uAC00 package.json \uC5D0 \uC120\uC5B8\uB3FC \uC788\uC9C0\uB9CC Plug'n'Play \uC124\uCE58\uB77C \uC124\uCE58\uBCF8\uC744 \uD310\uC815\uD558\uC9C0 \uBABB\uD574 \uBE0C\uB79C\uB4DC\uB97C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`;
5511
+ }
5512
+ const install = g.kind === "install-at-root" ? `\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8\uC5D0\uC11C \`${g.installCommand}\`` : `\`${g.kind === "install" ? g.installCommand : g.addCommand}\``;
5344
5513
  switch (status) {
5345
5514
  case "not-installed":
5346
- return [
5347
- `${TOKENS_PACKAGE} \uAC00 \uC124\uCE58\uB3FC \uC788\uC9C0 \uC54A\uC544 \uBE0C\uB79C\uB4DC\uB97C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`,
5348
- "",
5349
- `- \uC124\uCE58: \`${installCommand}\``
5350
- ].join("\n");
5515
+ return [`${TOKENS_PACKAGE} \uAC00 \uC124\uCE58\uB3FC \uC788\uC9C0 \uC54A\uC544 \uBE0C\uB79C\uB4DC\uB97C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`, "", `- \uC124\uCE58: ${install}`].join("\n");
5351
5516
  case "unsupported":
5352
5517
  return [
5353
5518
  `${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).`,
5354
5519
  "",
5355
- `- \uC5C5\uADF8\uB808\uC774\uB4DC: \`${installCommand}\``
5520
+ g.source === "root" ? `- \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8 package.json \uC758 ${TOKENS_PACKAGE} \uC120\uC5B8\uC744 ^${MIN_BRAND_TOKENS_VERSION} \uC774\uC0C1\uC73C\uB85C \uC62C\uB9AC\uACE0 \uC124\uCE58\uD558\uC138\uC694.` : `- \uC5C5\uADF8\uB808\uC774\uB4DC: \`${g.addCommand}\``
5356
5521
  ].join("\n");
5357
5522
  case "not-built":
5358
5523
  return [
5359
5524
  `${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.`,
5360
5525
  "",
5361
- `- \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4\uBA74 \`${workspaceRunCommand(installCommand, TOKENS_PACKAGE, "build")}\`, \uC544\uB2C8\uBA74 \uC7AC\uC124\uCE58: \`${installCommand}\``
5526
+ `- \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4\uBA74 \`${workspaceRunCommand(g.installCommand, TOKENS_PACKAGE, "build")}\`, \uC544\uB2C8\uBA74 \uC7AC\uC124\uCE58: ${install}`
5362
5527
  ].join("\n");
5363
5528
  }
5364
5529
  }
@@ -6016,7 +6181,7 @@ server.tool(
6016
6181
  async ({ targetDir }) => {
6017
6182
  try {
6018
6183
  const result = await handleListBrands({ targetDir });
6019
- const formatted = formatListBrandsResult(result, tokensInstallCommandFor(targetDir));
6184
+ const formatted = formatListBrandsResult(result, tokensInstallGuideFor(targetDir));
6020
6185
  return {
6021
6186
  content: [
6022
6187
  {
@@ -6049,7 +6214,7 @@ server.tool(
6049
6214
  async ({ name, targetDir }) => {
6050
6215
  try {
6051
6216
  const result = await handleGetBrand({ name, targetDir });
6052
- const formatted = formatGetBrandResult(result, name, tokensInstallCommandFor(targetDir));
6217
+ const formatted = formatGetBrandResult(result, name, tokensInstallGuideFor(targetDir));
6053
6218
  return {
6054
6219
  content: [
6055
6220
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axis-ds/mcp",
3
- "version": "1.4.3",
3
+ "version": "1.4.5",
4
4
  "type": "module",
5
5
  "description": "AXIS Design System - MCP Server for AI-assisted component discovery",
6
6
  "bin": {