@axis-ds/cli 1.7.0 → 1.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -84,7 +84,7 @@ axis theme add ontology
84
84
  ```
85
85
 
86
86
  - `axis theme add` 는 `axis.config.json` 의 `globalCss` → `app/globals.css` → `src/app/globals.css` → `styles/globals.css` → `src/styles/globals.css` → 루트 `globals.css` 순으로 찾은 파일의 앞쪽 import 구간 끝(`@tailwind` 등 규칙보다 앞)에 줄을 넣습니다. 1.6.0 부터 브랜드 import 앞에 기본 토큰 CSS 도 넣습니다: Tailwind 3 은 `@axis-ds/tokens/css` · `@axis-ds/tokens/css/shadcn`, Tailwind 4 는 `@axis-ds/tokens/css/tailwind4` 와 `package.json` 에 선언된 `@axis-ds/ui-react` · `@axis-ds/agentic-ui` 의 `tailwind.css` 입니다. 이미 있는 줄은 다시 넣지 않으므로 여러 번 실행해도 같습니다. 규칙 뒤에 있는 기존 `@import` 는 CSS 규칙상 무시될 수 있어 맨 앞으로 옮기라고 알립니다(Next.js 15 는 위치와 관계없이 앞으로 끌어올려 적용함을 측정).
87
- - `package.json` 에 `@axis-ds/tokens` 가 없으면 `dependencies` 에 `^1.6.0`(1.6.0 부터, 이전에는 `^1.3.0`)을 더하고 설치 명령만 안내합니다. 설치는 실행하지 않습니다. 설치 · 업그레이드 안내 명령은 lockfile 로 고릅니다(`npm install` · `yarn add` · `pnpm add`).
87
+ - `package.json` 에 `@axis-ds/tokens` 가 없으면 `dependencies` 에 `^1.6.0`(1.6.0 부터, 이전에는 `^1.3.0`)을 더하고 설치 명령만 안내합니다. 설치는 실행하지 않습니다. 설치 · 업그레이드 안내 명령은 lockfile 로 고릅니다(`npm install` · `yarn add` · `pnpm add`). lockfile 이 없으면 `package.json` 의 `packageManager`(`npm@…` · `yarn@…` · `pnpm@…`)를 보고, 둘 다 없으면 `pnpm` 입니다(1.7.2 부터 `packageManager` 를 봄).
88
88
  - 브랜드 CSS 는 `[data-brand="<brand>"]` 선택자로 적용되므로 루트 레이아웃의 `<html data-brand>` 나 `ThemeProvider` 의 `setBrand` 가 있어야 화면이 바뀝니다. 코드는 고치지 않고, 레이아웃에서 찾지 못하면 두 방법의 예시를 냅니다(`defaultBrand` 는 `@axis-ds/theme` 1.6.2 이하에서 `setBrand(undefined)` 로 끈 상태가 새로고침 뒤 유지되지 않아 예시에 쓰지 않습니다). 마크업 `data-brand` 와 `ThemeProvider` 를 함께 쓰는 레이아웃이면 theme 1.7.0 이상이 필요하다고 한 줄 더 알립니다(1.6.1 부터). Tailwind 3 이면 preset · content 가 빠졌을 때 넣을 줄도 안내합니다(설정 파일은 고치지 않음). preset 안내는 1.6.1 부터 CommonJS 에서 모든 tokens 버전에 되는 `require("@axis-ds/tokens/tailwind").default` 입니다(1.6.0 의 `.default` 없는 형태는 tokens 1.6.2 이하에서 preset 이 빠짐).
89
89
  - 넣은 import 줄의 출력은 1.6.1 부터 실제 위치 · 분류를 따릅니다. 컴포넌트 `tailwind.css` 는 「컴포넌트 스캔 경로, 위치 무관」, 기본 토큰 CSS 는 브랜드 줄보다 앞에 들어갔을 때만 「브랜드 앞」이고 뒤에 들어가면 옮기라고 경고합니다.
90
90
  - `@axis-ds/tokens` 가 없거나 1.3.0 미만이면 목록 대신 설치 · 업그레이드 안내를 내고 종료 코드 1 로 끝납니다.
@@ -107,7 +107,7 @@ axis theme add ontology
107
107
  - dependencies 항목(1.6.1 부터): `tailwindcss` 선언이 없으면 FAIL, `clsx` · `tailwind-merge` 선언이 없으면 WARN(종전과 같음). 여기에 선언된 `tailwindcss` · `clsx` · `tailwind-merge` · `@axis-ds/*` 가 `node_modules` 에 없으면 「선언됐지만 설치되지 않은 의존성」 WARN 과 lockfile 에 맞는 설치 명령을 냅니다. 상위 디렉터리의 `node_modules`(모노레포 호이스팅)도 찾고, Plug'n'Play(`.pnp.cjs`)는 설치본을 읽지 않고 선언 기준입니다. 1.6.0 까지는 선언만 보고 「모두 설치되어 있습니다」였습니다.
108
108
  - 해당할 때만 더하는 항목(1.6.0 부터, brand 항목 뒤 이 순서):
109
109
  - `brand.activation`: `brand` 가 있을 때 루트 레이아웃(`app/layout.tsx` · `src/app/layout.tsx` · `pages/_app` 등, 그 레이아웃이 import 하는 같은 디렉터리의 `./providers` 한 단계 포함)에 `data-brand` · `setBrand(` · `defaultBrand=` 가 없으면 WARN 입니다(휴리스틱, `ThemeProvider` 단독은 켜는 코드가 아님). 1.6.1 부터 마크업 `data-brand` 와 `ThemeProvider` 만 있고 `setBrand(` · `defaultBrand=` 가 없으면 설치본(없으면 선언) `@axis-ds/theme` 가 1.7.0 미만이거나 판정할 수 없을 때 WARN 입니다(1.6.2 이하는 마운트 때 속성을 지움).
110
- - `tailwind.content`: Tailwind 3 설정 파일이 있고 `componentsDir` 나 선언된 `@axis-ds/ui-react` · `@axis-ds/agentic-ui` 가 있을 때, `content` 의 문자열 glob 이 그 경로를 덮지 않으면 FAIL 입니다(그 경로의 클래스가 생성되지 않음). `content` 를 정적으로 다 읽지 못하면(변수 · 펼침) WARN 입니다.
110
+ - `tailwind.content`: Tailwind 3 설정 파일이 있고 `componentsDir` 나 선언된 `@axis-ds/ui-react` · `@axis-ds/agentic-ui` 가 있을 때, `content` 의 문자열 glob 이 그 경로를 덮지 않으면 FAIL 입니다(그 경로의 클래스가 생성되지 않음). `content` 를 정적으로 다 읽지 못하면(변수 · 펼침) WARN 입니다. 패키지 경로는 프로젝트 디렉터리부터 상위로 찾은 설치본 위치 기준이라, npm workspaces · pnpm hoisted 처럼 루트에만 설치된 앱은 `../../node_modules/@axis-ds/ui-react/dist/**/*.{js,mjs}` 를 안내합니다(1.7.1 부터, 설치본이 없거나 Plug'n'Play 면 `./node_modules/...`). `**` 는 `../` 를 넘지 않는 것으로, 절대경로 glob 은 실제 위치와 비교해 판정합니다. 상위 경로는 Tailwind 3 가 빌드를 실행한 디렉터리 기준으로 풀리므로 모노레포 루트에서 빌드하면 `content: { relative: true, files: [...] }` 를 쓰세요(판정은 프로젝트 디렉터리에서 빌드한다고 보고 함).
111
111
  - `animation`: `@axis-ds/ui-react` 선언이나 `componentsDir` 의 `animate-in` 류 클래스가 있을 때 애니메이션 플러그인(Tailwind 3 `tailwindcss-animate` 선언 + 설치본 + `plugins` 배열 참조, Tailwind 4 `@import "tw-animate-css"`)이 없으면 WARN 입니다. 1.6.1 부터 Tailwind 3 은 `plugins` 배열 안의 `require("tailwindcss-animate")` 나 그 패키지를 받은 식별자(`import animate from` · `const animate = require(...)`)만 인정합니다(import 만 있거나 다른 곳의 같은 문자열은 아님). 배열을 정적으로 다 읽지 못하면(펼침 · 단축 속성) 「확인하지 못함」 WARN 입니다.
112
112
  - FAIL 이 하나라도 있으면 종료 코드 1 로 끝납니다. WARN 만 있으면 0 입니다(1.3.0 부터, 이전에는 결과와 관계없이 0).
113
113
  - MCP `axis.check_project` 도 같은 항목 · 문구를 냅니다.
@@ -128,6 +128,7 @@ stdin 이 터미널이 아니면 질문을 띄우지 않습니다(1.7.0 부터).
128
128
 
129
129
  - `axis template init -y` 는 질문을 기본값으로 답하고, 비어 있지 않은 디렉터리도 진행합니다(`template apply -y` 와 같은 뜻). 터미널에서도 같습니다.
130
130
  - 판정은 stdin 이 터미널인지 하나로 합니다. 그래서 `printf '\n' | axis template init dashboard` 처럼 파이프로 응답을 넣던 스크립트도 이제는 묻지 않고 위 표대로 동작합니다. 값은 `--dir` · `-y` 같은 옵션으로 주세요.
131
+ - `axis template apply` 의 「필요한 의존성」 명령과 `axis template init` 의 다음 단계(`install` · `dev`)는 대상 디렉터리의 lockfile(없으면 `packageManager`)로 매니저를 고르고 범위를 따옴표로 감쌉니다(1.7.2 부터, 1.7.1 까지는 `pnpm` 고정).
131
132
  - Windows Git Bash(mintty)처럼 사람이 앞에 있어도 stdin 이 터미널로 잡히지 않는 환경에서는 질문 없이 위 표대로 동작할 수 있습니다(실측 전). 질문이 뜨지 않으면 `winpty npx axis ...` 로 실행하거나, PowerShell · Windows Terminal 에서 실행하거나, `--dir` · `-y` 같은 옵션으로 값을 주세요.
132
133
 
133
134
  ## 문서
@@ -0,0 +1,473 @@
1
+ import {
2
+ ask,
3
+ failNonInteractive,
4
+ isInteractive
5
+ } from "./chunk-HFMMK6R4.js";
6
+
7
+ // src/monet.ts
8
+ import chalk from "chalk";
9
+ import fs from "fs-extra";
10
+ import path2 from "path";
11
+
12
+ // ../axis-registry-core/src/project.ts
13
+ import { existsSync, readFileSync, realpathSync, statSync } from "fs";
14
+ import path from "path";
15
+ function resolveBaseDir(baseDirInput) {
16
+ const resolved = path.resolve(baseDirInput);
17
+ try {
18
+ return realpathSync(resolved);
19
+ } catch {
20
+ return resolved;
21
+ }
22
+ }
23
+ var LOCKFILE_COMMANDS = [
24
+ ["pnpm-lock.yaml", "pnpm install"],
25
+ ["yarn.lock", "yarn install"],
26
+ ["package-lock.json", "npm install"]
27
+ ];
28
+ function lockfileCommandAt(dir) {
29
+ return LOCKFILE_COMMANDS.find(([file]) => existsSync(path.join(dir, file)))?.[1];
30
+ }
31
+ function isWorkspaceRootDir(dir) {
32
+ if (isFile(path.join(dir, "pnpm-workspace.yaml"))) return true;
33
+ let parsed;
34
+ try {
35
+ parsed = JSON.parse(readFileSync(path.join(dir, "package.json"), "utf8"));
36
+ } catch {
37
+ return false;
38
+ }
39
+ if (!isPlainObject(parsed)) return false;
40
+ const ws = parsed.workspaces;
41
+ return Array.isArray(ws) || isPlainObject(ws) && Array.isArray(ws.packages);
42
+ }
43
+ function findWorkspaceRoot(baseDirInput) {
44
+ let dir = resolveBaseDir(baseDirInput);
45
+ for (; ; ) {
46
+ if (isWorkspaceRootDir(dir)) return dir;
47
+ if (existsSync(path.join(dir, ".git"))) return void 0;
48
+ const parent = path.dirname(dir);
49
+ if (parent === dir) return void 0;
50
+ dir = parent;
51
+ }
52
+ }
53
+ var PACKAGE_MANAGER_COMMANDS = {
54
+ pnpm: "pnpm install",
55
+ yarn: "yarn install",
56
+ npm: "npm install"
57
+ };
58
+ function packageManagerCommandAt(dir) {
59
+ let parsed;
60
+ try {
61
+ parsed = JSON.parse(readFileSync(path.join(dir, "package.json"), "utf8"));
62
+ } catch {
63
+ return void 0;
64
+ }
65
+ if (!isPlainObject(parsed) || typeof parsed.packageManager !== "string") return void 0;
66
+ const name = /^(pnpm|yarn|npm)@/.exec(parsed.packageManager.trim())?.[1];
67
+ return name !== void 0 ? PACKAGE_MANAGER_COMMANDS[name] : void 0;
68
+ }
69
+ function detectInstallCommand(baseDirInput) {
70
+ const baseDir = resolveBaseDir(baseDirInput);
71
+ const own = lockfileCommandAt(baseDir);
72
+ if (own !== void 0) return own;
73
+ const found = findWorkspaceRoot(baseDir);
74
+ const root = found !== void 0 && found !== baseDir ? found : void 0;
75
+ if (root !== void 0) {
76
+ const fromRoot = lockfileCommandAt(root);
77
+ if (fromRoot !== void 0) return fromRoot;
78
+ }
79
+ const ownManager = packageManagerCommandAt(baseDir);
80
+ if (ownManager !== void 0) return ownManager;
81
+ if (root !== void 0) {
82
+ const rootManager = packageManagerCommandAt(root);
83
+ if (rootManager !== void 0) return rootManager;
84
+ }
85
+ return "pnpm install";
86
+ }
87
+ function packageManagerOf(command) {
88
+ if (command.startsWith("yarn")) return "yarn";
89
+ if (command.startsWith("npm")) return "npm";
90
+ return "pnpm";
91
+ }
92
+ function hasDependency(pkg, name) {
93
+ for (const field of ["dependencies", "devDependencies"]) {
94
+ const deps = pkg[field];
95
+ if (typeof deps === "object" && deps !== null && name in deps) {
96
+ return String(deps[name]);
97
+ }
98
+ }
99
+ return void 0;
100
+ }
101
+ function workspaceRootPackage(baseDirInput) {
102
+ const baseDir = resolveBaseDir(baseDirInput);
103
+ const root = findWorkspaceRoot(baseDir);
104
+ if (root === void 0 || root === baseDir) return void 0;
105
+ try {
106
+ const parsed = JSON.parse(readFileSync(path.join(root, "package.json"), "utf8"));
107
+ return isPlainObject(parsed) ? parsed : void 0;
108
+ } catch {
109
+ return void 0;
110
+ }
111
+ }
112
+ function declaredDependency(baseDir, pkg, name, rootPkg) {
113
+ if (pkg === void 0) return void 0;
114
+ const own = hasDependency(pkg, name);
115
+ if (own !== void 0) return { range: own, source: "app" };
116
+ const root = rootPkg === void 0 ? workspaceRootPackage(baseDir) : rootPkg ?? void 0;
117
+ const fromRoot = root !== void 0 ? hasDependency(root, name) : void 0;
118
+ return fromRoot !== void 0 ? { range: fromRoot, source: "root" } : void 0;
119
+ }
120
+ function isPlainObject(value) {
121
+ return typeof value === "object" && value !== null && !Array.isArray(value);
122
+ }
123
+ function addCommandPrefix(installCommand, options = {}) {
124
+ const dev = options.dev ? " -D" : "";
125
+ switch (packageManagerOf(installCommand)) {
126
+ case "yarn":
127
+ return `yarn add${dev}`;
128
+ case "npm":
129
+ return `npm install${dev}`;
130
+ default:
131
+ return `pnpm add${dev}`;
132
+ }
133
+ }
134
+ function addCommandFor(installCommand, deps, options = {}) {
135
+ const specs = Object.entries(deps).map(([name, range]) => `"${name}@${range}"`).join(" ");
136
+ return `${addCommandPrefix(installCommand, options)} ${specs}`;
137
+ }
138
+ function runScriptCommand(installCommand, script) {
139
+ switch (packageManagerOf(installCommand)) {
140
+ case "yarn":
141
+ return `yarn ${script}`;
142
+ case "npm":
143
+ return `npm run ${script}`;
144
+ default:
145
+ return `pnpm ${script}`;
146
+ }
147
+ }
148
+ function workspaceRunCommand(installCommand, pkg, script) {
149
+ switch (packageManagerOf(installCommand)) {
150
+ case "yarn":
151
+ return `yarn workspace ${pkg} ${script}`;
152
+ case "npm":
153
+ return `npm run ${script} -w ${pkg}`;
154
+ default:
155
+ return `pnpm --filter ${pkg} ${script}`;
156
+ }
157
+ }
158
+ var GLOBAL_CSS_CANDIDATES = [
159
+ "app/globals.css",
160
+ "src/app/globals.css",
161
+ "styles/globals.css",
162
+ "src/styles/globals.css",
163
+ "globals.css"
164
+ ];
165
+ var TAILWIND_CONFIG_CANDIDATES = [
166
+ "tailwind.config.ts",
167
+ "tailwind.config.js",
168
+ "tailwind.config.mjs",
169
+ "tailwind.config.cjs"
170
+ ];
171
+ function isFile(file) {
172
+ try {
173
+ return statSync(file).isFile();
174
+ } catch {
175
+ return false;
176
+ }
177
+ }
178
+ function isDirectory(dir) {
179
+ try {
180
+ return statSync(dir).isDirectory();
181
+ } catch {
182
+ return false;
183
+ }
184
+ }
185
+ function findFirstFile(baseDir, candidates) {
186
+ return candidates.find((c) => isFile(path.join(baseDir, c)));
187
+ }
188
+ function detectProjectPaths(baseDirInput) {
189
+ const baseDir = path.resolve(baseDirInput);
190
+ const hasSrc = isDirectory(path.join(baseDir, "src"));
191
+ const css = findFirstFile(baseDir, GLOBAL_CSS_CANDIDATES);
192
+ const tw = findFirstFile(baseDir, TAILWIND_CONFIG_CANDIDATES);
193
+ return {
194
+ componentsDir: hasSrc ? "./src/components/ui" : "./components/ui",
195
+ globalCss: css !== void 0 ? `./${css}` : hasSrc ? "./src/app/globals.css" : "./app/globals.css",
196
+ globalCssSource: css !== void 0 ? "found" : "default",
197
+ tailwindConfig: tw !== void 0 ? `./${tw}` : "./tailwind.config.ts",
198
+ tailwindConfigSource: tw !== void 0 ? "found" : "default"
199
+ };
200
+ }
201
+ function projectFileExists(baseDir, relPath) {
202
+ return isFile(path.resolve(baseDir, relPath));
203
+ }
204
+ var ROOT_LAYOUT_FILES = [
205
+ "app/layout.tsx",
206
+ "app/layout.jsx",
207
+ "app/layout.js",
208
+ "src/app/layout.tsx",
209
+ "src/app/layout.jsx",
210
+ "src/app/layout.js",
211
+ "pages/_app.tsx",
212
+ "pages/_app.jsx",
213
+ "pages/_document.tsx",
214
+ "pages/_document.jsx",
215
+ "src/pages/_app.tsx",
216
+ "src/pages/_app.jsx",
217
+ "src/pages/_document.tsx",
218
+ "src/pages/_document.jsx"
219
+ ];
220
+ function exactVersionOf(version) {
221
+ if (typeof version !== "string") return void 0;
222
+ const v = version.trim();
223
+ if (!/^v?\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(v) || /^v?0\.0\.0-/.test(v)) return void 0;
224
+ return v;
225
+ }
226
+ function findInstalledPackage(baseDirInput, name) {
227
+ let dir = resolveBaseDir(baseDirInput);
228
+ for (; ; ) {
229
+ const pkgDir = path.join(dir, "node_modules", ...name.split("/"));
230
+ const file = path.join(pkgDir, "package.json");
231
+ if (isFile(file)) {
232
+ let version;
233
+ try {
234
+ const parsed = JSON.parse(readFileSync(file, "utf8"));
235
+ version = isPlainObject(parsed) ? exactVersionOf(parsed.version) : void 0;
236
+ } catch {
237
+ version = void 0;
238
+ }
239
+ return { dir: pkgDir, version };
240
+ }
241
+ const parent = path.dirname(dir);
242
+ if (parent === dir) return void 0;
243
+ dir = parent;
244
+ }
245
+ }
246
+ function isPnpProject(baseDirInput) {
247
+ let dir = resolveBaseDir(baseDirInput);
248
+ for (; ; ) {
249
+ if (isFile(path.join(dir, ".pnp.cjs")) || isFile(path.join(dir, ".pnp.js"))) return true;
250
+ const parent = path.dirname(dir);
251
+ if (parent === dir) return false;
252
+ dir = parent;
253
+ }
254
+ }
255
+ function compareExactVersions(a, b) {
256
+ const parse = (v) => {
257
+ const m = /^v?(\d+)\.(\d+)\.(\d+)(-[0-9A-Za-z.-]+)?/.exec(v.trim());
258
+ return m ? { core: [Number(m[1]), Number(m[2]), Number(m[3])], pre: m[4] !== void 0 } : void 0;
259
+ };
260
+ const x = parse(a);
261
+ const y = parse(b);
262
+ if (!x || !y) return void 0;
263
+ for (let i = 0; i < 3; i++) if (x.core[i] !== y.core[i]) return x.core[i] - y.core[i];
264
+ return Number(y.pre) - Number(x.pre);
265
+ }
266
+
267
+ // src/monet.ts
268
+ var MONET_CATEGORIES = [
269
+ { id: "hero", name: "Hero Sections", count: 120 },
270
+ { id: "feature", name: "Feature Sections", count: 191 },
271
+ { id: "testimonial", name: "Testimonials", count: 47 },
272
+ { id: "pricing", name: "Pricing Tables", count: 35 },
273
+ { id: "cta", name: "Call to Action", count: 42 },
274
+ { id: "stats", name: "Statistics", count: 28 },
275
+ { id: "footer", name: "Footers", count: 38 },
276
+ { id: "header", name: "Headers/Navigation", count: 45 },
277
+ { id: "logo-cloud", name: "Logo Clouds", count: 22 },
278
+ { id: "team", name: "Team Sections", count: 31 },
279
+ { id: "faq", name: "FAQ Sections", count: 24 },
280
+ { id: "contact", name: "Contact Forms", count: 29 },
281
+ { id: "newsletter", name: "Newsletter", count: 18 },
282
+ { id: "landing", name: "Full Landing Pages", count: 28 }
283
+ ];
284
+ var MonetClient = class {
285
+ apiKey;
286
+ baseUrl = "https://www.monet.design";
287
+ constructor(apiKey) {
288
+ this.apiKey = apiKey || process.env.MONET_API_KEY;
289
+ }
290
+ // 카테고리 목록 출력
291
+ async listCategories() {
292
+ console.log(chalk.blue("\n\u{1F3A8} Monet Design \uCE74\uD14C\uACE0\uB9AC\n"));
293
+ console.log(chalk.gray("https://monet.design - 1000+ React UI \uCEF4\uD3EC\uB10C\uD2B8\n"));
294
+ for (const cat of MONET_CATEGORIES) {
295
+ console.log(
296
+ ` ${chalk.cyan(cat.id.padEnd(15))} ${cat.name.padEnd(25)} ${chalk.gray(`(${cat.count}\uAC1C)`)}`
297
+ );
298
+ }
299
+ console.log(chalk.gray("\n\uC0AC\uC6A9\uBC95:"));
300
+ console.log(chalk.gray(" npx @axis-ds/cli monet browse <category> # \uCE74\uD14C\uACE0\uB9AC \uCEF4\uD3EC\uB10C\uD2B8 \uBCF4\uAE30"));
301
+ console.log(chalk.gray(" npx @axis-ds/cli monet search <query> # \uCEF4\uD3EC\uB10C\uD2B8 \uAC80\uC0C9\n"));
302
+ }
303
+ // 컴포넌트 검색 (웹 URL 안내)
304
+ async searchComponents(query) {
305
+ console.log(chalk.blue(`
306
+ \u{1F50D} "${query}" \uAC80\uC0C9 \uC911...
307
+ `));
308
+ const searchUrl = `${this.baseUrl}/c?q=${encodeURIComponent(query)}`;
309
+ console.log(chalk.yellow("Monet\uC740 \uC9C1\uC811 API\uB97C \uC81C\uACF5\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4."));
310
+ console.log(chalk.gray("\uB2E4\uC74C \uBC29\uBC95\uC73C\uB85C \uCEF4\uD3EC\uB10C\uD2B8\uB97C \uAC00\uC838\uC62C \uC218 \uC788\uC2B5\uB2C8\uB2E4:\n"));
311
+ console.log(chalk.bold("1. \uC6F9\uC5D0\uC11C \uAC80\uC0C9:"));
312
+ console.log(chalk.cyan(` ${searchUrl}
313
+ `));
314
+ console.log(chalk.bold("2. MCP \uC5F0\uB3D9 (Claude Code):"));
315
+ console.log(chalk.gray(" .claude/mcp.json\uC5D0 monet \uC11C\uBC84 \uD65C\uC131\uD654 \uD6C4"));
316
+ console.log(chalk.cyan(' "monet\uC5D0\uC11C hero \uCEF4\uD3EC\uB10C\uD2B8 \uCC3E\uC544\uC918"'));
317
+ console.log(chalk.gray(" \u2192 API \uD0A4 \uD544\uC694: https://monet.design/mcp\n"));
318
+ console.log(chalk.bold("3. \uC218\uB3D9 \uBCF5\uC0AC-\uBD99\uC5EC\uB123\uAE30:"));
319
+ console.log(chalk.gray(" \uC6F9\uC5D0\uC11C \uCEF4\uD3EC\uB10C\uD2B8 \uC120\uD0DD \u2192 Copy Code \u2192 \uD504\uB85C\uC81D\uD2B8\uC5D0 \uBD99\uC5EC\uB123\uAE30\n"));
320
+ }
321
+ // 카테고리별 컴포넌트 브라우징
322
+ async browseCategory(category) {
323
+ const cat = MONET_CATEGORIES.find((c) => c.id === category);
324
+ if (!cat) {
325
+ console.log(chalk.red(`
326
+ \u274C '${category}' \uCE74\uD14C\uACE0\uB9AC\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.
327
+ `));
328
+ console.log(chalk.gray("\uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uCE74\uD14C\uACE0\uB9AC:"));
329
+ MONET_CATEGORIES.forEach((c) => console.log(chalk.cyan(` ${c.id}`)));
330
+ return;
331
+ }
332
+ console.log(chalk.blue(`
333
+ \u{1F4C2} ${cat.name} (${cat.count}\uAC1C \uCEF4\uD3EC\uB10C\uD2B8)
334
+ `));
335
+ const browseUrl = `${this.baseUrl}/c?category=${category}`;
336
+ console.log(chalk.gray("\uC6F9\uC5D0\uC11C \uCEF4\uD3EC\uB10C\uD2B8\uB97C \uD655\uC778\uD558\uC138\uC694:"));
337
+ console.log(chalk.cyan(`${browseUrl}
338
+ `));
339
+ console.log(chalk.bold("\uC778\uAE30 \uCEF4\uD3EC\uB10C\uD2B8 \uC608\uC2DC:"));
340
+ const examples = this.getCategoryExamples(category);
341
+ examples.forEach((ex) => {
342
+ console.log(` ${chalk.cyan(ex.id.padEnd(30))} ${chalk.gray(ex.desc)}`);
343
+ });
344
+ console.log(chalk.gray("\n\uCEF4\uD3EC\uB10C\uD2B8 \uAC00\uC838\uC624\uAE30:"));
345
+ console.log(chalk.gray(" 1. \uC6F9\uC5D0\uC11C \uCEF4\uD3EC\uB10C\uD2B8 \uC120\uD0DD"));
346
+ console.log(chalk.gray(" 2. Copy Code \uBC84\uD2BC \uD074\uB9AD"));
347
+ console.log(chalk.gray(" 3. npx @axis-ds/cli monet import \uC2E4\uD589\n"));
348
+ }
349
+ // 카테고리별 예시 컴포넌트
350
+ getCategoryExamples(category) {
351
+ const examples = {
352
+ hero: [
353
+ { id: "hero-with-image", desc: "\uC774\uBBF8\uC9C0 \uBC30\uACBD \uD788\uC5B4\uB85C" },
354
+ { id: "hero-centered", desc: "\uC911\uC559 \uC815\uB82C \uD788\uC5B4\uB85C" },
355
+ { id: "hero-split", desc: "\uBD84\uD560 \uB808\uC774\uC544\uC6C3 \uD788\uC5B4\uB85C" }
356
+ ],
357
+ feature: [
358
+ { id: "feature-grid", desc: "\uADF8\uB9AC\uB4DC \uD615\uC2DD \uAE30\uB2A5 \uC18C\uAC1C" },
359
+ { id: "feature-alternating", desc: "\uAD50\uCC28 \uB808\uC774\uC544\uC6C3 \uAE30\uB2A5" },
360
+ { id: "feature-icons", desc: "\uC544\uC774\uCF58 \uAE30\uBC18 \uAE30\uB2A5" }
361
+ ],
362
+ pricing: [
363
+ { id: "pricing-three-tiers", desc: "3\uB2E8 \uC694\uAE08\uC81C" },
364
+ { id: "pricing-comparison", desc: "\uBE44\uAD50 \uD14C\uC774\uBE14 \uC694\uAE08\uC81C" },
365
+ { id: "pricing-toggle", desc: "\uC6D4/\uC5F0 \uD1A0\uAE00 \uC694\uAE08\uC81C" }
366
+ ],
367
+ landing: [
368
+ { id: "deepcon-ai-landing", desc: "AI SaaS \uB79C\uB529\uD398\uC774\uC9C0" },
369
+ { id: "saas-landing", desc: "SaaS \uC2A4\uD0C0\uD130 \uB79C\uB529" },
370
+ { id: "agency-landing", desc: "\uC5D0\uC774\uC804\uC2DC \uB79C\uB529\uD398\uC774\uC9C0" }
371
+ ]
372
+ };
373
+ return examples[category] || [{ id: `${category}-basic`, desc: "\uAE30\uBCF8 \uD15C\uD50C\uB9BF" }];
374
+ }
375
+ // 클립보드에서 Monet 코드 가져오기
376
+ /** opts.interactive: 질문을 띄울 수 있는지(WI-0075). 없으면 stdin 이 TTY 인지로 정합니다 */
377
+ async importFromClipboard(opts = {}) {
378
+ console.log(chalk.blue("\n\u{1F4CB} \uD074\uB9BD\uBCF4\uB4DC\uC5D0\uC11C Monet \uCEF4\uD3EC\uB10C\uD2B8 \uAC00\uC838\uC624\uAE30\n"));
379
+ console.log(chalk.yellow("\uC900\uBE44 \uB2E8\uACC4:"));
380
+ console.log(chalk.gray(" 1. monet.design\uC5D0\uC11C \uCEF4\uD3EC\uB10C\uD2B8 \uC120\uD0DD"));
381
+ console.log(chalk.gray(" 2. 'Copy Code' \uBC84\uD2BC\uC73C\uB85C \uCF54\uB4DC \uBCF5\uC0AC"));
382
+ console.log(chalk.gray(" 3. \uC774 \uBA85\uB839\uC5B4 \uC2E4\uD589\n"));
383
+ const interactive = opts.interactive ?? isInteractive();
384
+ if (!interactive) {
385
+ failNonInteractive("\uCEF4\uD3EC\uB10C\uD2B8 \uC774\uB984\uACFC \uBD99\uC5EC \uB123\uC744 \uCF54\uB4DC\uB97C \uBB3C\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC544\uBB34\uAC83\uB3C4 \uC4F0\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.", [
386
+ "\uB300\uD654\uD615 \uD130\uBBF8\uB110\uC5D0\uC11C \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694: axis monet import"
387
+ ]);
388
+ return;
389
+ }
390
+ const { componentName } = await ask(
391
+ {
392
+ type: "text",
393
+ name: "componentName",
394
+ message: "\uCEF4\uD3EC\uB10C\uD2B8 \uC774\uB984 (\uC608: hero-section):",
395
+ validate: (v) => v.length > 0 ? true : "\uC774\uB984\uC744 \uC785\uB825\uD558\uC138\uC694"
396
+ },
397
+ interactive
398
+ );
399
+ if (!componentName) return;
400
+ const { componentCode } = await ask(
401
+ {
402
+ type: "text",
403
+ name: "componentCode",
404
+ message: "\uBCF5\uC0AC\uD55C \uCF54\uB4DC\uB97C \uBD99\uC5EC\uB123\uC73C\uC138\uC694 (\uD55C \uC904\uB85C):"
405
+ },
406
+ interactive
407
+ );
408
+ if (!componentCode) {
409
+ console.log(chalk.yellow("\n\uCF54\uB4DC\uAC00 \uC785\uB825\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4."));
410
+ return;
411
+ }
412
+ const configPath = path2.resolve("axis.config.json");
413
+ let componentsDir = "./src/components/monet";
414
+ if (await fs.pathExists(configPath)) {
415
+ const config = await fs.readJSON(configPath);
416
+ componentsDir = config.monetDir || path2.join(path2.dirname(config.componentsDir), "monet");
417
+ }
418
+ const fileName = `${componentName}.tsx`;
419
+ const filePath = path2.join(componentsDir, fileName);
420
+ await fs.ensureDir(componentsDir);
421
+ await fs.writeFile(filePath, componentCode);
422
+ console.log(chalk.green(`
423
+ \u2713 ${filePath} \uC800\uC7A5 \uC644\uB8CC`));
424
+ console.log(chalk.gray("\n\uB2E4\uC74C \uB2E8\uACC4:"));
425
+ for (const line of monetImportNextSteps(process.cwd())) console.log(chalk.gray(` ${line}`));
426
+ console.log("");
427
+ }
428
+ };
429
+ function monetImportNextSteps(baseDir) {
430
+ return [
431
+ "1. \uD30C\uC77C\uC744 \uC5F4\uC5B4 import \uACBD\uB85C \uC218\uC815",
432
+ `2. \uD544\uC694\uD55C \uC758\uC874\uC131 \uC124\uCE58 (${addCommandPrefix(detectInstallCommand(baseDir))} ...)`,
433
+ "3. \uCEF4\uD3EC\uB10C\uD2B8 \uC0AC\uC6A9"
434
+ ];
435
+ }
436
+ function getMonetUrl(path3) {
437
+ return `https://monet.design${path3}`;
438
+ }
439
+ function getMonetMcpConfig() {
440
+ return {
441
+ monet: {
442
+ url: "https://www.monet.design/api/remote/mcp",
443
+ headers: {
444
+ Authorization: "Bearer ${MONET_API_KEY}"
445
+ }
446
+ }
447
+ };
448
+ }
449
+
450
+ export {
451
+ resolveBaseDir,
452
+ detectInstallCommand,
453
+ hasDependency,
454
+ workspaceRootPackage,
455
+ declaredDependency,
456
+ isPlainObject,
457
+ addCommandPrefix,
458
+ addCommandFor,
459
+ runScriptCommand,
460
+ workspaceRunCommand,
461
+ GLOBAL_CSS_CANDIDATES,
462
+ detectProjectPaths,
463
+ projectFileExists,
464
+ ROOT_LAYOUT_FILES,
465
+ findInstalledPackage,
466
+ isPnpProject,
467
+ compareExactVersions,
468
+ MONET_CATEGORIES,
469
+ MonetClient,
470
+ monetImportNextSteps,
471
+ getMonetUrl,
472
+ getMonetMcpConfig
473
+ };