@docyrus/cli 0.3.1 → 0.4.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/cli.js CHANGED
@@ -1,25 +1,478 @@
1
1
  #!/usr/bin/env node
2
+ import { join, resolve, dirname } from 'path';
3
+ import { URL as URL$1, fileURLToPath } from 'url';
2
4
  import { Command } from 'commander';
3
5
  import { RestApiClient, AuthenticationError as AuthenticationError$1 } from '@docyrus/api-client';
4
6
  import { createServer } from 'http';
5
- import { URL, fileURLToPath } from 'url';
6
7
  import { randomBytes, createDecipheriv, createCipheriv, scryptSync, createHash } from 'crypto';
7
8
  import open from 'open';
8
9
  import { existsSync, readFileSync, mkdirSync, writeFileSync, unlinkSync, constants as constants$1 } from 'fs';
9
- import { join, resolve, dirname } from 'path';
10
10
  import { homedir, arch, release, platform, hostname, userInfo } from 'os';
11
11
  import Conf from 'conf';
12
12
  import chalk4 from 'chalk';
13
13
  import ora from 'ora';
14
14
  import { select, input, password, confirm } from '@inquirer/prompts';
15
- import { exec } from 'child_process';
15
+ import { exec, execFileSync } from 'child_process';
16
16
  import { promisify } from 'util';
17
17
  import { access, constants, writeFile, readFile, mkdir, cp, rm, readdir } from 'fs/promises';
18
18
  import { downloadTemplate } from 'giget';
19
19
  import { generateFromOpenAPI } from '@docyrus/tanstack-db-generator';
20
20
 
21
+ var __create = Object.create;
22
+ var __defProp = Object.defineProperty;
23
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
24
+ var __getOwnPropNames = Object.getOwnPropertyNames;
25
+ var __getProtoOf = Object.getPrototypeOf;
26
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
27
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
28
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
29
+ }) : x)(function(x) {
30
+ if (typeof require !== "undefined") return require.apply(this, arguments);
31
+ throw Error('Dynamic require of "' + x + '" is not supported');
32
+ });
33
+ var __esm = (fn, res) => function __init() {
34
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
35
+ };
36
+ var __commonJS = (cb, mod) => function __require2() {
37
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
38
+ };
39
+ var __copyProps = (to, from, except, desc) => {
40
+ if (from && typeof from === "object" || typeof from === "function") {
41
+ for (let key of __getOwnPropNames(from))
42
+ if (!__hasOwnProp.call(to, key) && key !== except)
43
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
44
+ }
45
+ return to;
46
+ };
47
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
48
+ // If the importer is in node compatibility mode or this is not an ESM
49
+ // file that has been converted to a CommonJS file using a Babel-
50
+ // compatible transform (i.e. "__esModule" has not been set), then set
51
+ // "default" to the CommonJS "module.exports" for node compatibility.
52
+ __defProp(target, "default", { value: mod, enumerable: true }) ,
53
+ mod
54
+ ));
55
+ var init_esm_shims = __esm({
56
+ "../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/esm_shims.js"() {
57
+ }
58
+ });
59
+
60
+ // ../../node_modules/.pnpm/dotenv@17.2.4/node_modules/dotenv/package.json
61
+ var require_package = __commonJS({
62
+ "../../node_modules/.pnpm/dotenv@17.2.4/node_modules/dotenv/package.json"(exports$1, module) {
63
+ module.exports = {
64
+ name: "dotenv",
65
+ version: "17.2.4",
66
+ description: "Loads environment variables from .env file",
67
+ main: "lib/main.js",
68
+ types: "lib/main.d.ts",
69
+ exports: {
70
+ ".": {
71
+ types: "./lib/main.d.ts",
72
+ require: "./lib/main.js",
73
+ default: "./lib/main.js"
74
+ },
75
+ "./config": "./config.js",
76
+ "./config.js": "./config.js",
77
+ "./lib/env-options": "./lib/env-options.js",
78
+ "./lib/env-options.js": "./lib/env-options.js",
79
+ "./lib/cli-options": "./lib/cli-options.js",
80
+ "./lib/cli-options.js": "./lib/cli-options.js",
81
+ "./package.json": "./package.json"
82
+ },
83
+ scripts: {
84
+ "dts-check": "tsc --project tests/types/tsconfig.json",
85
+ lint: "standard",
86
+ pretest: "npm run lint && npm run dts-check",
87
+ test: "tap run tests/**/*.js --allow-empty-coverage --disable-coverage --timeout=60000",
88
+ "test:coverage": "tap run tests/**/*.js --show-full-coverage --timeout=60000 --coverage-report=text --coverage-report=lcov",
89
+ prerelease: "npm test",
90
+ release: "standard-version"
91
+ },
92
+ repository: {
93
+ type: "git",
94
+ url: "git://github.com/motdotla/dotenv.git"
95
+ },
96
+ homepage: "https://github.com/motdotla/dotenv#readme",
97
+ funding: "https://dotenvx.com",
98
+ keywords: [
99
+ "dotenv",
100
+ "env",
101
+ ".env",
102
+ "environment",
103
+ "variables",
104
+ "config",
105
+ "settings"
106
+ ],
107
+ readmeFilename: "README.md",
108
+ license: "BSD-2-Clause",
109
+ devDependencies: {
110
+ "@types/node": "^18.11.3",
111
+ decache: "^4.6.2",
112
+ sinon: "^14.0.1",
113
+ standard: "^17.0.0",
114
+ "standard-version": "^9.5.0",
115
+ tap: "^19.2.0",
116
+ typescript: "^4.8.4"
117
+ },
118
+ engines: {
119
+ node: ">=12"
120
+ },
121
+ browser: {
122
+ fs: false
123
+ }
124
+ };
125
+ }
126
+ });
127
+
128
+ // ../../node_modules/.pnpm/dotenv@17.2.4/node_modules/dotenv/lib/main.js
129
+ var require_main = __commonJS({
130
+ "../../node_modules/.pnpm/dotenv@17.2.4/node_modules/dotenv/lib/main.js"(exports$1, module) {
131
+ init_esm_shims();
132
+ var fs = __require("fs");
133
+ var path2 = __require("path");
134
+ var os = __require("os");
135
+ var crypto2 = __require("crypto");
136
+ var packageJson = require_package();
137
+ var version = packageJson.version;
138
+ var TIPS = [
139
+ "\u{1F510} encrypt with Dotenvx: https://dotenvx.com",
140
+ "\u{1F510} prevent committing .env to code: https://dotenvx.com/precommit",
141
+ "\u{1F510} prevent building .env in docker: https://dotenvx.com/prebuild",
142
+ "\u{1F4E1} add observability to secrets: https://dotenvx.com/ops",
143
+ "\u{1F465} sync secrets across teammates & machines: https://dotenvx.com/ops",
144
+ "\u{1F5C2}\uFE0F backup and recover secrets: https://dotenvx.com/ops",
145
+ "\u2705 audit secrets and track compliance: https://dotenvx.com/ops",
146
+ "\u{1F504} add secrets lifecycle management: https://dotenvx.com/ops",
147
+ "\u{1F511} add access controls to secrets: https://dotenvx.com/ops",
148
+ "\u{1F6E0}\uFE0F run anywhere with `dotenvx run -- yourcommand`",
149
+ "\u2699\uFE0F specify custom .env file path with { path: '/custom/path/.env' }",
150
+ "\u2699\uFE0F enable debug logging with { debug: true }",
151
+ "\u2699\uFE0F override existing env vars with { override: true }",
152
+ "\u2699\uFE0F suppress all logs with { quiet: true }",
153
+ "\u2699\uFE0F write to custom object with { processEnv: myObject }",
154
+ "\u2699\uFE0F load multiple .env files with { path: ['.env.local', '.env'] }"
155
+ ];
156
+ function _getRandomTip() {
157
+ return TIPS[Math.floor(Math.random() * TIPS.length)];
158
+ }
159
+ function parseBoolean(value) {
160
+ if (typeof value === "string") {
161
+ return !["false", "0", "no", "off", ""].includes(value.toLowerCase());
162
+ }
163
+ return Boolean(value);
164
+ }
165
+ function supportsAnsi() {
166
+ return process.stdout.isTTY;
167
+ }
168
+ function dim(text) {
169
+ return supportsAnsi() ? `\x1B[2m${text}\x1B[0m` : text;
170
+ }
171
+ var LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg;
172
+ function parse(src) {
173
+ const obj = {};
174
+ let lines = src.toString();
175
+ lines = lines.replace(/\r\n?/mg, "\n");
176
+ let match;
177
+ while ((match = LINE.exec(lines)) != null) {
178
+ const key = match[1];
179
+ let value = match[2] || "";
180
+ value = value.trim();
181
+ const maybeQuote = value[0];
182
+ value = value.replace(/^(['"`])([\s\S]*)\1$/mg, "$2");
183
+ if (maybeQuote === '"') {
184
+ value = value.replace(/\\n/g, "\n");
185
+ value = value.replace(/\\r/g, "\r");
186
+ }
187
+ obj[key] = value;
188
+ }
189
+ return obj;
190
+ }
191
+ function _parseVault(options) {
192
+ options = options || {};
193
+ const vaultPath = _vaultPath(options);
194
+ options.path = vaultPath;
195
+ const result = DotenvModule.configDotenv(options);
196
+ if (!result.parsed) {
197
+ const err = new Error(`MISSING_DATA: Cannot parse ${vaultPath} for an unknown reason`);
198
+ err.code = "MISSING_DATA";
199
+ throw err;
200
+ }
201
+ const keys = _dotenvKey(options).split(",");
202
+ const length = keys.length;
203
+ let decrypted;
204
+ for (let i = 0; i < length; i++) {
205
+ try {
206
+ const key = keys[i].trim();
207
+ const attrs = _instructions(result, key);
208
+ decrypted = DotenvModule.decrypt(attrs.ciphertext, attrs.key);
209
+ break;
210
+ } catch (error) {
211
+ if (i + 1 >= length) {
212
+ throw error;
213
+ }
214
+ }
215
+ }
216
+ return DotenvModule.parse(decrypted);
217
+ }
218
+ function _warn(message) {
219
+ console.error(`[dotenv@${version}][WARN] ${message}`);
220
+ }
221
+ function _debug(message) {
222
+ console.log(`[dotenv@${version}][DEBUG] ${message}`);
223
+ }
224
+ function _log(message) {
225
+ console.log(`[dotenv@${version}] ${message}`);
226
+ }
227
+ function _dotenvKey(options) {
228
+ if (options && options.DOTENV_KEY && options.DOTENV_KEY.length > 0) {
229
+ return options.DOTENV_KEY;
230
+ }
231
+ if (process.env.DOTENV_KEY && process.env.DOTENV_KEY.length > 0) {
232
+ return process.env.DOTENV_KEY;
233
+ }
234
+ return "";
235
+ }
236
+ function _instructions(result, dotenvKey) {
237
+ let uri;
238
+ try {
239
+ uri = new URL(dotenvKey);
240
+ } catch (error) {
241
+ if (error.code === "ERR_INVALID_URL") {
242
+ const err = new Error("INVALID_DOTENV_KEY: Wrong format. Must be in valid uri format like dotenv://:key_1234@dotenvx.com/vault/.env.vault?environment=development");
243
+ err.code = "INVALID_DOTENV_KEY";
244
+ throw err;
245
+ }
246
+ throw error;
247
+ }
248
+ const key = uri.password;
249
+ if (!key) {
250
+ const err = new Error("INVALID_DOTENV_KEY: Missing key part");
251
+ err.code = "INVALID_DOTENV_KEY";
252
+ throw err;
253
+ }
254
+ const environment = uri.searchParams.get("environment");
255
+ if (!environment) {
256
+ const err = new Error("INVALID_DOTENV_KEY: Missing environment part");
257
+ err.code = "INVALID_DOTENV_KEY";
258
+ throw err;
259
+ }
260
+ const environmentKey = `DOTENV_VAULT_${environment.toUpperCase()}`;
261
+ const ciphertext = result.parsed[environmentKey];
262
+ if (!ciphertext) {
263
+ const err = new Error(`NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate environment ${environmentKey} in your .env.vault file.`);
264
+ err.code = "NOT_FOUND_DOTENV_ENVIRONMENT";
265
+ throw err;
266
+ }
267
+ return { ciphertext, key };
268
+ }
269
+ function _vaultPath(options) {
270
+ let possibleVaultPath = null;
271
+ if (options && options.path && options.path.length > 0) {
272
+ if (Array.isArray(options.path)) {
273
+ for (const filepath of options.path) {
274
+ if (fs.existsSync(filepath)) {
275
+ possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`;
276
+ }
277
+ }
278
+ } else {
279
+ possibleVaultPath = options.path.endsWith(".vault") ? options.path : `${options.path}.vault`;
280
+ }
281
+ } else {
282
+ possibleVaultPath = path2.resolve(process.cwd(), ".env.vault");
283
+ }
284
+ if (fs.existsSync(possibleVaultPath)) {
285
+ return possibleVaultPath;
286
+ }
287
+ return null;
288
+ }
289
+ function _resolveHome(envPath) {
290
+ return envPath[0] === "~" ? path2.join(os.homedir(), envPath.slice(1)) : envPath;
291
+ }
292
+ function _configVault(options) {
293
+ const debug = parseBoolean(process.env.DOTENV_CONFIG_DEBUG || options && options.debug);
294
+ const quiet = parseBoolean(process.env.DOTENV_CONFIG_QUIET || options && options.quiet);
295
+ if (debug || !quiet) {
296
+ _log("Loading env from encrypted .env.vault");
297
+ }
298
+ const parsed = DotenvModule._parseVault(options);
299
+ let processEnv = process.env;
300
+ if (options && options.processEnv != null) {
301
+ processEnv = options.processEnv;
302
+ }
303
+ DotenvModule.populate(processEnv, parsed, options);
304
+ return { parsed };
305
+ }
306
+ function configDotenv(options) {
307
+ const dotenvPath = path2.resolve(process.cwd(), ".env");
308
+ let encoding = "utf8";
309
+ let processEnv = process.env;
310
+ if (options && options.processEnv != null) {
311
+ processEnv = options.processEnv;
312
+ }
313
+ let debug = parseBoolean(processEnv.DOTENV_CONFIG_DEBUG || options && options.debug);
314
+ let quiet = parseBoolean(processEnv.DOTENV_CONFIG_QUIET || options && options.quiet);
315
+ if (options && options.encoding) {
316
+ encoding = options.encoding;
317
+ } else {
318
+ if (debug) {
319
+ _debug("No encoding is specified. UTF-8 is used by default");
320
+ }
321
+ }
322
+ let optionPaths = [dotenvPath];
323
+ if (options && options.path) {
324
+ if (!Array.isArray(options.path)) {
325
+ optionPaths = [_resolveHome(options.path)];
326
+ } else {
327
+ optionPaths = [];
328
+ for (const filepath of options.path) {
329
+ optionPaths.push(_resolveHome(filepath));
330
+ }
331
+ }
332
+ }
333
+ let lastError;
334
+ const parsedAll = {};
335
+ for (const path3 of optionPaths) {
336
+ try {
337
+ const parsed = DotenvModule.parse(fs.readFileSync(path3, { encoding }));
338
+ DotenvModule.populate(parsedAll, parsed, options);
339
+ } catch (e) {
340
+ if (debug) {
341
+ _debug(`Failed to load ${path3} ${e.message}`);
342
+ }
343
+ lastError = e;
344
+ }
345
+ }
346
+ const populated = DotenvModule.populate(processEnv, parsedAll, options);
347
+ debug = parseBoolean(processEnv.DOTENV_CONFIG_DEBUG || debug);
348
+ quiet = parseBoolean(processEnv.DOTENV_CONFIG_QUIET || quiet);
349
+ if (debug || !quiet) {
350
+ const keysCount = Object.keys(populated).length;
351
+ const shortPaths = [];
352
+ for (const filePath of optionPaths) {
353
+ try {
354
+ const relative = path2.relative(process.cwd(), filePath);
355
+ shortPaths.push(relative);
356
+ } catch (e) {
357
+ if (debug) {
358
+ _debug(`Failed to load ${filePath} ${e.message}`);
359
+ }
360
+ lastError = e;
361
+ }
362
+ }
363
+ _log(`injecting env (${keysCount}) from ${shortPaths.join(",")} ${dim(`-- tip: ${_getRandomTip()}`)}`);
364
+ }
365
+ if (lastError) {
366
+ return { parsed: parsedAll, error: lastError };
367
+ } else {
368
+ return { parsed: parsedAll };
369
+ }
370
+ }
371
+ function config(options) {
372
+ if (_dotenvKey(options).length === 0) {
373
+ return DotenvModule.configDotenv(options);
374
+ }
375
+ const vaultPath = _vaultPath(options);
376
+ if (!vaultPath) {
377
+ _warn(`You set DOTENV_KEY but you are missing a .env.vault file at ${vaultPath}. Did you forget to build it?`);
378
+ return DotenvModule.configDotenv(options);
379
+ }
380
+ return DotenvModule._configVault(options);
381
+ }
382
+ function decrypt2(encrypted, keyStr) {
383
+ const key = Buffer.from(keyStr.slice(-64), "hex");
384
+ let ciphertext = Buffer.from(encrypted, "base64");
385
+ const nonce = ciphertext.subarray(0, 12);
386
+ const authTag = ciphertext.subarray(-16);
387
+ ciphertext = ciphertext.subarray(12, -16);
388
+ try {
389
+ const aesgcm = crypto2.createDecipheriv("aes-256-gcm", key, nonce);
390
+ aesgcm.setAuthTag(authTag);
391
+ return `${aesgcm.update(ciphertext)}${aesgcm.final()}`;
392
+ } catch (error) {
393
+ const isRange = error instanceof RangeError;
394
+ const invalidKeyLength = error.message === "Invalid key length";
395
+ const decryptionFailed = error.message === "Unsupported state or unable to authenticate data";
396
+ if (isRange || invalidKeyLength) {
397
+ const err = new Error("INVALID_DOTENV_KEY: It must be 64 characters long (or more)");
398
+ err.code = "INVALID_DOTENV_KEY";
399
+ throw err;
400
+ } else if (decryptionFailed) {
401
+ const err = new Error("DECRYPTION_FAILED: Please check your DOTENV_KEY");
402
+ err.code = "DECRYPTION_FAILED";
403
+ throw err;
404
+ } else {
405
+ throw error;
406
+ }
407
+ }
408
+ }
409
+ function populate(processEnv, parsed, options = {}) {
410
+ const debug = Boolean(options && options.debug);
411
+ const override = Boolean(options && options.override);
412
+ const populated = {};
413
+ if (typeof parsed !== "object") {
414
+ const err = new Error("OBJECT_REQUIRED: Please check the processEnv argument being passed to populate");
415
+ err.code = "OBJECT_REQUIRED";
416
+ throw err;
417
+ }
418
+ for (const key of Object.keys(parsed)) {
419
+ if (Object.prototype.hasOwnProperty.call(processEnv, key)) {
420
+ if (override === true) {
421
+ processEnv[key] = parsed[key];
422
+ populated[key] = parsed[key];
423
+ }
424
+ if (debug) {
425
+ if (override === true) {
426
+ _debug(`"${key}" is already defined and WAS overwritten`);
427
+ } else {
428
+ _debug(`"${key}" is already defined and was NOT overwritten`);
429
+ }
430
+ }
431
+ } else {
432
+ processEnv[key] = parsed[key];
433
+ populated[key] = parsed[key];
434
+ }
435
+ }
436
+ return populated;
437
+ }
438
+ var DotenvModule = {
439
+ configDotenv,
440
+ _configVault,
441
+ _parseVault,
442
+ config,
443
+ decrypt: decrypt2,
444
+ parse,
445
+ populate
446
+ };
447
+ module.exports.configDotenv = DotenvModule.configDotenv;
448
+ module.exports._configVault = DotenvModule._configVault;
449
+ module.exports._parseVault = DotenvModule._parseVault;
450
+ module.exports.config = DotenvModule.config;
451
+ module.exports.decrypt = DotenvModule.decrypt;
452
+ module.exports.parse = DotenvModule.parse;
453
+ module.exports.populate = DotenvModule.populate;
454
+ module.exports = DotenvModule;
455
+ }
456
+ });
457
+
458
+ // src/cli.ts
459
+ init_esm_shims();
460
+
461
+ // src/commands/index.ts
462
+ init_esm_shims();
463
+
464
+ // src/commands/login.ts
465
+ init_esm_shims();
466
+
467
+ // src/auth/index.ts
468
+ init_esm_shims();
469
+
470
+ // src/auth/credential-auth.ts
471
+ init_esm_shims();
472
+
21
473
  // src/config/constants.ts
22
- var DOCYRUS_API_URL = "https://api.docyrus.com";
474
+ init_esm_shims();
475
+ var DOCYRUS_API_URL = "https://alpha-api.docyrus.com";
23
476
  var OAUTH_CLIENT_ID = "90565525-8283-4881-82a9-8613eb82ae27";
24
477
  var OAUTH_SCOPES = "offline_access Read.All Users.Read Users.Read.All DS.Read.All".split(" ");
25
478
  var OAUTH_CALLBACK_PORT_MIN = 9876;
@@ -36,11 +489,13 @@ var TOKEN_KEYS = {
36
489
  GITHUB_TOKEN: "githubToken"
37
490
  // For private template access - persists after logout
38
491
  };
492
+ var REGISTRY_BASE_URL = "https://ui.docy.app/r";
39
493
  var CLI_NAME = "docyrus";
40
494
  var CLI_VERSION = "0.0.1";
41
495
  var NPM_PACKAGE_NAME = "docyrus";
42
496
 
43
497
  // src/utils/errors.ts
498
+ init_esm_shims();
44
499
  var CliError = class extends Error {
45
500
  exitCode;
46
501
  suggestion;
@@ -107,6 +562,36 @@ var ConflictingFlagsError = class extends CliError {
107
562
  this.name = "ConflictingFlagsError";
108
563
  }
109
564
  };
565
+ var ComponentNotFoundError = class extends CliError {
566
+ constructor(name) {
567
+ super(
568
+ `"${name}" not found in the registry.`,
569
+ 1,
570
+ "Check the name and try again. Run `docyrus add --help` for usage."
571
+ );
572
+ this.name = "ComponentNotFoundError";
573
+ }
574
+ };
575
+ var PremiumAuthError = class extends CliError {
576
+ constructor(name) {
577
+ super(
578
+ `"${name}" is a premium component. Authentication required.`,
579
+ 1,
580
+ "Run `docyrus login` to authenticate, then try again."
581
+ );
582
+ this.name = "PremiumAuthError";
583
+ }
584
+ };
585
+ var MissingConfigError = class extends CliError {
586
+ constructor() {
587
+ super(
588
+ "components.json not found.",
589
+ 1,
590
+ "Run `npx shadcn@latest init` first to initialize your project."
591
+ );
592
+ this.name = "MissingConfigError";
593
+ }
594
+ };
110
595
 
111
596
  // src/auth/credential-auth.ts
112
597
  function getApiClient() {
@@ -195,6 +680,12 @@ async function refreshAccessToken(refreshToken) {
195
680
  throw new AuthenticationError("Session expired. Please log in again.");
196
681
  }
197
682
  }
683
+
684
+ // src/auth/oauth-auth.ts
685
+ init_esm_shims();
686
+
687
+ // src/auth/oauth-server.ts
688
+ init_esm_shims();
198
689
  var SUCCESS_HTML = `
199
690
  <!DOCTYPE html>
200
691
  <html>
@@ -377,7 +868,7 @@ async function startCallbackServer(expectedState) {
377
868
  rejectResult(new TimeoutError("OAuth authentication timed out."));
378
869
  }, OAUTH_TIMEOUT_MS);
379
870
  const server = createServer((req, res) => {
380
- const url = new URL(req.url || "/", `http://127.0.0.1:${port}`);
871
+ const url = new URL$1(req.url || "/", `http://127.0.0.1:${port}`);
381
872
  if (url.pathname === "/callback") {
382
873
  const code = url.searchParams.get("code");
383
874
  const state2 = url.searchParams.get("state");
@@ -421,6 +912,9 @@ async function startCallbackServer(expectedState) {
421
912
  cleanup
422
913
  };
423
914
  }
915
+
916
+ // src/utils/browser.ts
917
+ init_esm_shims();
424
918
  async function openBrowser(url) {
425
919
  await open(url);
426
920
  }
@@ -505,12 +999,24 @@ async function loginWithOAuth(options = {}) {
505
999
  cleanup();
506
1000
  }
507
1001
  }
1002
+
1003
+ // src/storage/index.ts
1004
+ init_esm_shims();
1005
+
1006
+ // src/storage/file-storage.ts
1007
+ init_esm_shims();
1008
+
1009
+ // src/utils/platform.ts
1010
+ init_esm_shims();
508
1011
  function getHomeDir() {
509
1012
  return homedir();
510
1013
  }
511
1014
  function getConfigDir() {
512
1015
  return join(getHomeDir(), CONFIG_DIR);
513
1016
  }
1017
+
1018
+ // src/storage/encryption.ts
1019
+ init_esm_shims();
514
1020
  var ALGORITHM = "aes-256-gcm";
515
1021
  var KEY_LENGTH = 32;
516
1022
  var IV_LENGTH = 16;
@@ -626,6 +1132,7 @@ var FileStorage = class {
626
1132
  };
627
1133
 
628
1134
  // src/storage/keychain-storage.ts
1135
+ init_esm_shims();
629
1136
  var keytar = null;
630
1137
  async function getKeytar() {
631
1138
  if (keytar !== null) {
@@ -696,6 +1203,7 @@ var KeychainStorage = class {
696
1203
  };
697
1204
 
698
1205
  // src/storage/cli-token-manager.ts
1206
+ init_esm_shims();
699
1207
  var CliTokenManager = class {
700
1208
  keychain;
701
1209
  fileStorage;
@@ -795,6 +1303,12 @@ function getTokenManager() {
795
1303
  }
796
1304
  return instance;
797
1305
  }
1306
+
1307
+ // src/config/index.ts
1308
+ init_esm_shims();
1309
+
1310
+ // src/config/config-manager.ts
1311
+ init_esm_shims();
798
1312
  var defaults = {
799
1313
  telemetryEnabled: true
800
1314
  };
@@ -826,6 +1340,12 @@ var ConfigManager = class {
826
1340
  }
827
1341
  };
828
1342
  var configManager = new ConfigManager();
1343
+
1344
+ // src/ui/index.ts
1345
+ init_esm_shims();
1346
+
1347
+ // src/ui/logger.ts
1348
+ init_esm_shims();
829
1349
  var logger = {
830
1350
  success(message) {
831
1351
  console.info(chalk4.green("\u2713"), message);
@@ -860,6 +1380,9 @@ var logger = {
860
1380
  return chalk4.cyan.underline(url);
861
1381
  }
862
1382
  };
1383
+
1384
+ // src/ui/output.ts
1385
+ init_esm_shims();
863
1386
  var state = {
864
1387
  format: "text",
865
1388
  data: {}
@@ -944,6 +1467,7 @@ var output = {
944
1467
  };
945
1468
 
946
1469
  // src/ui/messages.ts
1470
+ init_esm_shims();
947
1471
  var MESSAGES = {
948
1472
  // Login
949
1473
  LOGIN_SUCCESS: (email) => `Successfully logged in as ${email}`,
@@ -990,7 +1514,7 @@ var MESSAGES = {
990
1514
  CREATE_SUCCESS: "Project created successfully!",
991
1515
  CREATE_DOWNLOAD_OPENAPI: "Download OpenAPI spec for code generation?",
992
1516
  CREATE_DOWNLOADING_OPENAPI: "Downloading OpenAPI specification...",
993
- CREATE_OPENAPI_SUCCESS: (path) => `OpenAPI spec saved to ${path}`,
1517
+ CREATE_OPENAPI_SUCCESS: (path2) => `OpenAPI spec saved to ${path2}`,
994
1518
  // TanStack DB Generator
995
1519
  TANSTACK_GENERATOR_PROMPT: "Generate TanStack Query collections from OpenAPI spec?",
996
1520
  TANSTACK_GENERATOR_RUNNING: "Generating TanStack Query collections...",
@@ -1012,8 +1536,26 @@ var MESSAGES = {
1012
1536
  DOCYRUS_TOKEN_REQUIRED: "Docyrus token required for template download",
1013
1537
  DOCYRUS_TOKEN_PROMPT: "Enter Docyrus token:",
1014
1538
  DOCYRUS_TOKEN_SAVED: "Docyrus token saved successfully",
1015
- DOCYRUS_TOKEN_INVALID: "Invalid Docyrus token. Please check and try again."
1539
+ DOCYRUS_TOKEN_INVALID: "Invalid Docyrus token. Please check and try again.",
1540
+ // Add command
1541
+ ADD_RESOLVING: "Resolving components and dependencies...",
1542
+ ADD_INSTALLING_ITEM: (name) => `Installing ${name}...`,
1543
+ ADD_INSTALLING_DEPS: (count) => `Installing ${count} dependenc${count === 1 ? "y" : "ies"}...`,
1544
+ ADD_SUCCESS: (items, files) => `Done! Added ${items} item(s), ${files} file(s).`,
1545
+ ADD_NO_ITEMS: "Please specify at least one item to add.",
1546
+ ADD_NOT_FOUND: (name) => `"${name}" not found in the registry.`,
1547
+ ADD_PREMIUM_AUTH: (name) => `"${name}" is a premium component. Authentication required.`,
1548
+ ADD_FILE_EXISTS: (path2) => `File ${path2} already exists. Overwrite?`,
1549
+ ADD_FILE_SKIPPED: (path2) => `Skipped: ${path2}`,
1550
+ ADD_DRY_RUN_HEADER: "Dry run \u2014 the following changes would be made:",
1551
+ ADD_MISSING_CONFIG: "components.json not found.",
1552
+ ADD_MISSING_CONFIG_HINT: "Run `npx shadcn@latest init` first to initialize your project.",
1553
+ ADD_DETECTED_STYLE: (style) => `Detected style: ${style}`,
1554
+ ADD_DEPRECATED_BASE_STYLE: 'Your components.json uses a deprecated "base-*" style. The radix/base variant distinction has been removed. All components now use the unified registry (@docyrus/ui-*).'
1016
1555
  };
1556
+
1557
+ // src/ui/spinner.ts
1558
+ init_esm_shims();
1017
1559
  function createSpinner(text) {
1018
1560
  return ora({
1019
1561
  text,
@@ -1042,6 +1584,9 @@ async function withSpinner(text, fn, options) {
1042
1584
  throw error;
1043
1585
  }
1044
1586
  }
1587
+
1588
+ // src/ui/prompts.ts
1589
+ init_esm_shims();
1045
1590
  async function promptEmail(defaultValue) {
1046
1591
  return input({
1047
1592
  message: MESSAGES.LOGIN_PROMPT_EMAIL,
@@ -1066,6 +1611,9 @@ async function promptPassword() {
1066
1611
  }
1067
1612
  });
1068
1613
  }
1614
+
1615
+ // src/ui/progress.ts
1616
+ init_esm_shims();
1069
1617
  ({
1070
1618
  pending: chalk4.dim("\u25CB"),
1071
1619
  in_progress: chalk4.cyan("\u25D0"),
@@ -1170,6 +1718,7 @@ function registerLoginCommand(program2) {
1170
1718
  }
1171
1719
 
1172
1720
  // src/commands/logout.ts
1721
+ init_esm_shims();
1173
1722
  function registerLogoutCommand(program2) {
1174
1723
  program2.command("logout").description("Log out from Docyrus").action(async () => {
1175
1724
  const tokenManager = getTokenManager();
@@ -1192,7 +1741,11 @@ function registerLogoutCommand(program2) {
1192
1741
  });
1193
1742
  }
1194
1743
 
1744
+ // src/commands/whoami.ts
1745
+ init_esm_shims();
1746
+
1195
1747
  // src/utils/auth-guard.ts
1748
+ init_esm_shims();
1196
1749
  function decodeJwtPayload(token) {
1197
1750
  try {
1198
1751
  const parts = token.split(".");
@@ -1286,6 +1839,12 @@ function registerWhoamiCommand(program2) {
1286
1839
  }
1287
1840
  });
1288
1841
  }
1842
+
1843
+ // src/commands/create.ts
1844
+ init_esm_shims();
1845
+
1846
+ // src/utils/linter-config.ts
1847
+ init_esm_shims();
1289
1848
  var ESLINT_CONFIGS = {
1290
1849
  react: {
1291
1850
  imports: ["baseConfig", "reactConfig"],
@@ -1354,6 +1913,9 @@ async function applyLinterConfig(targetDir, framework, linter, onProgress) {
1354
1913
  };
1355
1914
  await writeFile(targetPkgPath, JSON.stringify(targetPkg, null, 2));
1356
1915
  }
1916
+
1917
+ // src/utils/alias-config.ts
1918
+ init_esm_shims();
1357
1919
  async function applyAliasConfig(targetDir, framework, aliasPrefix) {
1358
1920
  if (aliasPrefix === "@" || aliasPrefix === "@/") return;
1359
1921
  const config = {
@@ -1426,6 +1988,9 @@ async function updateImportsRecursive(dir, newPrefix, framework) {
1426
1988
  }
1427
1989
  }
1428
1990
  }
1991
+
1992
+ // src/utils/api-client.ts
1993
+ init_esm_shims();
1429
1994
  function createAuthenticatedClient(accessToken) {
1430
1995
  const apiUrl = configManager.get("apiUrl") || DOCYRUS_API_URL;
1431
1996
  const client = new RestApiClient({ baseURL: apiUrl });
@@ -1441,6 +2006,9 @@ async function downloadOpenApiSpec(accessToken, targetDir, filename = "openapi.j
1441
2006
  await writeFile(targetPath, JSON.stringify(spec, null, 2));
1442
2007
  return targetPath;
1443
2008
  }
2009
+
2010
+ // src/utils/ui-variant-swap.ts
2011
+ init_esm_shims();
1444
2012
  async function applyUIVariants(targetDir, uiLibrary) {
1445
2013
  const variantsDir = join(targetDir, "__ui-variants__");
1446
2014
  try {
@@ -1458,6 +2026,9 @@ async function applyUIVariants(targetDir, uiLibrary) {
1458
2026
  }
1459
2027
  await rm(variantsDir, { recursive: true, force: true });
1460
2028
  }
2029
+
2030
+ // src/utils/ui-library-setup.ts
2031
+ init_esm_shims();
1461
2032
  var execAsync = promisify(exec);
1462
2033
  var DICEUI_COMPONENTS = "action-bar,avatar-group,badge-overflow,checkbox-group,circular-progress,color-picker,color-swatch,combobox,compare-slider,cropper,editable,file-upload,gauge,kanban,key-value,listbox,mask-input,media-player,mention,phone-input,qr-code,rating,relative-time-card,responsive-dialog,scroll-spy,scroller,segmented-input,sortable,speed-dial,stack,stat,status,stepper,swap,tags-input,time-picker,timeline,tour".split(",");
1463
2034
  var UI_LIBRARY_COMPATIBILITY = {
@@ -1707,6 +2278,9 @@ async function addHeroUIProviderReact(targetDir) {
1707
2278
  } catch {
1708
2279
  }
1709
2280
  }
2281
+
2282
+ // src/utils/state-management-setup.ts
2283
+ init_esm_shims();
1710
2284
  var STATE_MANAGEMENT_VERSIONS = {
1711
2285
  zustand: { name: "zustand", version: "5.0.11" },
1712
2286
  "tanstack-query": { name: "@tanstack/react-query", version: "5.90.20" },
@@ -1901,7 +2475,7 @@ import { vueQueryOptions } from './lib/query-client';
1901
2475
 
1902
2476
  // src/commands/create.ts
1903
2477
  var execAsync2 = promisify(exec);
1904
- var SHADCN_BASE_COLORS = "zinc,slate,neutral,stone,gray".split(",");
2478
+ var SHADCN_BASE_COLORS = "slate,gray,zinc,neutral,stone,red,orange,amber,yellow,lime,green,emerald,teal,cyan,sky".split(",");
1905
2479
  function validateConflictingFlags(options) {
1906
2480
  const frameworkFlags = [
1907
2481
  options.nextjs,
@@ -2069,13 +2643,13 @@ var PACKAGE_MANAGERS = [
2069
2643
  { name: "yarn", label: "yarn", description: "Fast, reliable, and secure" },
2070
2644
  { name: "bun", label: "bun", description: "All-in-one JavaScript runtime" }
2071
2645
  ];
2072
- var TEMPLATE_REPO_BASE = "Docyrus/docyrus-devkit/templates";
2646
+ var TEMPLATE_REPO_BASE = "Docyrus/docyrus-ui/templates";
2073
2647
  function getTemplateRepo(framework) {
2074
2648
  return `${TEMPLATE_REPO_BASE}/${framework}`;
2075
2649
  }
2076
- async function directoryExists(path) {
2650
+ async function directoryExists(path2) {
2077
2651
  try {
2078
- await access(path, constants.F_OK);
2652
+ await access(path2, constants.F_OK);
2079
2653
  return true;
2080
2654
  } catch {
2081
2655
  return false;
@@ -2265,20 +2839,36 @@ Examples:
2265
2839
  );
2266
2840
  }
2267
2841
  await cp(localTemplateDir, targetDir, { recursive: true });
2842
+ if (!process.env.DOCYRUS_DEVKIT_PATH) {
2843
+ const { config } = await Promise.resolve().then(() => __toESM(require_main(), 1));
2844
+ config({ path: resolve(cliPackageDir, "..", "..", ".env") });
2845
+ }
2268
2846
  const pkgJsonPath = join(targetDir, "package.json");
2269
2847
  const pkgJson = JSON.parse(await readFile(pkgJsonPath, "utf-8"));
2270
- const packagesDir = resolve(cliPackageDir, "..");
2848
+ const monorepoRoot = resolve(cliPackageDir, "..", "..");
2849
+ const devkitPath = process.env.DOCYRUS_DEVKIT_PATH;
2850
+ const packagesDirs = [
2851
+ resolve(monorepoRoot, "packages"),
2852
+ ...devkitPath ? [resolve(devkitPath, "packages")] : [resolve(monorepoRoot, "..", "docyrus-devkit", "packages")]
2853
+ ];
2271
2854
  for (const depType of ["dependencies", "devDependencies"]) {
2272
2855
  const deps = pkgJson[depType];
2273
2856
  if (!deps) continue;
2274
2857
  for (const [name2, version] of Object.entries(deps)) {
2275
2858
  if (name2.startsWith("@docyrus/") && version === "latest") {
2276
2859
  const pkgName = name2.replace("@docyrus/", "");
2277
- const localPkgDir = resolve(packagesDir, pkgName);
2278
- try {
2279
- await access(localPkgDir, constants.F_OK);
2280
- deps[name2] = `file:${localPkgDir}`;
2281
- } catch {
2860
+ let found = false;
2861
+ for (const packagesDir of packagesDirs) {
2862
+ const localPkgDir = resolve(packagesDir, pkgName);
2863
+ try {
2864
+ await access(localPkgDir, constants.F_OK);
2865
+ deps[name2] = `file:${localPkgDir}`;
2866
+ found = true;
2867
+ break;
2868
+ } catch {
2869
+ }
2870
+ }
2871
+ if (!found) {
2282
2872
  logger.warn(`Local package not found for ${name2}, keeping "latest"`);
2283
2873
  }
2284
2874
  }
@@ -2303,7 +2893,7 @@ Examples:
2303
2893
  await withSpinner(
2304
2894
  MESSAGES.CREATE_DOWNLOADING,
2305
2895
  async () => {
2306
- const gigetCachePath = resolve(homedir(), ".cache/giget/gh/Docyrus-docyrus-devkit");
2896
+ const gigetCachePath = resolve(homedir(), ".cache/giget/gh/Docyrus-docyrus-ui");
2307
2897
  if (existsSync(gigetCachePath)) {
2308
2898
  await rm(gigetCachePath, { recursive: true, force: true });
2309
2899
  }
@@ -2455,6 +3045,9 @@ ${errorMessage}`
2455
3045
  logger.dim(MESSAGES.API_CLIENT_DOCS);
2456
3046
  });
2457
3047
  }
3048
+
3049
+ // src/commands/generate.ts
3050
+ init_esm_shims();
2458
3051
  function registerGenerateCommand(program2) {
2459
3052
  const generate = program2.command("generate").description("Code generation commands");
2460
3053
  generate.command("api-spec").description("Download OpenAPI specification from Docyrus API").option("-o, --output <path>", "Output file path", "openapi.json").action(async (options) => {
@@ -2539,6 +3132,9 @@ function findSpecFile(specPath) {
2539
3132
  }
2540
3133
  return null;
2541
3134
  }
3135
+
3136
+ // src/commands/upgrade.ts
3137
+ init_esm_shims();
2542
3138
  var execAsync3 = promisify(exec);
2543
3139
  async function getLatestVersion() {
2544
3140
  try {
@@ -2633,6 +3229,7 @@ function registerUpgradeCommand(program2) {
2633
3229
  }
2634
3230
 
2635
3231
  // src/commands/completion.ts
3232
+ init_esm_shims();
2636
3233
  var BASH_COMPLETION = `
2637
3234
  ###-begin-${CLI_NAME}-completions-###
2638
3235
  _${CLI_NAME}_completions() {
@@ -2752,6 +3349,9 @@ function registerCompletionCommand(program2) {
2752
3349
  }
2753
3350
  });
2754
3351
  }
3352
+
3353
+ // src/commands/info.ts
3354
+ init_esm_shims();
2755
3355
  function registerInfoCommand(program2) {
2756
3356
  program2.command("info").description("Display CLI and environment information").action(async () => {
2757
3357
  const tokenManager = getTokenManager();
@@ -2804,6 +3404,9 @@ function registerInfoCommand(program2) {
2804
3404
  }
2805
3405
  });
2806
3406
  }
3407
+
3408
+ // src/commands/config.ts
3409
+ init_esm_shims();
2807
3410
  function registerConfigCommand(program2) {
2808
3411
  program2.command("config").description("Manage CLI configuration").option("--token", "Update Docyrus token for private template access").option("--show", "Show current configuration").action(async (options) => {
2809
3412
  const tokenManager = getTokenManager();
@@ -2847,18 +3450,251 @@ function registerConfigCommand(program2) {
2847
3450
  });
2848
3451
  }
2849
3452
 
3453
+ // src/commands/add.ts
3454
+ init_esm_shims();
3455
+ function findProjectRoot() {
3456
+ let dir = process.cwd();
3457
+ while (dir !== dirname(dir)) {
3458
+ if (existsSync(join(dir, "package.json"))) {
3459
+ return dir;
3460
+ }
3461
+ dir = dirname(dir);
3462
+ }
3463
+ throw new CliError(
3464
+ "Could not find a package.json in the current directory or any parent directory.",
3465
+ 1,
3466
+ "Run this command from within a JavaScript/TypeScript project."
3467
+ );
3468
+ }
3469
+ async function readComponentsJson(projectRoot) {
3470
+ const configPath = join(projectRoot, "components.json");
3471
+ if (!existsSync(configPath)) {
3472
+ throw new MissingConfigError();
3473
+ }
3474
+ const raw = await readFile(configPath, "utf-8");
3475
+ return JSON.parse(raw);
3476
+ }
3477
+ function detectPackageManager2(projectRoot) {
3478
+ if (existsSync(join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
3479
+ if (existsSync(join(projectRoot, "yarn.lock"))) return "yarn";
3480
+ if (existsSync(join(projectRoot, "bun.lockb")) || existsSync(join(projectRoot, "bun.lock"))) return "bun";
3481
+ return "npm";
3482
+ }
3483
+ function normalizeRegistryName(input3) {
3484
+ if (input3.startsWith("@docyrus/")) return input3;
3485
+ if (input3.startsWith("hooks-")) return `@docyrus/${input3}`;
3486
+ if (input3.startsWith("utils-")) return `@docyrus/${input3}`;
3487
+ return `@docyrus/ui-${input3}`;
3488
+ }
3489
+ async function fetchRegistryItem(name, token) {
3490
+ const url = `${REGISTRY_BASE_URL}/${encodeURIComponent(name)}.json`;
3491
+ const headers = {
3492
+ Accept: "application/json"
3493
+ };
3494
+ if (token) {
3495
+ headers.Authorization = `Bearer ${token}`;
3496
+ }
3497
+ let response;
3498
+ try {
3499
+ response = await fetch(url, { headers });
3500
+ } catch {
3501
+ throw new NetworkError();
3502
+ }
3503
+ if (response.status === 401 || response.status === 403) {
3504
+ throw new PremiumAuthError(name);
3505
+ }
3506
+ if (response.status === 404) {
3507
+ throw new ComponentNotFoundError(name);
3508
+ }
3509
+ if (!response.ok) {
3510
+ throw new NetworkError(`Failed to fetch "${name}" from registry (HTTP ${response.status})`);
3511
+ }
3512
+ return response.json();
3513
+ }
3514
+ async function resolveAllDependencies(names, token) {
3515
+ const resolved = /* @__PURE__ */ new Map();
3516
+ const queue = [...names];
3517
+ while (queue.length > 0) {
3518
+ const name = queue.shift();
3519
+ if (resolved.has(name)) continue;
3520
+ const item = await fetchRegistryItem(name, token);
3521
+ resolved.set(name, item);
3522
+ if (item.registryDependencies?.length > 0) {
3523
+ for (const dep of item.registryDependencies) {
3524
+ if (!resolved.has(dep)) {
3525
+ queue.push(dep);
3526
+ }
3527
+ }
3528
+ }
3529
+ }
3530
+ return resolved;
3531
+ }
3532
+ function resolveTargetDir(projectRoot) {
3533
+ if (existsSync(join(projectRoot, "src"))) {
3534
+ return join(projectRoot, "src");
3535
+ }
3536
+ return projectRoot;
3537
+ }
3538
+ async function writeItemFiles(item, baseDir, overwrite) {
3539
+ const written = [];
3540
+ for (const file of item.files) {
3541
+ const targetPath = join(baseDir, file.path);
3542
+ const targetDir = dirname(targetPath);
3543
+ if (!existsSync(targetDir)) {
3544
+ await mkdir(targetDir, { recursive: true });
3545
+ }
3546
+ if (existsSync(targetPath) && !overwrite) {
3547
+ const shouldOverwrite = await confirm({
3548
+ message: MESSAGES.ADD_FILE_EXISTS(file.path),
3549
+ default: false
3550
+ });
3551
+ if (!shouldOverwrite) {
3552
+ logger.dim(` ${MESSAGES.ADD_FILE_SKIPPED(file.path)}`);
3553
+ continue;
3554
+ }
3555
+ }
3556
+ await writeFile(targetPath, file.content, "utf-8");
3557
+ written.push(file.path);
3558
+ }
3559
+ return written;
3560
+ }
3561
+ async function getExistingDependencies(projectRoot) {
3562
+ const pkgPath = join(projectRoot, "package.json");
3563
+ try {
3564
+ const raw = await readFile(pkgPath, "utf-8");
3565
+ const pkg = JSON.parse(raw);
3566
+ const deps = /* @__PURE__ */ new Set();
3567
+ for (const key of Object.keys(pkg.dependencies || {})) {
3568
+ deps.add(key);
3569
+ }
3570
+ for (const key of Object.keys(pkg.devDependencies || {})) {
3571
+ deps.add(key);
3572
+ }
3573
+ return deps;
3574
+ } catch {
3575
+ return /* @__PURE__ */ new Set();
3576
+ }
3577
+ }
3578
+ function installNpmDependencies(deps, projectRoot) {
3579
+ if (deps.length === 0) return;
3580
+ const pm = detectPackageManager2(projectRoot);
3581
+ const pmArgs = {
3582
+ npm: ["install", ...deps],
3583
+ pnpm: ["add", ...deps],
3584
+ yarn: ["add", ...deps],
3585
+ bun: ["add", ...deps]
3586
+ };
3587
+ execFileSync(pm, pmArgs[pm], { cwd: projectRoot, stdio: "pipe" });
3588
+ }
3589
+ function handleDryRun(items, baseDir, allNewDeps) {
3590
+ logger.newline();
3591
+ logger.bold(MESSAGES.ADD_DRY_RUN_HEADER);
3592
+ logger.newline();
3593
+ for (const [name, item] of items) {
3594
+ logger.info(`${item.title || name}`);
3595
+ for (const file of item.files) {
3596
+ const targetPath = join(baseDir, file.path);
3597
+ const exists = existsSync(targetPath);
3598
+ logger.dim(` ${exists ? "(overwrite)" : "(create)"} ${file.path}`);
3599
+ }
3600
+ }
3601
+ if (allNewDeps.length > 0) {
3602
+ logger.newline();
3603
+ logger.info("Dependencies to install:");
3604
+ logger.dim(` ${allNewDeps.join(", ")}`);
3605
+ }
3606
+ logger.newline();
3607
+ }
3608
+ function registerAddCommand(program2) {
3609
+ program2.command("add [items...]").description("Add Docyrus UI components, hooks, or utilities to your project").option("-o, --overwrite", "Overwrite existing files without prompting").option("-d, --dry-run", "Show what would be installed without making changes").option("-p, --path <path>", "Custom target path for files").addHelpText("after", `
3610
+ Examples:
3611
+ $ docyrus add button Add a component
3612
+ $ docyrus add button dialog Add multiple components
3613
+ $ docyrus add hooks-debounce Add a hook
3614
+ $ docyrus add utils-cn Add a utility
3615
+ $ docyrus add button --dry-run Preview installation
3616
+ $ docyrus add button -o Overwrite existing files
3617
+ `).action(async (items, options) => {
3618
+ if (!items || items.length === 0) {
3619
+ throw new CliError(
3620
+ MESSAGES.ADD_NO_ITEMS,
3621
+ 1,
3622
+ "Usage: docyrus add <item...>\nExample: docyrus add button dialog hooks-debounce"
3623
+ );
3624
+ }
3625
+ const projectRoot = findProjectRoot();
3626
+ const config = await readComponentsJson(projectRoot);
3627
+ const baseDir = options.path ? join(projectRoot, options.path) : resolveTargetDir(projectRoot);
3628
+ logger.newline();
3629
+ logger.dim(MESSAGES.ADD_DETECTED_STYLE(config.style));
3630
+ if (config.style.startsWith("base")) {
3631
+ logger.warn(MESSAGES.ADD_DEPRECATED_BASE_STYLE);
3632
+ }
3633
+ let token;
3634
+ try {
3635
+ token = await requireAuth();
3636
+ } catch {
3637
+ }
3638
+ const registryNames = items.map((item) => normalizeRegistryName(item));
3639
+ const allItems = await withSpinner(
3640
+ MESSAGES.ADD_RESOLVING,
3641
+ () => resolveAllDependencies(registryNames, token),
3642
+ { successText: `Resolved ${registryNames.length} item(s)` }
3643
+ );
3644
+ const existingDeps = await getExistingDependencies(projectRoot);
3645
+ const allNewDeps = [];
3646
+ for (const item of allItems.values()) {
3647
+ for (const dep of item.dependencies) {
3648
+ if (!existingDeps.has(dep) && !allNewDeps.includes(dep)) {
3649
+ allNewDeps.push(dep);
3650
+ }
3651
+ }
3652
+ }
3653
+ if (options.dryRun) {
3654
+ handleDryRun(allItems, baseDir, allNewDeps);
3655
+ return;
3656
+ }
3657
+ let totalFiles = 0;
3658
+ for (const [name, item] of allItems) {
3659
+ const written = await withSpinner(
3660
+ MESSAGES.ADD_INSTALLING_ITEM(item.title || name),
3661
+ () => writeItemFiles(item, baseDir, !!options.overwrite),
3662
+ { successText: `${item.title || name}` }
3663
+ );
3664
+ totalFiles += written.length;
3665
+ }
3666
+ if (allNewDeps.length > 0) {
3667
+ await withSpinner(
3668
+ MESSAGES.ADD_INSTALLING_DEPS(allNewDeps.length),
3669
+ async () => installNpmDependencies(allNewDeps, projectRoot),
3670
+ { successText: `${allNewDeps.length} dependenc${allNewDeps.length === 1 ? "y" : "ies"} installed` }
3671
+ );
3672
+ }
3673
+ logger.newline();
3674
+ logger.success(MESSAGES.ADD_SUCCESS(allItems.size, totalFiles));
3675
+ if (allNewDeps.length > 0) {
3676
+ logger.dim(` Dependencies: ${allNewDeps.join(", ")}`);
3677
+ }
3678
+ logger.newline();
3679
+ });
3680
+ }
3681
+
2850
3682
  // src/commands/index.ts
2851
3683
  function registerCommands(program2) {
2852
3684
  registerLoginCommand(program2);
2853
3685
  registerLogoutCommand(program2);
2854
3686
  registerWhoamiCommand(program2);
2855
3687
  registerCreateCommand(program2);
3688
+ registerAddCommand(program2);
2856
3689
  registerGenerateCommand(program2);
2857
3690
  registerUpgradeCommand(program2);
2858
3691
  registerCompletionCommand(program2);
2859
3692
  registerInfoCommand(program2);
2860
3693
  registerConfigCommand(program2);
2861
3694
  }
3695
+
3696
+ // src/utils/update-checker.ts
3697
+ init_esm_shims();
2862
3698
  function isNewerVersion2(current, latest) {
2863
3699
  const currentParts = current.replace(/^v/, "").split(".").map(Number);
2864
3700
  const latestParts = latest.replace(/^v/, "").split(".").map(Number);