ui8kit 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2,15 +2,14 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { Command } from "commander";
5
- import chalk7 from "chalk";
5
+ import chalk12 from "chalk";
6
6
 
7
7
  // src/commands/add.ts
8
- import chalk3 from "chalk";
9
- import ora from "ora";
10
- import path3 from "path";
11
- import fs4 from "fs-extra";
12
- import { execa } from "execa";
8
+ import ora4 from "ora";
9
+ import path8 from "path";
10
+ import fs7 from "fs-extra";
13
11
  import fetch3 from "node-fetch";
12
+ import prompts3 from "prompts";
14
13
 
15
14
  // src/registry/api.ts
16
15
  import fetch from "node-fetch";
@@ -87,6 +86,7 @@ var SCHEMA_CONFIG = {
87
86
  schema: "JSON Schema URL",
88
87
  framework: "Target framework",
89
88
  typescript: "Whether the project uses TypeScript",
89
+ globalCss: "Path to global styles entry file",
90
90
  aliases: "Path aliases for imports in UI8Kit structure",
91
91
  registry: "Default component registry",
92
92
  componentsDir: "Directory where utility components will be installed",
@@ -153,285 +153,294 @@ var configSchema = z.object({
153
153
  $schema: z.string().optional(),
154
154
  framework: z.literal(SCHEMA_CONFIG.supportedFrameworks[0]),
155
155
  typescript: z.boolean().default(true),
156
+ globalCss: z.string().default("src/index.css"),
156
157
  aliases: z.record(z.string(), z.string()).default(SCHEMA_CONFIG.defaultAliases),
157
158
  registry: z.string().default(SCHEMA_CONFIG.defaultRegistry),
158
159
  componentsDir: z.string().default(SCHEMA_CONFIG.defaultDirectories.components),
159
160
  libDir: z.string().default(SCHEMA_CONFIG.defaultDirectories.lib)
160
161
  });
161
162
 
162
- // src/registry/api.ts
163
- var registryCache = /* @__PURE__ */ new Map();
164
- function isUrl(path7) {
165
- try {
166
- new URL(path7);
167
- return true;
168
- } catch {
169
- return false;
170
- }
171
- }
172
- function getRegistryCache(registryType) {
173
- if (!registryCache.has(registryType)) {
174
- registryCache.set(registryType, {
175
- workingCDN: null,
176
- registryIndex: null
177
- });
163
+ // src/utils/logger.ts
164
+ import chalk from "chalk";
165
+ import ora from "ora";
166
+ var verboseEnabled = false;
167
+ function output(level, message, ...args) {
168
+ const prefix = (() => {
169
+ switch (level) {
170
+ case "info":
171
+ return chalk.blue("\u2139");
172
+ case "success":
173
+ return chalk.green("\u2705");
174
+ case "warn":
175
+ return chalk.yellow("\u26A0\uFE0F");
176
+ case "error":
177
+ return chalk.red("\u274C");
178
+ case "debug":
179
+ return chalk.gray("\u{1F41E}");
180
+ default:
181
+ return "";
182
+ }
183
+ })();
184
+ if (level === "debug" && !verboseEnabled) {
185
+ return;
178
186
  }
179
- return registryCache.get(registryType);
187
+ console.log(`${prefix} ${message}`, ...args);
180
188
  }
181
- async function findWorkingCDN(registryType) {
182
- const cache = getRegistryCache(registryType);
183
- if (cache.workingCDN) {
184
- return cache.workingCDN;
189
+ var logger = {
190
+ setVerbose(enabled) {
191
+ verboseEnabled = enabled;
192
+ },
193
+ info(message, ...args) {
194
+ output("info", message, ...args);
195
+ },
196
+ success(message, ...args) {
197
+ output("success", message, ...args);
198
+ },
199
+ warn(message, ...args) {
200
+ output("warn", message, ...args);
201
+ },
202
+ error(message, ...args) {
203
+ output("error", message, ...args);
204
+ },
205
+ debug(message, ...args) {
206
+ output("debug", message, ...args);
207
+ },
208
+ spinner(text) {
209
+ return ora(text).start();
185
210
  }
186
- const cdnUrls = getCdnUrls(registryType);
187
- for (const baseUrl of cdnUrls) {
188
- try {
189
- console.log(`\u{1F50D} Testing ${registryType} CDN: ${baseUrl}`);
190
- const response = await fetch(`${baseUrl}/index.json`);
191
- if (response.ok) {
192
- cache.workingCDN = baseUrl;
193
- console.log(`\u2705 Using ${registryType} CDN: ${baseUrl}`);
194
- return baseUrl;
195
- }
196
- } catch (error) {
197
- console.log(`\u274C ${registryType} CDN failed: ${baseUrl}`);
198
- }
211
+ };
212
+
213
+ // src/utils/cache.ts
214
+ import fs from "fs-extra";
215
+ import os from "os";
216
+ import path from "path";
217
+ var DEFAULT_TTL_MS = 36e5;
218
+ function normalizeKey(key) {
219
+ return key.trim().replace(/^\/+/, "").replace(/\\/g, "/");
220
+ }
221
+ function cacheFilePath(key) {
222
+ const cacheDir = getCacheDir();
223
+ const normalized = normalizeKey(key);
224
+ const normalizedWithJson = normalized.endsWith(".json") ? normalized : `${normalized}.json`;
225
+ const dataPath = path.join(cacheDir, normalizedWithJson);
226
+ return { dataPath, metaPath: `${dataPath}.meta.json` };
227
+ }
228
+ function getCacheDir() {
229
+ return path.join(os.homedir(), ".ui8kit", "cache");
230
+ }
231
+ function parseTimestamp(timestamp) {
232
+ if (typeof timestamp !== "number") {
233
+ return null;
199
234
  }
200
- throw new Error(`No working ${registryType} CDN found`);
235
+ return Number.isFinite(timestamp) ? timestamp : null;
201
236
  }
202
- async function fetchFromWorkingCDN(path7, registryType) {
203
- const baseUrl = await findWorkingCDN(registryType);
204
- const url = `${baseUrl}/${path7}`;
205
- console.log(`\u{1F3AF} Fetching from ${registryType}: ${url}`);
206
- const response = await fetch(url);
207
- if (!response.ok) {
208
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
237
+ async function getCachedJson(key, options = {}) {
238
+ const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
239
+ if (options.noCache) {
240
+ return null;
209
241
  }
210
- return await response.json();
211
- }
212
- async function getRegistryIndex(registryType) {
213
- const cache = getRegistryCache(registryType);
214
- if (cache.registryIndex) {
215
- return cache.registryIndex;
242
+ const { dataPath, metaPath } = cacheFilePath(key);
243
+ if (!await fs.pathExists(dataPath) || !await fs.pathExists(metaPath)) {
244
+ return null;
216
245
  }
217
- console.log(`\u{1F310} Fetching ${registryType} registry index`);
218
- cache.registryIndex = await fetchFromWorkingCDN("index.json", registryType);
219
- return cache.registryIndex;
220
- }
221
- async function getComponentByType(name, registryType) {
222
246
  try {
223
- const index = await getRegistryIndex(registryType);
224
- const componentInfo = index.components?.find((c) => c.name === name);
225
- if (!componentInfo) {
226
- console.log(`\u274C Component ${name} not found in ${registryType} registry`);
247
+ const meta = await fs.readJson(metaPath);
248
+ const lastFetched = parseTimestamp(meta?.lastFetched);
249
+ const metaTtl = parseTimestamp(meta?.ttl) ?? ttlMs;
250
+ if (!lastFetched) {
227
251
  return null;
228
252
  }
229
- const folder = componentInfo.type === "registry:variants" ? "components/variants" : TYPE_TO_FOLDER[componentInfo.type];
230
- if (!folder) {
231
- console.log(`\u274C Unknown component type: ${componentInfo.type}`);
253
+ const now = Date.now();
254
+ if (now - lastFetched > metaTtl) {
232
255
  return null;
233
256
  }
234
- console.log(`\u{1F3AF} Loading ${name} from /${folder}/ (type: ${componentInfo.type})`);
235
- const data = await fetchFromWorkingCDN(`${folder}/${name}.json`, registryType);
236
- return componentSchema.parse(data);
237
- } catch (error) {
238
- console.log(`\u274C Failed to get component by type: ${error.message}`);
239
- return null;
240
- }
241
- }
242
- async function getComponent(name, registryType = SCHEMA_CONFIG.defaultRegistryType) {
243
- try {
244
- if (isUrl(name)) {
245
- return await fetchFromUrl(name);
246
- }
247
- return await getComponentByType(name, registryType);
248
- } catch (error) {
249
- console.error(`\u274C Failed to fetch ${name} from ${registryType}:`, error.message);
257
+ return await fs.readJson(dataPath);
258
+ } catch {
250
259
  return null;
251
260
  }
252
261
  }
253
- async function fetchFromUrl(url) {
254
- console.log(`\u{1F310} Fetching component from: ${url}`);
255
- const response = await fetch(url);
256
- if (!response.ok) {
257
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
258
- }
259
- const data = await response.json();
260
- return componentSchema.parse(data);
262
+ async function setCachedJson(key, data, options = {}) {
263
+ const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
264
+ const { dataPath, metaPath } = cacheFilePath(key);
265
+ await fs.ensureDir(path.dirname(dataPath));
266
+ await fs.writeJson(dataPath, data, { spaces: 2 });
267
+ await fs.writeJson(metaPath, {
268
+ lastFetched: Date.now(),
269
+ ttl: ttlMs
270
+ });
261
271
  }
262
- async function getAllComponents(registryType = SCHEMA_CONFIG.defaultRegistryType) {
263
- try {
264
- console.log(`\u{1F310} Fetching all ${registryType} components using optimized approach`);
265
- const indexData = await getRegistryIndex(registryType);
266
- const components = [];
267
- if (indexData.components && Array.isArray(indexData.components)) {
268
- for (const componentInfo of indexData.components) {
269
- const component = await getComponent(componentInfo.name, registryType);
270
- if (component) {
271
- components.push(component);
272
- }
273
- }
274
- }
275
- return components;
276
- } catch (error) {
277
- console.error(`\u274C Failed to fetch all ${registryType} components:`, error.message);
278
- return [];
272
+ async function clearCache() {
273
+ const cacheDir = getCacheDir();
274
+ if (await fs.pathExists(cacheDir)) {
275
+ await fs.remove(cacheDir);
279
276
  }
280
277
  }
281
278
 
282
- // src/registry/retry-api.ts
283
- import fetch2 from "node-fetch";
284
- var MAX_RETRIES = 3;
285
- var RETRY_DELAY = 2e3;
286
- var registryCache2 = /* @__PURE__ */ new Map();
287
- function isUrl2(path7) {
279
+ // src/registry/api.ts
280
+ var REGISTRY_INDEX_CACHE_TTL_MS = 36e5;
281
+ var registryCache = /* @__PURE__ */ new Map();
282
+ var DEFAULT_TIMEOUT_MS = 1e4;
283
+ var DEFAULT_MAX_RETRIES = 1;
284
+ var RETRY_DELAY_MS = 1200;
285
+ function isUrl(path13) {
288
286
  try {
289
- new URL(path7);
287
+ new URL(path13);
290
288
  return true;
291
289
  } catch {
292
290
  return false;
293
291
  }
294
292
  }
295
- function getRegistryCache2(registryType) {
296
- if (!registryCache2.has(registryType)) {
297
- registryCache2.set(registryType, {
293
+ function getRegistryCache(registryType) {
294
+ if (!registryCache.has(registryType)) {
295
+ registryCache.set(registryType, {
298
296
  workingCDN: null,
299
297
  registryIndex: null
300
298
  });
301
299
  }
302
- return registryCache2.get(registryType);
300
+ return registryCache.get(registryType);
303
301
  }
304
- async function checkInternetConnection() {
302
+ async function fetchJsonWithTimeout(url, timeoutMs) {
303
+ const controller = new AbortController();
304
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
305
305
  try {
306
- console.log("\u{1F310} Checking internet connection...");
307
- const controller = new AbortController();
308
- const timeoutId = setTimeout(() => controller.abort(), 5e3);
309
- const response = await fetch2("https://www.google.com", {
310
- method: "HEAD",
311
- signal: controller.signal
312
- });
306
+ const response = await fetch(url, { signal: controller.signal });
307
+ if (!response.ok) {
308
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
309
+ }
310
+ return await response.json();
311
+ } finally {
313
312
  clearTimeout(timeoutId);
314
- return response.ok;
315
- } catch (error) {
316
- console.log("\u274C No internet connection detected");
317
- return false;
318
313
  }
319
314
  }
320
- async function fetchWithRetry(url, retries = MAX_RETRIES) {
321
- for (let attempt = 1; attempt <= retries; attempt++) {
315
+ async function fetchJsonWithRetry(url, maxRetries, timeoutMs) {
316
+ let attempt = 0;
317
+ let lastError;
318
+ while (attempt < maxRetries) {
319
+ attempt += 1;
322
320
  try {
323
- console.log(`\u{1F504} Attempt ${attempt}/${retries}: ${url}`);
324
- const controller = new AbortController();
325
- const timeoutId = setTimeout(() => controller.abort(), 1e4);
326
- const response = await fetch2(url, {
327
- signal: controller.signal
328
- });
329
- clearTimeout(timeoutId);
330
- if (response.ok) {
331
- return await response.json();
332
- } else if (response.status === 404) {
333
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
334
- } else {
335
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
336
- }
321
+ return await fetchJsonWithTimeout(url, timeoutMs);
337
322
  } catch (error) {
338
- console.log(`\u274C Attempt ${attempt} failed: ${error.message}`);
339
- if (attempt === retries) {
340
- throw error;
323
+ lastError = error;
324
+ if (attempt >= maxRetries) {
325
+ break;
341
326
  }
342
- console.log(`\u23F3 Waiting ${RETRY_DELAY / 1e3}s before retry...`);
343
- await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY));
327
+ await new Promise((resolve3) => setTimeout(resolve3, RETRY_DELAY_MS));
344
328
  }
345
329
  }
330
+ throw lastError instanceof Error ? lastError : new Error("Request failed");
346
331
  }
347
- async function findWorkingCDNWithRetry(registryType) {
348
- const cache = getRegistryCache2(registryType);
349
- if (cache.workingCDN) {
350
- return cache.workingCDN;
351
- }
352
- if (!await checkInternetConnection()) {
353
- throw new Error("No internet connection available");
354
- }
332
+ async function fetchFromRegistryPath(requestPath, registryType, options = {}) {
333
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
334
+ const maxRetries = Math.max(1, options.maxRetries ?? DEFAULT_MAX_RETRIES);
335
+ const cache = getRegistryCache(registryType);
355
336
  const cdnUrls = getCdnUrls(registryType);
356
- for (const baseUrl of cdnUrls) {
337
+ const orderedUrls = cache.workingCDN ? [cache.workingCDN, ...cdnUrls.filter((url) => url !== cache.workingCDN)] : [...cdnUrls];
338
+ let lastError;
339
+ for (const baseUrl of orderedUrls) {
357
340
  try {
358
- console.log(`\u{1F50D} Testing ${registryType} CDN with retry: ${baseUrl}`);
359
- await fetchWithRetry(`${baseUrl}/index.json`, 2);
341
+ const data = await fetchJsonWithRetry(`${baseUrl}/${requestPath}`, maxRetries, timeoutMs);
342
+ if (cache.workingCDN !== baseUrl) {
343
+ cache.registryIndex = null;
344
+ }
360
345
  cache.workingCDN = baseUrl;
361
- console.log(`\u2705 Using ${registryType} CDN: ${baseUrl}`);
362
- return baseUrl;
346
+ return data;
363
347
  } catch (error) {
364
- console.log(`\u274C ${registryType} CDN failed: ${baseUrl}`);
348
+ lastError = error;
349
+ if (cache.workingCDN === baseUrl) {
350
+ cache.workingCDN = null;
351
+ }
365
352
  }
366
353
  }
367
- throw new Error(`No working ${registryType} CDN found`);
354
+ throw lastError instanceof Error ? lastError : new Error(`No working ${registryType} CDN found`);
368
355
  }
369
- async function fetchFromWorkingCDNWithRetry(path7, registryType) {
370
- const baseUrl = await findWorkingCDNWithRetry(registryType);
371
- const url = `${baseUrl}/${path7}`;
372
- console.log(`\u{1F3AF} Fetching from ${registryType} with retry: ${url}`);
373
- return await fetchWithRetry(url);
374
- }
375
- async function getRegistryIndexWithRetry(registryType) {
376
- const cache = getRegistryCache2(registryType);
377
- if (cache.registryIndex) {
378
- return cache.registryIndex;
356
+ async function getRegistryIndex(registryType, options = {}) {
357
+ if (cacheStateHasIndex(registryType, options)) {
358
+ return getCachedInMemory(registryType);
359
+ }
360
+ const cached = await getCachedJson(
361
+ `${registryType}/index.json`,
362
+ { noCache: options.noCache, ttlMs: REGISTRY_INDEX_CACHE_TTL_MS }
363
+ );
364
+ if (cached) {
365
+ setCachedInMemory(registryType, cached);
366
+ return cached;
379
367
  }
380
- console.log(`\u{1F310} Fetching ${registryType} registry index with retry`);
381
- cache.registryIndex = await fetchFromWorkingCDNWithRetry("index.json", registryType);
368
+ const cache = getRegistryCache(registryType);
369
+ cache.registryIndex = await fetchFromRegistryPath("index.json", registryType, options);
370
+ await setCachedJson(
371
+ `${registryType}/index.json`,
372
+ cache.registryIndex,
373
+ { ttlMs: REGISTRY_INDEX_CACHE_TTL_MS }
374
+ );
382
375
  return cache.registryIndex;
383
376
  }
384
- async function getComponentByTypeWithRetry(name, registryType) {
377
+ async function getComponentByType(name, registryType, options = {}) {
378
+ const normalizedName = name.toLowerCase();
379
+ const cachedComponent = await getCachedJson(
380
+ `${registryType}/components/${normalizedName}.json`,
381
+ { noCache: options.noCache, ttlMs: REGISTRY_INDEX_CACHE_TTL_MS }
382
+ );
383
+ if (cachedComponent) {
384
+ try {
385
+ return componentSchema.parse(cachedComponent);
386
+ } catch (error) {
387
+ logger.debug(`Invalid cached component for ${name}, refetching`);
388
+ }
389
+ }
385
390
  try {
386
- const index = await getRegistryIndexWithRetry(registryType);
387
- const componentInfo = index.components?.find((c) => c.name === name);
391
+ const index = await getRegistryIndex(registryType, options);
392
+ const excludeTypes = options.excludeTypes ?? [];
393
+ const componentInfo = index.components?.find(
394
+ (c) => typeof c?.name === "string" && c.name.toLowerCase() === normalizedName && !excludeTypes.includes(c.type)
395
+ );
388
396
  if (!componentInfo) {
389
- console.log(`\u274C Component ${name} not found in ${registryType} registry`);
397
+ logger.debug(`Component ${name} not found in ${registryType} registry`);
390
398
  return null;
391
399
  }
392
- const folder = TYPE_TO_FOLDER[componentInfo.type];
400
+ const folder = componentInfo.type === "registry:variants" ? "components/variants" : TYPE_TO_FOLDER[componentInfo.type];
393
401
  if (!folder) {
394
- console.log(`\u274C Unknown component type: ${componentInfo.type}`);
402
+ logger.debug(`Unknown component type: ${componentInfo.type}`);
395
403
  return null;
396
404
  }
397
- console.log(`\u{1F3AF} Loading ${name} from /${registryType}/${folder}/ (type: ${componentInfo.type})`);
398
- const data = await fetchFromWorkingCDNWithRetry(`${folder}/${name}.json`, registryType);
405
+ logger.debug(`Loading ${name} from /${folder}/ (type: ${componentInfo.type})`);
406
+ const data = await fetchFromRegistryPath(`${folder}/${name}.json`, registryType, options);
407
+ await setCachedJson(`${registryType}/components/${normalizedName}.json`, data, { ttlMs: REGISTRY_INDEX_CACHE_TTL_MS });
399
408
  return componentSchema.parse(data);
400
409
  } catch (error) {
401
- console.log(`\u274C Failed to get component by type: ${error.message}`);
410
+ logger.debug(`Failed to get component by type: ${error.message}`);
402
411
  return null;
403
412
  }
404
413
  }
405
- async function getComponentWithRetry(name, registryType = SCHEMA_CONFIG.defaultRegistryType) {
414
+ async function getComponent(name, registryType = SCHEMA_CONFIG.defaultRegistryType, options = {}) {
406
415
  try {
407
- if (isUrl2(name)) {
408
- return await fetchFromUrlWithRetry(name);
409
- }
410
- if (!await checkInternetConnection()) {
411
- throw new Error("No internet connection available");
416
+ if (isUrl(name)) {
417
+ return await fetchFromUrl(name, options);
412
418
  }
413
- return await getComponentByTypeWithRetry(name, registryType);
419
+ return await getComponentByType(name, registryType, options);
414
420
  } catch (error) {
415
- console.error(`\u274C Failed to fetch ${name} from ${registryType}:`, error.message);
421
+ logger.debug(`Failed to fetch ${name} from ${registryType}: ${error.message}`);
416
422
  return null;
417
423
  }
418
424
  }
419
- async function fetchFromUrlWithRetry(url) {
420
- console.log(`\u{1F310} Fetching component from URL with retry: ${url}`);
421
- const data = await fetchWithRetry(url);
425
+ async function fetchFromUrl(url, options = {}) {
426
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
427
+ const maxRetries = Math.max(1, options.maxRetries ?? DEFAULT_MAX_RETRIES);
428
+ logger.debug(`Fetching component from: ${url}`);
429
+ const data = await fetchJsonWithRetry(url, maxRetries, timeoutMs);
422
430
  return componentSchema.parse(data);
423
431
  }
424
- async function getAllComponentsWithRetry(registryType = SCHEMA_CONFIG.defaultRegistryType) {
432
+ async function getAllComponents(registryType = SCHEMA_CONFIG.defaultRegistryType, options = {}) {
425
433
  try {
426
- console.log(`\u{1F310} Fetching all ${registryType} components with retry logic`);
427
- if (!await checkInternetConnection()) {
428
- throw new Error("No internet connection available");
429
- }
430
- const indexData = await getRegistryIndexWithRetry(registryType);
434
+ logger.debug(`Fetching all ${registryType} components using optimized approach`);
435
+ const indexData = await getRegistryIndex(registryType, options);
431
436
  const components = [];
437
+ const excludeTypes = options.excludeTypes ?? [];
432
438
  if (indexData.components && Array.isArray(indexData.components)) {
433
439
  for (const componentInfo of indexData.components) {
434
- const component = await getComponentWithRetry(componentInfo.name, registryType);
440
+ if (excludeTypes.includes(componentInfo.type)) {
441
+ continue;
442
+ }
443
+ const component = await getComponent(componentInfo.name, registryType, options);
435
444
  if (component) {
436
445
  components.push(component);
437
446
  }
@@ -439,14 +448,28 @@ async function getAllComponentsWithRetry(registryType = SCHEMA_CONFIG.defaultReg
439
448
  }
440
449
  return components;
441
450
  } catch (error) {
442
- console.error(`\u274C Failed to fetch all ${registryType} components:`, error.message);
451
+ logger.debug(`Failed to fetch all ${registryType} components: ${error.message}`);
443
452
  return [];
444
453
  }
445
454
  }
455
+ function cacheStateHasIndex(registryType, options) {
456
+ if (options.noCache) {
457
+ return false;
458
+ }
459
+ const cache = getRegistryCache(registryType);
460
+ return Boolean(cache.registryIndex);
461
+ }
462
+ function getCachedInMemory(registryType) {
463
+ return getRegistryCache(registryType).registryIndex;
464
+ }
465
+ function setCachedInMemory(registryType, index) {
466
+ const cache = getRegistryCache(registryType);
467
+ cache.registryIndex = index;
468
+ }
446
469
 
447
470
  // src/utils/project.ts
448
- import fs from "fs-extra";
449
- import path from "path";
471
+ import fs2 from "fs-extra";
472
+ import path2 from "path";
450
473
  var MODERN_CONFIG_NAME = "ui8kit.config.json";
451
474
  async function isViteProject() {
452
475
  const viteConfigFiles = [
@@ -456,134 +479,71 @@ async function isViteProject() {
456
479
  "vite.config.mjs"
457
480
  ];
458
481
  for (const file of viteConfigFiles) {
459
- if (await fs.pathExists(file)) {
482
+ if (await fs2.pathExists(file)) {
460
483
  return true;
461
484
  }
462
485
  }
463
486
  return false;
464
487
  }
465
488
  async function hasReact() {
466
- const packageJsonPath = path.join(process.cwd(), "package.json");
467
- if (!await fs.pathExists(packageJsonPath)) {
489
+ const packageJsonPath = path2.join(process.cwd(), "package.json");
490
+ if (!await fs2.pathExists(packageJsonPath)) {
468
491
  return false;
469
492
  }
470
- const packageJson = await fs.readJson(packageJsonPath);
493
+ const packageJson = await fs2.readJson(packageJsonPath);
471
494
  const deps = {
472
495
  ...packageJson.dependencies,
473
496
  ...packageJson.devDependencies
474
497
  };
475
498
  return "react" in deps;
476
499
  }
477
- async function findConfig(_registryType) {
500
+ async function findConfig(registryType) {
501
+ const rootConfig = await getConfig();
502
+ if (rootConfig)
503
+ return rootConfig;
478
504
  const srcConfig = await getConfig("./src");
479
505
  if (srcConfig)
480
506
  return srcConfig;
481
- if (_registryType) {
482
- const registryConfig = await getConfig(`./${_registryType}`);
507
+ if (registryType) {
508
+ const registryConfig = await getConfig(`./${registryType}`);
483
509
  if (registryConfig)
484
510
  return registryConfig;
485
511
  }
486
- return await getConfig();
512
+ return null;
487
513
  }
488
514
  async function getConfig(registryPath) {
489
- const baseDir = registryPath ? path.join(process.cwd(), registryPath) : process.cwd();
490
- const configPath = path.join(baseDir, MODERN_CONFIG_NAME);
491
- if (!await fs.pathExists(configPath)) {
515
+ const baseDir = registryPath ? path2.join(process.cwd(), registryPath) : process.cwd();
516
+ const configPath = path2.join(baseDir, MODERN_CONFIG_NAME);
517
+ if (!await fs2.pathExists(configPath)) {
492
518
  return null;
493
519
  }
494
520
  try {
495
- const config = await fs.readJson(configPath);
521
+ const config = await fs2.readJson(configPath);
496
522
  return configSchema.parse(config);
497
523
  } catch (error) {
498
524
  console.error("\u274C Invalid ui8kit.config.json:", error.message);
499
525
  return null;
500
526
  }
501
527
  }
502
- async function saveConfig(config, registryPath) {
503
- const configPath = registryPath ? path.join(process.cwd(), registryPath, MODERN_CONFIG_NAME) : path.join(process.cwd(), MODERN_CONFIG_NAME);
504
- await fs.ensureDir(path.dirname(configPath));
505
- await fs.writeJson(configPath, config, { spaces: 2 });
528
+ async function saveConfig(config) {
529
+ const configPath = path2.join(process.cwd(), MODERN_CONFIG_NAME);
530
+ await fs2.ensureDir(path2.dirname(configPath));
531
+ await fs2.writeJson(configPath, config, { spaces: 2 });
506
532
  }
507
533
  async function ensureDir(dirPath) {
508
- await fs.ensureDir(dirPath);
534
+ await fs2.ensureDir(dirPath);
509
535
  }
510
536
 
511
537
  // src/utils/registry-validator.ts
512
- import fs2 from "fs-extra";
513
- import chalk from "chalk";
514
- async function validateComponentInstallation(components, registryType) {
515
- return { isValid: true };
516
- }
517
- function handleValidationError(result) {
518
- console.error(chalk.red("\u274C Registry Validation Error:"));
519
- console.error(chalk.red(result.message));
520
- if (result.missingComponents && result.missingComponents.length > 0) {
521
- console.log(chalk.yellow("\n\u{1F4A1} Suggestion:"));
522
- console.log(`Install missing components first: ${chalk.cyan(`npx ui8kit add ${result.missingComponents.join(" ")}`)}
523
- `);
524
- }
525
- process.exit(1);
526
- }
538
+ import fs6 from "fs-extra";
539
+ import path6 from "path";
540
+ import chalk6 from "chalk";
541
+ import prompts2 from "prompts";
527
542
 
528
- // src/utils/dependency-checker.ts
529
- import fs3 from "fs-extra";
530
- import path2 from "path";
531
- import chalk2 from "chalk";
532
- async function checkProjectDependencies(requiredDeps) {
533
- const packageJsonPath = path2.join(process.cwd(), "package.json");
534
- if (!await fs3.pathExists(packageJsonPath)) {
535
- return {
536
- installed: [],
537
- missing: requiredDeps,
538
- workspace: []
539
- };
540
- }
541
- try {
542
- const packageJson = await fs3.readJson(packageJsonPath);
543
- const allDeps = {
544
- ...packageJson.dependencies,
545
- ...packageJson.devDependencies
546
- };
547
- const installed = [];
548
- const missing = [];
549
- const workspace = [];
550
- for (const dep of requiredDeps) {
551
- const version = allDeps[dep];
552
- if (!version) {
553
- missing.push(dep);
554
- } else if (version.startsWith("workspace:")) {
555
- workspace.push(dep);
556
- } else {
557
- installed.push(dep);
558
- }
559
- }
560
- return { installed, missing, workspace };
561
- } catch (error) {
562
- return {
563
- installed: [],
564
- missing: requiredDeps,
565
- workspace: []
566
- };
567
- }
568
- }
569
- function showDependencyStatus(deps) {
570
- if (deps.installed.length > 0) {
571
- console.log(chalk2.green(` \u2705 Already installed: ${deps.installed.join(", ")}`));
572
- }
573
- if (deps.workspace.length > 0) {
574
- console.log(chalk2.blue(` \u{1F517} Workspace dependencies: ${deps.workspace.join(", ")}`));
575
- }
576
- if (deps.missing.length > 0) {
577
- console.log(chalk2.yellow(` \u{1F4E6} Will install: ${deps.missing.join(", ")}`));
578
- }
579
- }
580
- async function filterMissingDependencies(dependencies) {
581
- const status = await checkProjectDependencies(dependencies);
582
- return status.missing;
583
- }
584
- function isWorkspaceError(errorMessage) {
585
- return errorMessage.includes("EUNSUPPORTEDPROTOCOL") || errorMessage.includes("workspace:") || errorMessage.includes('Unsupported URL Type "workspace:"');
586
- }
543
+ // src/commands/init.ts
544
+ import chalk5 from "chalk";
545
+ import prompts from "prompts";
546
+ import ora3 from "ora";
587
547
 
588
548
  // src/utils/cli-messages.ts
589
549
  var CLI_MESSAGES = {
@@ -599,14 +559,17 @@ var CLI_MESSAGES = {
599
559
  noCDNFound: (registryType) => `No working ${registryType} CDN found`,
600
560
  componentNotFound: (name, registryType) => `Component "${name}" not found in ${registryType} registry`,
601
561
  unknownComponentType: (type) => `Unknown component type: ${type}`,
602
- fileNotFound: (path7) => `File not found: ${path7}`,
562
+ fileNotFound: (path13) => `File not found: ${path13}`,
603
563
  invalidConfig: "Invalid ui8kit.config.json:",
604
564
  failedToInstall: (name, registryType) => `Failed to install ${name} from ${registryType}`,
605
565
  failedToFetch: (registryType) => `Failed to fetch components from ${registryType}`,
606
566
  failedToFetchComponent: (name, registryType) => `Failed to fetch ${name} from ${registryType}:`,
607
- failedToAnalyzeDeps: (path7) => `Warning: Could not analyze dependencies for ${path7}:`,
567
+ failedToAnalyzeDeps: (path13) => `Warning: Could not analyze dependencies for ${path13}:`,
608
568
  dependenciesFailed: "Failed to install dependencies",
609
- couldNotInstallDeps: (name) => `Warning: Could not install some dependencies for ${name}`
569
+ couldNotInstallDeps: (name) => `Warning: Could not install some dependencies for ${name}`,
570
+ noLocalInstall: "No installed local components were found in this project.",
571
+ invalidRegistryForDiff: "Could not load registry data for diff check.",
572
+ cacheClearFailed: "Could not clear cache."
610
573
  },
611
574
  // Success messages
612
575
  success: {
@@ -619,14 +582,16 @@ var CLI_MESSAGES = {
619
582
  registryBuilt: "Registry built successfully!",
620
583
  schemasGenerated: "Schema files generated successfully!",
621
584
  registryGenerated: (registryName) => `${registryName} registry generated successfully!`,
622
- depsAvailable: "All dependencies already available"
585
+ depsAvailable: "All dependencies already available",
586
+ cacheCleared: "UI8Kit cache cleared successfully."
623
587
  },
624
588
  // Info messages
625
589
  info: {
590
+ tryListComponents: (registryType) => `Run "npx ui8kit@latest add --all --registry ${registryType}" to list available components`,
626
591
  initializing: (registryName) => `Initializing UI8Kit in your project (${registryName} registry)...`,
627
592
  installing: (registryName) => `Installing from ${registryName} registry...`,
628
593
  installingAll: (registryName) => `Installing all available components from ${registryName} registry...`,
629
- retryEnabled: "Retry mode enabled - using enhanced connection logic",
594
+ retryEnabled: "Aggressive retry mode enabled (3 attempts per request)",
630
595
  fetchingComponentList: (registryType) => `Fetching component list from ${registryType}...`,
631
596
  fetchingRegistry: (registryType) => `Fetching ${registryType} registry index`,
632
597
  scanningComponents: (registryName) => `Scanning ${registryName} components...`,
@@ -641,12 +606,16 @@ var CLI_MESSAGES = {
641
606
  using: (registryType, baseUrl) => `Using ${registryType} CDN: ${baseUrl}`,
642
607
  loading: (name, registryType, folder, type) => `Loading ${name} from /${registryType}/${folder}/ (type: ${type})`,
643
608
  fetching: (registryType, url) => `Fetching from ${registryType}: ${url}`,
609
+ listingComponents: "Listing available components",
644
610
  fetchingUrl: "Fetching component from:",
645
- fetchingUrlWithRetry: "Fetching component from URL with retry:"
611
+ fetchingUrlWithRetry: "Fetching component from URL with retry:",
612
+ checkingForUpdates: "Checking for component updates...",
613
+ localDiffSummary: "Local components compared with registry."
646
614
  },
647
615
  // Prompts
648
616
  prompts: {
649
- typescript: "Are you using TypeScript?",
617
+ globalCss: "Where is your global CSS file?",
618
+ aliasComponents: "Configure import aliases?",
650
619
  overwrite: (registryName) => `UI8Kit is already initialized for ${registryName} registry. Overwrite configuration?`
651
620
  },
652
621
  // Examples and help text
@@ -694,235 +663,82 @@ var CLI_MESSAGES = {
694
663
  }
695
664
  };
696
665
 
697
- // src/commands/add.ts
698
- async function addCommand(components, options) {
699
- const registryType = resolveRegistryType(options.registry);
700
- if (options.all || components.includes("all")) {
701
- return await addAllComponents(options, registryType);
702
- }
703
- if (components.length === 0) {
704
- console.error(chalk3.red(`\u274C ${CLI_MESSAGES.errors.noComponentsSpecified}`));
705
- CLI_MESSAGES.examples.add.forEach((example) => console.log(example));
706
- process.exit(1);
707
- }
708
- const validation = await validateComponentInstallation(components, registryType);
709
- if (!validation.isValid) {
710
- handleValidationError(validation);
711
- }
712
- const config = await findConfig(registryType);
713
- if (!config) {
714
- console.error(chalk3.red(`\u274C ${CLI_MESSAGES.errors.notInitialized}`));
715
- console.log(`Run 'npx ui8kit@latest init' first.`);
716
- console.log(`For ${registryType} registry, run: npx ui8kit@latest init --registry ${registryType}`);
717
- process.exit(1);
718
- }
719
- const getComponentFn = options.retry ? getComponentWithRetry : getComponent;
720
- if (options.retry) {
721
- console.log(chalk3.blue(`\u{1F504} ${CLI_MESSAGES.info.retryEnabled}`));
722
- }
723
- console.log(chalk3.blue(`\u{1F4E6} ${CLI_MESSAGES.info.installing(registryType)}`));
724
- const results = await processComponents(components, registryType, config, getComponentFn, options);
725
- displayInstallationSummary(registryType, results);
726
- }
727
- async function addAllComponents(options, registryType) {
728
- console.log(chalk3.blue(`\u{1F680} ${CLI_MESSAGES.info.installingAll(registryType)}`));
729
- const config = await findConfig(registryType);
730
- if (!config) {
731
- console.error(chalk3.red(`\u274C ${CLI_MESSAGES.errors.notInitialized}`));
732
- console.log(`Run 'npx ui8kit@latest init' first.`);
733
- console.log(`For ${registryType} registry, run: npx ui8kit@latest init --registry ${registryType}`);
734
- process.exit(1);
735
- }
736
- const getAllComponentsFn = options.retry ? getAllComponentsWithRetry : getAllComponents;
737
- if (options.retry) {
738
- console.log(chalk3.blue(`\u{1F504} ${CLI_MESSAGES.info.retryEnabled}`));
666
+ // src/utils/package-manager.ts
667
+ import chalk3 from "chalk";
668
+ import ora2 from "ora";
669
+ import path4 from "path";
670
+ import fs4 from "fs-extra";
671
+ import { execa } from "execa";
672
+
673
+ // src/utils/dependency-checker.ts
674
+ import fs3 from "fs-extra";
675
+ import path3 from "path";
676
+ import chalk2 from "chalk";
677
+ async function checkProjectDependencies(requiredDeps) {
678
+ const packageJsonPath = path3.join(process.cwd(), "package.json");
679
+ if (!await fs3.pathExists(packageJsonPath)) {
680
+ return {
681
+ installed: [],
682
+ missing: requiredDeps,
683
+ workspace: []
684
+ };
739
685
  }
740
- const spinner = ora(CLI_MESSAGES.info.fetchingComponentList(registryType)).start();
741
686
  try {
742
- const allComponents = await getAllComponentsFn(registryType);
743
- if (allComponents.length === 0) {
744
- spinner.fail(`No components found in ${registryType} registry`);
745
- console.log(chalk3.yellow(`
746
- \u26A0\uFE0F ${registryType} ${CLI_MESSAGES.errors.registryTempUnavailable}`));
747
- console.log("Try these alternatives:");
748
- CLI_MESSAGES.examples.troubleshooting.forEach((alt) => console.log(` \u2022 ${alt}`));
749
- return;
750
- }
751
- spinner.succeed(CLI_MESSAGES.status.foundComponents(allComponents.length, registryType));
752
- if (options.dryRun) {
753
- console.log(chalk3.blue(`
754
- \u{1F4CB} ${CLI_MESSAGES.status.wouldInstallFrom(registryType)}`));
755
- allComponents.forEach((comp) => {
756
- console.log(` - ${comp.name} (${comp.type})`);
757
- });
758
- return;
759
- }
760
- const results = await processComponents(
761
- allComponents.map((c) => c.name),
762
- registryType,
763
- config,
764
- options.retry ? getComponentWithRetry : getComponent,
765
- options,
766
- allComponents
767
- );
768
- await installComponentsIndex(registryType, config);
769
- displayInstallationSummary(registryType, results);
770
- } catch (error) {
771
- spinner.fail(CLI_MESSAGES.errors.failedToFetch(registryType));
772
- console.error(chalk3.red("\u274C Error:"), error.message);
773
- console.log(chalk3.yellow(`
774
- \u26A0\uFE0F ${registryType} ${CLI_MESSAGES.errors.registryTempUnavailable}`));
775
- console.log("Try these alternatives:");
776
- CLI_MESSAGES.examples.troubleshooting.forEach((alt) => console.log(` \u2022 ${alt}`));
777
- process.exit(1);
778
- }
779
- }
780
- async function processComponents(componentNames, registryType, config, getComponentFn, options, preloadedComponents) {
781
- const results = [];
782
- const componentMap = new Map(preloadedComponents?.map((c) => [c.name, c]));
783
- for (const componentName of componentNames) {
784
- const spinner = ora(CLI_MESSAGES.status.installing(componentName, registryType)).start();
785
- try {
786
- let component = componentMap?.get(componentName);
787
- if (!component) {
788
- component = await getComponentFn(componentName, registryType);
789
- }
790
- if (!component) {
791
- throw new Error(CLI_MESSAGES.errors.componentNotFound(componentName, registryType));
792
- }
793
- if (options.dryRun) {
794
- spinner.succeed(CLI_MESSAGES.status.wouldInstall(component.name, registryType));
795
- console.log(` Type: ${component.type}`);
796
- console.log(` Files: ${component.files.length}`);
797
- console.log(` Dependencies: ${component.dependencies.join(", ") || "none"}`);
798
- if (component.dependencies.length > 0) {
799
- const depStatus = await checkProjectDependencies(component.dependencies);
800
- showDependencyStatus(depStatus);
801
- }
802
- continue;
803
- }
804
- await installComponentFiles(component, config, options.force);
805
- if (component.dependencies.length > 0) {
806
- try {
807
- await installDependencies(component.dependencies);
808
- } catch (error) {
809
- console.log(chalk3.yellow(` \u26A0\uFE0F ${CLI_MESSAGES.errors.couldNotInstallDeps(component.name)}`));
810
- console.log(chalk3.yellow(` Dependencies: ${component.dependencies.join(", ")}`));
811
- console.log(chalk3.yellow(` Please install them manually if needed`));
812
- }
687
+ const packageJson = await fs3.readJson(packageJsonPath);
688
+ const allDeps = {
689
+ ...packageJson.dependencies,
690
+ ...packageJson.devDependencies
691
+ };
692
+ const installed = [];
693
+ const missing = [];
694
+ const workspace = [];
695
+ for (const dep of requiredDeps) {
696
+ const version = allDeps[dep];
697
+ if (!version) {
698
+ missing.push(dep);
699
+ } else if (version.startsWith("workspace:")) {
700
+ workspace.push(dep);
701
+ } else {
702
+ installed.push(dep);
813
703
  }
814
- spinner.succeed(CLI_MESSAGES.status.installing(component.name, registryType));
815
- results.push({ name: component.name, status: "success" });
816
- } catch (error) {
817
- spinner.fail(CLI_MESSAGES.errors.failedToInstall(componentName, registryType));
818
- console.error(chalk3.red(` Error: ${error.message}`));
819
- results.push({
820
- name: componentName,
821
- status: "error",
822
- error: error.message
823
- });
824
- }
825
- }
826
- return results;
827
- }
828
- function displayInstallationSummary(registryType, results) {
829
- const successful = results.filter((r) => r.status === "success");
830
- const failed = results.filter((r) => r.status === "error");
831
- console.log(chalk3.blue("\n\u{1F4CA} Installation Summary:"));
832
- console.log(` Registry: ${registryType}`);
833
- console.log(` \u2705 Successful: ${successful.length}`);
834
- console.log(` \u274C Failed: ${failed.length}`);
835
- if (successful.length > 0) {
836
- console.log(chalk3.green(`
837
- \u{1F389} ${CLI_MESSAGES.success.componentsInstalled}`));
838
- console.log("You can now import and use them in your project.");
839
- }
840
- if (failed.length > 0) {
841
- process.exit(1);
842
- }
843
- }
844
- async function installComponentFiles(component, config, force = false) {
845
- for (const file of component.files) {
846
- const fileName = path3.basename(file.path);
847
- const target = file.target || inferTargetFromType(component.type);
848
- const installDir = resolveInstallDir(target, config);
849
- const targetPath = path3.join(process.cwd(), installDir, fileName);
850
- if (!force && await fs4.pathExists(targetPath)) {
851
- console.log(` \u26A0\uFE0F ${CLI_MESSAGES.status.skipped(fileName)}`);
852
- continue;
853
704
  }
854
- await fs4.ensureDir(path3.dirname(targetPath));
855
- await fs4.writeFile(targetPath, file.content, "utf-8");
856
- }
857
- }
858
- function inferTargetFromType(componentType) {
859
- switch (componentType) {
860
- case "registry:ui":
861
- return "ui";
862
- case "registry:composite":
863
- return "components";
864
- case "registry:block":
865
- return "blocks";
866
- case "registry:component":
867
- return "components";
868
- case "registry:layout":
869
- return "layouts";
870
- case "registry:lib":
871
- return "lib";
872
- case "registry:variants":
873
- return "variants";
874
- default:
875
- return "components";
705
+ return { installed, missing, workspace };
706
+ } catch (error) {
707
+ return {
708
+ installed: [],
709
+ missing: requiredDeps,
710
+ workspace: []
711
+ };
876
712
  }
877
713
  }
878
- function resolveInstallDir(target, config) {
879
- const normalizedTarget = target.replace(/\\/g, "/").replace(/^\/?src\//i, "");
880
- if (normalizedTarget === "lib") {
881
- return normalizeDir(config.libDir || SCHEMA_CONFIG.defaultDirectories.lib);
882
- }
883
- if (normalizedTarget === "variants") {
884
- return normalizeDir(SCHEMA_CONFIG.defaultDirectories.variants);
714
+ function showDependencyStatus(deps) {
715
+ if (deps.installed.length > 0) {
716
+ console.log(chalk2.green(` \u2705 Already installed: ${deps.installed.join(", ")}`));
885
717
  }
886
- const baseComponentsDir = normalizeDir(config.componentsDir || SCHEMA_CONFIG.defaultDirectories.components);
887
- if (normalizedTarget.includes("/")) {
888
- const parentRoot = baseComponentsDir.replace(/[/\\]components$/i, "") || "src";
889
- return path3.join(parentRoot, normalizedTarget).replace(/\\/g, "/");
718
+ if (deps.workspace.length > 0) {
719
+ console.log(chalk2.blue(` \u{1F517} Workspace dependencies: ${deps.workspace.join(", ")}`));
890
720
  }
891
- if (normalizedTarget === "ui")
892
- return path3.join(baseComponentsDir, "ui").replace(/\\/g, "/");
893
- if (normalizedTarget === "components")
894
- return baseComponentsDir;
895
- switch (normalizedTarget) {
896
- case "blocks":
897
- return normalizeDir(SCHEMA_CONFIG.defaultDirectories.blocks);
898
- case "layouts":
899
- return normalizeDir(SCHEMA_CONFIG.defaultDirectories.layouts);
900
- default:
901
- return baseComponentsDir;
721
+ if (deps.missing.length > 0) {
722
+ console.log(chalk2.yellow(` \u{1F4E6} Will install: ${deps.missing.join(", ")}`));
902
723
  }
903
724
  }
904
- function normalizeDir(dir) {
905
- return dir.replace(/^\.\//, "").replace(/\\/g, "/");
725
+ async function filterMissingDependencies(dependencies) {
726
+ const status = await checkProjectDependencies(dependencies);
727
+ return status.missing;
906
728
  }
907
- function resolveRegistryType(registryInput) {
908
- if (!registryInput) {
909
- return SCHEMA_CONFIG.defaultRegistryType;
910
- }
911
- if (SCHEMA_CONFIG.registryTypes.includes(registryInput)) {
912
- return registryInput;
913
- }
914
- console.warn(chalk3.yellow(`\u26A0\uFE0F Unknown registry type: ${registryInput}`));
915
- console.log(`Available registries: ${SCHEMA_CONFIG.registryTypes.join(", ")}`);
916
- console.log(`Using default: ${SCHEMA_CONFIG.defaultRegistryType}`);
917
- return SCHEMA_CONFIG.defaultRegistryType;
729
+ function isWorkspaceError(errorMessage) {
730
+ return errorMessage.includes("EUNSUPPORTEDPROTOCOL") || errorMessage.includes("workspace:") || errorMessage.includes('Unsupported URL Type "workspace:"');
918
731
  }
919
- async function installDependencies(dependencies) {
920
- const spinner = ora(CLI_MESSAGES.status.installing("dependencies", "")).start();
732
+
733
+ // src/utils/package-manager.ts
734
+ async function installDependencies(dependencies, options = {}) {
735
+ const useSpinner = options.useSpinner ?? true;
736
+ const spinner = useSpinner ? ora2(options.spinnerText ?? CLI_MESSAGES.status.installing("dependencies", "")).start() : null;
921
737
  try {
922
738
  const depStatus = await checkProjectDependencies(dependencies);
923
739
  const missingDependencies = await filterMissingDependencies(dependencies);
924
740
  if (missingDependencies.length === 0) {
925
- spinner.succeed(CLI_MESSAGES.success.depsAvailable);
741
+ spinner?.succeed(CLI_MESSAGES.success.depsAvailable);
926
742
  if (depStatus.workspace.length > 0) {
927
743
  console.log(chalk3.blue(` \u{1F517} Using workspace dependencies: ${depStatus.workspace.join(", ")}`));
928
744
  }
@@ -935,15 +751,15 @@ async function installDependencies(dependencies) {
935
751
  cwd: process.cwd(),
936
752
  stdio: "pipe"
937
753
  });
938
- spinner.succeed(CLI_MESSAGES.success.depsInstalled);
754
+ spinner?.succeed(CLI_MESSAGES.success.depsInstalled);
939
755
  } catch (error) {
940
- spinner.fail(CLI_MESSAGES.errors.dependenciesFailed);
756
+ spinner?.fail(CLI_MESSAGES.errors.dependenciesFailed);
941
757
  const errorMessage = error.stderr || error.message;
942
758
  if (isWorkspaceError(errorMessage)) {
943
759
  console.log(chalk3.yellow(`
944
760
  \u{1F4A1} ${CLI_MESSAGES.info.workspaceDepsDetected}`));
945
761
  const results = await installDependenciesIndividually(dependencies);
946
- if (results.some((r) => r.success)) {
762
+ if (results.some((result) => result.success)) {
947
763
  console.log(chalk3.green("\u2705 Some dependencies installed successfully"));
948
764
  return;
949
765
  }
@@ -964,7 +780,7 @@ async function installDependenciesIndividually(dependencies) {
964
780
  });
965
781
  console.log(chalk3.green(` \u2705 Installed ${dep}`));
966
782
  results.push({ dep, success: true });
967
- } catch (error) {
783
+ } catch {
968
784
  console.log(chalk3.yellow(` \u26A0\uFE0F Skipped ${dep} (may already be available)`));
969
785
  results.push({ dep, success: false });
970
786
  }
@@ -974,14 +790,14 @@ async function installDependenciesIndividually(dependencies) {
974
790
  async function detectPackageManager() {
975
791
  let dir = process.cwd();
976
792
  while (true) {
977
- if (await fs4.pathExists(path3.join(dir, "bun.lock")) || await fs4.pathExists(path3.join(dir, "bun.lockb"))) {
793
+ if (await fs4.pathExists(path4.join(dir, "bun.lock")) || await fs4.pathExists(path4.join(dir, "bun.lockb"))) {
978
794
  return "bun";
979
795
  }
980
- if (await fs4.pathExists(path3.join(dir, "pnpm-lock.yaml")))
796
+ if (await fs4.pathExists(path4.join(dir, "pnpm-lock.yaml")))
981
797
  return "pnpm";
982
- if (await fs4.pathExists(path3.join(dir, "yarn.lock")))
798
+ if (await fs4.pathExists(path4.join(dir, "yarn.lock")))
983
799
  return "yarn";
984
- const packageJsonPath = path3.join(dir, "package.json");
800
+ const packageJsonPath = path4.join(dir, "package.json");
985
801
  if (await fs4.pathExists(packageJsonPath)) {
986
802
  try {
987
803
  const packageJson = await fs4.readJson(packageJsonPath);
@@ -997,66 +813,107 @@ async function detectPackageManager() {
997
813
  } catch {
998
814
  }
999
815
  }
1000
- const parent = path3.dirname(dir);
816
+ const parent = path4.dirname(dir);
1001
817
  if (parent === dir)
1002
818
  break;
1003
819
  dir = parent;
1004
820
  }
1005
821
  return "npm";
1006
822
  }
1007
- async function installComponentsIndex(registryType, config) {
1008
- const spinner = ora("Installing components index...").start();
1009
- try {
1010
- const cdnUrls = SCHEMA_CONFIG.cdnBaseUrls;
1011
- for (const baseUrl of cdnUrls) {
1012
- try {
1013
- const url = `${baseUrl}/components/index.json`;
1014
- const response = await fetch3(url);
1015
- if (response.ok) {
1016
- const component = await response.json();
1017
- for (const file of component.files) {
1018
- const fileName = path3.basename(file.path);
1019
- const targetDir = config.componentsDir;
1020
- const targetPath = path3.join(process.cwd(), targetDir, fileName);
1021
- await fs4.ensureDir(path3.dirname(targetPath));
1022
- await fs4.writeFile(targetPath, file.content || "", "utf-8");
1023
- }
1024
- spinner.succeed("Installed components index");
1025
- return;
1026
- }
1027
- } catch {
1028
- continue;
1029
- }
823
+
824
+ // src/commands/init.ts
825
+ import path5 from "path";
826
+ import fs5 from "fs-extra";
827
+ import fetch2 from "node-fetch";
828
+
829
+ // src/utils/errors.ts
830
+ import chalk4 from "chalk";
831
+ var Ui8kitError = class extends Error {
832
+ suggestion;
833
+ constructor(message, suggestion) {
834
+ super(message);
835
+ this.name = this.constructor.name;
836
+ this.suggestion = suggestion;
837
+ }
838
+ };
839
+ var ConfigNotFoundError = class extends Ui8kitError {
840
+ constructor(registry) {
841
+ super(
842
+ `ui8kit config not found for registry "${registry}".`,
843
+ `Run: npx ui8kit@latest init --registry ${registry}`
844
+ );
845
+ }
846
+ };
847
+ function handleError(error) {
848
+ if (error instanceof Ui8kitError) {
849
+ if (error.message) {
850
+ console.error(chalk4.red(error.message));
1030
851
  }
1031
- spinner.info("Components index not found in registry (optional)");
1032
- } catch (error) {
1033
- spinner.fail("Could not install components index");
852
+ if (error.suggestion) {
853
+ console.log(chalk4.yellow(`\u{1F4A1} ${error.suggestion}`));
854
+ }
855
+ process.exit(1);
856
+ }
857
+ if (isZodError(error)) {
858
+ console.error(chalk4.red("\u274C Configuration validation error:"));
859
+ error.errors.forEach((issue) => {
860
+ const path13 = issue.path.join(".") || "root";
861
+ console.log(chalk4.yellow(` - ${path13}: ${issue.message}`));
862
+ });
863
+ process.exit(1);
1034
864
  }
865
+ console.error(chalk4.red("\u274C Unexpected error:"));
866
+ console.error(chalk4.red(error.message ?? String(error)));
867
+ process.exit(1);
868
+ }
869
+ function isZodError(error) {
870
+ return Boolean(error && typeof error === "object" && "issues" in error);
1035
871
  }
1036
872
 
1037
873
  // src/commands/init.ts
1038
- import chalk4 from "chalk";
1039
- import prompts from "prompts";
1040
- import ora2 from "ora";
1041
- import path4 from "path";
1042
- import fs5 from "fs-extra";
1043
- import fetch4 from "node-fetch";
874
+ function buildInitConfig(options) {
875
+ const registryName = options.registry || SCHEMA_CONFIG.defaultRegistryType;
876
+ const aliases = SCHEMA_CONFIG.defaultAliases;
877
+ const globalCss = options.globalCss || "src/index.css";
878
+ const aliasComponents = options.aliasComponents?.trim() || "@/components";
879
+ if (options.yes) {
880
+ return {
881
+ $schema: `${SCHEMA_CONFIG.baseUrl}.json`,
882
+ framework: "vite-react",
883
+ typescript: true,
884
+ globalCss,
885
+ aliases,
886
+ registry: SCHEMA_CONFIG.defaultRegistry,
887
+ componentsDir: SCHEMA_CONFIG.defaultDirectories.components,
888
+ libDir: SCHEMA_CONFIG.defaultDirectories.lib
889
+ };
890
+ }
891
+ return {
892
+ $schema: `${SCHEMA_CONFIG.baseUrl}.json`,
893
+ framework: "vite-react",
894
+ typescript: true,
895
+ globalCss,
896
+ aliases: { ...aliases, "@/components": aliasComponents },
897
+ registry: SCHEMA_CONFIG.defaultRegistry,
898
+ componentsDir: SCHEMA_CONFIG.defaultDirectories.components,
899
+ libDir: SCHEMA_CONFIG.defaultDirectories.lib
900
+ };
901
+ }
1044
902
  async function initCommand(options) {
1045
903
  const registryName = options.registry || SCHEMA_CONFIG.defaultRegistryType;
1046
- const registryPath = `./${registryName}`;
1047
- console.log(chalk4.blue(`\u{1F680} ${CLI_MESSAGES.info.initializing(registryName)}`));
904
+ logger.info(CLI_MESSAGES.info.initializing(registryName));
1048
905
  const viteDetected = await isViteProject();
1049
906
  if (!viteDetected) {
1050
- console.error(chalk4.red(`\u274C ${CLI_MESSAGES.errors.notViteProject}`));
907
+ console.error(chalk5.red(`\u274C ${CLI_MESSAGES.errors.notViteProject}`));
1051
908
  console.log("Please run this command in a Vite project directory.");
1052
909
  process.exit(1);
1053
910
  }
1054
911
  if (!await hasReact()) {
1055
- console.error(chalk4.red(`\u274C ${CLI_MESSAGES.errors.reactNotInstalled}`));
912
+ console.error(chalk5.red(`\u274C ${CLI_MESSAGES.errors.reactNotInstalled}`));
1056
913
  console.log("Please install React first: npm install react react-dom");
1057
914
  process.exit(1);
1058
915
  }
1059
- const existingConfig = await getConfig("./src");
916
+ const existingConfig = await findConfig(registryName);
1060
917
  if (existingConfig && !options.yes) {
1061
918
  const { overwrite } = await prompts({
1062
919
  type: "confirm",
@@ -1065,68 +922,67 @@ async function initCommand(options) {
1065
922
  initial: false
1066
923
  });
1067
924
  if (!overwrite) {
1068
- console.log(chalk4.yellow(`\u26A0\uFE0F ${CLI_MESSAGES.info.installationCancelled}`));
925
+ logger.warn(CLI_MESSAGES.info.installationCancelled);
1069
926
  return;
1070
927
  }
1071
928
  }
1072
- const aliases = SCHEMA_CONFIG.defaultAliases;
1073
929
  let config;
1074
930
  if (options.yes) {
1075
- config = {
1076
- $schema: `${SCHEMA_CONFIG.baseUrl}.json`,
1077
- framework: "vite-react",
1078
- typescript: true,
1079
- aliases,
1080
- registry: SCHEMA_CONFIG.defaultRegistry,
1081
- componentsDir: SCHEMA_CONFIG.defaultDirectories.components,
1082
- libDir: SCHEMA_CONFIG.defaultDirectories.lib
1083
- };
931
+ config = buildInitConfig({ yes: true, registry: registryName });
1084
932
  } else {
1085
933
  const responses = await prompts([
1086
934
  {
1087
- type: "confirm",
1088
- name: "typescript",
1089
- message: CLI_MESSAGES.prompts.typescript,
1090
- initial: true
935
+ type: "text",
936
+ name: "globalCss",
937
+ message: CLI_MESSAGES.prompts.globalCss,
938
+ initial: "src/index.css"
939
+ },
940
+ {
941
+ type: "text",
942
+ name: "aliasComponents",
943
+ message: CLI_MESSAGES.prompts.aliasComponents,
944
+ initial: "@/components"
1091
945
  }
1092
946
  ]);
1093
- config = {
1094
- $schema: `${SCHEMA_CONFIG.baseUrl}.json`,
1095
- framework: "vite-react",
1096
- typescript: responses.typescript,
1097
- aliases,
1098
- registry: SCHEMA_CONFIG.defaultRegistry,
1099
- componentsDir: SCHEMA_CONFIG.defaultDirectories.components,
1100
- libDir: SCHEMA_CONFIG.defaultDirectories.lib
1101
- };
947
+ const aliasComponents = responses.aliasComponents?.trim() || "@/components";
948
+ const globalCss = responses.globalCss || "src/index.css";
949
+ config = buildInitConfig({
950
+ yes: false,
951
+ registry: registryName,
952
+ globalCss,
953
+ aliasComponents
954
+ });
1102
955
  }
1103
- const spinner = ora2(CLI_MESSAGES.info.initializing(registryName)).start();
956
+ const spinner = ora3(CLI_MESSAGES.info.initializing(registryName)).start();
1104
957
  try {
1105
- await saveConfig(config, "./src");
958
+ await saveConfig(config);
1106
959
  await ensureDir(config.libDir);
1107
960
  await ensureDir(config.componentsDir);
1108
- await ensureDir(path4.join(config.componentsDir, "ui"));
961
+ await ensureDir(path5.join(config.componentsDir, "ui"));
1109
962
  await ensureDir(SCHEMA_CONFIG.defaultDirectories.blocks);
1110
963
  await ensureDir(SCHEMA_CONFIG.defaultDirectories.layouts);
1111
964
  await ensureDir(SCHEMA_CONFIG.defaultDirectories.variants);
1112
965
  spinner.text = "Installing core utilities and variants...";
1113
966
  await installCoreFiles(registryName, config, spinner);
967
+ spinner.text = "Installing core dependencies...";
968
+ await installDependencies(["clsx", "tailwind-merge"], {
969
+ useSpinner: false
970
+ });
1114
971
  spinner.succeed(CLI_MESSAGES.success.initialized(registryName));
1115
- console.log(chalk4.green(`
1116
- \u2705 ${CLI_MESSAGES.success.setupComplete(registryName)}`));
972
+ logger.success(`
973
+ \u2705 ${CLI_MESSAGES.success.setupComplete(registryName)}`);
1117
974
  console.log("\nDirectories created:");
1118
- console.log(` ${chalk4.cyan("src/lib/")} - Utils, helpers, functions`);
1119
- console.log(` ${chalk4.cyan("src/variants/")} - CVA variant configurations`);
1120
- console.log(` ${chalk4.cyan("src/components/ui/")} - UI components`);
1121
- console.log(` ${chalk4.cyan("src/components/")} - Complex components`);
1122
- console.log(` ${chalk4.cyan("src/layouts/")} - Page layouts and structures`);
1123
- console.log(` ${chalk4.cyan("src/blocks/")} - Component blocks`);
975
+ console.log(` ${chalk5.cyan("src/lib/")} - Utils, helpers, functions`);
976
+ console.log(` ${chalk5.cyan("src/variants/")} - CVA variant configurations`);
977
+ console.log(` ${chalk5.cyan("src/components/ui/")} - UI components`);
978
+ console.log(` ${chalk5.cyan("src/components/")} - Complex components`);
979
+ console.log(` ${chalk5.cyan("src/layouts/")} - Page layouts and structures`);
980
+ console.log(` ${chalk5.cyan("src/blocks/")} - Component blocks`);
1124
981
  console.log("\nNext steps:");
1125
- CLI_MESSAGES.examples.init.forEach((example) => console.log(` ${chalk4.cyan(example)}`));
982
+ CLI_MESSAGES.examples.init.forEach((example) => console.log(` ${chalk5.cyan(example)}`));
1126
983
  } catch (error) {
1127
984
  spinner.fail(CLI_MESSAGES.errors.buildFailed);
1128
- console.error(chalk4.red("\u274C Error:"), error.message);
1129
- process.exit(1);
985
+ handleError(error);
1130
986
  }
1131
987
  }
1132
988
  async function installCoreFiles(registryType, config, spinner) {
@@ -1135,7 +991,7 @@ async function installCoreFiles(registryType, config, spinner) {
1135
991
  for (const baseUrl of cdnUrls) {
1136
992
  try {
1137
993
  const indexUrl = `${baseUrl}/index.json`;
1138
- const response = await fetch4(indexUrl);
994
+ const response = await fetch2(indexUrl);
1139
995
  if (response.ok) {
1140
996
  registryIndex = await response.json();
1141
997
  break;
@@ -1149,87 +1005,795 @@ async function installCoreFiles(registryType, config, spinner) {
1149
1005
  await createUtilsFile(config.libDir, config.typescript);
1150
1006
  return;
1151
1007
  }
1152
- const variantItems = registryIndex.components.filter((c) => c.type === "registry:variants");
1153
- const libItems = registryIndex.components.filter((c) => c.type === "registry:lib");
1154
- for (const item of libItems) {
1155
- spinner.text = `Installing ${item.name}...`;
1156
- await installComponentFromRegistry(item.name, "registry:lib", cdnUrls, config);
1008
+ const variantItems = registryIndex.components.filter((c) => c.type === "registry:variants");
1009
+ const libItems = registryIndex.components.filter((c) => c.type === "registry:lib");
1010
+ for (const item of libItems) {
1011
+ spinner.text = `Installing ${item.name}...`;
1012
+ await installComponentFromRegistry(item.name, "registry:lib", cdnUrls, config);
1013
+ }
1014
+ for (const item of variantItems) {
1015
+ spinner.text = `Installing variant: ${item.name}...`;
1016
+ await installComponentFromRegistry(item.name, "registry:variants", cdnUrls, config);
1017
+ }
1018
+ spinner.text = "Syncing variants index...";
1019
+ const variantsIndexStatus = await installVariantsIndex(cdnUrls);
1020
+ if (variantsIndexStatus === "updated") {
1021
+ spinner.text = "Updated variants/index.ts from CDN";
1022
+ } else if (variantsIndexStatus === "created") {
1023
+ spinner.text = "Created variants/index.ts from CDN";
1024
+ } else if (variantsIndexStatus === "unchanged") {
1025
+ spinner.text = "variants/index.ts is up to date";
1026
+ } else {
1027
+ spinner.text = "variants/index.ts not found in registry (skipped)";
1028
+ }
1029
+ spinner.text = `\u2705 Installed ${libItems.length} utilities and ${variantItems.length} variants`;
1030
+ }
1031
+ async function installComponentFromRegistry(name, type, cdnUrls, config) {
1032
+ const folder = type === "registry:lib" ? "lib" : type === "registry:variants" ? "components/variants" : "components/ui";
1033
+ for (const baseUrl of cdnUrls) {
1034
+ try {
1035
+ const url = `${baseUrl}/${folder}/${name}.json`;
1036
+ const response = await fetch2(url);
1037
+ if (response.ok) {
1038
+ const component = await response.json();
1039
+ for (const file of component.files) {
1040
+ const fileName = path5.basename(file.path);
1041
+ let targetDir;
1042
+ if (type === "registry:lib") {
1043
+ targetDir = config.libDir;
1044
+ } else if (type === "registry:variants") {
1045
+ targetDir = SCHEMA_CONFIG.defaultDirectories.variants;
1046
+ } else {
1047
+ targetDir = path5.join(config.componentsDir, "ui");
1048
+ }
1049
+ const targetPath = path5.join(process.cwd(), targetDir, fileName);
1050
+ await fs5.ensureDir(path5.dirname(targetPath));
1051
+ await fs5.writeFile(targetPath, file.content || "", "utf-8");
1052
+ }
1053
+ return;
1054
+ }
1055
+ } catch {
1056
+ continue;
1057
+ }
1058
+ }
1059
+ }
1060
+ async function installVariantsIndex(cdnUrls) {
1061
+ for (const baseUrl of cdnUrls) {
1062
+ try {
1063
+ const url = `${baseUrl}/components/variants/index.json`;
1064
+ const response = await fetch2(url);
1065
+ if (response.ok) {
1066
+ const component = await response.json();
1067
+ for (const file of component.files) {
1068
+ const fileName = path5.basename(file.path);
1069
+ if (!fileName.startsWith("index.")) {
1070
+ continue;
1071
+ }
1072
+ const targetDir = SCHEMA_CONFIG.defaultDirectories.variants;
1073
+ const targetPath = path5.join(process.cwd(), targetDir, fileName);
1074
+ const incomingContent = file.content || "";
1075
+ const exists = await fs5.pathExists(targetPath);
1076
+ if (exists) {
1077
+ const currentContent = await fs5.readFile(targetPath, "utf-8");
1078
+ if (currentContent === incomingContent) {
1079
+ return "unchanged";
1080
+ }
1081
+ }
1082
+ await fs5.ensureDir(path5.dirname(targetPath));
1083
+ await fs5.writeFile(targetPath, incomingContent, "utf-8");
1084
+ return exists ? "updated" : "created";
1085
+ }
1086
+ }
1087
+ } catch {
1088
+ continue;
1089
+ }
1090
+ }
1091
+ return "skipped";
1092
+ }
1093
+ async function createUtilsFile(libDir, typescript) {
1094
+ const utilsContent = `import { type ClassValue, clsx } from "clsx"
1095
+ import { twMerge } from "tailwind-merge"
1096
+
1097
+ export function cn(...inputs: ClassValue[]) {
1098
+ return twMerge(clsx(inputs))
1099
+ }`;
1100
+ const fileName = typescript ? "utils.ts" : "utils.js";
1101
+ const filePath = path5.join(process.cwd(), libDir, fileName);
1102
+ await fs5.writeFile(filePath, utilsContent, "utf-8");
1103
+ }
1104
+
1105
+ // src/utils/registry-validator.ts
1106
+ async function validateComponentInstallation(components, registryType) {
1107
+ const packageJsonPath = path6.join(process.cwd(), "package.json");
1108
+ if (!await fs6.pathExists(packageJsonPath)) {
1109
+ return {
1110
+ isValid: false,
1111
+ message: "No package.json found in the current directory. Run this command from your project root."
1112
+ };
1113
+ }
1114
+ const nodeMajorVersion = Number.parseInt(process.versions.node.split(".")[0] ?? "0", 10);
1115
+ if (Number.isNaN(nodeMajorVersion) || nodeMajorVersion < 18) {
1116
+ return {
1117
+ isValid: false,
1118
+ message: `Node.js 18+ is required. Current version: ${process.versions.node}`
1119
+ };
1120
+ }
1121
+ const existingConfig = await findConfig(registryType);
1122
+ if (!existingConfig) {
1123
+ const { runInit } = await prompts2({
1124
+ type: "confirm",
1125
+ name: "runInit",
1126
+ message: "ui8kit.config.json not found. Run init now?",
1127
+ initial: true
1128
+ });
1129
+ if (runInit) {
1130
+ await initCommand({ registry: registryType });
1131
+ const configAfterInit = await findConfig(registryType);
1132
+ if (configAfterInit) {
1133
+ return { isValid: true };
1134
+ }
1135
+ }
1136
+ return {
1137
+ isValid: false,
1138
+ message: `ui8kit is not initialized. Run: npx ui8kit@latest init --registry ${registryType}`
1139
+ };
1140
+ }
1141
+ return { isValid: true };
1142
+ }
1143
+ function handleValidationError(result) {
1144
+ console.error(chalk6.red("\u274C Registry Validation Error:"));
1145
+ console.error(chalk6.red(result.message));
1146
+ if (result.missingComponents && result.missingComponents.length > 0) {
1147
+ console.log(chalk6.yellow("\n\u{1F4A1} Suggestion:"));
1148
+ console.log(`Install missing components first: ${chalk6.cyan(`npx ui8kit add ${result.missingComponents.join(" ")}`)}
1149
+ `);
1150
+ }
1151
+ process.exit(1);
1152
+ }
1153
+
1154
+ // src/utils/dependency-resolver.ts
1155
+ async function resolveRegistryTree(componentNames, registryType, getComponent2) {
1156
+ const componentsByName = /* @__PURE__ */ new Map();
1157
+ const state = /* @__PURE__ */ new Map();
1158
+ const visitStack = [];
1159
+ const normalized = componentNames.map((name) => name.toLowerCase());
1160
+ const ensureComponent = async (name) => {
1161
+ const key = name.toLowerCase();
1162
+ if (state.get(key) === "done") {
1163
+ return componentsByName.get(key) ?? null;
1164
+ }
1165
+ if (state.get(key) === "visiting") {
1166
+ const cycle = visitStack.includes(key) ? [...visitStack, key].join(" -> ") : key;
1167
+ logger.warn(`Circular registry dependency detected: ${cycle}`);
1168
+ return componentsByName.get(key) ?? null;
1169
+ }
1170
+ const component = await getComponent2(name, registryType);
1171
+ if (!component) {
1172
+ logger.warn(`Component ${name} not found in ${registryType} registry, skipping`);
1173
+ state.set(key, "done");
1174
+ return null;
1175
+ }
1176
+ componentsByName.set(key, component);
1177
+ state.set(key, "visiting");
1178
+ visitStack.push(key);
1179
+ for (const dependency of component.registryDependencies ?? []) {
1180
+ await ensureComponent(dependency);
1181
+ }
1182
+ visitStack.pop();
1183
+ state.set(key, "done");
1184
+ return component;
1185
+ };
1186
+ for (const name of normalized) {
1187
+ await ensureComponent(name);
1188
+ }
1189
+ const graph = /* @__PURE__ */ new Map();
1190
+ const inDegree = /* @__PURE__ */ new Map();
1191
+ for (const component of componentsByName.values()) {
1192
+ const name = component.name.toLowerCase();
1193
+ if (!graph.has(name)) {
1194
+ graph.set(name, /* @__PURE__ */ new Set());
1195
+ inDegree.set(name, 0);
1196
+ }
1197
+ }
1198
+ for (const component of componentsByName.values()) {
1199
+ const from = component.name.toLowerCase();
1200
+ const deps = component.registryDependencies ?? [];
1201
+ for (const dep of deps) {
1202
+ const to = dep.toLowerCase();
1203
+ if (!componentsByName.has(to)) {
1204
+ continue;
1205
+ }
1206
+ const targets = graph.get(to);
1207
+ if (targets) {
1208
+ targets.add(from);
1209
+ } else {
1210
+ graph.set(to, /* @__PURE__ */ new Set([from]));
1211
+ }
1212
+ inDegree.set(from, (inDegree.get(from) ?? 0) + 1);
1213
+ }
1214
+ }
1215
+ const queue = [];
1216
+ inDegree.forEach((value, name) => {
1217
+ if (value === 0) {
1218
+ queue.push(name);
1219
+ }
1220
+ });
1221
+ const orderedKeys = [];
1222
+ while (queue.length > 0) {
1223
+ const current = queue.shift();
1224
+ if (!current)
1225
+ continue;
1226
+ orderedKeys.push(current);
1227
+ const targets = graph.get(current) || /* @__PURE__ */ new Set();
1228
+ for (const next of targets) {
1229
+ const degree = (inDegree.get(next) ?? 1) - 1;
1230
+ inDegree.set(next, degree);
1231
+ if (degree === 0) {
1232
+ queue.push(next);
1233
+ }
1234
+ }
1235
+ }
1236
+ for (const key of componentsByName.keys()) {
1237
+ if (!orderedKeys.includes(key)) {
1238
+ logger.warn(`Unresolved dependency cycle detected for ${key}, appending in current order`);
1239
+ orderedKeys.push(key);
1240
+ }
1241
+ }
1242
+ return orderedKeys.map((key) => componentsByName.get(key)).filter((component) => Boolean(component));
1243
+ }
1244
+
1245
+ // src/utils/diff-utils.ts
1246
+ import { createTwoFilesPatch } from "diff";
1247
+ function buildUnifiedDiff(oldFilePath, newFilePath, oldContent, newContent) {
1248
+ return createTwoFilesPatch(
1249
+ oldFilePath,
1250
+ newFilePath,
1251
+ oldContent,
1252
+ newContent,
1253
+ "local",
1254
+ "registry",
1255
+ { context: 3 }
1256
+ );
1257
+ }
1258
+ function hasDiff(oldContent, newContent) {
1259
+ return oldContent !== newContent;
1260
+ }
1261
+ function formatDiffPreview(diff, maxLines = 80) {
1262
+ const lines = diff.split("\n");
1263
+ if (lines.length <= maxLines) {
1264
+ return diff;
1265
+ }
1266
+ return `${lines.slice(0, maxLines).join("\n")}
1267
+ ...`;
1268
+ }
1269
+
1270
+ // src/utils/transform.ts
1271
+ import path7 from "path";
1272
+ import ts from "typescript";
1273
+ var IMPORT_NODE_KIND = ts.SyntaxKind.ImportDeclaration;
1274
+ function normalizeAliasKey(alias) {
1275
+ return alias.replace(/\\/g, "/").replace(/\/+$/, "");
1276
+ }
1277
+ function toPosix(value) {
1278
+ return value.replace(/\\/g, "/");
1279
+ }
1280
+ function normalizeAliasTarget(alias) {
1281
+ return toPosix(alias).replace(/\/+$/, "");
1282
+ }
1283
+ function normalizeDefaultAliases() {
1284
+ const map = /* @__PURE__ */ new Map();
1285
+ for (const [alias, target] of Object.entries(SCHEMA_CONFIG.defaultAliases)) {
1286
+ map.set(normalizeAliasKey(alias), normalizeAliasTarget(target));
1287
+ }
1288
+ return map;
1289
+ }
1290
+ function normalizeConfiguredAliases(aliasMap) {
1291
+ const normalized = /* @__PURE__ */ new Map();
1292
+ for (const [alias, target] of Object.entries(aliasMap)) {
1293
+ normalized.set(normalizeAliasKey(alias), normalizeAliasTarget(target));
1294
+ }
1295
+ return normalized;
1296
+ }
1297
+ function pickAliasForImport(importPath, configuredAliases) {
1298
+ const pathValue = toPosix(importPath);
1299
+ if (!pathValue.startsWith("@/")) {
1300
+ return void 0;
1301
+ }
1302
+ const trimmed = pathValue.slice(2);
1303
+ const [root] = trimmed.split("/");
1304
+ const rootAlias = `@/${root}`;
1305
+ const directAlias = Array.from(configuredAliases.keys()).filter((alias) => pathValue === alias || pathValue.startsWith(`${alias}/`)).sort((a, b) => b.length - a.length)[0];
1306
+ if (directAlias) {
1307
+ const aliasValue = configuredAliases.get(directAlias);
1308
+ if (!aliasValue || !aliasValue.startsWith("@/")) {
1309
+ return void 0;
1310
+ }
1311
+ const remainder = pathValue.slice(directAlias.length).replace(/^\/+/, "");
1312
+ if (!remainder) {
1313
+ return aliasValue;
1314
+ }
1315
+ const remainderParts = remainder.split("/");
1316
+ const targetParts = normalizeAliasKey(aliasValue).replace(/^@\//, "").split("/");
1317
+ const aliasTail = targetParts[targetParts.length - 1];
1318
+ const normalizedRemainder = remainderParts[0] === aliasTail ? remainderParts.slice(1).join("/") : remainder;
1319
+ return normalizedRemainder ? `${aliasValue}/${normalizedRemainder}` : aliasValue;
1320
+ }
1321
+ const defaultAliasCandidates = Array.from(normalizeDefaultAliases().keys()).filter((alias) => pathValue === alias || pathValue.startsWith(`${alias}/`)).sort((a, b) => b.length - a.length);
1322
+ for (const defaultAlias of defaultAliasCandidates) {
1323
+ const defaultParts = normalizeAliasKey(defaultAlias).replace(/^@\//, "").split("/");
1324
+ const remainderFromDefault = trimmed.split("/").slice(defaultParts.length);
1325
+ if (remainderFromDefault.length === 0) {
1326
+ continue;
1327
+ }
1328
+ const candidateAliasTail = remainderFromDefault[0];
1329
+ const candidateAlias = `@/${candidateAliasTail}`;
1330
+ if (configuredAliases.has(candidateAlias)) {
1331
+ const remainderPath = remainderFromDefault.slice(1).join("/");
1332
+ return remainderPath ? `${candidateAlias}/${remainderPath}` : candidateAlias;
1333
+ }
1334
+ }
1335
+ return void 0;
1336
+ }
1337
+ function rewriteModuleSpecifier(specifierText, configuredAliases) {
1338
+ if (!specifierText.startsWith("@/")) {
1339
+ return specifierText;
1340
+ }
1341
+ const aliasesMap = normalizeConfiguredAliases(configuredAliases);
1342
+ const rewrittenRemainder = pickAliasForImport(specifierText, aliasesMap);
1343
+ if (!rewrittenRemainder || rewrittenRemainder === normalizeAliasKey(specifierText)) {
1344
+ return specifierText;
1345
+ }
1346
+ if (rewrittenRemainder) {
1347
+ return rewrittenRemainder;
1348
+ }
1349
+ return specifierText;
1350
+ }
1351
+ function transformImports(content, aliases) {
1352
+ const sourceFile = ts.createSourceFile("component.tsx", content, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
1353
+ const importSpans = [];
1354
+ const configuredAliases = normalizeConfiguredAliases(aliases);
1355
+ function visit(node) {
1356
+ if (node.kind === IMPORT_NODE_KIND && ts.isImportDeclaration(node)) {
1357
+ const moduleSpecifier = node.moduleSpecifier;
1358
+ if (ts.isStringLiteral(moduleSpecifier)) {
1359
+ const value = moduleSpecifier.text;
1360
+ const rewritten = rewriteModuleSpecifier(value, Object.fromEntries(configuredAliases));
1361
+ if (rewritten !== value) {
1362
+ importSpans.push({
1363
+ start: moduleSpecifier.getStart(sourceFile),
1364
+ end: moduleSpecifier.getEnd(),
1365
+ replacement: `"${rewritten}"`
1366
+ });
1367
+ }
1368
+ }
1369
+ }
1370
+ ts.forEachChild(node, visit);
1371
+ }
1372
+ ts.forEachChild(sourceFile, visit);
1373
+ if (importSpans.length === 0) {
1374
+ return content;
1375
+ }
1376
+ importSpans.sort((a, b) => b.start - a.start);
1377
+ let transformed = content;
1378
+ for (const span of importSpans) {
1379
+ transformed = `${transformed.slice(0, span.start)}${span.replacement}${transformed.slice(span.end)}`;
1380
+ }
1381
+ return transformed;
1382
+ }
1383
+ function transformCleanup(content) {
1384
+ return content.replace(/\r\n/g, "\n").replace(/\n{3,}/g, "\n\n").trimEnd() + "\n";
1385
+ }
1386
+ function applyTransforms(content, aliases) {
1387
+ const withImports = transformImports(content, aliases);
1388
+ return transformCleanup(withImports);
1389
+ }
1390
+ function shouldTransformFile(fileName) {
1391
+ return [".ts", ".tsx"].includes(path7.extname(fileName));
1392
+ }
1393
+
1394
+ // src/commands/add.ts
1395
+ var ADD_EXCLUDED_COMPONENT_TYPES = ["registry:variants", "registry:lib"];
1396
+ async function addCommand(components, options) {
1397
+ const registryType = resolveRegistryType(options.registry);
1398
+ const requestOptions = {
1399
+ excludeTypes: ADD_EXCLUDED_COMPONENT_TYPES,
1400
+ maxRetries: options.retry ? 3 : 1,
1401
+ noCache: options.cache === false
1402
+ };
1403
+ try {
1404
+ if (options.all || components.includes("all")) {
1405
+ await addAllComponents(options, registryType, requestOptions);
1406
+ return;
1407
+ }
1408
+ const selectedComponents = components.length > 0 ? components : await pickComponentsFromPrompt(registryType, requestOptions);
1409
+ if (selectedComponents.length === 0) {
1410
+ logger.warn(CLI_MESSAGES.errors.noComponentsSpecified);
1411
+ return;
1412
+ }
1413
+ const validation = await validateComponentInstallation(selectedComponents, registryType);
1414
+ if (!validation.isValid) {
1415
+ handleValidationError(validation);
1416
+ }
1417
+ const config = await findConfig(registryType);
1418
+ if (!config) {
1419
+ throw new ConfigNotFoundError(registryType);
1420
+ }
1421
+ if (options.retry) {
1422
+ logger.info(CLI_MESSAGES.info.retryEnabled);
1423
+ }
1424
+ logger.info(CLI_MESSAGES.info.installing(registryType));
1425
+ const getComponentFn = (name, type) => getComponent(name, type, requestOptions);
1426
+ const results = await installRequestedComponents(
1427
+ selectedComponents,
1428
+ registryType,
1429
+ config,
1430
+ getComponentFn,
1431
+ options
1432
+ );
1433
+ displayInstallationSummary(registryType, results);
1434
+ } catch (error) {
1435
+ handleError(error);
1436
+ }
1437
+ }
1438
+ async function addAllComponents(options, registryType, requestOptions) {
1439
+ logger.info(CLI_MESSAGES.info.installingAll(registryType));
1440
+ const validation = await validateComponentInstallation([], registryType);
1441
+ if (!validation.isValid) {
1442
+ handleValidationError(validation);
1443
+ }
1444
+ const config = await findConfig(registryType);
1445
+ if (!config) {
1446
+ throw new ConfigNotFoundError(registryType);
1447
+ }
1448
+ const getAllComponentsFn = (type) => getAllComponents(type, requestOptions);
1449
+ if (options.retry) {
1450
+ logger.info(CLI_MESSAGES.info.retryEnabled);
1451
+ }
1452
+ const spinner = ora4(CLI_MESSAGES.info.fetchingComponentList(registryType)).start();
1453
+ try {
1454
+ const allComponents = await getAllComponentsFn(registryType);
1455
+ if (allComponents.length === 0) {
1456
+ spinner.fail(`No components found in ${registryType} registry`);
1457
+ logger.warn(`
1458
+ \u26A0\uFE0F ${registryType} ${CLI_MESSAGES.errors.registryTempUnavailable}`);
1459
+ console.log("Try these alternatives:");
1460
+ CLI_MESSAGES.examples.troubleshooting.forEach((alt) => console.log(` \u2022 ${alt}`));
1461
+ return;
1462
+ }
1463
+ spinner.succeed(CLI_MESSAGES.status.foundComponents(allComponents.length, registryType));
1464
+ if (options.dryRun) {
1465
+ await installRequestedComponents(
1466
+ allComponents.map((c) => c.name),
1467
+ registryType,
1468
+ config,
1469
+ (name, type) => getComponent(name, type, requestOptions),
1470
+ options,
1471
+ allComponents
1472
+ );
1473
+ return;
1474
+ }
1475
+ const results = await installRequestedComponents(
1476
+ allComponents.map((c) => c.name),
1477
+ registryType,
1478
+ config,
1479
+ (name, type) => getComponent(name, type, requestOptions),
1480
+ options,
1481
+ allComponents
1482
+ );
1483
+ await installComponentsIndex(registryType, config);
1484
+ displayInstallationSummary(registryType, results);
1485
+ } catch (error) {
1486
+ spinner.fail(CLI_MESSAGES.errors.failedToFetch(registryType));
1487
+ logger.error(`Error: ${error.message}`);
1488
+ logger.warn(`
1489
+ \u26A0\uFE0F ${registryType} ${CLI_MESSAGES.errors.registryTempUnavailable}`);
1490
+ console.log("Try these alternatives:");
1491
+ CLI_MESSAGES.examples.troubleshooting.forEach((alt) => console.log(` \u2022 ${alt}`));
1492
+ process.exit(1);
1493
+ }
1494
+ }
1495
+ async function processComponents(componentNames, registryType, config, getComponentFn, options, preloadedComponents, totalCount) {
1496
+ const results = [];
1497
+ const componentMap = new Map(preloadedComponents?.map((c) => [c.name.toLowerCase(), c]));
1498
+ const total = totalCount ?? componentNames.length;
1499
+ if (total > 1) {
1500
+ logger.info(`Installing ${total} components...`);
1501
+ }
1502
+ for (let i = 0; i < componentNames.length; i += 1) {
1503
+ const componentName = componentNames[i];
1504
+ const position = `${i + 1}/${total}`;
1505
+ const spinner = ora4(`[${position}] ${CLI_MESSAGES.status.installing(componentName, registryType)}`).start();
1506
+ try {
1507
+ const lookupName = componentName.toLowerCase();
1508
+ let component = componentMap?.get(lookupName) ?? null;
1509
+ if (!component) {
1510
+ component = await getComponentFn(componentName, registryType);
1511
+ }
1512
+ if (!component) {
1513
+ throw new Error(CLI_MESSAGES.errors.componentNotFound(componentName, registryType));
1514
+ }
1515
+ if (options.dryRun) {
1516
+ spinner.succeed(`[${position}] ${CLI_MESSAGES.status.wouldInstall(component.name, registryType)}`);
1517
+ logger.info(` Type: ${component.type}`);
1518
+ if (component.registryDependencies && component.registryDependencies.length > 0) {
1519
+ logger.info(` Registry deps: ${component.registryDependencies.join(" -> ")}`);
1520
+ }
1521
+ logger.info(` Files: ${component.files.length}`);
1522
+ logger.info(` Dependencies: ${component.dependencies.join(", ") || "none"}`);
1523
+ for (const file of component.files) {
1524
+ const fileName = path8.basename(file.path);
1525
+ const target = file.target || inferTargetFromType(component.type);
1526
+ const installDir = resolveInstallDir(target, config);
1527
+ const targetPath = path8.join(process.cwd(), installDir, fileName);
1528
+ const exists = await fs7.pathExists(targetPath);
1529
+ const status = exists ? "overwrite" : "create";
1530
+ logger.info(` ${status}: ${targetPath}`);
1531
+ if (exists) {
1532
+ const currentContent = await fs7.readFile(targetPath, "utf-8");
1533
+ const transformedIncoming = shouldTransformFile(fileName) ? applyTransforms(file.content, config.aliases) : file.content;
1534
+ const changed = hasDiff(currentContent, transformedIncoming);
1535
+ if (changed) {
1536
+ const patch = buildUnifiedDiff(targetPath, `${component.name}/${fileName}`, currentContent, transformedIncoming);
1537
+ console.log(formatDiffPreview(patch, 40));
1538
+ }
1539
+ }
1540
+ }
1541
+ if (component.dependencies.length > 0) {
1542
+ const depStatus = await checkProjectDependencies(component.dependencies);
1543
+ showDependencyStatus(depStatus);
1544
+ }
1545
+ continue;
1546
+ }
1547
+ await installComponentFiles(component, config, options.force);
1548
+ if (component.dependencies.length > 0) {
1549
+ try {
1550
+ await installDependencies(component.dependencies);
1551
+ } catch (error) {
1552
+ logger.warn(CLI_MESSAGES.errors.couldNotInstallDeps(component.name));
1553
+ logger.warn(` Dependencies: ${component.dependencies.join(", ")}`);
1554
+ logger.warn(" Please install them manually if needed");
1555
+ }
1556
+ }
1557
+ spinner.succeed(`[${position}] ${CLI_MESSAGES.status.installing(component.name, registryType)}`);
1558
+ results.push({ name: component.name, status: "success" });
1559
+ } catch (error) {
1560
+ spinner.fail(`[${position}] ${CLI_MESSAGES.errors.failedToInstall(componentName, registryType)}`);
1561
+ logger.error(` Error: ${error.message}`);
1562
+ results.push({
1563
+ name: componentName,
1564
+ status: "error",
1565
+ error: error.message
1566
+ });
1567
+ }
1568
+ }
1569
+ return results;
1570
+ }
1571
+ async function pickComponentsFromPrompt(registryType, requestOptions) {
1572
+ const allComponents = await getAllComponents(registryType, requestOptions);
1573
+ if (allComponents.length === 0) {
1574
+ logger.warn(`No components found in ${registryType} registry`);
1575
+ return [];
1576
+ }
1577
+ const sorted = allComponents.filter((component) => !ADD_EXCLUDED_COMPONENT_TYPES.includes(component.type)).sort((a, b) => {
1578
+ if (a.type === b.type) {
1579
+ return a.name.localeCompare(b.name);
1580
+ }
1581
+ return a.type.localeCompare(b.type);
1582
+ });
1583
+ const grouped = /* @__PURE__ */ new Map();
1584
+ for (const component of sorted) {
1585
+ const group = grouped.get(component.type) ?? [];
1586
+ group.push(component);
1587
+ if (!grouped.has(component.type)) {
1588
+ grouped.set(component.type, group);
1589
+ }
1590
+ }
1591
+ const choices = [];
1592
+ for (const [type, components] of grouped) {
1593
+ choices.push({
1594
+ title: `
1595
+ ${type}`,
1596
+ value: "__separator__",
1597
+ description: "",
1598
+ disabled: true
1599
+ });
1600
+ for (const component of components) {
1601
+ choices.push({
1602
+ title: component.name,
1603
+ value: component.name,
1604
+ description: component.description || component.type
1605
+ });
1606
+ }
1607
+ }
1608
+ if (choices.length === 0) {
1609
+ logger.warn(`No selectable components found in ${registryType} registry`);
1610
+ return [];
1611
+ }
1612
+ const { selected } = await prompts3({
1613
+ type: "multiselect",
1614
+ name: "selected",
1615
+ message: "Which components would you like to add?",
1616
+ instructions: false,
1617
+ choices,
1618
+ hint: "Space to select, Enter to confirm"
1619
+ });
1620
+ return selected || [];
1621
+ }
1622
+ async function installRequestedComponents(componentNames, registryType, config, getComponentFn, options, preloadedComponents = []) {
1623
+ const componentMap = /* @__PURE__ */ new Map();
1624
+ for (const component of preloadedComponents) {
1625
+ componentMap.set(component.name.toLowerCase(), component);
1626
+ }
1627
+ const resolverGetComponent = async (name, type) => {
1628
+ const normalized = name.toLowerCase();
1629
+ const cached = componentMap.get(normalized);
1630
+ if (cached) {
1631
+ return cached;
1632
+ }
1633
+ const component = await getComponentFn(name, type);
1634
+ if (!component) {
1635
+ return null;
1636
+ }
1637
+ componentMap.set(component.name.toLowerCase(), component);
1638
+ return component;
1639
+ };
1640
+ const orderedComponents = await resolveRegistryTree(
1641
+ componentNames,
1642
+ registryType,
1643
+ (name, type) => resolverGetComponent(name, type)
1644
+ );
1645
+ if (options.dryRun && orderedComponents.length > 0) {
1646
+ logger.info("\n\u{1F4E6} Resolved registry dependency tree:");
1647
+ orderedComponents.forEach((component, index) => {
1648
+ console.log(` ${index + 1}. ${component.name}`);
1649
+ });
1650
+ }
1651
+ const orderedNames = new Set(orderedComponents.map((component) => component.name.toLowerCase()));
1652
+ const normalizedRequested = Array.from(new Set(componentNames.map((name) => name.toLowerCase())));
1653
+ const missingRequested = normalizedRequested.filter((name) => !orderedNames.has(name));
1654
+ const missingResults = missingRequested.map((name) => ({
1655
+ name,
1656
+ status: "error",
1657
+ error: `Component "${name}" was not found in ${registryType} registry`
1658
+ }));
1659
+ const processingResults = await processComponents(
1660
+ orderedComponents.map((component) => component.name),
1661
+ registryType,
1662
+ config,
1663
+ resolverGetComponent,
1664
+ options,
1665
+ orderedComponents,
1666
+ orderedComponents.length
1667
+ );
1668
+ return [...missingResults, ...processingResults];
1669
+ }
1670
+ function displayInstallationSummary(registryType, results) {
1671
+ const successful = results.filter((r) => r.status === "success");
1672
+ const failed = results.filter((r) => r.status === "error");
1673
+ logger.info("\n\u{1F4CA} Installation Summary:");
1674
+ console.log(` Registry: ${registryType}`);
1675
+ console.log(` \u2705 Successful: ${successful.length}`);
1676
+ console.log(` \u274C Failed: ${failed.length}`);
1677
+ if (successful.length > 0) {
1678
+ logger.success(`
1679
+ \u{1F389} ${CLI_MESSAGES.success.componentsInstalled}`);
1680
+ console.log("You can now import and use them in your project.");
1681
+ }
1682
+ if (failed.length > 0) {
1683
+ process.exit(1);
1684
+ }
1685
+ }
1686
+ async function installComponentFiles(component, config, force = false) {
1687
+ for (const file of component.files) {
1688
+ const fileName = path8.basename(file.path);
1689
+ const target = file.target || inferTargetFromType(component.type);
1690
+ const installDir = resolveInstallDir(target, config);
1691
+ const targetPath = path8.join(process.cwd(), installDir, fileName);
1692
+ if (!force && await fs7.pathExists(targetPath)) {
1693
+ console.log(` \u26A0\uFE0F ${CLI_MESSAGES.status.skipped(fileName)}`);
1694
+ continue;
1695
+ }
1696
+ await fs7.ensureDir(path8.dirname(targetPath));
1697
+ const preparedContent = shouldTransformFile(fileName) ? applyTransforms(file.content, config.aliases) : file.content;
1698
+ await fs7.writeFile(targetPath, preparedContent, "utf-8");
1699
+ }
1700
+ }
1701
+ function inferTargetFromType(componentType) {
1702
+ switch (componentType) {
1703
+ case "registry:ui":
1704
+ return "ui";
1705
+ case "registry:composite":
1706
+ return "components";
1707
+ case "registry:block":
1708
+ return "blocks";
1709
+ case "registry:component":
1710
+ return "components";
1711
+ case "registry:layout":
1712
+ return "layouts";
1713
+ case "registry:lib":
1714
+ return "lib";
1715
+ case "registry:variants":
1716
+ return "variants";
1717
+ default:
1718
+ return "components";
1719
+ }
1720
+ }
1721
+ function resolveInstallDir(target, config) {
1722
+ const normalizedTarget = target.replace(/\\/g, "/").replace(/^\/?src\//i, "");
1723
+ if (normalizedTarget === "lib") {
1724
+ return normalizeDir(config.libDir || SCHEMA_CONFIG.defaultDirectories.lib);
1725
+ }
1726
+ if (normalizedTarget === "variants") {
1727
+ return normalizeDir(SCHEMA_CONFIG.defaultDirectories.variants);
1157
1728
  }
1158
- for (const item of variantItems) {
1159
- spinner.text = `Installing variant: ${item.name}...`;
1160
- await installComponentFromRegistry(item.name, "registry:variants", cdnUrls, config);
1729
+ const baseComponentsDir = normalizeDir(config.componentsDir || SCHEMA_CONFIG.defaultDirectories.components);
1730
+ if (normalizedTarget.includes("/")) {
1731
+ const parentRoot = baseComponentsDir.replace(/[/\\]components$/i, "") || "src";
1732
+ return path8.join(parentRoot, normalizedTarget).replace(/\\/g, "/");
1733
+ }
1734
+ if (normalizedTarget === "ui")
1735
+ return path8.join(baseComponentsDir, "ui").replace(/\\/g, "/");
1736
+ if (normalizedTarget === "components")
1737
+ return baseComponentsDir;
1738
+ switch (normalizedTarget) {
1739
+ case "blocks":
1740
+ return normalizeDir(SCHEMA_CONFIG.defaultDirectories.blocks);
1741
+ case "layouts":
1742
+ return normalizeDir(SCHEMA_CONFIG.defaultDirectories.layouts);
1743
+ default:
1744
+ return baseComponentsDir;
1161
1745
  }
1162
- spinner.text = `Installing variants index...`;
1163
- await installVariantsIndex(cdnUrls, config);
1164
- spinner.text = `\u2705 Installed ${libItems.length} utilities and ${variantItems.length} variants`;
1165
1746
  }
1166
- async function installComponentFromRegistry(name, type, cdnUrls, config) {
1167
- const folder = type === "registry:lib" ? "lib" : type === "registry:variants" ? "components/variants" : "components/ui";
1168
- for (const baseUrl of cdnUrls) {
1169
- try {
1170
- const url = `${baseUrl}/${folder}/${name}.json`;
1171
- const response = await fetch4(url);
1172
- if (response.ok) {
1173
- const component = await response.json();
1174
- for (const file of component.files) {
1175
- const fileName = path4.basename(file.path);
1176
- let targetDir;
1177
- if (type === "registry:lib") {
1178
- targetDir = config.libDir;
1179
- } else if (type === "registry:variants") {
1180
- targetDir = SCHEMA_CONFIG.defaultDirectories.variants;
1181
- } else {
1182
- targetDir = path4.join(config.componentsDir, "ui");
1183
- }
1184
- const targetPath = path4.join(process.cwd(), targetDir, fileName);
1185
- await fs5.ensureDir(path4.dirname(targetPath));
1186
- await fs5.writeFile(targetPath, file.content || "", "utf-8");
1187
- }
1188
- return;
1189
- }
1190
- } catch {
1191
- continue;
1192
- }
1747
+ function normalizeDir(dir) {
1748
+ return dir.replace(/^\.\//, "").replace(/\\/g, "/");
1749
+ }
1750
+ function resolveRegistryType(registryInput) {
1751
+ if (!registryInput) {
1752
+ return SCHEMA_CONFIG.defaultRegistryType;
1753
+ }
1754
+ if (SCHEMA_CONFIG.registryTypes.includes(registryInput)) {
1755
+ return registryInput;
1193
1756
  }
1757
+ logger.warn(`\u26A0\uFE0F Unknown registry type: ${registryInput}`);
1758
+ console.log(`Available registries: ${SCHEMA_CONFIG.registryTypes.join(", ")}`);
1759
+ console.log(`Using default: ${SCHEMA_CONFIG.defaultRegistryType}`);
1760
+ return SCHEMA_CONFIG.defaultRegistryType;
1194
1761
  }
1195
- async function installVariantsIndex(cdnUrls, config) {
1196
- for (const baseUrl of cdnUrls) {
1197
- try {
1198
- const url = `${baseUrl}/components/variants/index.json`;
1199
- const response = await fetch4(url);
1200
- if (response.ok) {
1201
- const component = await response.json();
1202
- for (const file of component.files) {
1203
- const fileName = path4.basename(file.path);
1204
- const targetDir = SCHEMA_CONFIG.defaultDirectories.variants;
1205
- const targetPath = path4.join(process.cwd(), targetDir, fileName);
1206
- await fs5.ensureDir(path4.dirname(targetPath));
1207
- await fs5.writeFile(targetPath, file.content || "", "utf-8");
1762
+ async function installComponentsIndex(registryType, config) {
1763
+ const spinner = ora4("Installing components index...").start();
1764
+ try {
1765
+ const cdnUrls = SCHEMA_CONFIG.cdnBaseUrls;
1766
+ for (const baseUrl of cdnUrls) {
1767
+ try {
1768
+ const url = `${baseUrl}/components/index.json`;
1769
+ const response = await fetch3(url);
1770
+ if (response.ok) {
1771
+ const component = await response.json();
1772
+ for (const file of component.files) {
1773
+ const fileName = path8.basename(file.path);
1774
+ const targetDir = config.componentsDir;
1775
+ const targetPath = path8.join(process.cwd(), targetDir, fileName);
1776
+ await fs7.ensureDir(path8.dirname(targetPath));
1777
+ await fs7.writeFile(targetPath, file.content || "", "utf-8");
1778
+ }
1779
+ spinner.succeed("Installed components index");
1780
+ return;
1208
1781
  }
1209
- return;
1782
+ } catch {
1783
+ continue;
1210
1784
  }
1211
- } catch {
1212
- continue;
1213
1785
  }
1786
+ spinner.info("Components index not found in registry (optional)");
1787
+ } catch (error) {
1788
+ spinner.fail("Could not install components index");
1214
1789
  }
1215
1790
  }
1216
- async function createUtilsFile(libDir, typescript) {
1217
- const utilsContent = `import { type ClassValue, clsx } from "clsx"
1218
- import { twMerge } from "tailwind-merge"
1219
-
1220
- export function cn(...inputs: ClassValue[]) {
1221
- return twMerge(clsx(inputs))
1222
- }`;
1223
- const fileName = typescript ? "utils.ts" : "utils.js";
1224
- const filePath = path4.join(process.cwd(), libDir, fileName);
1225
- await fs5.writeFile(filePath, utilsContent, "utf-8");
1226
- }
1227
1791
 
1228
1792
  // src/commands/build.ts
1229
- import fs6 from "fs-extra";
1230
- import path5 from "path";
1231
- import chalk5 from "chalk";
1232
- import ora3 from "ora";
1793
+ import fs8 from "fs-extra";
1794
+ import path9 from "path";
1795
+ import chalk7 from "chalk";
1796
+ import ora5 from "ora";
1233
1797
 
1234
1798
  // src/registry/build-schema.ts
1235
1799
  import { z as z2 } from "zod";
@@ -1323,6 +1887,11 @@ function generateConfigSchema() {
1323
1887
  "default": true,
1324
1888
  "description": SCHEMA_CONFIG.fieldDescriptions.typescript
1325
1889
  },
1890
+ "globalCss": {
1891
+ "type": "string",
1892
+ "default": "src/index.css",
1893
+ "description": SCHEMA_CONFIG.fieldDescriptions.globalCss
1894
+ },
1326
1895
  "aliases": {
1327
1896
  "type": "object",
1328
1897
  "additionalProperties": {
@@ -1450,44 +2019,44 @@ function generateRegistryItemSchema() {
1450
2019
  // src/commands/build.ts
1451
2020
  async function buildCommand(registryPath = "./src/registry.json", options = {}) {
1452
2021
  const buildOptions = {
1453
- cwd: path5.resolve(options.cwd || process.cwd()),
1454
- registryFile: path5.resolve(registryPath),
1455
- outputDir: path5.resolve(options.output || "./packages/registry/r")
2022
+ cwd: path9.resolve(options.cwd || process.cwd()),
2023
+ registryFile: path9.resolve(registryPath),
2024
+ outputDir: path9.resolve(options.output || "./packages/registry/r")
1456
2025
  };
1457
- console.log(chalk5.blue(CLI_MESSAGES.info.building));
2026
+ console.log(chalk7.blue(CLI_MESSAGES.info.building));
1458
2027
  try {
1459
- const registryContent = await fs6.readFile(buildOptions.registryFile, "utf-8");
2028
+ const registryContent = await fs8.readFile(buildOptions.registryFile, "utf-8");
1460
2029
  const registryData = JSON.parse(registryContent);
1461
2030
  const registry = registrySchema.parse(registryData);
1462
- await fs6.ensureDir(buildOptions.outputDir);
2031
+ await ensureVariantsIndexItem(registry, buildOptions.cwd);
2032
+ await fs8.ensureDir(buildOptions.outputDir);
1463
2033
  await generateSchemaFiles(buildOptions.outputDir);
1464
- const spinner = ora3(CLI_MESSAGES.info.processingComponents).start();
2034
+ const spinner = ora5(CLI_MESSAGES.info.processingComponents).start();
1465
2035
  for (const item of registry.items) {
1466
2036
  spinner.text = `Building ${item.name}...`;
1467
2037
  item.$schema = "https://ui.buildy.tw/schema/registry-item.json";
1468
2038
  for (const file of item.files) {
1469
- const filePath = path5.resolve(buildOptions.cwd, file.path);
1470
- if (await fs6.pathExists(filePath)) {
1471
- file.content = await fs6.readFile(filePath, "utf-8");
2039
+ const filePath = path9.resolve(buildOptions.cwd, file.path);
2040
+ if (await fs8.pathExists(filePath)) {
2041
+ file.content = await fs8.readFile(filePath, "utf-8");
1472
2042
  } else {
1473
2043
  throw new Error(CLI_MESSAGES.errors.fileNotFound(file.path));
1474
2044
  }
1475
2045
  }
1476
2046
  const validatedItem = registryItemSchema.parse(item);
1477
2047
  const typeDir = getOutputDir(validatedItem.type);
1478
- const outputPath = path5.join(buildOptions.outputDir, typeDir);
1479
- await fs6.ensureDir(outputPath);
1480
- const outputFile = path5.join(outputPath, `${validatedItem.name}.json`);
1481
- await fs6.writeFile(outputFile, JSON.stringify(validatedItem, null, 2));
2048
+ const outputPath = path9.join(buildOptions.outputDir, typeDir);
2049
+ await fs8.ensureDir(outputPath);
2050
+ const outputFile = path9.join(outputPath, `${validatedItem.name}.json`);
2051
+ await fs8.writeFile(outputFile, JSON.stringify(validatedItem, null, 2));
1482
2052
  }
1483
2053
  spinner.succeed(CLI_MESSAGES.status.builtComponents(registry.items.length));
1484
2054
  await createIndexFile(registry, buildOptions.outputDir);
1485
- console.log(chalk5.green(`\u2705 ${CLI_MESSAGES.success.registryBuilt}`));
2055
+ console.log(chalk7.green(`\u2705 ${CLI_MESSAGES.success.registryBuilt}`));
1486
2056
  console.log(`Output: ${buildOptions.outputDir}`);
1487
- console.log(chalk5.green(`\u2705 ${CLI_MESSAGES.success.schemasGenerated}`));
2057
+ console.log(chalk7.green(`\u2705 ${CLI_MESSAGES.success.schemasGenerated}`));
1488
2058
  } catch (error) {
1489
- console.error(chalk5.red(`\u274C ${CLI_MESSAGES.errors.buildFailed}`), error.message);
1490
- process.exit(1);
2059
+ handleError(error);
1491
2060
  }
1492
2061
  }
1493
2062
  var BUILD_OUTPUT_FOLDERS = {
@@ -1517,39 +2086,97 @@ async function createIndexFile(registry, outputDir) {
1517
2086
  lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
1518
2087
  registry: registry?.registry || SCHEMA_CONFIG.defaultRegistryType
1519
2088
  };
1520
- await fs6.writeFile(
1521
- path5.join(outputDir, "index.json"),
2089
+ await fs8.writeFile(
2090
+ path9.join(outputDir, "index.json"),
1522
2091
  JSON.stringify(index, null, 2)
1523
2092
  );
1524
2093
  }
1525
2094
  async function generateSchemaFiles(outputDir) {
1526
- const registryBaseDir = path5.dirname(outputDir);
1527
- const schemaDir = path5.join(registryBaseDir, "schema");
1528
- await fs6.ensureDir(schemaDir);
2095
+ const registryBaseDir = path9.dirname(outputDir);
2096
+ const schemaDir = path9.join(registryBaseDir, "schema");
2097
+ await fs8.ensureDir(schemaDir);
1529
2098
  const configSchemaJson = generateConfigSchema();
1530
2099
  const registrySchemaJson = generateRegistrySchema();
1531
2100
  const registryItemSchemaJson = generateRegistryItemSchema();
1532
- await fs6.writeFile(
1533
- path5.join(registryBaseDir, "schema.json"),
2101
+ await fs8.writeFile(
2102
+ path9.join(registryBaseDir, "schema.json"),
1534
2103
  JSON.stringify(configSchemaJson, null, 2)
1535
2104
  );
1536
- await fs6.writeFile(
1537
- path5.join(schemaDir, "registry.json"),
2105
+ await fs8.writeFile(
2106
+ path9.join(schemaDir, "registry.json"),
1538
2107
  JSON.stringify(registrySchemaJson, null, 2)
1539
2108
  );
1540
- await fs6.writeFile(
1541
- path5.join(schemaDir, "registry-item.json"),
2109
+ await fs8.writeFile(
2110
+ path9.join(schemaDir, "registry-item.json"),
1542
2111
  JSON.stringify(registryItemSchemaJson, null, 2)
1543
2112
  );
1544
2113
  }
2114
+ async function ensureVariantsIndexItem(registry, cwd) {
2115
+ const indexSourcePath = path9.join(cwd, "src/variants/index.ts");
2116
+ if (!await fs8.pathExists(indexSourcePath)) {
2117
+ return;
2118
+ }
2119
+ const sourceContent = await fs8.readFile(indexSourcePath, "utf-8");
2120
+ const dependencies = extractFileDependencies(sourceContent);
2121
+ const exportedModules = extractExportedModules(sourceContent);
2122
+ const indexItem = {
2123
+ name: "index",
2124
+ type: "registry:variants",
2125
+ description: exportedModules.length > 0 ? `Variant exports: ${exportedModules.join(", ")}` : "Variants export index",
2126
+ dependencies,
2127
+ devDependencies: [],
2128
+ files: [
2129
+ {
2130
+ path: path9.relative(cwd, indexSourcePath).replace(/\\/g, "/")
2131
+ }
2132
+ ]
2133
+ };
2134
+ const items = Array.isArray(registry.items) ? registry.items : [];
2135
+ const existingIndexIdx = items.findIndex(
2136
+ (item) => item && item.name === "index"
2137
+ );
2138
+ if (existingIndexIdx >= 0) {
2139
+ items[existingIndexIdx] = {
2140
+ ...items[existingIndexIdx],
2141
+ ...indexItem
2142
+ };
2143
+ } else {
2144
+ items.push(indexItem);
2145
+ }
2146
+ }
2147
+ function extractExportedModules(content) {
2148
+ const exports = /* @__PURE__ */ new Set();
2149
+ const starExportRegex = /export\s+\*\s+from\s+['"]\.\/([^'"]+)['"]/g;
2150
+ const namedExportRegex = /export\s+\{[^}]+\}\s+from\s+['"]\.\/([^'"]+)['"]/g;
2151
+ let match;
2152
+ while ((match = starExportRegex.exec(content)) !== null) {
2153
+ exports.add(match[1]);
2154
+ }
2155
+ while ((match = namedExportRegex.exec(content)) !== null) {
2156
+ exports.add(match[1]);
2157
+ }
2158
+ return [...exports];
2159
+ }
2160
+ function extractFileDependencies(content) {
2161
+ const dependencies = /* @__PURE__ */ new Set();
2162
+ const importRegex = /import\s+[^;\n]+?from\s+['"]([^'"]+)['"]/g;
2163
+ let match;
2164
+ while ((match = importRegex.exec(content)) !== null) {
2165
+ const moduleName = match[1];
2166
+ if (isExternalDependency(moduleName)) {
2167
+ dependencies.add(moduleName);
2168
+ }
2169
+ }
2170
+ return [...dependencies];
2171
+ }
1545
2172
 
1546
2173
  // src/commands/scan.ts
1547
- import fs7 from "fs-extra";
1548
- import path6 from "path";
1549
- import chalk6 from "chalk";
1550
- import ora4 from "ora";
2174
+ import fs9 from "fs-extra";
2175
+ import path10 from "path";
2176
+ import chalk8 from "chalk";
2177
+ import ora6 from "ora";
1551
2178
  import { glob } from "glob";
1552
- import * as ts from "typescript";
2179
+ import * as ts2 from "typescript";
1553
2180
  var DEV_PATTERNS = [
1554
2181
  "@types/",
1555
2182
  "eslint",
@@ -1574,28 +2201,28 @@ async function scanCommand(options = {}) {
1574
2201
  const registryName = options.registry || SCHEMA_CONFIG.defaultRegistryType;
1575
2202
  const registryPath = `./${registryName}`;
1576
2203
  const scanOptions = {
1577
- cwd: path6.resolve(options.cwd || process.cwd()),
1578
- registry: path6.resolve(registryPath),
1579
- outputFile: path6.resolve(options.output || "./src/registry.json"),
1580
- sourceDir: path6.resolve(options.source || "./src")
2204
+ cwd: path10.resolve(options.cwd || process.cwd()),
2205
+ registry: path10.resolve(registryPath),
2206
+ outputFile: path10.resolve(options.output || "./src/registry.json"),
2207
+ sourceDir: path10.resolve(options.source || "./src")
1581
2208
  };
1582
- console.log(chalk6.blue(`\u{1F50D} ${CLI_MESSAGES.info.scanningComponents(registryName)}`));
2209
+ console.log(chalk8.blue(`\u{1F50D} ${CLI_MESSAGES.info.scanningComponents(registryName)}`));
1583
2210
  try {
1584
- const spinner = ora4(CLI_MESSAGES.info.scanningDirectories).start();
1585
- const componentsDir = path6.resolve(scanOptions.cwd, normalizeDir2(SCHEMA_CONFIG.defaultDirectories.components));
1586
- const uiDir = path6.join(componentsDir, "ui");
1587
- const blocksDir = path6.resolve(scanOptions.cwd, normalizeDir2(SCHEMA_CONFIG.defaultDirectories.blocks));
1588
- const layoutsDir = path6.resolve(scanOptions.cwd, normalizeDir2(SCHEMA_CONFIG.defaultDirectories.layouts));
1589
- const libDir = path6.resolve(scanOptions.cwd, normalizeDir2(SCHEMA_CONFIG.defaultDirectories.lib));
1590
- const variantsDir = path6.resolve(scanOptions.cwd, normalizeDir2(SCHEMA_CONFIG.defaultDirectories.variants));
2211
+ const spinner = ora6(CLI_MESSAGES.info.scanningDirectories).start();
2212
+ const componentsDir = path10.resolve(scanOptions.cwd, normalizeDir2(SCHEMA_CONFIG.defaultDirectories.components));
2213
+ const uiDir = path10.join(componentsDir, "ui");
2214
+ const blocksDir = path10.resolve(scanOptions.cwd, normalizeDir2(SCHEMA_CONFIG.defaultDirectories.blocks));
2215
+ const layoutsDir = path10.resolve(scanOptions.cwd, normalizeDir2(SCHEMA_CONFIG.defaultDirectories.layouts));
2216
+ const libDir = path10.resolve(scanOptions.cwd, normalizeDir2(SCHEMA_CONFIG.defaultDirectories.lib));
2217
+ const variantsDir = path10.resolve(scanOptions.cwd, normalizeDir2(SCHEMA_CONFIG.defaultDirectories.variants));
1591
2218
  const uiComponents = await scanDirectory(uiDir, "registry:ui");
1592
2219
  const compositeComponents = await scanDirectoryFlat(componentsDir, "registry:composite", ["index.ts"]);
1593
2220
  const variantComponents = await scanDirectory(variantsDir, "registry:variants", ["index.ts"]);
1594
2221
  const blockComponents = await scanDirectory(blocksDir, "registry:block");
1595
2222
  const layoutComponents = await scanDirectory(layoutsDir, "registry:layout");
1596
2223
  const libComponents = await scanDirectory(libDir, "registry:lib");
1597
- const variantsIndexItem = await scanSingleFile(path6.join(variantsDir, "index.ts"), "registry:variants");
1598
- const componentsIndexItem = await scanSingleFile(path6.join(componentsDir, "index.ts"), "registry:composite");
2224
+ const variantsIndexItem = await scanSingleFile(path10.join(variantsDir, "index.ts"), "registry:variants");
2225
+ const componentsIndexItem = await scanSingleFile(path10.join(componentsDir, "index.ts"), "registry:composite");
1599
2226
  const allComponentsRaw = [
1600
2227
  ...uiComponents,
1601
2228
  ...compositeComponents,
@@ -1631,16 +2258,16 @@ async function scanCommand(options = {}) {
1631
2258
  lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
1632
2259
  registry: registryName
1633
2260
  };
1634
- await fs7.ensureDir(path6.dirname(scanOptions.outputFile));
1635
- await fs7.writeFile(scanOptions.outputFile, JSON.stringify(registry, null, 2));
2261
+ await fs9.ensureDir(path10.dirname(scanOptions.outputFile));
2262
+ await fs9.writeFile(scanOptions.outputFile, JSON.stringify(registry, null, 2));
1636
2263
  spinner.succeed(CLI_MESSAGES.status.scannedComponents(allComponents.length));
1637
- console.log(chalk6.green(`\u2705 ${CLI_MESSAGES.success.registryGenerated(registryName)}`));
2264
+ console.log(chalk8.green(`\u2705 ${CLI_MESSAGES.success.registryGenerated(registryName)}`));
1638
2265
  console.log(`Output: ${scanOptions.outputFile}`);
1639
2266
  const summary = allComponents.reduce((acc, comp) => {
1640
2267
  acc[comp.type] = (acc[comp.type] || 0) + 1;
1641
2268
  return acc;
1642
2269
  }, {});
1643
- console.log(chalk6.blue("\n\u{1F4CA} Component Summary:"));
2270
+ console.log(chalk8.blue("\n\u{1F4CA} Component Summary:"));
1644
2271
  Object.entries(summary).forEach(([type, count]) => {
1645
2272
  console.log(` ${type}: ${count}`);
1646
2273
  });
@@ -1650,30 +2277,29 @@ async function scanCommand(options = {}) {
1650
2277
  comp.dependencies.forEach((dep) => allDeps.add(dep));
1651
2278
  comp.devDependencies.forEach((dep) => allDevDeps.add(dep));
1652
2279
  });
1653
- console.log(chalk6.blue("\n\u{1F4E6} Dependencies Summary:"));
2280
+ console.log(chalk8.blue("\n\u{1F4E6} Dependencies Summary:"));
1654
2281
  console.log(` Dependencies: ${allDeps.size} unique (${Array.from(allDeps).join(", ") || "none"})`);
1655
2282
  console.log(` DevDependencies: ${allDevDeps.size} unique (${Array.from(allDevDeps).join(", ") || "none"})`);
1656
2283
  } catch (error) {
1657
- console.error(chalk6.red(`\u274C ${CLI_MESSAGES.errors.scanFailed}`), error.message);
1658
- process.exit(1);
2284
+ handleError(error);
1659
2285
  }
1660
2286
  }
1661
2287
  async function scanDirectory(dirPath, type, ignorePatterns = []) {
1662
- if (!await fs7.pathExists(dirPath)) {
2288
+ if (!await fs9.pathExists(dirPath)) {
1663
2289
  return [];
1664
2290
  }
1665
2291
  const components = [];
1666
- const pattern = path6.join(dirPath, "**/*.{ts,tsx,js,jsx}").replace(/\\/g, "/");
2292
+ const pattern = path10.join(dirPath, "**/*.{ts,tsx,js,jsx}").replace(/\\/g, "/");
1667
2293
  const ignore = ignorePatterns.map((p) => p.replace(/\\/g, "/"));
1668
2294
  const files = await glob(pattern, { windowsPathsNoEscape: true, ignore });
1669
2295
  for (const filePath of files) {
1670
- const relativePath = path6.relative(process.cwd(), filePath).replace(/\\/g, "/");
1671
- const fileName = path6.basename(filePath, path6.extname(filePath));
2296
+ const relativePath = path10.relative(process.cwd(), filePath).replace(/\\/g, "/");
2297
+ const fileName = path10.basename(filePath, path10.extname(filePath));
1672
2298
  if (fileName === "index" || fileName.startsWith("_")) {
1673
2299
  continue;
1674
2300
  }
1675
2301
  try {
1676
- const content = await fs7.readFile(filePath, "utf-8");
2302
+ const content = await fs9.readFile(filePath, "utf-8");
1677
2303
  const description = extractDescription(content);
1678
2304
  if (!hasValidExports(content)) {
1679
2305
  continue;
@@ -1696,20 +2322,20 @@ async function scanDirectory(dirPath, type, ignorePatterns = []) {
1696
2322
  return components;
1697
2323
  }
1698
2324
  async function scanDirectoryFlat(dirPath, type, ignoreFiles = []) {
1699
- if (!await fs7.pathExists(dirPath)) {
2325
+ if (!await fs9.pathExists(dirPath)) {
1700
2326
  return [];
1701
2327
  }
1702
2328
  const components = [];
1703
- const pattern = path6.join(dirPath, "*.{ts,tsx,js,jsx}").replace(/\\/g, "/");
2329
+ const pattern = path10.join(dirPath, "*.{ts,tsx,js,jsx}").replace(/\\/g, "/");
1704
2330
  const files = await glob(pattern, { windowsPathsNoEscape: true });
1705
2331
  for (const filePath of files) {
1706
- const relativePath = path6.relative(process.cwd(), filePath).replace(/\\/g, "/");
1707
- const fileName = path6.basename(filePath, path6.extname(filePath));
1708
- if (ignoreFiles.includes(fileName + path6.extname(filePath)) || fileName.startsWith("_")) {
2332
+ const relativePath = path10.relative(process.cwd(), filePath).replace(/\\/g, "/");
2333
+ const fileName = path10.basename(filePath, path10.extname(filePath));
2334
+ if (ignoreFiles.includes(fileName + path10.extname(filePath)) || fileName.startsWith("_")) {
1709
2335
  continue;
1710
2336
  }
1711
2337
  try {
1712
- const content = await fs7.readFile(filePath, "utf-8");
2338
+ const content = await fs9.readFile(filePath, "utf-8");
1713
2339
  const description = extractDescription(content);
1714
2340
  if (!hasValidExports(content)) {
1715
2341
  continue;
@@ -1732,17 +2358,17 @@ async function scanDirectoryFlat(dirPath, type, ignoreFiles = []) {
1732
2358
  return components;
1733
2359
  }
1734
2360
  async function scanSingleFile(filePath, type) {
1735
- if (!await fs7.pathExists(filePath)) {
2361
+ if (!await fs9.pathExists(filePath)) {
1736
2362
  return null;
1737
2363
  }
1738
2364
  try {
1739
- const content = await fs7.readFile(filePath, "utf-8");
2365
+ const content = await fs9.readFile(filePath, "utf-8");
1740
2366
  const description = extractDescription(content);
1741
2367
  if (!hasValidExports(content)) {
1742
2368
  return null;
1743
2369
  }
1744
- const relativePath = path6.relative(process.cwd(), filePath).replace(/\\/g, "/");
1745
- const fileName = path6.basename(filePath, path6.extname(filePath));
2370
+ const relativePath = path10.relative(process.cwd(), filePath).replace(/\\/g, "/");
2371
+ const fileName = path10.basename(filePath, path10.extname(filePath));
1746
2372
  return {
1747
2373
  name: fileName,
1748
2374
  type,
@@ -1779,12 +2405,12 @@ async function analyzeComponentDependencies(files, cwd) {
1779
2405
  let description;
1780
2406
  for (const file of files) {
1781
2407
  try {
1782
- const filePath = path6.resolve(cwd, file.path);
1783
- const content = await fs7.readFile(filePath, "utf-8");
1784
- const sourceFile = ts.createSourceFile(
2408
+ const filePath = path10.resolve(cwd, file.path);
2409
+ const content = await fs9.readFile(filePath, "utf-8");
2410
+ const sourceFile = ts2.createSourceFile(
1785
2411
  file.path,
1786
2412
  content,
1787
- ts.ScriptTarget.Latest,
2413
+ ts2.ScriptTarget.Latest,
1788
2414
  true
1789
2415
  );
1790
2416
  const analysis = analyzeAST(sourceFile);
@@ -1809,9 +2435,9 @@ function analyzeAST(sourceFile) {
1809
2435
  let description;
1810
2436
  let hasExports = false;
1811
2437
  function visit(node) {
1812
- if (ts.isImportDeclaration(node)) {
2438
+ if (ts2.isImportDeclaration(node)) {
1813
2439
  const moduleSpecifier = node.moduleSpecifier;
1814
- if (ts.isStringLiteral(moduleSpecifier)) {
2440
+ if (ts2.isStringLiteral(moduleSpecifier)) {
1815
2441
  const moduleName = moduleSpecifier.text;
1816
2442
  if (isExternalDependency(moduleName)) {
1817
2443
  if (isDevDependency(moduleName)) {
@@ -1822,9 +2448,9 @@ function analyzeAST(sourceFile) {
1822
2448
  }
1823
2449
  }
1824
2450
  }
1825
- if (ts.isExportDeclaration(node)) {
2451
+ if (ts2.isExportDeclaration(node)) {
1826
2452
  hasExports = true;
1827
- } else if (ts.isExportAssignment(node)) {
2453
+ } else if (ts2.isExportAssignment(node)) {
1828
2454
  hasExports = true;
1829
2455
  } else if (hasExportModifier(node)) {
1830
2456
  hasExports = true;
@@ -1833,7 +2459,7 @@ function analyzeAST(sourceFile) {
1833
2459
  if (jsDocComment && !description) {
1834
2460
  description = jsDocComment;
1835
2461
  }
1836
- ts.forEachChild(node, visit);
2462
+ ts2.forEachChild(node, visit);
1837
2463
  }
1838
2464
  visit(sourceFile);
1839
2465
  return {
@@ -1849,16 +2475,16 @@ function isDevDependency(moduleName) {
1849
2475
  function hasExportModifier(node) {
1850
2476
  if ("modifiers" in node && node.modifiers) {
1851
2477
  return node.modifiers.some(
1852
- (mod) => mod.kind === ts.SyntaxKind.ExportKeyword
2478
+ (mod) => mod.kind === ts2.SyntaxKind.ExportKeyword
1853
2479
  );
1854
2480
  }
1855
2481
  return false;
1856
2482
  }
1857
2483
  function getJSDocComment(node) {
1858
2484
  try {
1859
- const jsDocTags = ts.getJSDocCommentsAndTags(node);
2485
+ const jsDocTags = ts2.getJSDocCommentsAndTags(node);
1860
2486
  for (const tag of jsDocTags) {
1861
- if (ts.isJSDoc(tag) && tag.comment) {
2487
+ if (ts2.isJSDoc(tag) && tag.comment) {
1862
2488
  if (typeof tag.comment === "string") {
1863
2489
  return tag.comment.trim();
1864
2490
  } else if (Array.isArray(tag.comment)) {
@@ -1878,19 +2504,427 @@ function normalizeDir2(dir) {
1878
2504
  return dir.replace(/^\.\//, "").replace(/\\/g, "/");
1879
2505
  }
1880
2506
 
2507
+ // src/commands/list.ts
2508
+ import chalk9 from "chalk";
2509
+ var LIST_EXCLUDED_COMPONENT_TYPES = ["registry:variants", "registry:lib"];
2510
+ async function listCommand(options = {}) {
2511
+ const registryType = resolveRegistryType2(options.registry);
2512
+ const requestOptions = {
2513
+ excludeTypes: LIST_EXCLUDED_COMPONENT_TYPES,
2514
+ noCache: options.cache === false
2515
+ };
2516
+ try {
2517
+ const components = await getAllComponents(registryType, requestOptions);
2518
+ if (options.json) {
2519
+ console.log(JSON.stringify(components, null, 2));
2520
+ return;
2521
+ }
2522
+ const byType = /* @__PURE__ */ new Map();
2523
+ for (const component of components) {
2524
+ const group = byType.get(component.type) ?? [];
2525
+ group.push(component);
2526
+ byType.set(component.type, group);
2527
+ }
2528
+ const sortedGroups = Array.from(byType.entries()).sort((a, b) => a[0].localeCompare(b[0]));
2529
+ if (sortedGroups.length === 0) {
2530
+ logger.warn(CLI_MESSAGES.errors.registryTempUnavailable);
2531
+ return;
2532
+ }
2533
+ logger.info(CLI_MESSAGES.info.listingComponents);
2534
+ for (const [type, group] of sortedGroups) {
2535
+ const entries = group.sort((a, b) => a.name.localeCompare(b.name));
2536
+ console.log(chalk9.cyan(`${type} (${entries.length} components)`));
2537
+ for (const component of entries) {
2538
+ const description = component.description ? chalk9.dim(component.description) : "";
2539
+ console.log(chalk9.white(` ${component.name.padEnd(14)}`) + description);
2540
+ }
2541
+ console.log("");
2542
+ }
2543
+ } catch (error) {
2544
+ logger.error(error.message);
2545
+ }
2546
+ }
2547
+ function resolveRegistryType2(registryInput) {
2548
+ if (!registryInput) {
2549
+ return SCHEMA_CONFIG.defaultRegistryType;
2550
+ }
2551
+ if (SCHEMA_CONFIG.registryTypes.includes(registryInput)) {
2552
+ return registryInput;
2553
+ }
2554
+ logger.warn(`\u26A0\uFE0F Unknown registry type: ${registryInput}`);
2555
+ logger.warn(`Available registries: ${SCHEMA_CONFIG.registryTypes.join(", ")}`);
2556
+ return SCHEMA_CONFIG.defaultRegistryType;
2557
+ }
2558
+
2559
+ // src/commands/diff.ts
2560
+ import fs10 from "fs-extra";
2561
+ import path11 from "path";
2562
+ import { glob as glob2 } from "glob";
2563
+ import chalk10 from "chalk";
2564
+ async function diffCommand(componentName, options = {}) {
2565
+ try {
2566
+ const registryType = resolveRegistryType3(options.registry);
2567
+ const config = await findConfig(registryType);
2568
+ const defaultConfig = config ?? {
2569
+ framework: SCHEMA_CONFIG.supportedFrameworks[0],
2570
+ typescript: true,
2571
+ globalCss: "src/index.css",
2572
+ aliases: SCHEMA_CONFIG.defaultAliases,
2573
+ registry: SCHEMA_CONFIG.defaultRegistry,
2574
+ componentsDir: SCHEMA_CONFIG.defaultDirectories.components,
2575
+ libDir: SCHEMA_CONFIG.defaultDirectories.lib
2576
+ };
2577
+ const installed = await scanLocalComponents(defaultConfig);
2578
+ if (installed.length === 0) {
2579
+ logger.warn(CLI_MESSAGES.errors.noLocalInstall);
2580
+ return;
2581
+ }
2582
+ const registryComponents = await getAllComponents(registryType, { noCache: options.cache === false });
2583
+ const registryIndex = new Map(registryComponents.map((item) => [item.name.toLowerCase(), item]));
2584
+ const targets = componentName ? installed.filter((item) => item.name.toLowerCase() === componentName.toLowerCase()) : installed;
2585
+ if (componentName && targets.length === 0) {
2586
+ logger.warn(`Component "${componentName}" not found in local project structure`);
2587
+ return;
2588
+ }
2589
+ const results = [];
2590
+ logger.info(CLI_MESSAGES.info.checkingForUpdates);
2591
+ for (const item of targets) {
2592
+ const remoteComponent = registryIndex.get(item.name.toLowerCase());
2593
+ if (!remoteComponent) {
2594
+ results.push({
2595
+ component: item.name,
2596
+ type: "unknown",
2597
+ status: "missing-remote",
2598
+ files: [{ path: item.filePath, changed: false }]
2599
+ });
2600
+ continue;
2601
+ }
2602
+ const fileSummary = await compareComponentFiles(item, remoteComponent, defaultConfig);
2603
+ const hasChanges = fileSummary.some((file) => file.changed);
2604
+ results.push({
2605
+ component: item.name,
2606
+ type: remoteComponent.type,
2607
+ status: hasChanges ? "update" : "up-to-date",
2608
+ files: fileSummary
2609
+ });
2610
+ }
2611
+ if (options.json) {
2612
+ console.log(JSON.stringify(results, null, 2));
2613
+ return;
2614
+ }
2615
+ const updates = results.filter((item) => item.status === "update").length;
2616
+ const upToDate = results.filter((item) => item.status === "up-to-date").length;
2617
+ for (const result of results) {
2618
+ if (result.status === "missing-remote") {
2619
+ logger.warn(`
2620
+ \u26A0\uFE0F ${result.component}: not found in registry`);
2621
+ continue;
2622
+ }
2623
+ const statusTitle = result.status === "update" ? `${chalk10.yellow("UPDATE")}` : chalk10.green("UP-TO-DATE");
2624
+ const title = `${statusTitle} ${result.component} (${result.type})`;
2625
+ logger.info(title);
2626
+ for (const file of result.files) {
2627
+ console.log(` ${chalk10.white(file.path)}`);
2628
+ if (file.changed && file.diff) {
2629
+ const preview = formatDiffPreview(file.diff, 120);
2630
+ console.log(colorDiff(preview));
2631
+ } else {
2632
+ console.log(chalk10.dim(" No changes"));
2633
+ }
2634
+ }
2635
+ }
2636
+ console.log(
2637
+ `
2638
+ ${CLI_MESSAGES.info.localDiffSummary} ${chalk10.yellow(updates)} component(s) have updates, ${chalk10.green(upToDate)} up to date`
2639
+ );
2640
+ if (updates > 0) {
2641
+ console.log('Run "ui8kit add <component> --force" to update.');
2642
+ }
2643
+ } catch (error) {
2644
+ handleError(error);
2645
+ }
2646
+ }
2647
+ async function compareComponentFiles(installed, remote, config) {
2648
+ const localContent = await fs10.readFile(installed.filePath, "utf-8");
2649
+ const remoteCandidate = remote.files.find((file) => {
2650
+ const candidateName = path11.basename(file.path);
2651
+ return candidateName.toLowerCase() === path11.basename(installed.filePath).toLowerCase();
2652
+ });
2653
+ if (!remoteCandidate) {
2654
+ return [{ path: installed.filePath, changed: false }];
2655
+ }
2656
+ const remoteContent = applyTransforms(remoteCandidate.content, config.aliases);
2657
+ const changed = hasDiff(localContent, remoteContent);
2658
+ return changed ? [{
2659
+ path: installed.filePath,
2660
+ changed: true,
2661
+ diff: buildUnifiedDiff(installed.filePath, `${remote.name}/${path11.basename(installed.filePath)}`, localContent, remoteContent)
2662
+ }] : [{ path: installed.filePath, changed: false }];
2663
+ }
2664
+ async function scanLocalComponents(config) {
2665
+ const componentsDir = path11.resolve(process.cwd(), config.componentsDir || SCHEMA_CONFIG.defaultDirectories.components);
2666
+ const componentsUiDir = path11.join(componentsDir, "ui");
2667
+ const blocksDir = path11.resolve(process.cwd(), SCHEMA_CONFIG.defaultDirectories.blocks);
2668
+ const layoutsDir = path11.resolve(process.cwd(), SCHEMA_CONFIG.defaultDirectories.layouts);
2669
+ const directories = [componentsUiDir, componentsDir, blocksDir, layoutsDir];
2670
+ const entries = [];
2671
+ const patterns = directories.map((dir) => path11.join(dir, "*.{ts,tsx}").replace(/\\/g, "/"));
2672
+ for (const pattern of patterns) {
2673
+ const baseDir = path11.dirname(pattern);
2674
+ if (!await fs10.pathExists(baseDir)) {
2675
+ continue;
2676
+ }
2677
+ const filePaths = await glob2(pattern, { windowsPathsNoEscape: true });
2678
+ for (const filePath of filePaths) {
2679
+ const fileName = path11.basename(filePath);
2680
+ if (fileName === "index.tsx" || fileName === "index.ts") {
2681
+ continue;
2682
+ }
2683
+ entries.push({
2684
+ name: path11.parse(fileName).name.toLowerCase(),
2685
+ filePath: path11.resolve(process.cwd(), filePath)
2686
+ });
2687
+ }
2688
+ }
2689
+ const uniqueByName = /* @__PURE__ */ new Map();
2690
+ for (const entry of entries) {
2691
+ if (!uniqueByName.has(entry.name)) {
2692
+ uniqueByName.set(entry.name, entry);
2693
+ }
2694
+ }
2695
+ return Array.from(uniqueByName.values());
2696
+ }
2697
+ function colorDiff(value) {
2698
+ return value.split("\n").map((line) => {
2699
+ if (line.startsWith("+")) {
2700
+ return chalk10.green(line);
2701
+ }
2702
+ if (line.startsWith("-")) {
2703
+ return chalk10.red(line);
2704
+ }
2705
+ return line;
2706
+ }).join("\n");
2707
+ }
2708
+ function resolveRegistryType3(registryInput) {
2709
+ if (!registryInput) {
2710
+ return SCHEMA_CONFIG.defaultRegistryType;
2711
+ }
2712
+ if (SCHEMA_CONFIG.registryTypes.includes(registryInput)) {
2713
+ return registryInput;
2714
+ }
2715
+ logger.warn(`\u26A0\uFE0F Unknown registry type: ${registryInput}`);
2716
+ logger.warn(`Available registries: ${SCHEMA_CONFIG.registryTypes.join(", ")}`);
2717
+ return SCHEMA_CONFIG.defaultRegistryType;
2718
+ }
2719
+
2720
+ // src/commands/cache.ts
2721
+ async function cacheClearCommand() {
2722
+ try {
2723
+ await clearCache();
2724
+ logger.success(`${CLI_MESSAGES.success.cacheCleared} (${getCacheDir()})`);
2725
+ } catch (error) {
2726
+ handleError(error);
2727
+ }
2728
+ }
2729
+
2730
+ // src/commands/info.ts
2731
+ import fs11 from "fs-extra";
2732
+ import os2 from "os";
2733
+ import path12 from "path";
2734
+ import fetch4 from "node-fetch";
2735
+ import chalk11 from "chalk";
2736
+
2737
+ // src/utils/cli-version.ts
2738
+ import { existsSync, readFileSync } from "node:fs";
2739
+ import { dirname, resolve } from "node:path";
2740
+ import { fileURLToPath } from "node:url";
2741
+ var __dirname = dirname(fileURLToPath(import.meta.url));
2742
+ function findPackageJsonPath() {
2743
+ const roots = [process.argv[1], __dirname];
2744
+ for (const rawRoot of roots) {
2745
+ if (!rawRoot) {
2746
+ continue;
2747
+ }
2748
+ let current = rawRoot.endsWith(".js") ? dirname(rawRoot) : rawRoot;
2749
+ for (let i = 0; i < 8; i += 1) {
2750
+ const candidate = resolve(current, "package.json");
2751
+ if (existsSync(candidate)) {
2752
+ return candidate;
2753
+ }
2754
+ const parent = dirname(current);
2755
+ if (parent === current) {
2756
+ break;
2757
+ }
2758
+ current = parent;
2759
+ }
2760
+ }
2761
+ return null;
2762
+ }
2763
+ function getCliVersion() {
2764
+ const packageJsonPath = findPackageJsonPath();
2765
+ if (!packageJsonPath) {
2766
+ return "0.0.0";
2767
+ }
2768
+ try {
2769
+ const pkg = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
2770
+ return pkg.version ?? "0.0.0";
2771
+ } catch {
2772
+ return "0.0.0";
2773
+ }
2774
+ }
2775
+
2776
+ // src/commands/info.ts
2777
+ async function infoCommand(options = {}) {
2778
+ const version = getCliVersion();
2779
+ const pm = await detectPackageManager();
2780
+ const cwd = process.cwd();
2781
+ const configStatus = await readConfigStatus();
2782
+ const cache = await readCacheStatus();
2783
+ const cdn = await checkPrimaryCdn();
2784
+ if (options.json) {
2785
+ console.log(JSON.stringify({
2786
+ version,
2787
+ node: process.version,
2788
+ os: `${os2.platform()} ${os2.arch()}`,
2789
+ packageManager: pm,
2790
+ cwd,
2791
+ config: configStatus.config,
2792
+ configFound: configStatus.found,
2793
+ cache,
2794
+ cdn,
2795
+ registry: SCHEMA_CONFIG.defaultRegistry
2796
+ }, null, 2));
2797
+ return;
2798
+ }
2799
+ console.log(`ui8kit v${version}`);
2800
+ console.log(`Node ${process.version}`);
2801
+ console.log(`OS ${os2.platform()} ${os2.arch()}`);
2802
+ console.log(`PM ${pm}`);
2803
+ console.log(`CWD ${cwd}`);
2804
+ console.log("");
2805
+ if (configStatus.found) {
2806
+ console.log(chalk11.green(`Config ${configStatus.path} (found)`));
2807
+ const config = configStatus.config;
2808
+ console.log(` framework ${config.framework}`);
2809
+ console.log(` typescript ${config.typescript}`);
2810
+ console.log(` globalCss ${config.globalCss}`);
2811
+ console.log(` componentsDir ${config.componentsDir}`);
2812
+ console.log(` libDir ${config.libDir}`);
2813
+ } else {
2814
+ console.log(chalk11.yellow("Config not found"));
2815
+ }
2816
+ console.log("");
2817
+ console.log(`Registry ${SCHEMA_CONFIG.defaultRegistry}`);
2818
+ console.log(`CDN ${cdn.url} (${cdn.ok ? "ok" : "failed"})`);
2819
+ console.log(`Cache ${cache.path} (${cache.items} items, ${cache.mb} MB)`);
2820
+ }
2821
+ async function readConfigStatus() {
2822
+ const candidatePaths = [
2823
+ path12.join(process.cwd(), "ui8kit.config.json"),
2824
+ path12.join(process.cwd(), "src", "ui8kit.config.json"),
2825
+ path12.join(process.cwd(), SCHEMA_CONFIG.defaultRegistryType, "ui8kit.config.json")
2826
+ ];
2827
+ for (const configPath of candidatePaths) {
2828
+ if (await fs11.pathExists(configPath)) {
2829
+ try {
2830
+ const config = await fs11.readJson(configPath);
2831
+ return {
2832
+ found: true,
2833
+ path: `./${path12.relative(process.cwd(), configPath).replace(/\\/g, "/")}`,
2834
+ config: {
2835
+ framework: config.framework ?? "unknown",
2836
+ typescript: config.typescript ?? false,
2837
+ globalCss: config.globalCss ?? "src/index.css",
2838
+ componentsDir: config.componentsDir ?? SCHEMA_CONFIG.defaultDirectories.components,
2839
+ libDir: config.libDir ?? SCHEMA_CONFIG.defaultDirectories.lib
2840
+ }
2841
+ };
2842
+ } catch {
2843
+ continue;
2844
+ }
2845
+ }
2846
+ }
2847
+ return {
2848
+ found: false,
2849
+ path: null,
2850
+ config: null
2851
+ };
2852
+ }
2853
+ async function readCacheStatus() {
2854
+ const cachePath = getCacheDir();
2855
+ let items = 0;
2856
+ let bytes = 0;
2857
+ if (await fs11.pathExists(cachePath)) {
2858
+ const result = await countCacheFiles(cachePath);
2859
+ items = result.count;
2860
+ bytes = result.bytes;
2861
+ }
2862
+ return {
2863
+ path: cachePath.replace(/\\/g, "/"),
2864
+ items,
2865
+ mb: `${(bytes / (1024 * 1024)).toFixed(1)}`
2866
+ };
2867
+ }
2868
+ async function countCacheFiles(dirPath) {
2869
+ let count = 0;
2870
+ let size = 0;
2871
+ const entries = await fs11.readdir(dirPath, { withFileTypes: true });
2872
+ for (const entry of entries) {
2873
+ const fullPath = path12.join(dirPath, entry.name);
2874
+ if (entry.isDirectory()) {
2875
+ const nested = await countCacheFiles(fullPath);
2876
+ count += nested.count;
2877
+ size += nested.bytes;
2878
+ continue;
2879
+ }
2880
+ count += 1;
2881
+ const stat = await fs11.stat(fullPath);
2882
+ size += stat.size;
2883
+ }
2884
+ return { count, bytes: size };
2885
+ }
2886
+ async function checkPrimaryCdn() {
2887
+ const url = SCHEMA_CONFIG.cdnBaseUrls[0];
2888
+ try {
2889
+ const response = await fetch4(`${url}/index.json`, { method: "HEAD" });
2890
+ if (response.status >= 200 && response.status < 400) {
2891
+ return { url, ok: true };
2892
+ }
2893
+ } catch {
2894
+ }
2895
+ return { url, ok: false };
2896
+ }
2897
+
1881
2898
  // src/index.ts
2899
+ import { resolve as resolve2 } from "node:path";
1882
2900
  var program = new Command();
1883
- program.name("ui8kit").description("A CLI for adding UI components to your Vite React projects (UI8Kit registry)").version("0.1.0");
2901
+ program.option("-c, --cwd <dir>", "Working directory", process.cwd()).option("-v, --verbose", "Enable verbose output").option("--no-cache", "Bypass registry cache").name("ui8kit").description("A CLI for adding UI components to your Vite React projects (UI8Kit registry)").version(getCliVersion());
2902
+ program.command("list").description("List available components in registry").option("-r, --registry <type>", "Registry type: ui", "ui").option("--json", "Output raw JSON").action((options) => listCommand(options));
2903
+ program.command("diff").description("Show local vs registry differences").argument("[component]", "Component name").option("-r, --registry <type>", "Registry type: ui", "ui").option("--json", "Output diff in machine-readable JSON").action((component, options) => diffCommand(component, options));
2904
+ program.command("cache").description("Manage local cache").command("clear").description("Clear registry cache").action(cacheClearCommand);
2905
+ program.command("info").description("Show environment and config diagnostics").option("--json", "Output diagnostics as JSON").action(infoCommand);
1884
2906
  program.command("init").description("Initialize UI8Kit structure in your project").option("-y, --yes", "Skip prompts and use defaults").option("-r, --registry <type>", "Registry type: ui", "ui").action(initCommand);
1885
- program.command("add").description("Add components to your project from the registry").argument("[components...]", "Components to add").option("-a, --all", "Install all available components").option("-f, --force", "Overwrite existing files").option("-r, --registry <type>", "Registry type: ui", "ui").option("--dry-run", "Show what would be installed without installing").option("--retry", "Enable retry logic for unreliable connections").action(addCommand);
1886
- program.command("scan").description("Scan and generate registry from existing components").option("-r, --registry <type|path>", "Registry type (ui) or custom path", "ui").option("-o, --output <file>", "Output registry file").option("-s, --source <dir>", "Source directory to scan").option("--cwd <dir>", "Working directory").action(async (options) => {
2907
+ program.command("add").description("Add components to your project from the registry").argument("[components...]", "Components to add").option("-a, --all", "Install all available components").option("-f, --force", "Overwrite existing files").option("-r, --registry <type>", "Registry type: ui", "ui").option("--dry-run", "Show what would be installed without installing").option("--retry", "Aggressive retry mode (3 attempts per CDN request)").action(addCommand);
2908
+ program.command("scan").description("Scan and generate registry from existing components").option("-r, --registry <type|path>", "Registry type (ui) or custom path", "ui").option("-o, --output <file>", "Output registry file").option("-s, --source <dir>", "Source directory to scan").action(async (options) => {
1887
2909
  await scanCommand(options);
1888
2910
  });
1889
- program.command("build").description("Build components registry").argument("[registry]", "Path to registry.json file", "./src/registry.json").option("-o, --output <path>", "Output directory", "./packages/registry/r").option("-c, --cwd <cwd>", "Working directory", process.cwd()).action(buildCommand);
2911
+ program.command("build").description("Build components registry").argument("[registry]", "Path to registry.json file", "./src/registry.json").option("-o, --output <path>", "Output directory", "./packages/registry/r").action(buildCommand);
1890
2912
  program.on("command:*", () => {
1891
- console.error(chalk7.red(`Invalid command: ${program.args.join(" ")}`));
2913
+ console.error(chalk12.red(`Invalid command: ${program.args.join(" ")}`));
1892
2914
  console.log("See --help for a list of available commands.");
1893
2915
  process.exit(1);
1894
2916
  });
2917
+ program.hook("preAction", (_, actionCommand) => {
2918
+ const actionOptions = actionCommand?.opts?.();
2919
+ const globalOptions = program.opts();
2920
+ const verbose = globalOptions.verbose || actionOptions?.verbose;
2921
+ const cwd = actionOptions?.cwd || globalOptions.cwd;
2922
+ if (verbose) {
2923
+ logger.setVerbose(true);
2924
+ }
2925
+ if (cwd && resolve2(process.cwd()) !== resolve2(cwd)) {
2926
+ process.chdir(cwd);
2927
+ }
2928
+ });
1895
2929
  program.parse();
1896
2930
  //# sourceMappingURL=index.js.map