@axis-ds/mcp 1.4.4 → 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 +151 -39
  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 {
@@ -1908,30 +1966,56 @@ function checkAnimationPlugin(baseDirInput) {
1908
1966
  if (!declared && !copied) return null;
1909
1967
  const by = declared ? "@axis-ds/ui-react \uC120\uC5B8" : "componentsDir \uC758 animate-in \uB958 \uD074\uB798\uC2A4";
1910
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
+ };
1911
1977
  if (tailwind.major !== void 0 && tailwind.major >= 4) {
1912
1978
  const file = TAILWIND_CSS_CANDIDATES.find((c) => existsSync3(join2(baseDir, c)));
1913
1979
  const css = file !== void 0 ? stripCssComments(readFileSync4(join2(baseDir, file), "utf8")) : "";
1914
- const ok = new RegExp(`@import\\s+(?:url\\(\\s*)?["']${ANIMATION_PLUGIN_TAILWIND4}["']`).test(css) || new RegExp(`@plugin\\s+["']${ANIMATION_PLUGIN_TAILWIND3}["']`).test(css);
1915
- 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 {
1916
1993
  name: "animation",
1917
1994
  severity: "warn",
1918
- 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`
1919
1996
  };
1920
1997
  }
1921
1998
  const configFile = TAILWIND_CONFIG_FILES.find((f) => existsSync3(join2(baseDir, f)));
1922
1999
  const source = configFile !== void 0 ? stripJsComments(readFileSync4(join2(baseDir, configFile), "utf8")) : "";
1923
2000
  const plugins = configFile !== void 0 ? readAnimationPluginUse(source) : "absent";
1924
- const pluginDeclared = declaredRange(baseDir, pkg, ANIMATION_PLUGIN_TAILWIND3) !== void 0;
1925
- const pluginInstalled = pluginDeclared && (isPnpProject(baseDir) || findInstalledPackage(baseDir, ANIMATION_PLUGIN_TAILWIND3) !== void 0);
1926
- if (plugins === "found" && pluginDeclared && pluginInstalled) {
2001
+ const pluginReason = pluginProblem(ANIMATION_PLUGIN_TAILWIND3);
2002
+ if (plugins === "found" && pluginReason === void 0) {
1927
2003
  return { name: "animation", severity: "pass", message: `${configFile}\uC758 plugins \uC5D0 ${ANIMATION_PLUGIN_TAILWIND3} \uAC00 \uC788\uC2B5\uB2C8\uB2E4` };
1928
2004
  }
1929
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
+ }
1930
2016
  const reasons = [];
1931
- if (!pluginDeclared) reasons.push(`${ANIMATION_PLUGIN_TAILWIND3} \uC120\uC5B8 \uC5C6\uC74C`);
1932
- else if (!pluginInstalled) reasons.push(`${ANIMATION_PLUGIN_TAILWIND3} \uBBF8\uC124\uCE58(\uC120\uC5B8\uB9CC \uC788\uC74C)`);
1933
- if (plugins === "dynamic") reasons.push(`${cfg} plugins \uB97C \uC815\uC801\uC73C\uB85C \uB2E4 \uC77D\uC9C0 \uBABB\uD574 \uD655\uC778\uD558\uC9C0 \uBABB\uD568`);
1934
- 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);
1935
2019
  return {
1936
2020
  name: "animation",
1937
2021
  severity: "warn",
@@ -4924,7 +5008,11 @@ function inspectBrandActivation(baseDir) {
4924
5008
  if (typeof brand !== "string" || brand.trim() === "") return null;
4925
5009
  const location = findBrandActivation(baseDir);
4926
5010
  if (!location.markupWithProvider) return { brand, ...location };
4927
- 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
+ };
4928
5016
  }
4929
5017
  var THEME_PACKAGE = "@axis-ds/theme";
4930
5018
  function readProjectPackageJson(baseDir) {
@@ -4997,25 +5085,43 @@ function checkDependencies(baseDirInput) {
4997
5085
  return { name: "dependencies", severity: "fail", message: "package.json \uC744 \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4(JSON \uD655\uC778)" };
4998
5086
  }
4999
5087
  const rootPkg = workspaceRootPackage(baseDir) ?? null;
5000
- 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;
5001
5091
  const fromRoot = (d) => declared(d)?.source === "root";
5002
- const missingRequired = REQUIRED_DEPENDENCIES.filter((d) => declared(d) === void 0);
5003
- if (missingRequired.length > 0) {
5004
- return { name: "dependencies", severity: "fail", message: `\uD544\uC218 \uC758\uC874\uC131 \uB204\uB77D: ${missingRequired.join(", ")}` };
5005
- }
5006
- const missingRecommended = RECOMMENDED_DEPENDENCIES.filter((d) => declared(d) === void 0);
5007
- 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];
5008
5107
  const rootNote = rootDeclared.length > 0 ? `\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8\uC5D0 \uC120\uC5B8: ${rootDeclared.join(", ")}` : void 0;
5009
5108
  const targets = [
5010
5109
  ...[...REQUIRED_DEPENDENCIES, ...RECOMMENDED_DEPENDENCIES].filter((d) => declared(d) !== void 0),
5011
- ...declaredAxisPackages(pkg)
5110
+ ...appAxis,
5111
+ ...rootAxis
5012
5112
  ];
5013
- const pnp = isPnpProject(baseDir);
5014
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));
5015
5116
  const problems = [];
5016
5117
  if (missingRecommended.length > 0) problems.push(`\uAD8C\uC7A5 \uC758\uC874\uC131 \uB204\uB77D: ${missingRecommended.join(", ")}`);
5017
- if (notInstalled.length > 0) {
5018
- 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)}`);
5019
5125
  }
5020
5126
  if (problems.length > 0) {
5021
5127
  return { name: "dependencies", severity: "warn", message: problems.join(" / ") + (rootNote ? ` (${rootNote})` : "") };
@@ -5185,6 +5291,13 @@ function brandCheckResult(check) {
5185
5291
  }
5186
5292
  }
5187
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
+ }
5188
5301
  if (check.found && check.theme && check.theme.status !== "ok") {
5189
5302
  const label = versionGateLabel(check.theme);
5190
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`;
@@ -5378,10 +5491,8 @@ var TOKENS_INSTALL_COMMAND = addCommandFor("pnpm install", { [TOKENS_PACKAGE]: `
5378
5491
  function baseDirOf(targetDir) {
5379
5492
  return path10.resolve(targetDir || process.cwd());
5380
5493
  }
5381
- function tokensInstallCommandFor(targetDir) {
5382
- return addCommandFor(detectInstallCommand(baseDirOf(targetDir)), {
5383
- [TOKENS_PACKAGE]: `^${TOKENS_DEPENDENCY_MIN_VERSION}`
5384
- });
5494
+ function tokensInstallGuideFor(targetDir) {
5495
+ return dependencyInstallGuide(baseDirOf(targetDir), TOKENS_PACKAGE, `^${TOKENS_DEPENDENCY_MIN_VERSION}`);
5385
5496
  }
5386
5497
  async function handleListBrands(params) {
5387
5498
  return listBrands(baseDirOf(params.targetDir));
@@ -5394,24 +5505,25 @@ async function handleGetBrand(params) {
5394
5505
  return getBrand(baseDirOf(params.targetDir), name);
5395
5506
  }
5396
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}\``;
5397
5513
  switch (status) {
5398
5514
  case "not-installed":
5399
- return [
5400
- `${TOKENS_PACKAGE} \uAC00 \uC124\uCE58\uB3FC \uC788\uC9C0 \uC54A\uC544 \uBE0C\uB79C\uB4DC\uB97C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`,
5401
- "",
5402
- `- \uC124\uCE58: \`${installCommand}\``
5403
- ].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");
5404
5516
  case "unsupported":
5405
5517
  return [
5406
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).`,
5407
5519
  "",
5408
- `- \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}\``
5409
5521
  ].join("\n");
5410
5522
  case "not-built":
5411
5523
  return [
5412
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.`,
5413
5525
  "",
5414
- `- \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}`
5415
5527
  ].join("\n");
5416
5528
  }
5417
5529
  }
@@ -6069,7 +6181,7 @@ server.tool(
6069
6181
  async ({ targetDir }) => {
6070
6182
  try {
6071
6183
  const result = await handleListBrands({ targetDir });
6072
- const formatted = formatListBrandsResult(result, tokensInstallCommandFor(targetDir));
6184
+ const formatted = formatListBrandsResult(result, tokensInstallGuideFor(targetDir));
6073
6185
  return {
6074
6186
  content: [
6075
6187
  {
@@ -6102,7 +6214,7 @@ server.tool(
6102
6214
  async ({ name, targetDir }) => {
6103
6215
  try {
6104
6216
  const result = await handleGetBrand({ name, targetDir });
6105
- const formatted = formatGetBrandResult(result, name, tokensInstallCommandFor(targetDir));
6217
+ const formatted = formatGetBrandResult(result, name, tokensInstallGuideFor(targetDir));
6106
6218
  return {
6107
6219
  content: [
6108
6220
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axis-ds/mcp",
3
- "version": "1.4.4",
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": {