@axis-ds/cli 1.6.2 → 1.7.1

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
@@ -45,7 +45,7 @@ axis list
45
45
 
46
46
  - `axis.config.json` 과 `cn()` 을 담은 `utils.ts` 를 만들고, `clsx` · `tailwind-merge` 가 `package.json` 에 없으면 `dependencies` 에 더합니다. 설치는 실행하지 않고 명령만 안내합니다.
47
47
  - `axis.config.json` 의 `registry` 는 기본값(`https://axis-design-system.pages.dev/r`)이거나, 이미 있던 config 의 값입니다. 종전 기본값 `https://axis.minu.best/r` 는 Cloudflare 403 이라 바꿨습니다. add · list 는 그 값을 새 기본값으로 바꿔 읽으며 한 줄 알립니다(위 「컴포넌트 추가」, 파일은 고치지 않음). init 을 다시 실행하면 그 값을 새 기본값으로 바꿔 쓰고 「registry 를 새 기본 주소로 갱신」으로 알립니다. 사용자가 지정한 다른 값은 init 도 그대로 둡니다.
48
- - `utils.ts` 가 이미 있으면 같은 내용이면 「유지(이미 같은 내용)」(다시 쓰지 않음), 다르면 init 의 utils 로 덮어쓰고 「덮어씀」으로 알립니다. `utils.ts` 쓰기는 `axis add` 와 같은 규칙입니다: 심볼릭 링크면 같은 내용일 때 「유지(이미 같은 내용, 링크)」로 두고, 내용이 다른 링크(프로젝트 안 · 밖) · 깨진 링크 · 디렉터리 · 읽을 수 없는 파일 · 다른 경로와 하드링크로 이어진 파일은 쓰지 않고 그 사실을 알린 뒤 rc 1 입니다. 이 판정은 쓰기 전에 하므로 이때는 `axis.config.json` · `package.json` 도 쓰지 않습니다. 다른 경로와 하드링크로 이어진 `axis.config.json` 을 다시 써야 할 때도 같습니다.
48
+ - `utils.ts` 가 이미 있으면 같은 내용이면 「유지(이미 같은 내용)」(다시 쓰지 않음), 다르면 init 의 utils 로 덮어쓰고 「덮어씀」으로 알립니다(터미널이 아닌 곳에서 `-y` 없이 실행하면 덮어쓰지 않고 rc 1, 아래 「터미널이 아닌 곳에서 실행」). `utils.ts` 쓰기는 `axis add` 와 같은 규칙입니다: 심볼릭 링크면 같은 내용일 때 「유지(이미 같은 내용, 링크)」로 두고, 내용이 다른 링크(프로젝트 안 · 밖) · 깨진 링크 · 디렉터리 · 읽을 수 없는 파일 · 다른 경로와 하드링크로 이어진 파일은 쓰지 않고 그 사실을 알린 뒤 rc 1 입니다. 이 판정은 쓰기 전에 하므로 이때는 `axis.config.json` · `package.json` 도 쓰지 않습니다. 다른 경로와 하드링크로 이어진 `axis.config.json` 을 다시 써야 할 때도 같습니다.
49
49
  - 경로 기본값(`-y` 값 · 대화형 초기값)은 실제 프로젝트를 보고 정합니다(1.6.0 부터). `componentsDir` 는 `src/` 가 있으면 `./src/components/ui`, 없으면 `./components/ui` 이고 `src/` 를 새로 만들지 않습니다. `globalCss` 는 `app/globals.css` · `src/app/globals.css` · `styles/globals.css` · `src/styles/globals.css` · `globals.css` 가운데 실제로 있는 첫 파일, `tailwindConfig` 는 있는 `tailwind.config.{ts,js,mjs,cjs}` 가운데 첫 파일입니다. 찾지 못하면 기본값을 기록하고 그 사실을 한 줄 알립니다. Tailwind 4 안내 제목도 이 경로를 씁니다.
50
50
  - config 가 이미 있으면 출력이 「생성 완료」가 아니라 사실대로 「유지(이미 같은 내용)」 또는 「갱신」입니다. 갱신은 init 이 다루는 키(`$schema` · `componentsDir` · `tailwindConfig` · `globalCss` · `registry`)만 바꾸고, 그 밖의 키(`brand` · `monetDir` · `template` · `agenticDir` 등)는 값과 순서를 그대로 둡니다(1.6.0 부터, 이전에는 빠졌음). 들여쓰기도 기존 파일을 따릅니다. 기존 `componentsDir` 는 그대로 두고, `globalCss` · `tailwindConfig` 는 가리키는 파일이 있으면 그대로, 없으면 찾은 파일로 바꾸고 알립니다(찾지 못하면 그대로 두고 알림). Tailwind 4 이상이면 `tailwindConfig` 를 지우고 알립니다. `registry` 가 종전 기본값이면 위처럼 새 기본값으로 바꿉니다. `AXIS_REGISTRY_URL` 은 config 에 기록하지 않고 add · list 를 실행할 때만 우선합니다.
51
51
  - 애니메이션 플러그인(`tailwindcss-animate` · `tw-animate-css`)이 `package.json` 에 없으면, Dialog · Select 등의 열고 닫는 애니메이션에 필요하다는 안내 한 줄을 냅니다(Tailwind 3 은 plugins, Tailwind 4 는 `@import "tw-animate-css";`).
@@ -107,11 +107,29 @@ 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` 도 같은 항목 · 문구를 냅니다.
114
114
 
115
+ ## 터미널이 아닌 곳에서 실행 (CI · 파이프 · `</dev/null`)
116
+
117
+ stdin 이 터미널이 아니면 질문을 띄우지 않습니다(1.7.0 부터). 이전에는 `axis add` 말고는 입력이 닫힌 곳(`</dev/null` · 빈 파이프)에서 질문 한 줄만 찍고 아무것도 하지 않은 채 rc 0 으로 끝났고, 파이프로 응답을 넣으면 그 값으로 동작했습니다. 질문의 성격에 따라 이렇게 합니다.
118
+
119
+ | 명령 | 터미널이 아닐 때 |
120
+ |---|---|
121
+ | `axis init`(`-y` 없음) | `-y` 와 같은 기본값으로 진행하고 쓴 값을 한 줄 알림. 단 내용이 다른 기존 `utils.ts` 가 있으면 덮어쓰지 않고 rc 1, 아무것도 쓰지 않음(`-y` 면 덮어씀) |
122
+ | `axis template init <name>`(`--dir` 없음) | 템플릿 이름 디렉터리(`./<name>`)로 진행하고 한 줄 알림 |
123
+ | `axis template init`(이름 없음) | rc 1, 템플릿 이름 안내(템플릿 선택에는 기본값이 없음) |
124
+ | `axis template init` 대상이 비어 있지 않음 | rc 1, 아무것도 쓰지 않음. `-y` 면 진행(템플릿과 같은 경로의 파일은 덮어씀) |
125
+ | `axis template apply` 충돌 파일 있음 | rc 1, 아무것도 쓰지 않음. `-y` 면 덮어씀, `--dry-run` 으로 미리보기 |
126
+ | `axis add` 내용이 다른 기존 파일 | `-y` 와 같이 건너뛰고 목록을 알림(종전과 같음) |
127
+ | `axis v0 import` · `axis monet import` | rc 1, 대화형 터미널에서 실행하라는 안내(`v0` 는 `axis v0 convert <file>` 도 안내) |
128
+
129
+ - `axis template init -y` 는 질문을 기본값으로 답하고, 비어 있지 않은 디렉터리도 진행합니다(`template apply -y` 와 같은 뜻). 터미널에서도 같습니다.
130
+ - 판정은 stdin 이 터미널인지 하나로 합니다. 그래서 `printf '\n' | axis template init dashboard` 처럼 파이프로 응답을 넣던 스크립트도 이제는 묻지 않고 위 표대로 동작합니다. 값은 `--dir` · `-y` 같은 옵션으로 주세요.
131
+ - Windows Git Bash(mintty)처럼 사람이 앞에 있어도 stdin 이 터미널로 잡히지 않는 환경에서는 질문 없이 위 표대로 동작할 수 있습니다(실측 전). 질문이 뜨지 않으면 `winpty npx axis ...` 로 실행하거나, PowerShell · Windows Terminal 에서 실행하거나, `--dir` · `-y` 같은 옵션으로 값을 주세요.
132
+
115
133
  ## 문서
116
134
 
117
135
  자세한 사용법은 [AXIS Design System 문서](https://axis-design-system.pages.dev)를 참고하세요.
@@ -1,8 +1,13 @@
1
+ import {
2
+ ask,
3
+ failNonInteractive,
4
+ isInteractive
5
+ } from "./chunk-HFMMK6R4.js";
6
+
1
7
  // src/v0.ts
2
8
  import chalk from "chalk";
3
9
  import fs from "fs-extra";
4
10
  import path from "path";
5
- import prompts from "prompts";
6
11
  var V0_CONVERSION_RULES = [
7
12
  // import 경로 변환
8
13
  { from: /@\/components\/ui\//g, to: "@/components/ui/" },
@@ -69,7 +74,8 @@ ${result}`;
69
74
  console.log(chalk.gray(" - \uD658\uACBD \uBCC0\uC218 \uBC0F API \uC5D4\uB4DC\uD3EC\uC778\uD2B8\n"));
70
75
  }
71
76
  // V0 URL에서 가져오기 안내
72
- async importFromUrl() {
77
+ async importFromUrl(opts = {}) {
78
+ const interactive = opts.interactive ?? isInteractive();
73
79
  console.log(chalk.blue("\n\u{1F517} V0 URL\uC5D0\uC11C \uCEF4\uD3EC\uB10C\uD2B8 \uAC00\uC838\uC624\uAE30\n"));
74
80
  console.log(chalk.yellow("V0\uB294 \uC9C1\uC811 API\uB97C \uC81C\uACF5\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4."));
75
81
  console.log(chalk.gray("\uB2E4\uC74C \uBC29\uBC95\uC73C\uB85C \uCF54\uB4DC\uB97C \uAC00\uC838\uC62C \uC218 \uC788\uC2B5\uB2C8\uB2E4:\n"));
@@ -85,26 +91,36 @@ ${result}`;
85
91
  console.log(chalk.gray(" 1. v0.app\uC5D0\uC11C \uCF54\uB4DC \uD0ED \uC5F4\uAE30"));
86
92
  console.log(chalk.gray(" 2. \uD30C\uC77C\uBCC4\uB85C \uCF54\uB4DC \uBCF5\uC0AC"));
87
93
  console.log(chalk.gray(" 3. npx @axis-ds/cli v0 convert\uB85C \uBCC0\uD658\n"));
88
- const { method } = await prompts({
89
- type: "select",
90
- name: "method",
91
- message: "\uC5B4\uB5A4 \uBC29\uBC95\uC744 \uC0AC\uC6A9\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
92
- choices: [
93
- { title: "GitHub\uC5D0\uC11C \uD074\uB860", value: "github" },
94
- { title: "\uB2E4\uC6B4\uB85C\uB4DC\uD55C ZIP \uD30C\uC77C \uBCC0\uD658", value: "zip" },
95
- { title: "\uD074\uB9BD\uBCF4\uB4DC\uC5D0\uC11C \uBCC0\uD658", value: "clipboard" },
96
- { title: "\uCDE8\uC18C", value: "cancel" }
97
- ]
98
- });
94
+ if (!interactive) {
95
+ failNonInteractive("\uBC29\uBC95\uC744 \uACE0\uB97C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC544\uBB34\uAC83\uB3C4 \uC4F0\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.", [
96
+ "\uC704 \uBC29\uBC95 1~3 \uC744 \uC9C1\uC811 \uB530\uB974\uAC70\uB098 \uB300\uD654\uD615 \uD130\uBBF8\uB110\uC5D0\uC11C \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.",
97
+ "\uD30C\uC77C\uC774 \uC788\uC73C\uBA74: axis v0 convert <file> [-o <path>]"
98
+ ]);
99
+ return;
100
+ }
101
+ const { method } = await ask(
102
+ {
103
+ type: "select",
104
+ name: "method",
105
+ message: "\uC5B4\uB5A4 \uBC29\uBC95\uC744 \uC0AC\uC6A9\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
106
+ choices: [
107
+ { title: "GitHub\uC5D0\uC11C \uD074\uB860", value: "github" },
108
+ { title: "\uB2E4\uC6B4\uB85C\uB4DC\uD55C ZIP \uD30C\uC77C \uBCC0\uD658", value: "zip" },
109
+ { title: "\uD074\uB9BD\uBCF4\uB4DC\uC5D0\uC11C \uBCC0\uD658", value: "clipboard" },
110
+ { title: "\uCDE8\uC18C", value: "cancel" }
111
+ ]
112
+ },
113
+ interactive
114
+ );
99
115
  switch (method) {
100
116
  case "github":
101
117
  await this.setupGitHubSync();
102
118
  break;
103
119
  case "zip":
104
- await this.convertFromZip();
120
+ await this.convertFromZip({ interactive });
105
121
  break;
106
122
  case "clipboard":
107
- await this.convertFromClipboard();
123
+ await this.convertFromClipboard({ interactive });
108
124
  break;
109
125
  }
110
126
  }
@@ -122,14 +138,24 @@ ${result}`;
122
138
  console.log(chalk.gray("\uD301: GitHub Actions\uB85C \uC790\uB3D9 \uBCC0\uD658 \uC6CC\uD06C\uD50C\uB85C\uB3C4 \uC124\uC815 \uAC00\uB2A5\uD569\uB2C8\uB2E4.\n"));
123
139
  }
124
140
  // ZIP 파일에서 변환
125
- async convertFromZip() {
141
+ async convertFromZip(opts = {}) {
126
142
  console.log(chalk.blue("\n\u{1F4E6} ZIP \uD30C\uC77C\uC5D0\uC11C \uBCC0\uD658\n"));
127
- const { zipPath } = await prompts({
128
- type: "text",
129
- name: "zipPath",
130
- message: "\uB2E4\uC6B4\uB85C\uB4DC\uD55C ZIP \uD30C\uC77C \uACBD\uB85C:",
131
- validate: (v) => v.length > 0 ? true : "\uACBD\uB85C\uB97C \uC785\uB825\uD558\uC138\uC694"
132
- });
143
+ const interactive = opts.interactive ?? isInteractive();
144
+ if (!interactive) {
145
+ failNonInteractive("ZIP \uD30C\uC77C \uACBD\uB85C\uB97C \uBB3C\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.", [
146
+ "\uC555\uCD95\uC744 \uD47C \uB4A4: axis v0 convert <file> [-o <path>]"
147
+ ]);
148
+ return;
149
+ }
150
+ const { zipPath } = await ask(
151
+ {
152
+ type: "text",
153
+ name: "zipPath",
154
+ message: "\uB2E4\uC6B4\uB85C\uB4DC\uD55C ZIP \uD30C\uC77C \uACBD\uB85C:",
155
+ validate: (v) => v.length > 0 ? true : "\uACBD\uB85C\uB97C \uC785\uB825\uD558\uC138\uC694"
156
+ },
157
+ interactive
158
+ );
133
159
  if (!zipPath) return;
134
160
  console.log(chalk.yellow("\nZIP \uBCC0\uD658 \uAE30\uB2A5\uC740 \uCD94\uD6C4 \uAD6C\uD604 \uC608\uC815\uC785\uB2C8\uB2E4."));
135
161
  console.log(chalk.gray("\uD604\uC7AC\uB294 \uC218\uB3D9\uC73C\uB85C \uC555\uCD95 \uD574\uC81C \uD6C4 convert \uBA85\uB839\uC5B4\uB97C \uC0AC\uC6A9\uD558\uC138\uC694:\n"));
@@ -137,22 +163,35 @@ ${result}`;
137
163
  console.log(chalk.cyan(" npx @axis-ds/cli v0 convert ./v0-project/components/*.tsx\n"));
138
164
  }
139
165
  // 클립보드에서 변환
140
- async convertFromClipboard() {
166
+ async convertFromClipboard(opts = {}) {
141
167
  console.log(chalk.blue("\n\u{1F4CB} \uD074\uB9BD\uBCF4\uB4DC\uC5D0\uC11C V0 \uCF54\uB4DC \uBCC0\uD658\n"));
142
- const { componentName } = await prompts({
143
- type: "text",
144
- name: "componentName",
145
- message: "\uCEF4\uD3EC\uB10C\uD2B8 \uC774\uB984 (\uC608: hero-section):",
146
- validate: (v) => v.length > 0 ? true : "\uC774\uB984\uC744 \uC785\uB825\uD558\uC138\uC694"
147
- });
168
+ const interactive = opts.interactive ?? isInteractive();
169
+ if (!interactive) {
170
+ failNonInteractive("\uBD99\uC5EC \uB123\uC744 \uCF54\uB4DC\uC640 \uC774\uB984\uC744 \uBB3C\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC544\uBB34\uAC83\uB3C4 \uC4F0\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.", [
171
+ "\uCF54\uB4DC\uB97C \uD30C\uC77C\uB85C \uC800\uC7A5\uD55C \uB4A4: axis v0 convert <file> [-o <path>]"
172
+ ]);
173
+ return;
174
+ }
175
+ const { componentName } = await ask(
176
+ {
177
+ type: "text",
178
+ name: "componentName",
179
+ message: "\uCEF4\uD3EC\uB10C\uD2B8 \uC774\uB984 (\uC608: hero-section):",
180
+ validate: (v) => v.length > 0 ? true : "\uC774\uB984\uC744 \uC785\uB825\uD558\uC138\uC694"
181
+ },
182
+ interactive
183
+ );
148
184
  if (!componentName) return;
149
185
  console.log(chalk.gray("\nV0\uC5D0\uC11C \uBCF5\uC0AC\uD55C \uCF54\uB4DC\uB97C \uBD99\uC5EC\uB123\uC73C\uC138\uC694."));
150
186
  console.log(chalk.gray("(\uC785\uB825 \uC644\uB8CC \uD6C4 \uBE48 \uC904\uC5D0\uC11C Enter \uB450 \uBC88)\n"));
151
- const { code } = await prompts({
152
- type: "text",
153
- name: "code",
154
- message: "\uCF54\uB4DC:"
155
- });
187
+ const { code } = await ask(
188
+ {
189
+ type: "text",
190
+ name: "code",
191
+ message: "\uCF54\uB4DC:"
192
+ },
193
+ interactive
194
+ );
156
195
  if (!code) {
157
196
  console.log(chalk.yellow("\n\uCF54\uB4DC\uAC00 \uC785\uB825\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4."));
158
197
  return;
@@ -0,0 +1,35 @@
1
+ // src/interactive.ts
2
+ import chalk from "chalk";
3
+ import prompts from "prompts";
4
+ var isInteractive = () => process.stdin.isTTY === true;
5
+ var NonInteractiveError = class extends Error {
6
+ constructor(message) {
7
+ super(message);
8
+ this.name = "NonInteractiveError";
9
+ }
10
+ };
11
+ async function ask(questions, interactive) {
12
+ if (!interactive) {
13
+ const list = Array.isArray(questions) ? questions : [questions];
14
+ const first = list.find((q) => q.type !== null);
15
+ const message = typeof first?.message === "string" ? first.message : String(first?.name ?? "");
16
+ throw new NonInteractiveError(`\uBE44\uB300\uD654\uD615 \uC2E4\uD589\uC774\uB77C \uC9C8\uBB38(${message})\uC5D0 \uB2F5\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uB300\uD654\uD615 \uD130\uBBF8\uB110\uC5D0\uC11C \uC2E4\uD589\uD558\uAC70\uB098 \uD544\uC694\uD55C \uC635\uC158\uC744 \uC8FC\uC138\uC694.`);
17
+ }
18
+ return prompts(questions);
19
+ }
20
+ function noteDefault(message) {
21
+ console.log(chalk.gray(`\uBE44\uB300\uD654\uD615 \uC2E4\uD589\uC774\uB77C ${message}`));
22
+ }
23
+ function failNonInteractive(reason, hints) {
24
+ console.error(chalk.red(`\uBE44\uB300\uD654\uD615 \uC2E4\uD589\uC774\uB77C ${reason}`));
25
+ for (const hint of hints) console.error(chalk.yellow(` ${hint}`));
26
+ process.exitCode = 1;
27
+ }
28
+
29
+ export {
30
+ isInteractive,
31
+ NonInteractiveError,
32
+ ask,
33
+ noteDefault,
34
+ failNonInteractive
35
+ };
@@ -1,8 +1,13 @@
1
+ import {
2
+ ask,
3
+ failNonInteractive,
4
+ isInteractive
5
+ } from "./chunk-HFMMK6R4.js";
6
+
1
7
  // src/monet.ts
2
8
  import chalk from "chalk";
3
9
  import fs from "fs-extra";
4
10
  import path from "path";
5
- import prompts from "prompts";
6
11
  var MONET_CATEGORIES = [
7
12
  { id: "hero", name: "Hero Sections", count: 120 },
8
13
  { id: "feature", name: "Feature Sections", count: 191 },
@@ -111,24 +116,38 @@ var MonetClient = class {
111
116
  return examples[category] || [{ id: `${category}-basic`, desc: "\uAE30\uBCF8 \uD15C\uD50C\uB9BF" }];
112
117
  }
113
118
  // 클립보드에서 Monet 코드 가져오기
114
- async importFromClipboard() {
119
+ /** opts.interactive: 질문을 띄울 수 있는지(WI-0075). 없으면 stdin 이 TTY 인지로 정합니다 */
120
+ async importFromClipboard(opts = {}) {
115
121
  console.log(chalk.blue("\n\u{1F4CB} \uD074\uB9BD\uBCF4\uB4DC\uC5D0\uC11C Monet \uCEF4\uD3EC\uB10C\uD2B8 \uAC00\uC838\uC624\uAE30\n"));
116
122
  console.log(chalk.yellow("\uC900\uBE44 \uB2E8\uACC4:"));
117
123
  console.log(chalk.gray(" 1. monet.design\uC5D0\uC11C \uCEF4\uD3EC\uB10C\uD2B8 \uC120\uD0DD"));
118
124
  console.log(chalk.gray(" 2. 'Copy Code' \uBC84\uD2BC\uC73C\uB85C \uCF54\uB4DC \uBCF5\uC0AC"));
119
125
  console.log(chalk.gray(" 3. \uC774 \uBA85\uB839\uC5B4 \uC2E4\uD589\n"));
120
- const { componentName } = await prompts({
121
- type: "text",
122
- name: "componentName",
123
- message: "\uCEF4\uD3EC\uB10C\uD2B8 \uC774\uB984 (\uC608: hero-section):",
124
- validate: (v) => v.length > 0 ? true : "\uC774\uB984\uC744 \uC785\uB825\uD558\uC138\uC694"
125
- });
126
+ const interactive = opts.interactive ?? isInteractive();
127
+ if (!interactive) {
128
+ 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.", [
129
+ "\uB300\uD654\uD615 \uD130\uBBF8\uB110\uC5D0\uC11C \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694: axis monet import"
130
+ ]);
131
+ return;
132
+ }
133
+ const { componentName } = await ask(
134
+ {
135
+ type: "text",
136
+ name: "componentName",
137
+ message: "\uCEF4\uD3EC\uB10C\uD2B8 \uC774\uB984 (\uC608: hero-section):",
138
+ validate: (v) => v.length > 0 ? true : "\uC774\uB984\uC744 \uC785\uB825\uD558\uC138\uC694"
139
+ },
140
+ interactive
141
+ );
126
142
  if (!componentName) return;
127
- const { componentCode } = await prompts({
128
- type: "text",
129
- name: "componentCode",
130
- message: "\uBCF5\uC0AC\uD55C \uCF54\uB4DC\uB97C \uBD99\uC5EC\uB123\uC73C\uC138\uC694 (\uD55C \uC904\uB85C):"
131
- });
143
+ const { componentCode } = await ask(
144
+ {
145
+ type: "text",
146
+ name: "componentCode",
147
+ message: "\uBCF5\uC0AC\uD55C \uCF54\uB4DC\uB97C \uBD99\uC5EC\uB123\uC73C\uC138\uC694 (\uD55C \uC904\uB85C):"
148
+ },
149
+ interactive
150
+ );
132
151
  if (!componentCode) {
133
152
  console.log(chalk.yellow("\n\uCF54\uB4DC\uAC00 \uC785\uB825\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4."));
134
153
  return;
package/dist/index.js CHANGED
@@ -1,10 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  MonetClient
4
- } from "./chunk-ZVVACWEW.js";
4
+ } from "./chunk-LVMDBOXD.js";
5
5
  import {
6
6
  V0Client
7
- } from "./chunk-NNFSJISD.js";
7
+ } from "./chunk-2PNCUPCG.js";
8
+ import {
9
+ NonInteractiveError,
10
+ ask,
11
+ failNonInteractive,
12
+ isInteractive,
13
+ noteDefault
14
+ } from "./chunk-HFMMK6R4.js";
8
15
  import {
9
16
  registerSyncCommand
10
17
  } from "./chunk-7CDXEBEE.js";
@@ -1806,7 +1813,6 @@ import fs8 from "fs-extra";
1806
1813
  import path9 from "path";
1807
1814
  import chalk5 from "chalk";
1808
1815
  import ora3 from "ora";
1809
- import prompts from "prompts";
1810
1816
 
1811
1817
  // src/template/post-install.ts
1812
1818
  import fs6 from "fs-extra";
@@ -1901,16 +1907,51 @@ async function readAxisConfig(baseDir) {
1901
1907
  }
1902
1908
 
1903
1909
  // ../axis-registry-core/src/template/tailwind-core.ts
1904
- import { existsSync as existsSync2, lstatSync, readdirSync, readFileSync as readFileSync2 } from "fs";
1905
- import { extname, join, resolve } from "path";
1910
+ import { existsSync as existsSync2, lstatSync, readdirSync, readFileSync as readFileSync2, realpathSync } from "fs";
1911
+ import { basename, dirname, extname, isAbsolute, join, relative, resolve, sep } from "path";
1906
1912
 
1907
1913
  // ../axis-registry-core/src/project.ts
1908
1914
  import { existsSync, readFileSync, statSync } from "fs";
1909
1915
  import path8 from "path";
1910
- function detectInstallCommand(baseDir) {
1911
- if (existsSync(path8.join(baseDir, "pnpm-lock.yaml"))) return "pnpm install";
1912
- if (existsSync(path8.join(baseDir, "yarn.lock"))) return "yarn install";
1913
- if (existsSync(path8.join(baseDir, "package-lock.json"))) return "npm install";
1916
+ var LOCKFILE_COMMANDS = [
1917
+ ["pnpm-lock.yaml", "pnpm install"],
1918
+ ["yarn.lock", "yarn install"],
1919
+ ["package-lock.json", "npm install"]
1920
+ ];
1921
+ function lockfileCommandAt(dir) {
1922
+ return LOCKFILE_COMMANDS.find(([file]) => existsSync(path8.join(dir, file)))?.[1];
1923
+ }
1924
+ function isWorkspaceRootDir(dir) {
1925
+ if (isFile(path8.join(dir, "pnpm-workspace.yaml"))) return true;
1926
+ let parsed;
1927
+ try {
1928
+ parsed = JSON.parse(readFileSync(path8.join(dir, "package.json"), "utf8"));
1929
+ } catch {
1930
+ return false;
1931
+ }
1932
+ if (!isPlainObject(parsed)) return false;
1933
+ const ws = parsed.workspaces;
1934
+ return Array.isArray(ws) || isPlainObject(ws) && Array.isArray(ws.packages);
1935
+ }
1936
+ function findWorkspaceRoot(baseDirInput) {
1937
+ let dir = path8.resolve(baseDirInput);
1938
+ for (; ; ) {
1939
+ if (isWorkspaceRootDir(dir)) return dir;
1940
+ if (existsSync(path8.join(dir, ".git"))) return void 0;
1941
+ const parent = path8.dirname(dir);
1942
+ if (parent === dir) return void 0;
1943
+ dir = parent;
1944
+ }
1945
+ }
1946
+ function detectInstallCommand(baseDirInput) {
1947
+ const baseDir = path8.resolve(baseDirInput);
1948
+ const own = lockfileCommandAt(baseDir);
1949
+ if (own !== void 0) return own;
1950
+ const root = findWorkspaceRoot(baseDir);
1951
+ if (root !== void 0 && root !== baseDir) {
1952
+ const fromRoot = lockfileCommandAt(root);
1953
+ if (fromRoot !== void 0) return fromRoot;
1954
+ }
1914
1955
  return "pnpm install";
1915
1956
  }
1916
1957
  function hasDependency(pkg, name) {
@@ -2125,22 +2166,17 @@ function majorFromVersion(version) {
2125
2166
  }
2126
2167
  function detectTailwind(baseDirInput, pkg) {
2127
2168
  const baseDir = resolve(baseDirInput);
2128
- const installedFile = join(baseDir, "node_modules", "tailwindcss", "package.json");
2129
2169
  const declared = pkg ? hasDependency2(pkg, "tailwindcss") : void 0;
2130
2170
  const bounds = declared !== void 0 ? tailwindMajorBounds(declared) : void 0;
2131
2171
  const declaredMajor = bounds?.lower;
2132
- try {
2133
- const installed = JSON.parse(readFileSync2(installedFile, "utf8"));
2134
- const version = isPlainObject2(installed) ? installed.version : void 0;
2135
- const major = majorFromVersion(version);
2136
- if (major !== void 0) {
2137
- const found = { major, source: "installed", value: String(version) };
2138
- if (declared !== void 0 && bounds !== void 0 && !majorWithin(major, bounds)) {
2139
- found.mismatch = { declared, declaredMajor: bounds.lower };
2140
- }
2141
- return found;
2172
+ const version = isPnpProject(baseDir) ? void 0 : findInstalledPackage(baseDir, "tailwindcss")?.version;
2173
+ const major = majorFromVersion(version);
2174
+ if (version !== void 0 && major !== void 0) {
2175
+ const found = { major, source: "installed", value: version };
2176
+ if (declared !== void 0 && bounds !== void 0 && !majorWithin(major, bounds)) {
2177
+ found.mismatch = { declared, declaredMajor: bounds.lower };
2142
2178
  }
2143
- } catch {
2179
+ return found;
2144
2180
  }
2145
2181
  if (declared !== void 0) {
2146
2182
  if (declaredMajor !== void 0) return { major: declaredMajor, source: "declared", value: declared };
@@ -2283,19 +2319,9 @@ function rangeLowerAtLeast(range, min) {
2283
2319
  if (!lower || !target) return void 0;
2284
2320
  return compareLower(lower, target) >= 0;
2285
2321
  }
2286
- function exactVersion(version) {
2287
- if (typeof version !== "string") return void 0;
2288
- const v = version.trim();
2289
- if (!/^v?\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(v) || /^v?0\.0\.0-/.test(v)) return void 0;
2290
- return v;
2291
- }
2292
- function installedVersion(baseDir, name) {
2293
- try {
2294
- const parsed = JSON.parse(readFileSync2(join(baseDir, "node_modules", ...name.split("/"), "package.json"), "utf8"));
2295
- return isPlainObject2(parsed) ? exactVersion(parsed.version) : void 0;
2296
- } catch {
2297
- return void 0;
2298
- }
2322
+ function installedAxisVersion(baseDir, name) {
2323
+ if (isPnpProject(baseDir)) return void 0;
2324
+ return findInstalledPackage(baseDir, name)?.version;
2299
2325
  }
2300
2326
  function axisPackagesBelowTailwind4(pkg, baseDirInput) {
2301
2327
  if (!pkg) return [];
@@ -2305,7 +2331,7 @@ function axisPackagesBelowTailwind4(pkg, baseDirInput) {
2305
2331
  for (const name of TAILWIND4_AXIS_PACKAGES) {
2306
2332
  const range = hasDependency2(pkg, name);
2307
2333
  if (range === void 0 || range.trim().startsWith("workspace:")) continue;
2308
- const installed = baseDir !== void 0 ? installedVersion(baseDir, name) : void 0;
2334
+ const installed = baseDir !== void 0 ? installedAxisVersion(baseDir, name) : void 0;
2309
2335
  const installedLower = installed !== void 0 ? lowerOf(installed) : void 0;
2310
2336
  if (installed !== void 0 && installedLower !== void 0) {
2311
2337
  if (compareLower(installedLower, min) < 0) out.push({ name, range, status: "below", source: "installed", installed });
@@ -2601,6 +2627,92 @@ function contentGlobToRegExp(glob) {
2601
2627
  }
2602
2628
  return new RegExp(`^${re}$`);
2603
2629
  }
2630
+ function leadingParentSegments(p) {
2631
+ const s = p.trim().replace(/\\/g, "/").replace(/^\.\//, "");
2632
+ return (/^(?:\.\.\/)*/.exec(s)?.[0].length ?? 0) / 3;
2633
+ }
2634
+ function isAbsoluteGlob(glob) {
2635
+ const t = glob.trim();
2636
+ return t.startsWith("/") || /^[A-Za-z]:[\\/]/.test(t);
2637
+ }
2638
+ var MAX_BRACE_EXPANSION = 256;
2639
+ function expandBraces(glob) {
2640
+ const open = glob.indexOf("{");
2641
+ if (open === -1) return [glob];
2642
+ let depth = 0;
2643
+ let close = -1;
2644
+ const commas = [];
2645
+ for (let i = open; i < glob.length; i++) {
2646
+ const c = glob[i];
2647
+ if (c === "{") depth++;
2648
+ else if (c === "}") {
2649
+ depth--;
2650
+ if (depth === 0) {
2651
+ close = i;
2652
+ break;
2653
+ }
2654
+ } else if (c === "," && depth === 1) commas.push(i);
2655
+ }
2656
+ const head = glob.slice(0, open + 1);
2657
+ if (close === -1 || commas.length === 0) {
2658
+ return expandBraces(glob.slice(open + 1))?.map((r) => head + r);
2659
+ }
2660
+ const parts = [];
2661
+ let from = open + 1;
2662
+ for (const c of [...commas, close]) {
2663
+ parts.push(glob.slice(from, c));
2664
+ from = c + 1;
2665
+ }
2666
+ const prefix = glob.slice(0, open);
2667
+ const suffix = glob.slice(close + 1);
2668
+ const out = [];
2669
+ for (const part of parts) {
2670
+ const expanded = expandBraces(prefix + part + suffix);
2671
+ if (expanded === void 0) return void 0;
2672
+ out.push(...expanded);
2673
+ if (out.length > MAX_BRACE_EXPANSION) return void 0;
2674
+ }
2675
+ return out;
2676
+ }
2677
+ function realpathPosix(p) {
2678
+ let cur = p;
2679
+ const tail = [];
2680
+ for (let i = 0; i < 64; i++) {
2681
+ try {
2682
+ return [realpathSync(cur), ...tail].join(sep).split(sep).join("/");
2683
+ } catch {
2684
+ const parent = dirname(cur);
2685
+ if (parent === cur) return void 0;
2686
+ tail.unshift(basename(cur));
2687
+ cur = parent;
2688
+ }
2689
+ }
2690
+ return void 0;
2691
+ }
2692
+ function contentGlobBranchCovers(glob, sample, baseDir) {
2693
+ if (isAbsoluteGlob(glob)) {
2694
+ const re = contentGlobToRegExp(glob.trim().replace(/\\/g, "/"));
2695
+ const absSample = resolve(baseDir, sample);
2696
+ if (re.test(absSample.split(sep).join("/"))) return true;
2697
+ const real = realpathPosix(absSample);
2698
+ return real !== void 0 && re.test(real);
2699
+ }
2700
+ return leadingParentSegments(glob) === leadingParentSegments(sample) && contentGlobToRegExp(glob).test(sample);
2701
+ }
2702
+ function contentGlobCovers(glob, sample, baseDir) {
2703
+ const branches = expandBraces(glob.trim()) ?? [glob];
2704
+ return branches.some((b) => contentGlobBranchCovers(b, sample, baseDir));
2705
+ }
2706
+ 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";
2707
+ function packageContentTarget(baseDir, name) {
2708
+ const found = isPnpProject(baseDir) ? void 0 : findInstalledPackage(baseDir, name);
2709
+ const rel3 = found === void 0 ? void 0 : relative(baseDir, found.dir);
2710
+ const relDir = rel3 === void 0 || rel3 === "" || isAbsolute(rel3) ? `node_modules/${name}` : rel3.split(sep).join("/");
2711
+ return {
2712
+ sample: `${relDir}/dist/index.js`,
2713
+ suggest: `${relDir.startsWith("../") ? "" : "./"}${relDir}/dist/**/*.{js,mjs}`
2714
+ };
2715
+ }
2604
2716
  function balancedSlice(src, start) {
2605
2717
  const open = src[start];
2606
2718
  const close = open === "[" ? "]" : "}";
@@ -2662,17 +2774,17 @@ function inspectTailwindContent(baseDirInput) {
2662
2774
  }
2663
2775
  for (const name of TAILWIND4_COMPONENT_PACKAGES) {
2664
2776
  if (pkg !== void 0 && hasDependency2(pkg, name) !== void 0) {
2665
- targets.push({ label: name, sample: `node_modules/${name}/dist/index.js`, suggest: `./node_modules/${name}/dist/**/*.{js,mjs}` });
2777
+ targets.push({ label: name, ...packageContentTarget(baseDir, name) });
2666
2778
  }
2667
2779
  }
2668
2780
  if (targets.length === 0) return null;
2669
2781
  const read = readTailwindContent(readFileSync2(join(baseDir, configFile), "utf8"));
2670
- const patterns = (read?.globs ?? []).filter((g) => !g.trim().startsWith("!")).map(contentGlobToRegExp);
2782
+ const globs = (read?.globs ?? []).filter((g) => !g.trim().startsWith("!"));
2671
2783
  return {
2672
2784
  configFile,
2673
2785
  unreadable: read === void 0,
2674
2786
  dynamic: read?.dynamic ?? false,
2675
- targets: targets.map((t) => ({ ...t, covered: patterns.some((re) => re.test(t.sample)) }))
2787
+ targets: targets.map((t) => ({ ...t, covered: globs.some((g) => contentGlobCovers(g, t.sample, baseDir)) }))
2676
2788
  };
2677
2789
  }
2678
2790
  function checkTailwindContent(baseDir) {
@@ -2687,17 +2799,18 @@ function checkTailwindContent(baseDir) {
2687
2799
  };
2688
2800
  }
2689
2801
  const list = missing.map((t) => `${t.label} \u2192 "${t.suggest}"`).join(" \xB7 ");
2802
+ const note = missing.some((t) => t.suggest.startsWith("../")) ? ` (${CONTENT_PARENT_PATH_NOTE})` : "";
2690
2803
  if (r.unreadable || r.dynamic) {
2691
2804
  return {
2692
2805
  name: "tailwind.content",
2693
2806
  severity: "warn",
2694
- message: `${r.configFile}\uC758 content \uB97C \uC815\uC801\uC73C\uB85C \uB2E4 \uC77D\uC9C0 \uBABB\uD574 \uD655\uC778\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4: ${list}`
2807
+ message: `${r.configFile}\uC758 content \uB97C \uC815\uC801\uC73C\uB85C \uB2E4 \uC77D\uC9C0 \uBABB\uD574 \uD655\uC778\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4: ${list}${note}`
2695
2808
  };
2696
2809
  }
2697
2810
  return {
2698
2811
  name: "tailwind.content",
2699
2812
  severity: "fail",
2700
- message: `${r.configFile}\uC758 content \uAC00 \uCEF4\uD3EC\uB10C\uD2B8 \uACBD\uB85C\uB97C \uB36E\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4(\uD074\uB798\uC2A4\uAC00 \uC0DD\uC131\uB418\uC9C0 \uC54A\uC74C): ${list}`
2813
+ message: `${r.configFile}\uC758 content \uAC00 \uCEF4\uD3EC\uB10C\uD2B8 \uACBD\uB85C\uB97C \uB36E\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4(\uD074\uB798\uC2A4\uAC00 \uC0DD\uC131\uB418\uC9C0 \uC54A\uC74C): ${list}${note}`
2701
2814
  };
2702
2815
  }
2703
2816
  var ANIMATION_PLUGIN_TAILWIND3 = "tailwindcss-animate";
@@ -2892,12 +3005,23 @@ async function applyTemplate(slug, options) {
2892
3005
  for (const c of conflicts) {
2893
3006
  console.log(chalk5.gray(` - ${c}`));
2894
3007
  }
2895
- const { proceed } = await prompts({
2896
- type: "confirm",
2897
- name: "proceed",
2898
- message: "\uCDA9\uB3CC \uD30C\uC77C\uC744 \uB36E\uC5B4\uC4F0\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
2899
- initial: false
2900
- });
3008
+ const interactive = options.interactive ?? isInteractive();
3009
+ if (!interactive) {
3010
+ failNonInteractive("\uCDA9\uB3CC \uD30C\uC77C\uC744 \uB36E\uC5B4\uC4F8\uC9C0 \uBB3C\uC744 \uC218 \uC5C6\uC5B4 \uC544\uBB34\uAC83\uB3C4 \uC4F0\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.", [
3011
+ `\uB36E\uC5B4\uC4F0\uB824\uBA74: axis template apply ${slug} -y`,
3012
+ `\uBBF8\uB9AC\uBCF4\uAE30: axis template apply ${slug} --dry-run`
3013
+ ]);
3014
+ return;
3015
+ }
3016
+ const { proceed } = await ask(
3017
+ {
3018
+ type: "confirm",
3019
+ name: "proceed",
3020
+ message: "\uCDA9\uB3CC \uD30C\uC77C\uC744 \uB36E\uC5B4\uC4F0\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
3021
+ initial: false
3022
+ },
3023
+ interactive
3024
+ );
2901
3025
  if (!proceed) {
2902
3026
  console.log(chalk5.gray("\uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4."));
2903
3027
  return;
@@ -2949,20 +3073,31 @@ async function applyTemplate(slug, options) {
2949
3073
  '${detail.name}' \uD15C\uD50C\uB9BF \uC801\uC6A9 \uC644\uB8CC!`));
2950
3074
  }
2951
3075
  async function initTemplate(slug, options) {
3076
+ const interactive = options.interactive ?? isInteractive();
2952
3077
  let selectedSlug = slug;
2953
3078
  if (!selectedSlug) {
3079
+ if (!interactive) {
3080
+ failNonInteractive("\uD15C\uD50C\uB9BF\uC744 \uACE0\uB97C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uD15C\uD50C\uB9BF \uC774\uB984\uC744 \uC8FC\uC138\uC694.", [
3081
+ "\uC0AC\uC6A9\uBC95: axis template init <name> [--dir <path>] [-y]",
3082
+ "\uBAA9\uB85D: axis template list"
3083
+ ]);
3084
+ return;
3085
+ }
2954
3086
  const { fetchTemplateIndex: fetchTemplateIndex2 } = await import("./registry-client-6WSGKPF2.js");
2955
3087
  const index = await fetchTemplateIndex2();
2956
- const { choice } = await prompts({
2957
- type: "select",
2958
- name: "choice",
2959
- message: "\uC0AC\uC6A9\uD560 \uD15C\uD50C\uB9BF\uC744 \uC120\uD0DD\uD558\uC138\uC694:",
2960
- choices: index.templates.map((t) => ({
2961
- title: `${t.name} (${t.category})`,
2962
- description: t.description,
2963
- value: t.slug
2964
- }))
2965
- });
3088
+ const { choice } = await ask(
3089
+ {
3090
+ type: "select",
3091
+ name: "choice",
3092
+ message: "\uC0AC\uC6A9\uD560 \uD15C\uD50C\uB9BF\uC744 \uC120\uD0DD\uD558\uC138\uC694:",
3093
+ choices: index.templates.map((t) => ({
3094
+ title: `${t.name} (${t.category})`,
3095
+ description: t.description,
3096
+ value: t.slug
3097
+ }))
3098
+ },
3099
+ interactive
3100
+ );
2966
3101
  if (!choice) {
2967
3102
  console.log(chalk5.gray("\uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4."));
2968
3103
  return;
@@ -2970,13 +3105,22 @@ async function initTemplate(slug, options) {
2970
3105
  selectedSlug = choice;
2971
3106
  }
2972
3107
  let targetDir = options.dir;
3108
+ if (!targetDir && (!interactive || options.yes)) {
3109
+ targetDir = selectedSlug;
3110
+ const msg = `\uD504\uB85C\uC81D\uD2B8 \uB514\uB809\uD1A0\uB9AC\uB97C \uAE30\uBCF8\uAC12 '${targetDir}' \uB85C \uC815\uD588\uC2B5\uB2C8\uB2E4(\uBC14\uAFB8\uB824\uBA74 --dir <path>).`;
3111
+ if (interactive) console.log(chalk5.gray(`-y \uB77C ${msg}`));
3112
+ else noteDefault(msg);
3113
+ }
2973
3114
  if (!targetDir) {
2974
- const { dir } = await prompts({
2975
- type: "text",
2976
- name: "dir",
2977
- message: "\uD504\uB85C\uC81D\uD2B8 \uB514\uB809\uD1A0\uB9AC:",
2978
- initial: selectedSlug
2979
- });
3115
+ const { dir } = await ask(
3116
+ {
3117
+ type: "text",
3118
+ name: "dir",
3119
+ message: "\uD504\uB85C\uC81D\uD2B8 \uB514\uB809\uD1A0\uB9AC:",
3120
+ initial: selectedSlug
3121
+ },
3122
+ interactive
3123
+ );
2980
3124
  if (!dir) {
2981
3125
  console.log(chalk5.gray("\uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4."));
2982
3126
  return;
@@ -2987,13 +3131,26 @@ async function initTemplate(slug, options) {
2987
3131
  if (await fs8.pathExists(resolved)) {
2988
3132
  const entries = await fs8.readdir(resolved);
2989
3133
  if (entries.length > 0) {
2990
- const { proceed } = await prompts({
2991
- type: "confirm",
2992
- name: "proceed",
2993
- message: `'${targetDir}'\uC5D0 \uC774\uBBF8 \uD30C\uC77C\uC774 \uC788\uC2B5\uB2C8\uB2E4. \uACC4\uC18D\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?`,
2994
- initial: false
2995
- });
2996
- if (!proceed) return;
3134
+ if (options.yes) {
3135
+ console.log(chalk5.yellow(`! '${targetDir}'\uC5D0 \uC774\uBBF8 \uD30C\uC77C\uC774 \uC788\uC9C0\uB9CC -y \uB77C \uC9C4\uD589\uD569\uB2C8\uB2E4(\uD15C\uD50C\uB9BF\uACFC \uAC19\uC740 \uACBD\uB85C\uC758 \uD30C\uC77C\uC740 \uB36E\uC5B4\uC500).`));
3136
+ } else if (!interactive) {
3137
+ failNonInteractive(`'${targetDir}'\uC5D0 \uC774\uBBF8 \uD30C\uC77C\uC774 \uC788\uC5B4 \uACC4\uC18D\uD560\uC9C0 \uBB3C\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC544\uBB34\uAC83\uB3C4 \uC4F0\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.`, [
3138
+ `\uBE48 \uB514\uB809\uD130\uB9AC\uB97C \uC8FC\uC138\uC694: axis template init ${selectedSlug} --dir <path>`,
3139
+ `\uADF8\uB300\uB85C \uC9C4\uD589\uD558\uB824\uBA74(\uAC19\uC740 \uACBD\uB85C\uC758 \uD30C\uC77C\uC740 \uB36E\uC5B4\uC500): axis template init ${selectedSlug} --dir ${targetDir} -y`
3140
+ ]);
3141
+ return;
3142
+ } else {
3143
+ const { proceed } = await ask(
3144
+ {
3145
+ type: "confirm",
3146
+ name: "proceed",
3147
+ message: `'${targetDir}'\uC5D0 \uC774\uBBF8 \uD30C\uC77C\uC774 \uC788\uC2B5\uB2C8\uB2E4. \uACC4\uC18D\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?`,
3148
+ initial: false
3149
+ },
3150
+ interactive
3151
+ );
3152
+ if (!proceed) return;
3153
+ }
2997
3154
  }
2998
3155
  } else {
2999
3156
  await fs8.ensureDir(resolved);
@@ -3130,7 +3287,7 @@ import chalk7 from "chalk";
3130
3287
  // src/theme/brand-core.ts
3131
3288
  import { existsSync as existsSync3, readFileSync as readFileSync3, statSync as statSync2 } from "fs";
3132
3289
  import { createRequire } from "module";
3133
- import { dirname, join as join2, relative, resolve as resolve2, sep } from "path";
3290
+ import { dirname as dirname2, join as join2, relative as relative2, resolve as resolve2, sep as sep2 } from "path";
3134
3291
  var TOKENS_PACKAGE = "@axis-ds/tokens";
3135
3292
  var BRAND_NAME_PATTERN = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
3136
3293
  var MIN_BRAND_TOKENS_VERSION = "1.3.0";
@@ -3161,7 +3318,7 @@ function findTokensPackageJson(baseDir) {
3161
3318
  for (; ; ) {
3162
3319
  const candidate = join2(dir, "node_modules", TOKENS_PACKAGE, "package.json");
3163
3320
  if (existsSync3(candidate)) return candidate;
3164
- const parent = dirname(dir);
3321
+ const parent = dirname2(dir);
3165
3322
  if (parent === dir) return void 0;
3166
3323
  dir = parent;
3167
3324
  }
@@ -3245,7 +3402,7 @@ function inspectBrandImport(baseDir) {
3245
3402
  if (!location.path) {
3246
3403
  return { ...base, state: "css-missing", imports: [] };
3247
3404
  }
3248
- const cssFile = relative(root, location.path).split(sep).join("/");
3405
+ const cssFile = relative2(root, location.path).split(sep2).join("/");
3249
3406
  const imports = [...new Set(findBrandImports(readFileSync3(location.path, "utf8")))];
3250
3407
  let state;
3251
3408
  if (imports.includes(brand)) state = "match";
@@ -3361,7 +3518,7 @@ function findBrandActivation(baseDir) {
3361
3518
  const providerTexts = [];
3362
3519
  for (const m of text.matchAll(/from\s+["']\.\/(providers?)["']/g)) {
3363
3520
  for (const ext of [".tsx", ".jsx", ".ts", ".js"]) {
3364
- const rel3 = `${dirname(candidate)}/${m[1]}${ext}`;
3521
+ const rel3 = `${dirname2(candidate)}/${m[1]}${ext}`;
3365
3522
  const p = join2(root, rel3);
3366
3523
  if (isFile2(p)) providerTexts.push({ rel: rel3, text: stripJsLikeComments(readFileSync3(p, "utf8")) });
3367
3524
  }
@@ -3444,6 +3601,16 @@ function declaredAxisPackages(pkg) {
3444
3601
  }
3445
3602
  return out;
3446
3603
  }
3604
+ function workspaceRootPackage(baseDir) {
3605
+ const root = findWorkspaceRoot(baseDir);
3606
+ if (root === void 0 || root === baseDir) return void 0;
3607
+ try {
3608
+ const parsed = JSON.parse(readFileSync4(join3(root, "package.json"), "utf8"));
3609
+ return isPlainObject(parsed) ? parsed : void 0;
3610
+ } catch {
3611
+ return void 0;
3612
+ }
3613
+ }
3447
3614
  function checkDependencies(baseDirInput) {
3448
3615
  const baseDir = resolve3(baseDirInput);
3449
3616
  let pkg;
@@ -3457,13 +3624,17 @@ function checkDependencies(baseDirInput) {
3457
3624
  }
3458
3625
  return { name: "dependencies", severity: "fail", message: "package.json \uC744 \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4(JSON \uD655\uC778)" };
3459
3626
  }
3460
- const missingRequired = REQUIRED_DEPENDENCIES.filter((d) => !hasDependency(pkg, d));
3627
+ const rootPkg = workspaceRootPackage(baseDir);
3628
+ const fromRoot = (d) => !hasDependency(pkg, d) && rootPkg !== void 0 && Boolean(hasDependency(rootPkg, d));
3629
+ const missingRequired = REQUIRED_DEPENDENCIES.filter((d) => !hasDependency(pkg, d) && !fromRoot(d));
3461
3630
  if (missingRequired.length > 0) {
3462
3631
  return { name: "dependencies", severity: "fail", message: `\uD544\uC218 \uC758\uC874\uC131 \uB204\uB77D: ${missingRequired.join(", ")}` };
3463
3632
  }
3464
- const missingRecommended = RECOMMENDED_DEPENDENCIES.filter((d) => !hasDependency(pkg, d));
3633
+ const missingRecommended = RECOMMENDED_DEPENDENCIES.filter((d) => !hasDependency(pkg, d) && !fromRoot(d));
3634
+ const rootDeclared = [...REQUIRED_DEPENDENCIES, ...RECOMMENDED_DEPENDENCIES].filter((d) => fromRoot(d));
3635
+ const rootNote = rootDeclared.length > 0 ? `\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8\uC5D0 \uC120\uC5B8: ${rootDeclared.join(", ")}` : void 0;
3465
3636
  const targets = [
3466
- ...[...REQUIRED_DEPENDENCIES, ...RECOMMENDED_DEPENDENCIES].filter((d) => hasDependency(pkg, d) !== void 0),
3637
+ ...[...REQUIRED_DEPENDENCIES, ...RECOMMENDED_DEPENDENCIES].filter((d) => hasDependency(pkg, d) !== void 0 || fromRoot(d)),
3467
3638
  ...declaredAxisPackages(pkg)
3468
3639
  ];
3469
3640
  const pnp = isPnpProject(baseDir);
@@ -3473,11 +3644,14 @@ function checkDependencies(baseDirInput) {
3473
3644
  if (notInstalled.length > 0) {
3474
3645
  problems.push(`\uC120\uC5B8\uB410\uC9C0\uB9CC \uC124\uCE58\uB418\uC9C0 \uC54A\uC740 \uC758\uC874\uC131: ${notInstalled.join(", ")} \u2192 ${detectInstallCommand(baseDir)}`);
3475
3646
  }
3476
- if (problems.length > 0) return { name: "dependencies", severity: "warn", message: problems.join(" / ") };
3647
+ if (problems.length > 0) {
3648
+ return { name: "dependencies", severity: "warn", message: problems.join(" / ") + (rootNote ? ` (${rootNote})` : "") };
3649
+ }
3650
+ const note = rootNote ? `, ${rootNote}` : "";
3477
3651
  return {
3478
3652
  name: "dependencies",
3479
3653
  severity: "pass",
3480
- message: pnp ? `\uD544\uC218 \uBC0F \uAD8C\uC7A5 \uC758\uC874\uC131\uC774 \uC120\uC5B8\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4 (Plug'n'Play \uB77C \uC124\uCE58\uBCF8\uC740 \uD655\uC778\uD558\uC9C0 \uC54A\uC74C, ${targets.length}\uAC1C)` : `\uD544\uC218 \uBC0F \uAD8C\uC7A5 \uC758\uC874\uC131\uC774 \uC120\uC5B8 \xB7 \uC124\uCE58\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4 (${targets.length}\uAC1C \uD655\uC778)`
3654
+ message: pnp ? `\uD544\uC218 \uBC0F \uAD8C\uC7A5 \uC758\uC874\uC131\uC774 \uC120\uC5B8\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4 (Plug'n'Play \uB77C \uC124\uCE58\uBCF8\uC740 \uD655\uC778\uD558\uC9C0 \uC54A\uC74C, ${targets.length}\uAC1C${note})` : `\uD544\uC218 \uBC0F \uAD8C\uC7A5 \uC758\uC874\uC131\uC774 \uC120\uC5B8 \xB7 \uC124\uCE58\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4 (${targets.length}\uAC1C \uD655\uC778${note})`
3481
3655
  };
3482
3656
  }
3483
3657
 
@@ -3632,7 +3806,8 @@ function brandActivationResult(check) {
3632
3806
  }
3633
3807
 
3634
3808
  // src/template/index.ts
3635
- function registerTemplateCommand(program2) {
3809
+ function registerTemplateCommand(program2, deps = {}) {
3810
+ const interactive = () => (deps.isInteractive ?? isInteractive)();
3636
3811
  const tplCmd = program2.command("template").alias("tpl").description("\uD504\uB85C\uC81D\uD2B8 \uD15C\uD50C\uB9BF \uAD00\uB9AC");
3637
3812
  tplCmd.command("list").alias("ls").description("\uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uD15C\uD50C\uB9BF \uBAA9\uB85D").option("--category <type>", "\uCE74\uD14C\uACE0\uB9AC \uD544\uD130").action(async (options) => {
3638
3813
  const spinner = ora5("\uD15C\uD50C\uB9BF \uBAA9\uB85D \uAC00\uC838\uC624\uB294 \uC911...").start();
@@ -3698,12 +3873,12 @@ function registerTemplateCommand(program2) {
3698
3873
  for (const f of detail.files) {
3699
3874
  console.log(` ${chalk8.cyan(f.path)} ${chalk8.gray(`(${f.type})`)}`);
3700
3875
  }
3701
- const deps = Object.keys(detail.dependencies || {});
3876
+ const deps2 = Object.keys(detail.dependencies || {});
3702
3877
  const devDeps = Object.keys(detail.devDependencies || {});
3703
- if (deps.length > 0 || devDeps.length > 0) {
3878
+ if (deps2.length > 0 || devDeps.length > 0) {
3704
3879
  console.log(chalk8.bold("\n \uC758\uC874\uC131:"));
3705
- if (deps.length > 0) {
3706
- console.log(` dependencies: ${deps.join(", ")}`);
3880
+ if (deps2.length > 0) {
3881
+ console.log(` dependencies: ${deps2.join(", ")}`);
3707
3882
  }
3708
3883
  if (devDeps.length > 0) {
3709
3884
  console.log(` devDependencies: ${devDeps.join(", ")}`);
@@ -3718,10 +3893,10 @@ function registerTemplateCommand(program2) {
3718
3893
  }
3719
3894
  });
3720
3895
  tplCmd.command("apply <name>").description("\uD15C\uD50C\uB9BF\uC744 \uD604\uC7AC \uD504\uB85C\uC81D\uD2B8\uC5D0 \uC801\uC6A9").option("-y, --yes", "\uD655\uC778 \uC5C6\uC774 \uB36E\uC5B4\uC4F0\uAE30").option("--dry-run", "\uC2E4\uC81C \uD30C\uC77C \uC0DD\uC131 \uC5C6\uC774 \uBBF8\uB9AC\uBCF4\uAE30").option("--skip-deps", "\uC758\uC874\uC131 \uC548\uB0B4 \uC0DD\uB7B5").option("-d, --dir <path>", "\uB300\uC0C1 \uB514\uB809\uD1A0\uB9AC").action(async (name, options) => {
3721
- await applyTemplate(name, options);
3896
+ await applyTemplate(name, { ...options, interactive: interactive() });
3722
3897
  });
3723
- tplCmd.command("init [name]").description("\uC0C8 \uD504\uB85C\uC81D\uD2B8\uB97C \uD15C\uD50C\uB9BF\uC73C\uB85C \uCD08\uAE30\uD654").option("-d, --dir <path>", "\uD504\uB85C\uC81D\uD2B8 \uB514\uB809\uD1A0\uB9AC").action(async (name, options) => {
3724
- await initTemplate(name, options);
3898
+ tplCmd.command("init [name]").description("\uC0C8 \uD504\uB85C\uC81D\uD2B8\uB97C \uD15C\uD50C\uB9BF\uC73C\uB85C \uCD08\uAE30\uD654").option("-d, --dir <path>", "\uD504\uB85C\uC81D\uD2B8 \uB514\uB809\uD1A0\uB9AC(\uC5C6\uC73C\uBA74 \uD15C\uD50C\uB9BF \uC774\uB984)").option("-y, --yes", "\uC9C8\uBB38\uC744 \uAE30\uBCF8\uAC12\uC73C\uB85C \uB2F5\uD558\uACE0, \uBE44\uC5B4 \uC788\uC9C0 \uC54A\uC740 \uB514\uB809\uD1A0\uB9AC\uB3C4 \uC9C4\uD589(\uAC19\uC740 \uACBD\uB85C\uC758 \uD30C\uC77C\uC740 \uB36E\uC5B4\uC500)").action(async (name, options) => {
3899
+ await initTemplate(name, { ...options, interactive: interactive() });
3725
3900
  });
3726
3901
  tplCmd.command("diff <name>").description("\uB85C\uCEEC \uD504\uB85C\uC81D\uD2B8\uC640 \uD15C\uD50C\uB9BF\uC758 \uCC28\uC774 \uBE44\uAD50").option("--verbose", "\uB85C\uCEEC \uC804\uC6A9 \uD30C\uC77C\uB3C4 \uD45C\uC2DC").option("-d, --dir <path>", "\uB300\uC0C1 \uB514\uB809\uD1A0\uB9AC").action(async (name, options) => {
3727
3902
  await diffTemplate(name, options);
@@ -3739,7 +3914,7 @@ import chalk9 from "chalk";
3739
3914
  import path13 from "path";
3740
3915
 
3741
3916
  // src/theme/theme-add.ts
3742
- import { accessSync, constants, existsSync as existsSync4, readFileSync as readFileSync5, realpathSync, unlinkSync, writeFileSync } from "fs";
3917
+ import { accessSync, constants, existsSync as existsSync4, readFileSync as readFileSync5, realpathSync as realpathSync2, unlinkSync, writeFileSync } from "fs";
3743
3918
  import path12 from "path";
3744
3919
  var TOKENS_DEPENDENCY_RANGE = `^${TOKENS_DEPENDENCY_MIN_VERSION}`;
3745
3920
  var TAILWIND3_BASE_IMPORTS = [`${TOKENS_PACKAGE}/css`, `${TOKENS_PACKAGE}/css/shadcn`];
@@ -3836,8 +4011,8 @@ function compareVersions(a, b) {
3836
4011
  return 0;
3837
4012
  }
3838
4013
  function realPathInside(baseDir, file) {
3839
- const realBase = realpathSync(baseDir);
3840
- const realPath = realpathSync(file);
4014
+ const realBase = realpathSync2(baseDir);
4015
+ const realPath = realpathSync2(file);
3841
4016
  const relPath = path12.relative(realBase, realPath);
3842
4017
  const inside = relPath !== "" && !relPath.startsWith("..") && !path12.isAbsolute(relPath);
3843
4018
  return { inside, realPath };
@@ -4349,6 +4524,7 @@ function renderThemeAdd(plan, written) {
4349
4524
  )
4350
4525
  );
4351
4526
  }
4527
+ if (tw.contentMissing.some((t) => t.suggest.startsWith("../"))) lines.push(chalk9.gray(` ${CONTENT_PARENT_PATH_NOTE}`));
4352
4528
  }
4353
4529
  const act = plan.activation;
4354
4530
  lines.push("", chalk9.bold(`\uBE0C\uB79C\uB4DC \uCF1C\uAE30 (ThemeProvider \uB294 ${THEME_PACKAGE} ${THEME_MIN_VERSION} \uC774\uC0C1)`));
@@ -4428,7 +4604,6 @@ function registerThemeCommand(program2) {
4428
4604
 
4429
4605
  // src/init/index.ts
4430
4606
  import chalk11 from "chalk";
4431
- import prompts2 from "prompts";
4432
4607
 
4433
4608
  // ../axis-registry-core/src/registry/schema.ts
4434
4609
  import { createHash } from "crypto";
@@ -4731,13 +4906,13 @@ async function fetchRegistryJson(url, options = {}) {
4731
4906
  }
4732
4907
 
4733
4908
  // src/init/init.ts
4734
- import { accessSync as accessSync2, constants as constants2, existsSync as existsSync6, lstatSync as lstatSync3, readFileSync as readFileSync6, realpathSync as realpathSync3, statSync as statSync4, writeFileSync as writeFileSync2 } from "fs";
4909
+ import { accessSync as accessSync2, constants as constants2, existsSync as existsSync6, lstatSync as lstatSync3, readFileSync as readFileSync6, realpathSync as realpathSync4, statSync as statSync4, writeFileSync as writeFileSync2 } from "fs";
4735
4910
  import path15 from "path";
4736
4911
  import chalk10 from "chalk";
4737
4912
  import fs12 from "fs-extra";
4738
4913
 
4739
4914
  // ../axis-registry-core/src/add/safe-write.ts
4740
- import { constants as fsConstants, existsSync as existsSync5, lstatSync as lstatSync2, realpathSync as realpathSync2, statSync as statSync3 } from "fs";
4915
+ import { constants as fsConstants, existsSync as existsSync5, lstatSync as lstatSync2, realpathSync as realpathSync3, statSync as statSync3 } from "fs";
4741
4916
  import path14 from "path";
4742
4917
  import fs11 from "fs-extra";
4743
4918
  var NEW_FILE_FLAGS = fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL | (fsConstants.O_NOFOLLOW ?? 0);
@@ -4764,12 +4939,12 @@ function nearestExisting(p) {
4764
4939
  return cur;
4765
4940
  }
4766
4941
  function escapeReason(root, target, options = {}) {
4767
- const realRoot = realpathSync2(root);
4942
+ const realRoot = realpathSync3(root);
4768
4943
  if (!isSameOrInside(path14.resolve(root), path14.resolve(target)) || path14.resolve(target) === path14.resolve(root)) {
4769
4944
  return `\uACBD\uB85C\uAC00 \uD504\uB85C\uC81D\uD2B8 \uB514\uB809\uD130\uB9AC \uBC16\uC785\uB2C8\uB2E4(${target})`;
4770
4945
  }
4771
4946
  const anchor = nearestExisting(path14.dirname(target));
4772
- const realAnchor = realpathSync2(anchor);
4947
+ const realAnchor = realpathSync3(anchor);
4773
4948
  if (!isSameOrInside(realRoot, realAnchor)) {
4774
4949
  return `\uC0C1\uC704 \uB514\uB809\uD130\uB9AC\uC758 \uC2E4\uC81C \uC704\uCE58(${realAnchor})\uAC00 \uD504\uB85C\uC81D\uD2B8 \uB514\uB809\uD130\uB9AC \uBC16\uC785\uB2C8\uB2E4`;
4775
4950
  }
@@ -4784,7 +4959,7 @@ function escapeReason(root, target, options = {}) {
4784
4959
  const shown = path14.relative(path14.resolve(root), path14.resolve(target)).split(path14.sep).join("/");
4785
4960
  let realTarget;
4786
4961
  try {
4787
- realTarget = realpathSync2(target);
4962
+ realTarget = realpathSync3(target);
4788
4963
  } catch {
4789
4964
  return `\uAE30\uC874 \uD30C\uC77C ${shown} \uC774 \uAE68\uC9C4 \uC2EC\uBCFC\uB9AD \uB9C1\uD06C\uB77C \uB530\uB77C\uAC08 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4(\uB9C1\uD06C\uB97C \uC9C0\uC6B0\uAC70\uB098 \uACE0\uCE5C \uB4A4 \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694)`;
4790
4965
  }
@@ -4817,8 +4992,8 @@ function isSymlink(p) {
4817
4992
  function outsideLinkTarget(root, target) {
4818
4993
  if (!isSymlink(target)) return null;
4819
4994
  try {
4820
- const realTarget = realpathSync2(target);
4821
- return isSameOrInside(realpathSync2(root), realTarget) ? null : realTarget;
4995
+ const realTarget = realpathSync3(target);
4996
+ return isSameOrInside(realpathSync3(root), realTarget) ? null : realTarget;
4822
4997
  } catch {
4823
4998
  return null;
4824
4999
  }
@@ -4835,10 +5010,10 @@ function hardLinkMessage(shownPath, nlink) {
4835
5010
  return `\uAE30\uC874 \uD30C\uC77C ${shownPath} \uC774 \uB2E4\uB978 \uACBD\uB85C\uC640 \uD558\uB4DC\uB9C1\uD06C\uB85C \uC774\uC5B4\uC9C4 \uD30C\uC77C\uC774\uB77C(\uB9C1\uD06C \uC218 ${nlink}) \uB36E\uC5B4\uC4F0\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uB36E\uC5B4\uC4F0\uBA74 \uC774\uC5B4\uC9C4 \uB2E4\uB978 \uACBD\uB85C\uC758 \uB0B4\uC6A9\uB3C4 \uBC14\uB01D\uB2C8\uB2E4. \uD558\uB4DC\uB9C1\uD06C\uB97C \uB04A\uC740(\uD30C\uC77C\uC744 \uBCF5\uC0AC\uD574 \uBC14\uAFBC) \uB4A4 \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694`;
4836
5011
  }
4837
5012
  function writtenOutside(root, target) {
4838
- const realRoot = realpathSync2(root);
5013
+ const realRoot = realpathSync3(root);
4839
5014
  let realTarget;
4840
5015
  try {
4841
- realTarget = realpathSync2(target);
5016
+ realTarget = realpathSync3(target);
4842
5017
  } catch {
4843
5018
  return target;
4844
5019
  }
@@ -4884,8 +5059,8 @@ async function overwriteFollowingLinks(target, content) {
4884
5059
  }
4885
5060
  }
4886
5061
  function checkParentInside(root, target) {
4887
- const realRoot = realpathSync2(root);
4888
- const realParent = realpathSync2(path14.dirname(target));
5062
+ const realRoot = realpathSync3(root);
5063
+ const realParent = realpathSync3(path14.dirname(target));
4889
5064
  if (!isSameOrInside(realRoot, realParent)) {
4890
5065
  return `\uBD80\uBAA8 \uB514\uB809\uD130\uB9AC\uC758 \uC2E4\uC81C \uC704\uCE58(${realParent})\uAC00 \uD504\uB85C\uC81D\uD2B8 \uB514\uB809\uD130\uB9AC \uBC16\uC785\uB2C8\uB2E4`;
4891
5066
  }
@@ -5237,13 +5412,13 @@ function planUtilsWrite(baseDirInput, utilsPath) {
5237
5412
  if (st.isSymbolicLink()) {
5238
5413
  let real;
5239
5414
  try {
5240
- real = realpathSync3(utilsPath);
5415
+ real = realpathSync4(utilsPath);
5241
5416
  } catch {
5242
5417
  return { action: "reject", reason: `${shown} \uC774 \uAE68\uC9C4 \uC2EC\uBCFC\uB9AD \uB9C1\uD06C\uB77C \uC4F0\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uB9C1\uD06C\uB97C \uC9C0\uC6B0\uAC70\uB098 \uACE0\uCE5C \uB4A4 \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694` };
5243
5418
  }
5244
5419
  let outside = null;
5245
5420
  try {
5246
- outside = isSameOrInside(realpathSync3(baseDir), real) ? null : real;
5421
+ outside = isSameOrInside(realpathSync4(baseDir), real) ? null : real;
5247
5422
  } catch {
5248
5423
  outside = null;
5249
5424
  }
@@ -5286,7 +5461,7 @@ function planUtilsWrite(baseDirInput, utilsPath) {
5286
5461
  }
5287
5462
  return { action: "overwrite" };
5288
5463
  }
5289
- async function runInit(baseDirInput, answers, registryUrl, log = console.log) {
5464
+ async function runInit(baseDirInput, answers, registryUrl, log = console.log, options = {}) {
5290
5465
  const baseDir = path15.resolve(baseDirInput);
5291
5466
  const current = await existingConfig(baseDir);
5292
5467
  const existingRegistry = nonEmptyString(current?.registry);
@@ -5333,6 +5508,12 @@ async function runInit(baseDirInput, answers, registryUrl, log = console.log) {
5333
5508
  const utilsPath = path15.resolve(baseDir, answers.componentsDir, "utils.ts");
5334
5509
  const utilsPlan = planUtilsWrite(baseDir, utilsPath);
5335
5510
  if (utilsPlan.action === "reject") rejects.push(utilsPlan.reason);
5511
+ if (utilsPlan.action === "overwrite" && options.allowOverwrite === false) {
5512
+ const shown = path15.relative(baseDir, utilsPath).split(path15.sep).join("/") || utilsPath;
5513
+ rejects.push(
5514
+ `\uBE44\uB300\uD654\uD615 \uC2E4\uD589\uC774\uB77C \uB0B4\uC6A9\uC774 \uB2E4\uB978 \uAE30\uC874 ${shown} \uB97C \uB36E\uC5B4\uC4F0\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uB36E\uC5B4\uC368\uB3C4 \uB418\uBA74 axis init -y \uB85C \uB2E4\uC2DC \uC2E4\uD589\uD558\uAC70\uB098 \uB300\uD654\uD615 \uD130\uBBF8\uB110\uC5D0\uC11C \uC2E4\uD589\uD558\uC138\uC694`
5515
+ );
5516
+ }
5336
5517
  if (rejects.length > 0) {
5337
5518
  log(chalk10.red("\u2717 \uC4F8 \uC218 \uC5C6\uB294 \uD30C\uC77C\uC774 \uC788\uC5B4 \uC544\uBB34\uAC83\uB3C4 \uC4F0\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4(axis.config.json \xB7 utils.ts \xB7 package.json \uADF8\uB300\uB85C)."));
5338
5519
  for (const r of rejects) log(chalk10.red(` - ${r}`));
@@ -5437,14 +5618,20 @@ ${head}`));
5437
5618
  }
5438
5619
 
5439
5620
  // src/init/index.ts
5440
- function registerInitCommand(program2, registryUrl = DEFAULT_REGISTRY_URL) {
5621
+ function registerInitCommand(program2, registryUrl = DEFAULT_REGISTRY_URL, deps = {}) {
5441
5622
  program2.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) => {
5442
5623
  console.log(chalk11.blue("\n\u{1F3A8} AXIS Design System \uCD08\uAE30\uD654\n"));
5443
5624
  const baseDir = process.cwd();
5444
5625
  const tailwind4 = isTailwind4(planUtilsDependencies(baseDir).tailwind);
5445
5626
  const resolved = await resolveInitAnswers(baseDir, tailwind4);
5446
5627
  for (const note of resolved.notes) console.log(note.startsWith("!") ? chalk11.yellow(note) : chalk11.gray(note));
5447
- const config = options.yes ? { ...resolved.answers } : await prompts2([
5628
+ const interactive = (deps.isInteractive ?? isInteractive)();
5629
+ if (!options.yes && !interactive) {
5630
+ const a = resolved.answers;
5631
+ const values = [`componentsDir=${a.componentsDir}`, ...tailwind4 ? [] : [`tailwindConfig=${a.tailwindConfig}`], `globalCss=${a.globalCss}`];
5632
+ noteDefault(`\uAE30\uBCF8\uAC12\uC73C\uB85C \uC9C4\uD589\uD569\uB2C8\uB2E4(-y \uC640 \uAC19\uC74C): ${values.join(", ")}`);
5633
+ }
5634
+ const config = options.yes || !interactive ? { ...resolved.answers } : await ask([
5448
5635
  {
5449
5636
  type: "text",
5450
5637
  name: "componentsDir",
@@ -5463,14 +5650,13 @@ function registerInitCommand(program2, registryUrl = DEFAULT_REGISTRY_URL) {
5463
5650
  message: "\uAE00\uB85C\uBC8C CSS \uD30C\uC77C:",
5464
5651
  initial: resolved.answers.globalCss
5465
5652
  }
5466
- ]);
5467
- await runInit(baseDir, config, registryUrl);
5653
+ ], interactive);
5654
+ await runInit(baseDir, config, registryUrl, console.log, { allowOverwrite: options.yes === true || interactive });
5468
5655
  });
5469
5656
  }
5470
5657
 
5471
5658
  // src/add/index.ts
5472
5659
  import chalk12 from "chalk";
5473
- import prompts3 from "prompts";
5474
5660
 
5475
5661
  // ../axis-registry-core/src/add/add.ts
5476
5662
  import { existsSync as existsSync8, readFileSync as readFileSync8, statSync as statSync5 } from "fs";
@@ -6535,7 +6721,7 @@ function configRegistryRejectedMessage(url, problem) {
6535
6721
  function createConfirm(interactive) {
6536
6722
  if (!interactive) return void 0;
6537
6723
  return async (message) => {
6538
- const { overwrite } = await prompts3({ type: "confirm", name: "overwrite", message, initial: false });
6724
+ const { overwrite } = await ask({ type: "confirm", name: "overwrite", message, initial: false }, interactive);
6539
6725
  return overwrite === true;
6540
6726
  };
6541
6727
  }
@@ -6543,7 +6729,7 @@ var defaultCreateClient = (cwd) => createClientFor(cwd, (m) => console.warn(chal
6543
6729
  function registerAddCommand(program2, deps = {}) {
6544
6730
  program2.command("add <components...>").description("\uB808\uC9C0\uC2A4\uD2B8\uB9AC\uC758 \uCEF4\uD3EC\uB10C\uD2B8\uB97C \uD504\uB85C\uC81D\uD2B8\uC5D0 \uBCF5\uC0AC(registryDependencies \uD3EC\uD568)").option("--agentic", "\uC774\uB984 \uC55E\uC5D0 agentic- \uB97C \uBD99\uC5EC \uCC3E\uC74C(\uC804\uCCB4 \uC774\uB984\uB3C4 \uD5C8\uC6A9)").option("-y, --yes", "\uBB3B\uC9C0 \uC54A\uACE0 \uC9C4\uD589. \uB0B4\uC6A9\uC774 \uB2E4\uB978 \uAE30\uC874 \uD30C\uC77C\uC740 \uAC74\uB108\uB700").option("--overwrite", "\uB0B4\uC6A9\uC774 \uB2E4\uB978 \uAE30\uC874 \uD30C\uC77C\uC744 \uB36E\uC5B4\uC500").action(async (components, options) => {
6545
6731
  const client = await (deps.createClient ?? defaultCreateClient)(process.cwd());
6546
- const confirm = createConfirm((deps.isInteractive ?? (() => process.stdin.isTTY === true))());
6732
+ const confirm = createConfirm((deps.isInteractive ?? isInteractive)());
6547
6733
  const result = await runAdd(process.cwd(), components, options, {
6548
6734
  client,
6549
6735
  log: (...a) => console.log(...a),
@@ -6786,4 +6972,10 @@ registerSyncCommand(program);
6786
6972
  registerTemplateCommand(program);
6787
6973
  registerCheckCommand(program);
6788
6974
  registerThemeCommand(program);
6789
- program.parse();
6975
+ try {
6976
+ await program.parseAsync();
6977
+ } catch (err) {
6978
+ if (!(err instanceof NonInteractiveError)) throw err;
6979
+ console.error(chalk13.red(err.message));
6980
+ process.exitCode = 1;
6981
+ }
package/dist/monet.d.ts CHANGED
@@ -82,7 +82,10 @@ declare class MonetClient {
82
82
  searchComponents(query: string): Promise<void>;
83
83
  browseCategory(category: string): Promise<void>;
84
84
  private getCategoryExamples;
85
- importFromClipboard(): Promise<void>;
85
+ /** opts.interactive: 질문을 띄울 수 있는지(WI-0075). 없으면 stdin 이 TTY 인지로 정합니다 */
86
+ importFromClipboard(opts?: {
87
+ interactive?: boolean;
88
+ }): Promise<void>;
86
89
  }
87
90
  declare function getMonetUrl(path: string): string;
88
91
  declare function getMonetMcpConfig(): object;
package/dist/monet.js CHANGED
@@ -3,7 +3,8 @@ import {
3
3
  MonetClient,
4
4
  getMonetMcpConfig,
5
5
  getMonetUrl
6
- } from "./chunk-ZVVACWEW.js";
6
+ } from "./chunk-LVMDBOXD.js";
7
+ import "./chunk-HFMMK6R4.js";
7
8
  export {
8
9
  MONET_CATEGORIES,
9
10
  MonetClient,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "axis",
3
3
  "homepage": "https://axis-design-system.pages.dev",
4
- "version": "1.8.0",
4
+ "version": "1.8.1",
5
5
  "items": [
6
6
  {
7
7
  "name": "utils",
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://ui.shadcn.com/schema/registry.json",
3
3
  "name": "axis",
4
4
  "homepage": "https://axis-design-system.pages.dev",
5
- "version": "1.8.0",
5
+ "version": "1.8.1",
6
6
  "items": [
7
7
  {
8
8
  "name": "utils",
package/dist/v0.d.ts CHANGED
@@ -8,19 +8,23 @@
8
8
  * npx @axis-ds/cli v0 import <url> # V0 URL에서 가져오기
9
9
  * npx @axis-ds/cli v0 setup # GitHub 연동 안내
10
10
  */
11
+ /** 질문을 띄울 수 있는지(WI-0075). 없으면 stdin 이 TTY 인지로 정합니다 */
12
+ interface V0PromptOptions {
13
+ interactive?: boolean;
14
+ }
11
15
  declare class V0Client {
12
16
  convertCode(code: string, options?: {
13
17
  preserveImports?: boolean;
14
18
  }): Promise<string>;
15
19
  convertFile(filePath: string, outputPath?: string): Promise<void>;
16
20
  private showConversionSummary;
17
- importFromUrl(): Promise<void>;
21
+ importFromUrl(opts?: V0PromptOptions): Promise<void>;
18
22
  setupGitHubSync(): Promise<void>;
19
- convertFromZip(): Promise<void>;
20
- convertFromClipboard(): Promise<void>;
23
+ convertFromZip(opts?: V0PromptOptions): Promise<void>;
24
+ convertFromClipboard(opts?: V0PromptOptions): Promise<void>;
21
25
  showGuide(): void;
22
26
  }
23
27
  declare function getV0Url(path?: string): string;
24
28
  declare function isV0Code(code: string): boolean;
25
29
 
26
- export { V0Client, getV0Url, isV0Code };
30
+ export { V0Client, type V0PromptOptions, getV0Url, isV0Code };
package/dist/v0.js CHANGED
@@ -2,7 +2,8 @@ import {
2
2
  V0Client,
3
3
  getV0Url,
4
4
  isV0Code
5
- } from "./chunk-NNFSJISD.js";
5
+ } from "./chunk-2PNCUPCG.js";
6
+ import "./chunk-HFMMK6R4.js";
6
7
  export {
7
8
  V0Client,
8
9
  getV0Url,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axis-ds/cli",
3
- "version": "1.6.2",
3
+ "version": "1.7.1",
4
4
  "type": "module",
5
5
  "description": "AXIS Design System - CLI for adding components (shadcn compatible)",
6
6
  "publishConfig": {