@docyrus/cli 0.3.1 → 0.5.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,475 @@
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
- import { existsSync, readFileSync, mkdirSync, writeFileSync, unlinkSync, constants as constants$1 } from 'fs';
9
- import { join, resolve, dirname } from 'path';
9
+ import { existsSync, readFileSync, readdirSync, mkdirSync, writeFileSync, unlinkSync, constants as constants$1 } from 'fs';
10
10
  import { homedir, arch, release, platform, hostname, userInfo } from 'os';
11
11
  import Conf from 'conf';
12
12
  import chalk4 from 'chalk';
13
- import ora from 'ora';
14
- import { select, input, password, confirm } from '@inquirer/prompts';
15
- import { exec } from 'child_process';
13
+ import ora2 from 'ora';
14
+ import { select, input, password, confirm, checkbox } from '@inquirer/prompts';
15
+ import { exec, execFileSync } from 'child_process';
16
16
  import { promisify } from 'util';
17
- import { access, constants, writeFile, readFile, mkdir, cp, rm, readdir } from 'fs/promises';
17
+ import { access, constants, writeFile, readFile, mkdir, chmod, 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.3.1/node_modules/dotenv/package.json
61
+ var require_package = __commonJS({
62
+ "../../node_modules/.pnpm/dotenv@17.3.1/node_modules/dotenv/package.json"(exports$1, module) {
63
+ module.exports = {
64
+ name: "dotenv",
65
+ version: "17.3.1",
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.3.1/node_modules/dotenv/lib/main.js
129
+ var require_main = __commonJS({
130
+ "../../node_modules/.pnpm/dotenv@17.3.1/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{1F916} agentic secret storage: https://dotenvx.com/as2",
143
+ "\u26A1\uFE0F secrets for agents: https://dotenvx.com/as2",
144
+ "\u{1F6E1}\uFE0F auth for agents: https://vestauth.com",
145
+ "\u{1F6E0}\uFE0F run anywhere with `dotenvx run -- yourcommand`",
146
+ "\u2699\uFE0F specify custom .env file path with { path: '/custom/path/.env' }",
147
+ "\u2699\uFE0F enable debug logging with { debug: true }",
148
+ "\u2699\uFE0F override existing env vars with { override: true }",
149
+ "\u2699\uFE0F suppress all logs with { quiet: true }",
150
+ "\u2699\uFE0F write to custom object with { processEnv: myObject }",
151
+ "\u2699\uFE0F load multiple .env files with { path: ['.env.local', '.env'] }"
152
+ ];
153
+ function _getRandomTip() {
154
+ return TIPS[Math.floor(Math.random() * TIPS.length)];
155
+ }
156
+ function parseBoolean(value) {
157
+ if (typeof value === "string") {
158
+ return !["false", "0", "no", "off", ""].includes(value.toLowerCase());
159
+ }
160
+ return Boolean(value);
161
+ }
162
+ function supportsAnsi() {
163
+ return process.stdout.isTTY;
164
+ }
165
+ function dim(text) {
166
+ return supportsAnsi() ? `\x1B[2m${text}\x1B[0m` : text;
167
+ }
168
+ var LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg;
169
+ function parse(src) {
170
+ const obj = {};
171
+ let lines = src.toString();
172
+ lines = lines.replace(/\r\n?/mg, "\n");
173
+ let match;
174
+ while ((match = LINE.exec(lines)) != null) {
175
+ const key = match[1];
176
+ let value = match[2] || "";
177
+ value = value.trim();
178
+ const maybeQuote = value[0];
179
+ value = value.replace(/^(['"`])([\s\S]*)\1$/mg, "$2");
180
+ if (maybeQuote === '"') {
181
+ value = value.replace(/\\n/g, "\n");
182
+ value = value.replace(/\\r/g, "\r");
183
+ }
184
+ obj[key] = value;
185
+ }
186
+ return obj;
187
+ }
188
+ function _parseVault(options) {
189
+ options = options || {};
190
+ const vaultPath = _vaultPath(options);
191
+ options.path = vaultPath;
192
+ const result = DotenvModule.configDotenv(options);
193
+ if (!result.parsed) {
194
+ const err = new Error(`MISSING_DATA: Cannot parse ${vaultPath} for an unknown reason`);
195
+ err.code = "MISSING_DATA";
196
+ throw err;
197
+ }
198
+ const keys = _dotenvKey(options).split(",");
199
+ const length = keys.length;
200
+ let decrypted;
201
+ for (let i = 0; i < length; i++) {
202
+ try {
203
+ const key = keys[i].trim();
204
+ const attrs = _instructions(result, key);
205
+ decrypted = DotenvModule.decrypt(attrs.ciphertext, attrs.key);
206
+ break;
207
+ } catch (error) {
208
+ if (i + 1 >= length) {
209
+ throw error;
210
+ }
211
+ }
212
+ }
213
+ return DotenvModule.parse(decrypted);
214
+ }
215
+ function _warn(message) {
216
+ console.error(`[dotenv@${version}][WARN] ${message}`);
217
+ }
218
+ function _debug(message) {
219
+ console.log(`[dotenv@${version}][DEBUG] ${message}`);
220
+ }
221
+ function _log(message) {
222
+ console.log(`[dotenv@${version}] ${message}`);
223
+ }
224
+ function _dotenvKey(options) {
225
+ if (options && options.DOTENV_KEY && options.DOTENV_KEY.length > 0) {
226
+ return options.DOTENV_KEY;
227
+ }
228
+ if (process.env.DOTENV_KEY && process.env.DOTENV_KEY.length > 0) {
229
+ return process.env.DOTENV_KEY;
230
+ }
231
+ return "";
232
+ }
233
+ function _instructions(result, dotenvKey) {
234
+ let uri;
235
+ try {
236
+ uri = new URL(dotenvKey);
237
+ } catch (error) {
238
+ if (error.code === "ERR_INVALID_URL") {
239
+ 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");
240
+ err.code = "INVALID_DOTENV_KEY";
241
+ throw err;
242
+ }
243
+ throw error;
244
+ }
245
+ const key = uri.password;
246
+ if (!key) {
247
+ const err = new Error("INVALID_DOTENV_KEY: Missing key part");
248
+ err.code = "INVALID_DOTENV_KEY";
249
+ throw err;
250
+ }
251
+ const environment = uri.searchParams.get("environment");
252
+ if (!environment) {
253
+ const err = new Error("INVALID_DOTENV_KEY: Missing environment part");
254
+ err.code = "INVALID_DOTENV_KEY";
255
+ throw err;
256
+ }
257
+ const environmentKey = `DOTENV_VAULT_${environment.toUpperCase()}`;
258
+ const ciphertext = result.parsed[environmentKey];
259
+ if (!ciphertext) {
260
+ const err = new Error(`NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate environment ${environmentKey} in your .env.vault file.`);
261
+ err.code = "NOT_FOUND_DOTENV_ENVIRONMENT";
262
+ throw err;
263
+ }
264
+ return { ciphertext, key };
265
+ }
266
+ function _vaultPath(options) {
267
+ let possibleVaultPath = null;
268
+ if (options && options.path && options.path.length > 0) {
269
+ if (Array.isArray(options.path)) {
270
+ for (const filepath of options.path) {
271
+ if (fs.existsSync(filepath)) {
272
+ possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`;
273
+ }
274
+ }
275
+ } else {
276
+ possibleVaultPath = options.path.endsWith(".vault") ? options.path : `${options.path}.vault`;
277
+ }
278
+ } else {
279
+ possibleVaultPath = path2.resolve(process.cwd(), ".env.vault");
280
+ }
281
+ if (fs.existsSync(possibleVaultPath)) {
282
+ return possibleVaultPath;
283
+ }
284
+ return null;
285
+ }
286
+ function _resolveHome(envPath) {
287
+ return envPath[0] === "~" ? path2.join(os.homedir(), envPath.slice(1)) : envPath;
288
+ }
289
+ function _configVault(options) {
290
+ const debug = parseBoolean(process.env.DOTENV_CONFIG_DEBUG || options && options.debug);
291
+ const quiet = parseBoolean(process.env.DOTENV_CONFIG_QUIET || options && options.quiet);
292
+ if (debug || !quiet) {
293
+ _log("Loading env from encrypted .env.vault");
294
+ }
295
+ const parsed = DotenvModule._parseVault(options);
296
+ let processEnv = process.env;
297
+ if (options && options.processEnv != null) {
298
+ processEnv = options.processEnv;
299
+ }
300
+ DotenvModule.populate(processEnv, parsed, options);
301
+ return { parsed };
302
+ }
303
+ function configDotenv(options) {
304
+ const dotenvPath = path2.resolve(process.cwd(), ".env");
305
+ let encoding = "utf8";
306
+ let processEnv = process.env;
307
+ if (options && options.processEnv != null) {
308
+ processEnv = options.processEnv;
309
+ }
310
+ let debug = parseBoolean(processEnv.DOTENV_CONFIG_DEBUG || options && options.debug);
311
+ let quiet = parseBoolean(processEnv.DOTENV_CONFIG_QUIET || options && options.quiet);
312
+ if (options && options.encoding) {
313
+ encoding = options.encoding;
314
+ } else {
315
+ if (debug) {
316
+ _debug("No encoding is specified. UTF-8 is used by default");
317
+ }
318
+ }
319
+ let optionPaths = [dotenvPath];
320
+ if (options && options.path) {
321
+ if (!Array.isArray(options.path)) {
322
+ optionPaths = [_resolveHome(options.path)];
323
+ } else {
324
+ optionPaths = [];
325
+ for (const filepath of options.path) {
326
+ optionPaths.push(_resolveHome(filepath));
327
+ }
328
+ }
329
+ }
330
+ let lastError;
331
+ const parsedAll = {};
332
+ for (const path3 of optionPaths) {
333
+ try {
334
+ const parsed = DotenvModule.parse(fs.readFileSync(path3, { encoding }));
335
+ DotenvModule.populate(parsedAll, parsed, options);
336
+ } catch (e) {
337
+ if (debug) {
338
+ _debug(`Failed to load ${path3} ${e.message}`);
339
+ }
340
+ lastError = e;
341
+ }
342
+ }
343
+ const populated = DotenvModule.populate(processEnv, parsedAll, options);
344
+ debug = parseBoolean(processEnv.DOTENV_CONFIG_DEBUG || debug);
345
+ quiet = parseBoolean(processEnv.DOTENV_CONFIG_QUIET || quiet);
346
+ if (debug || !quiet) {
347
+ const keysCount = Object.keys(populated).length;
348
+ const shortPaths = [];
349
+ for (const filePath of optionPaths) {
350
+ try {
351
+ const relative = path2.relative(process.cwd(), filePath);
352
+ shortPaths.push(relative);
353
+ } catch (e) {
354
+ if (debug) {
355
+ _debug(`Failed to load ${filePath} ${e.message}`);
356
+ }
357
+ lastError = e;
358
+ }
359
+ }
360
+ _log(`injecting env (${keysCount}) from ${shortPaths.join(",")} ${dim(`-- tip: ${_getRandomTip()}`)}`);
361
+ }
362
+ if (lastError) {
363
+ return { parsed: parsedAll, error: lastError };
364
+ } else {
365
+ return { parsed: parsedAll };
366
+ }
367
+ }
368
+ function config(options) {
369
+ if (_dotenvKey(options).length === 0) {
370
+ return DotenvModule.configDotenv(options);
371
+ }
372
+ const vaultPath = _vaultPath(options);
373
+ if (!vaultPath) {
374
+ _warn(`You set DOTENV_KEY but you are missing a .env.vault file at ${vaultPath}. Did you forget to build it?`);
375
+ return DotenvModule.configDotenv(options);
376
+ }
377
+ return DotenvModule._configVault(options);
378
+ }
379
+ function decrypt2(encrypted, keyStr) {
380
+ const key = Buffer.from(keyStr.slice(-64), "hex");
381
+ let ciphertext = Buffer.from(encrypted, "base64");
382
+ const nonce = ciphertext.subarray(0, 12);
383
+ const authTag = ciphertext.subarray(-16);
384
+ ciphertext = ciphertext.subarray(12, -16);
385
+ try {
386
+ const aesgcm = crypto2.createDecipheriv("aes-256-gcm", key, nonce);
387
+ aesgcm.setAuthTag(authTag);
388
+ return `${aesgcm.update(ciphertext)}${aesgcm.final()}`;
389
+ } catch (error) {
390
+ const isRange = error instanceof RangeError;
391
+ const invalidKeyLength = error.message === "Invalid key length";
392
+ const decryptionFailed = error.message === "Unsupported state or unable to authenticate data";
393
+ if (isRange || invalidKeyLength) {
394
+ const err = new Error("INVALID_DOTENV_KEY: It must be 64 characters long (or more)");
395
+ err.code = "INVALID_DOTENV_KEY";
396
+ throw err;
397
+ } else if (decryptionFailed) {
398
+ const err = new Error("DECRYPTION_FAILED: Please check your DOTENV_KEY");
399
+ err.code = "DECRYPTION_FAILED";
400
+ throw err;
401
+ } else {
402
+ throw error;
403
+ }
404
+ }
405
+ }
406
+ function populate(processEnv, parsed, options = {}) {
407
+ const debug = Boolean(options && options.debug);
408
+ const override = Boolean(options && options.override);
409
+ const populated = {};
410
+ if (typeof parsed !== "object") {
411
+ const err = new Error("OBJECT_REQUIRED: Please check the processEnv argument being passed to populate");
412
+ err.code = "OBJECT_REQUIRED";
413
+ throw err;
414
+ }
415
+ for (const key of Object.keys(parsed)) {
416
+ if (Object.prototype.hasOwnProperty.call(processEnv, key)) {
417
+ if (override === true) {
418
+ processEnv[key] = parsed[key];
419
+ populated[key] = parsed[key];
420
+ }
421
+ if (debug) {
422
+ if (override === true) {
423
+ _debug(`"${key}" is already defined and WAS overwritten`);
424
+ } else {
425
+ _debug(`"${key}" is already defined and was NOT overwritten`);
426
+ }
427
+ }
428
+ } else {
429
+ processEnv[key] = parsed[key];
430
+ populated[key] = parsed[key];
431
+ }
432
+ }
433
+ return populated;
434
+ }
435
+ var DotenvModule = {
436
+ configDotenv,
437
+ _configVault,
438
+ _parseVault,
439
+ config,
440
+ decrypt: decrypt2,
441
+ parse,
442
+ populate
443
+ };
444
+ module.exports.configDotenv = DotenvModule.configDotenv;
445
+ module.exports._configVault = DotenvModule._configVault;
446
+ module.exports._parseVault = DotenvModule._parseVault;
447
+ module.exports.config = DotenvModule.config;
448
+ module.exports.decrypt = DotenvModule.decrypt;
449
+ module.exports.parse = DotenvModule.parse;
450
+ module.exports.populate = DotenvModule.populate;
451
+ module.exports = DotenvModule;
452
+ }
453
+ });
454
+
455
+ // src/cli.ts
456
+ init_esm_shims();
457
+
458
+ // src/commands/index.ts
459
+ init_esm_shims();
460
+
461
+ // src/commands/login.ts
462
+ init_esm_shims();
463
+
464
+ // src/auth/index.ts
465
+ init_esm_shims();
466
+
467
+ // src/auth/credential-auth.ts
468
+ init_esm_shims();
469
+
21
470
  // src/config/constants.ts
22
- var DOCYRUS_API_URL = "https://api.docyrus.com";
471
+ init_esm_shims();
472
+ var DOCYRUS_API_URL = "https://alpha-api.docyrus.com";
23
473
  var OAUTH_CLIENT_ID = "90565525-8283-4881-82a9-8613eb82ae27";
24
474
  var OAUTH_SCOPES = "offline_access Read.All Users.Read Users.Read.All DS.Read.All".split(" ");
25
475
  var OAUTH_CALLBACK_PORT_MIN = 9876;
@@ -36,11 +486,13 @@ var TOKEN_KEYS = {
36
486
  GITHUB_TOKEN: "githubToken"
37
487
  // For private template access - persists after logout
38
488
  };
489
+ var REGISTRY_BASE_URL = "https://ui.docy.app/r";
39
490
  var CLI_NAME = "docyrus";
40
491
  var CLI_VERSION = "0.0.1";
41
492
  var NPM_PACKAGE_NAME = "docyrus";
42
493
 
43
494
  // src/utils/errors.ts
495
+ init_esm_shims();
44
496
  var CliError = class extends Error {
45
497
  exitCode;
46
498
  suggestion;
@@ -107,6 +559,36 @@ var ConflictingFlagsError = class extends CliError {
107
559
  this.name = "ConflictingFlagsError";
108
560
  }
109
561
  };
562
+ var ComponentNotFoundError = class extends CliError {
563
+ constructor(name) {
564
+ super(
565
+ `"${name}" not found in the registry.`,
566
+ 1,
567
+ "Check the name and try again. Run `docyrus add --help` for usage."
568
+ );
569
+ this.name = "ComponentNotFoundError";
570
+ }
571
+ };
572
+ var PremiumAuthError = class extends CliError {
573
+ constructor(name) {
574
+ super(
575
+ `"${name}" is a premium component. Authentication required.`,
576
+ 1,
577
+ "Run `docyrus login` to authenticate, then try again."
578
+ );
579
+ this.name = "PremiumAuthError";
580
+ }
581
+ };
582
+ var MissingConfigError = class extends CliError {
583
+ constructor() {
584
+ super(
585
+ "components.json not found.",
586
+ 1,
587
+ "Run `npx shadcn@latest init` first to initialize your project."
588
+ );
589
+ this.name = "MissingConfigError";
590
+ }
591
+ };
110
592
 
111
593
  // src/auth/credential-auth.ts
112
594
  function getApiClient() {
@@ -195,6 +677,12 @@ async function refreshAccessToken(refreshToken) {
195
677
  throw new AuthenticationError("Session expired. Please log in again.");
196
678
  }
197
679
  }
680
+
681
+ // src/auth/oauth-auth.ts
682
+ init_esm_shims();
683
+
684
+ // src/auth/oauth-server.ts
685
+ init_esm_shims();
198
686
  var SUCCESS_HTML = `
199
687
  <!DOCTYPE html>
200
688
  <html>
@@ -377,7 +865,7 @@ async function startCallbackServer(expectedState) {
377
865
  rejectResult(new TimeoutError("OAuth authentication timed out."));
378
866
  }, OAUTH_TIMEOUT_MS);
379
867
  const server = createServer((req, res) => {
380
- const url = new URL(req.url || "/", `http://127.0.0.1:${port}`);
868
+ const url = new URL$1(req.url || "/", `http://127.0.0.1:${port}`);
381
869
  if (url.pathname === "/callback") {
382
870
  const code = url.searchParams.get("code");
383
871
  const state2 = url.searchParams.get("state");
@@ -421,6 +909,9 @@ async function startCallbackServer(expectedState) {
421
909
  cleanup
422
910
  };
423
911
  }
912
+
913
+ // src/utils/browser.ts
914
+ init_esm_shims();
424
915
  async function openBrowser(url) {
425
916
  await open(url);
426
917
  }
@@ -505,12 +996,24 @@ async function loginWithOAuth(options = {}) {
505
996
  cleanup();
506
997
  }
507
998
  }
999
+
1000
+ // src/storage/index.ts
1001
+ init_esm_shims();
1002
+
1003
+ // src/storage/file-storage.ts
1004
+ init_esm_shims();
1005
+
1006
+ // src/utils/platform.ts
1007
+ init_esm_shims();
508
1008
  function getHomeDir() {
509
1009
  return homedir();
510
1010
  }
511
1011
  function getConfigDir() {
512
1012
  return join(getHomeDir(), CONFIG_DIR);
513
1013
  }
1014
+
1015
+ // src/storage/encryption.ts
1016
+ init_esm_shims();
514
1017
  var ALGORITHM = "aes-256-gcm";
515
1018
  var KEY_LENGTH = 32;
516
1019
  var IV_LENGTH = 16;
@@ -626,6 +1129,7 @@ var FileStorage = class {
626
1129
  };
627
1130
 
628
1131
  // src/storage/keychain-storage.ts
1132
+ init_esm_shims();
629
1133
  var keytar = null;
630
1134
  async function getKeytar() {
631
1135
  if (keytar !== null) {
@@ -696,6 +1200,7 @@ var KeychainStorage = class {
696
1200
  };
697
1201
 
698
1202
  // src/storage/cli-token-manager.ts
1203
+ init_esm_shims();
699
1204
  var CliTokenManager = class {
700
1205
  keychain;
701
1206
  fileStorage;
@@ -795,8 +1300,15 @@ function getTokenManager() {
795
1300
  }
796
1301
  return instance;
797
1302
  }
1303
+
1304
+ // src/config/index.ts
1305
+ init_esm_shims();
1306
+
1307
+ // src/config/config-manager.ts
1308
+ init_esm_shims();
798
1309
  var defaults = {
799
- telemetryEnabled: true
1310
+ telemetryEnabled: true,
1311
+ helpTheme: "default"
800
1312
  };
801
1313
  var ConfigManager = class {
802
1314
  config;
@@ -826,6 +1338,12 @@ var ConfigManager = class {
826
1338
  }
827
1339
  };
828
1340
  var configManager = new ConfigManager();
1341
+
1342
+ // src/ui/index.ts
1343
+ init_esm_shims();
1344
+
1345
+ // src/ui/logger.ts
1346
+ init_esm_shims();
829
1347
  var logger = {
830
1348
  success(message) {
831
1349
  console.info(chalk4.green("\u2713"), message);
@@ -860,6 +1378,9 @@ var logger = {
860
1378
  return chalk4.cyan.underline(url);
861
1379
  }
862
1380
  };
1381
+
1382
+ // src/ui/output.ts
1383
+ init_esm_shims();
863
1384
  var state = {
864
1385
  format: "text",
865
1386
  data: {}
@@ -944,6 +1465,7 @@ var output = {
944
1465
  };
945
1466
 
946
1467
  // src/ui/messages.ts
1468
+ init_esm_shims();
947
1469
  var MESSAGES = {
948
1470
  // Login
949
1471
  LOGIN_SUCCESS: (email) => `Successfully logged in as ${email}`,
@@ -990,7 +1512,7 @@ var MESSAGES = {
990
1512
  CREATE_SUCCESS: "Project created successfully!",
991
1513
  CREATE_DOWNLOAD_OPENAPI: "Download OpenAPI spec for code generation?",
992
1514
  CREATE_DOWNLOADING_OPENAPI: "Downloading OpenAPI specification...",
993
- CREATE_OPENAPI_SUCCESS: (path) => `OpenAPI spec saved to ${path}`,
1515
+ CREATE_OPENAPI_SUCCESS: (path2) => `OpenAPI spec saved to ${path2}`,
994
1516
  // TanStack DB Generator
995
1517
  TANSTACK_GENERATOR_PROMPT: "Generate TanStack Query collections from OpenAPI spec?",
996
1518
  TANSTACK_GENERATOR_RUNNING: "Generating TanStack Query collections...",
@@ -1012,10 +1534,58 @@ var MESSAGES = {
1012
1534
  DOCYRUS_TOKEN_REQUIRED: "Docyrus token required for template download",
1013
1535
  DOCYRUS_TOKEN_PROMPT: "Enter Docyrus token:",
1014
1536
  DOCYRUS_TOKEN_SAVED: "Docyrus token saved successfully",
1015
- DOCYRUS_TOKEN_INVALID: "Invalid Docyrus token. Please check and try again."
1537
+ DOCYRUS_TOKEN_INVALID: "Invalid Docyrus token. Please check and try again.",
1538
+ // Add command
1539
+ ADD_RESOLVING: "Resolving components and dependencies...",
1540
+ ADD_INSTALLING_ITEM: (name) => `Installing ${name}...`,
1541
+ ADD_INSTALLING_DEPS: (count) => `Installing ${count} dependenc${count === 1 ? "y" : "ies"}...`,
1542
+ ADD_SUCCESS: (items, files) => `Done! Added ${items} item(s), ${files} file(s).`,
1543
+ ADD_NO_ITEMS: "Please specify at least one item to add.",
1544
+ ADD_NOT_FOUND: (name) => `"${name}" not found in the registry.`,
1545
+ ADD_PREMIUM_AUTH: (name) => `"${name}" is a premium component. Authentication required.`,
1546
+ ADD_FILE_EXISTS: (path2) => `File ${path2} already exists. Overwrite?`,
1547
+ ADD_FILE_SKIPPED: (path2) => `Skipped: ${path2}`,
1548
+ ADD_DRY_RUN_HEADER: "Dry run \u2014 the following changes would be made:",
1549
+ ADD_MISSING_CONFIG: "components.json not found.",
1550
+ ADD_MISSING_CONFIG_HINT: "Run `npx shadcn@latest init` first to initialize your project.",
1551
+ ADD_DETECTED_STYLE: (style) => `Detected style: ${style}`,
1552
+ 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-*).',
1553
+ // List command
1554
+ LIST_HEADER: "Available Docyrus UI registry items:",
1555
+ LIST_FETCHING: "Fetching registry index...",
1556
+ LIST_FETCHING_NPM: "Fetching @docyrus npm packages...",
1557
+ LIST_NPM_SUCCESS: "Fetched npm packages",
1558
+ LIST_INSTALL_HINT: "Install with: docyrus add <name>",
1559
+ LIST_NPM_HEADER: "Published @docyrus npm packages:",
1560
+ LIST_NPM_LINK: "https://www.npmjs.com/settings/docyrus/packages",
1561
+ // Commitlint command
1562
+ COMMITLINT_DESCRIPTION: "Set up commitlint, husky, and lint-staged in your project",
1563
+ COMMITLINT_NO_PROJECT: "Could not find a package.json.",
1564
+ COMMITLINT_NO_PROJECT_HINT: "Run this command from within a JavaScript/TypeScript project.",
1565
+ COMMITLINT_IS_MONOREPO: "Is this a monorepo?",
1566
+ COMMITLINT_DETECTED_WORKSPACES: (count) => `Detected ${count} workspace(s)`,
1567
+ COMMITLINT_NO_WORKSPACES: "No workspaces detected. Enter scopes manually.",
1568
+ COMMITLINT_SCOPE_STRATEGY: "How should scopes be used?",
1569
+ COMMITLINT_SELECT_SCOPES: "Select scopes to include:",
1570
+ COMMITLINT_ADDITIONAL_SCOPES: "Enter additional scopes (comma-separated, or leave empty):",
1571
+ COMMITLINT_CONFIRM_SCOPES: (scopes) => `Scopes: ${scopes.join(", ")}`,
1572
+ COMMITLINT_CONFIG_EXISTS: "commitlint.config.ts already exists. Overwrite?",
1573
+ COMMITLINT_HUSKY_EXISTS: ".husky/ directory already exists. Overwrite hooks?",
1574
+ COMMITLINT_STEP_CONFIG: "Creating commitlint.config.ts",
1575
+ COMMITLINT_STEP_HUSKY: "Setting up Husky hooks",
1576
+ COMMITLINT_STEP_SCRIPT: "Creating commit-linter.sh script",
1577
+ COMMITLINT_STEP_PACKAGE_JSON: "Updating package.json",
1578
+ COMMITLINT_STEP_INSTALL: "Installing dependencies",
1579
+ COMMITLINT_STEP_INIT_HUSKY: "Initializing Husky",
1580
+ COMMITLINT_SUCCESS: "Commitlint setup complete!",
1581
+ COMMITLINT_SUCCESS_HINT: "Your commits will now be validated against Conventional Commits format.",
1582
+ COMMITLINT_DETECTED_PM: (pm) => `Package manager: ${pm}`
1016
1583
  };
1584
+
1585
+ // src/ui/spinner.ts
1586
+ init_esm_shims();
1017
1587
  function createSpinner(text) {
1018
- return ora({
1588
+ return ora2({
1019
1589
  text,
1020
1590
  spinner: "dots"
1021
1591
  });
@@ -1042,6 +1612,9 @@ async function withSpinner(text, fn, options) {
1042
1612
  throw error;
1043
1613
  }
1044
1614
  }
1615
+
1616
+ // src/ui/prompts.ts
1617
+ init_esm_shims();
1045
1618
  async function promptEmail(defaultValue) {
1046
1619
  return input({
1047
1620
  message: MESSAGES.LOGIN_PROMPT_EMAIL,
@@ -1066,12 +1639,80 @@ async function promptPassword() {
1066
1639
  }
1067
1640
  });
1068
1641
  }
1069
- ({
1642
+
1643
+ // src/ui/progress.ts
1644
+ init_esm_shims();
1645
+ var SYMBOLS = {
1070
1646
  pending: chalk4.dim("\u25CB"),
1071
1647
  in_progress: chalk4.cyan("\u25D0"),
1072
1648
  completed: chalk4.green("\u2713"),
1073
1649
  failed: chalk4.red("\u2717")
1074
- });
1650
+ };
1651
+ function createMultiStepProgress(title, stepLabels) {
1652
+ const steps = stepLabels.map((label) => ({
1653
+ label,
1654
+ status: "pending"
1655
+ }));
1656
+ let spinner = null;
1657
+ let currentStep = -1;
1658
+ const render = () => {
1659
+ console.log();
1660
+ console.log(chalk4.bold(title));
1661
+ console.log();
1662
+ for (const step of steps) {
1663
+ const symbol = SYMBOLS[step.status];
1664
+ const text = step.status === "in_progress" ? chalk4.cyan(step.label) : step.status === "completed" ? chalk4.green(step.label) : step.status === "failed" ? chalk4.red(step.label) : chalk4.dim(step.label);
1665
+ console.log(` ${symbol} ${text}`);
1666
+ if (step.status === "failed" && step.error) {
1667
+ console.log(` ${chalk4.red(step.error)}`);
1668
+ }
1669
+ }
1670
+ console.log();
1671
+ };
1672
+ return {
1673
+ start(stepIndex) {
1674
+ if (stepIndex < 0 || stepIndex >= steps.length) return;
1675
+ if (spinner) {
1676
+ spinner.stop();
1677
+ }
1678
+ currentStep = stepIndex;
1679
+ steps[stepIndex].status = "in_progress";
1680
+ spinner = ora2({
1681
+ text: steps[stepIndex].label,
1682
+ color: "cyan"
1683
+ }).start();
1684
+ },
1685
+ complete(stepIndex) {
1686
+ if (stepIndex < 0 || stepIndex >= steps.length) return;
1687
+ if (spinner && currentStep === stepIndex) {
1688
+ spinner.succeed(chalk4.green(steps[stepIndex].label));
1689
+ spinner = null;
1690
+ }
1691
+ steps[stepIndex].status = "completed";
1692
+ },
1693
+ fail(stepIndex, error) {
1694
+ if (stepIndex < 0 || stepIndex >= steps.length) return;
1695
+ if (spinner && currentStep === stepIndex) {
1696
+ spinner.fail(chalk4.red(steps[stepIndex].label));
1697
+ spinner = null;
1698
+ }
1699
+ steps[stepIndex].status = "failed";
1700
+ steps[stepIndex].error = error;
1701
+ },
1702
+ update(stepIndex, message) {
1703
+ if (spinner && currentStep === stepIndex) {
1704
+ spinner.text = message;
1705
+ }
1706
+ },
1707
+ finish() {
1708
+ if (spinner) {
1709
+ spinner.stop();
1710
+ spinner = null;
1711
+ }
1712
+ render();
1713
+ }
1714
+ };
1715
+ }
1075
1716
  function createSimpleProgress() {
1076
1717
  let spinner = null;
1077
1718
  return (step, current, total) => {
@@ -1080,7 +1721,7 @@ function createSimpleProgress() {
1080
1721
  spinner = null;
1081
1722
  }
1082
1723
  const text = current && total ? `${step} (${current}/${total})` : step;
1083
- spinner = ora({
1724
+ spinner = ora2({
1084
1725
  text,
1085
1726
  color: "cyan"
1086
1727
  }).start();
@@ -1091,6 +1732,71 @@ function createSimpleProgress() {
1091
1732
  };
1092
1733
  }
1093
1734
 
1735
+ // src/ui/help.ts
1736
+ init_esm_shims();
1737
+ var palettes = {
1738
+ default: {
1739
+ title: (str) => chalk4.bold.cyan(str),
1740
+ usage: (str) => chalk4.yellow(str),
1741
+ command: (str) => chalk4.green(str),
1742
+ argument: (str) => chalk4.magenta(str),
1743
+ description: (str) => chalk4.dim(str)
1744
+ },
1745
+ monochrome: {
1746
+ title: (str) => chalk4.bold(str),
1747
+ usage: (str) => chalk4.bold(str),
1748
+ command: (str) => chalk4.white(str),
1749
+ argument: (str) => chalk4.underline(str),
1750
+ description: (str) => chalk4.dim(str)
1751
+ },
1752
+ warm: {
1753
+ title: (str) => chalk4.bold.yellow(str),
1754
+ usage: (str) => chalk4.bold.red(str),
1755
+ command: (str) => chalk4.yellow(str),
1756
+ argument: (str) => chalk4.red(str),
1757
+ description: (str) => chalk4.dim(str)
1758
+ },
1759
+ cool: {
1760
+ title: (str) => chalk4.bold.blue(str),
1761
+ usage: (str) => chalk4.cyan(str),
1762
+ command: (str) => chalk4.blue(str),
1763
+ argument: (str) => chalk4.cyan(str),
1764
+ description: (str) => chalk4.dim(str)
1765
+ },
1766
+ neon: {
1767
+ title: (str) => chalk4.bold.magenta(str),
1768
+ usage: (str) => chalk4.cyan(str),
1769
+ command: (str) => chalk4.green(str),
1770
+ argument: (str) => chalk4.yellow(str),
1771
+ description: (str) => chalk4.dim(str)
1772
+ }
1773
+ };
1774
+ var HELP_THEMES = [
1775
+ "default",
1776
+ "monochrome",
1777
+ "warm",
1778
+ "cool",
1779
+ "neon"
1780
+ ];
1781
+ function previewTheme(theme) {
1782
+ const p = palettes[theme];
1783
+ return `${p.title("Commands:")} ${p.command("add")} ${p.argument("<name>")} ${p.description("description")}`;
1784
+ }
1785
+ function getHelpConfig(theme = "default") {
1786
+ const p = palettes[theme];
1787
+ return {
1788
+ sortSubcommands: true,
1789
+ sortOptions: true,
1790
+ styleTitle: p.title,
1791
+ styleUsage: p.usage,
1792
+ styleCommandText: p.command,
1793
+ styleOptionText: p.command,
1794
+ styleArgumentText: p.argument,
1795
+ styleSubcommandText: p.command,
1796
+ styleDescriptionText: p.description
1797
+ };
1798
+ }
1799
+
1094
1800
  // src/commands/login.ts
1095
1801
  async function handleCredentialLogin(email) {
1096
1802
  const lastEmail = configManager.get("lastLoginEmail");
@@ -1170,6 +1876,7 @@ function registerLoginCommand(program2) {
1170
1876
  }
1171
1877
 
1172
1878
  // src/commands/logout.ts
1879
+ init_esm_shims();
1173
1880
  function registerLogoutCommand(program2) {
1174
1881
  program2.command("logout").description("Log out from Docyrus").action(async () => {
1175
1882
  const tokenManager = getTokenManager();
@@ -1192,7 +1899,11 @@ function registerLogoutCommand(program2) {
1192
1899
  });
1193
1900
  }
1194
1901
 
1902
+ // src/commands/whoami.ts
1903
+ init_esm_shims();
1904
+
1195
1905
  // src/utils/auth-guard.ts
1906
+ init_esm_shims();
1196
1907
  function decodeJwtPayload(token) {
1197
1908
  try {
1198
1909
  const parts = token.split(".");
@@ -1286,6 +1997,12 @@ function registerWhoamiCommand(program2) {
1286
1997
  }
1287
1998
  });
1288
1999
  }
2000
+
2001
+ // src/commands/create.ts
2002
+ init_esm_shims();
2003
+
2004
+ // src/utils/linter-config.ts
2005
+ init_esm_shims();
1289
2006
  var ESLINT_CONFIGS = {
1290
2007
  react: {
1291
2008
  imports: ["baseConfig", "reactConfig"],
@@ -1354,6 +2071,9 @@ async function applyLinterConfig(targetDir, framework, linter, onProgress) {
1354
2071
  };
1355
2072
  await writeFile(targetPkgPath, JSON.stringify(targetPkg, null, 2));
1356
2073
  }
2074
+
2075
+ // src/utils/alias-config.ts
2076
+ init_esm_shims();
1357
2077
  async function applyAliasConfig(targetDir, framework, aliasPrefix) {
1358
2078
  if (aliasPrefix === "@" || aliasPrefix === "@/") return;
1359
2079
  const config = {
@@ -1426,6 +2146,9 @@ async function updateImportsRecursive(dir, newPrefix, framework) {
1426
2146
  }
1427
2147
  }
1428
2148
  }
2149
+
2150
+ // src/utils/api-client.ts
2151
+ init_esm_shims();
1429
2152
  function createAuthenticatedClient(accessToken) {
1430
2153
  const apiUrl = configManager.get("apiUrl") || DOCYRUS_API_URL;
1431
2154
  const client = new RestApiClient({ baseURL: apiUrl });
@@ -1441,6 +2164,9 @@ async function downloadOpenApiSpec(accessToken, targetDir, filename = "openapi.j
1441
2164
  await writeFile(targetPath, JSON.stringify(spec, null, 2));
1442
2165
  return targetPath;
1443
2166
  }
2167
+
2168
+ // src/utils/ui-variant-swap.ts
2169
+ init_esm_shims();
1444
2170
  async function applyUIVariants(targetDir, uiLibrary) {
1445
2171
  const variantsDir = join(targetDir, "__ui-variants__");
1446
2172
  try {
@@ -1458,6 +2184,9 @@ async function applyUIVariants(targetDir, uiLibrary) {
1458
2184
  }
1459
2185
  await rm(variantsDir, { recursive: true, force: true });
1460
2186
  }
2187
+
2188
+ // src/utils/ui-library-setup.ts
2189
+ init_esm_shims();
1461
2190
  var execAsync = promisify(exec);
1462
2191
  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
2192
  var UI_LIBRARY_COMPATIBILITY = {
@@ -1707,6 +2436,9 @@ async function addHeroUIProviderReact(targetDir) {
1707
2436
  } catch {
1708
2437
  }
1709
2438
  }
2439
+
2440
+ // src/utils/state-management-setup.ts
2441
+ init_esm_shims();
1710
2442
  var STATE_MANAGEMENT_VERSIONS = {
1711
2443
  zustand: { name: "zustand", version: "5.0.11" },
1712
2444
  "tanstack-query": { name: "@tanstack/react-query", version: "5.90.20" },
@@ -1901,7 +2633,7 @@ import { vueQueryOptions } from './lib/query-client';
1901
2633
 
1902
2634
  // src/commands/create.ts
1903
2635
  var execAsync2 = promisify(exec);
1904
- var SHADCN_BASE_COLORS = "zinc,slate,neutral,stone,gray".split(",");
2636
+ var SHADCN_BASE_COLORS = "slate,gray,zinc,neutral,stone,red,orange,amber,yellow,lime,green,emerald,teal,cyan,sky".split(",");
1905
2637
  function validateConflictingFlags(options) {
1906
2638
  const frameworkFlags = [
1907
2639
  options.nextjs,
@@ -2069,13 +2801,13 @@ var PACKAGE_MANAGERS = [
2069
2801
  { name: "yarn", label: "yarn", description: "Fast, reliable, and secure" },
2070
2802
  { name: "bun", label: "bun", description: "All-in-one JavaScript runtime" }
2071
2803
  ];
2072
- var TEMPLATE_REPO_BASE = "Docyrus/docyrus-devkit/templates";
2804
+ var TEMPLATE_REPO_BASE = "Docyrus/docyrus-ui/templates";
2073
2805
  function getTemplateRepo(framework) {
2074
2806
  return `${TEMPLATE_REPO_BASE}/${framework}`;
2075
2807
  }
2076
- async function directoryExists(path) {
2808
+ async function directoryExists(path2) {
2077
2809
  try {
2078
- await access(path, constants.F_OK);
2810
+ await access(path2, constants.F_OK);
2079
2811
  return true;
2080
2812
  } catch {
2081
2813
  return false;
@@ -2265,20 +2997,36 @@ Examples:
2265
2997
  );
2266
2998
  }
2267
2999
  await cp(localTemplateDir, targetDir, { recursive: true });
3000
+ if (!process.env.DOCYRUS_DEVKIT_PATH) {
3001
+ const { config } = await Promise.resolve().then(() => __toESM(require_main(), 1));
3002
+ config({ path: resolve(cliPackageDir, "..", "..", ".env") });
3003
+ }
2268
3004
  const pkgJsonPath = join(targetDir, "package.json");
2269
3005
  const pkgJson = JSON.parse(await readFile(pkgJsonPath, "utf-8"));
2270
- const packagesDir = resolve(cliPackageDir, "..");
3006
+ const monorepoRoot = resolve(cliPackageDir, "..", "..");
3007
+ const devkitPath = process.env.DOCYRUS_DEVKIT_PATH;
3008
+ const packagesDirs = [
3009
+ resolve(monorepoRoot, "packages"),
3010
+ ...devkitPath ? [resolve(devkitPath, "packages")] : [resolve(monorepoRoot, "..", "docyrus-devkit", "packages")]
3011
+ ];
2271
3012
  for (const depType of ["dependencies", "devDependencies"]) {
2272
3013
  const deps = pkgJson[depType];
2273
3014
  if (!deps) continue;
2274
3015
  for (const [name2, version] of Object.entries(deps)) {
2275
3016
  if (name2.startsWith("@docyrus/") && version === "latest") {
2276
3017
  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 {
3018
+ let found = false;
3019
+ for (const packagesDir of packagesDirs) {
3020
+ const localPkgDir = resolve(packagesDir, pkgName);
3021
+ try {
3022
+ await access(localPkgDir, constants.F_OK);
3023
+ deps[name2] = `file:${localPkgDir}`;
3024
+ found = true;
3025
+ break;
3026
+ } catch {
3027
+ }
3028
+ }
3029
+ if (!found) {
2282
3030
  logger.warn(`Local package not found for ${name2}, keeping "latest"`);
2283
3031
  }
2284
3032
  }
@@ -2303,7 +3051,7 @@ Examples:
2303
3051
  await withSpinner(
2304
3052
  MESSAGES.CREATE_DOWNLOADING,
2305
3053
  async () => {
2306
- const gigetCachePath = resolve(homedir(), ".cache/giget/gh/Docyrus-docyrus-devkit");
3054
+ const gigetCachePath = resolve(homedir(), ".cache/giget/gh/Docyrus-docyrus-ui");
2307
3055
  if (existsSync(gigetCachePath)) {
2308
3056
  await rm(gigetCachePath, { recursive: true, force: true });
2309
3057
  }
@@ -2455,8 +3203,11 @@ ${errorMessage}`
2455
3203
  logger.dim(MESSAGES.API_CLIENT_DOCS);
2456
3204
  });
2457
3205
  }
3206
+
3207
+ // src/commands/generate.ts
3208
+ init_esm_shims();
2458
3209
  function registerGenerateCommand(program2) {
2459
- const generate = program2.command("generate").description("Code generation commands");
3210
+ const generate = program2.command("generate").description("Generate code from OpenAPI specs (e.g. TanStack Query collections)");
2460
3211
  generate.command("api-spec").description("Download OpenAPI specification from Docyrus API").option("-o, --output <path>", "Output file path", "openapi.json").action(async (options) => {
2461
3212
  const token = await requireAuth();
2462
3213
  const outputPath = resolve(options.output);
@@ -2539,6 +3290,9 @@ function findSpecFile(specPath) {
2539
3290
  }
2540
3291
  return null;
2541
3292
  }
3293
+
3294
+ // src/commands/upgrade.ts
3295
+ init_esm_shims();
2542
3296
  var execAsync3 = promisify(exec);
2543
3297
  async function getLatestVersion() {
2544
3298
  try {
@@ -2633,6 +3387,7 @@ function registerUpgradeCommand(program2) {
2633
3387
  }
2634
3388
 
2635
3389
  // src/commands/completion.ts
3390
+ init_esm_shims();
2636
3391
  var BASH_COMPLETION = `
2637
3392
  ###-begin-${CLI_NAME}-completions-###
2638
3393
  _${CLI_NAME}_completions() {
@@ -2641,11 +3396,11 @@ _${CLI_NAME}_completions() {
2641
3396
  args=("\${COMP_WORDS[@]}")
2642
3397
 
2643
3398
  # Commands
2644
- type_list="login logout whoami create generate upgrade completion info help"
3399
+ type_list="login logout whoami create add commitlint generate upgrade completion info help"
2645
3400
 
2646
3401
  # Subcommands for generate
2647
3402
  if [[ \${args[1]} == "generate" ]]; then
2648
- type_list="db"
3403
+ type_list="api-spec db"
2649
3404
  fi
2650
3405
 
2651
3406
  COMPREPLY=($(compgen -W "\${type_list}" -- \${cur_word}))
@@ -2663,15 +3418,18 @@ _${CLI_NAME}() {
2663
3418
  'logout:Log out from Docyrus'
2664
3419
  'whoami:Display the current logged-in user'
2665
3420
  'create:Create a new Docyrus project from template'
2666
- 'generate:Code generation commands'
3421
+ 'generate:Generate code from OpenAPI specs'
2667
3422
  'upgrade:Upgrade Docyrus CLI to the latest version'
2668
3423
  'completion:Generate shell completion script'
3424
+ 'add:Add a component from the registry'
3425
+ 'commitlint:Set up commitlint, husky, and lint-staged'
2669
3426
  'info:Display CLI and environment information'
2670
3427
  'help:Display help for command'
2671
3428
  )
2672
3429
 
2673
3430
  local -a generate_commands
2674
3431
  generate_commands=(
3432
+ 'api-spec:Download OpenAPI specification from Docyrus API'
2675
3433
  'db:Generate TanStack Query collections from OpenAPI spec'
2676
3434
  )
2677
3435
 
@@ -2704,13 +3462,16 @@ complete -c ${CLI_NAME} -n "__fish_use_subcommand" -a "login" -d "Log in to Docy
2704
3462
  complete -c ${CLI_NAME} -n "__fish_use_subcommand" -a "logout" -d "Log out from Docyrus"
2705
3463
  complete -c ${CLI_NAME} -n "__fish_use_subcommand" -a "whoami" -d "Display the current logged-in user"
2706
3464
  complete -c ${CLI_NAME} -n "__fish_use_subcommand" -a "create" -d "Create a new Docyrus project"
2707
- complete -c ${CLI_NAME} -n "__fish_use_subcommand" -a "generate" -d "Code generation commands"
3465
+ complete -c ${CLI_NAME} -n "__fish_use_subcommand" -a "generate" -d "Generate code from OpenAPI specs"
2708
3466
  complete -c ${CLI_NAME} -n "__fish_use_subcommand" -a "upgrade" -d "Upgrade Docyrus CLI"
2709
3467
  complete -c ${CLI_NAME} -n "__fish_use_subcommand" -a "completion" -d "Generate shell completion"
3468
+ complete -c ${CLI_NAME} -n "__fish_use_subcommand" -a "add" -d "Add a component from the registry"
3469
+ complete -c ${CLI_NAME} -n "__fish_use_subcommand" -a "commitlint" -d "Set up commitlint and husky"
2710
3470
  complete -c ${CLI_NAME} -n "__fish_use_subcommand" -a "info" -d "Display CLI and environment information"
2711
3471
  complete -c ${CLI_NAME} -n "__fish_use_subcommand" -a "help" -d "Display help"
2712
3472
 
2713
3473
  # generate subcommands
3474
+ complete -c ${CLI_NAME} -n "__fish_seen_subcommand_from generate" -a "api-spec" -d "Download OpenAPI specification"
2714
3475
  complete -c ${CLI_NAME} -n "__fish_seen_subcommand_from generate" -a "db" -d "Generate TanStack Query collections"
2715
3476
  ###-end-${CLI_NAME}-completions-###
2716
3477
  `.trim();
@@ -2752,6 +3513,9 @@ function registerCompletionCommand(program2) {
2752
3513
  }
2753
3514
  });
2754
3515
  }
3516
+
3517
+ // src/commands/info.ts
3518
+ init_esm_shims();
2755
3519
  function registerInfoCommand(program2) {
2756
3520
  program2.command("info").description("Display CLI and environment information").action(async () => {
2757
3521
  const tokenManager = getTokenManager();
@@ -2804,18 +3568,40 @@ function registerInfoCommand(program2) {
2804
3568
  }
2805
3569
  });
2806
3570
  }
3571
+
3572
+ // src/commands/config.ts
3573
+ init_esm_shims();
3574
+ function showConfig(isLoggedIn, email, githubToken) {
3575
+ const currentTheme = configManager.get("helpTheme");
3576
+ logger.log("Current configuration:");
3577
+ logger.newline();
3578
+ logger.log(` Docyrus Login: ${isLoggedIn ? `\u2713 ${email}` : "\u2717 Not logged in"}`);
3579
+ logger.log(` Docyrus Token: ${githubToken ? "\u2713 Configured" : "\u2717 Not configured"}`);
3580
+ logger.log(` Help Theme: ${currentTheme}`);
3581
+ logger.newline();
3582
+ }
2807
3583
  function registerConfigCommand(program2) {
2808
- program2.command("config").description("Manage CLI configuration").option("--token", "Update Docyrus token for private template access").option("--show", "Show current configuration").action(async (options) => {
3584
+ program2.command("config").description("Manage CLI configuration").option("--token", "Update Docyrus token for private template access").option("--theme", "Change help output color theme").option("--show", "Show current configuration").action(async (options) => {
2809
3585
  const tokenManager = getTokenManager();
2810
3586
  if (options.show) {
2811
3587
  const githubToken2 = await tokenManager.getGithubToken();
2812
3588
  const isLoggedIn2 = await tokenManager.isLoggedIn();
2813
3589
  const email2 = await tokenManager.getUserEmail();
2814
- logger.log("Current configuration:");
2815
- logger.newline();
2816
- logger.log(` Docyrus Login: ${isLoggedIn2 ? `\u2713 ${email2}` : "\u2717 Not logged in"}`);
2817
- logger.log(` Docyrus Token: ${githubToken2 ? "\u2713 Configured" : "\u2717 Not configured"}`);
2818
- logger.newline();
3590
+ showConfig(isLoggedIn2, email2, githubToken2);
3591
+ return;
3592
+ }
3593
+ if (options.theme) {
3594
+ const currentTheme = configManager.get("helpTheme");
3595
+ const theme = await select({
3596
+ message: "Select help color theme:",
3597
+ choices: HELP_THEMES.map((t) => ({
3598
+ name: `${t.padEnd(12)} ${previewTheme(t)}`,
3599
+ value: t
3600
+ })),
3601
+ default: currentTheme
3602
+ });
3603
+ configManager.set("helpTheme", theme);
3604
+ logger.success(`Help theme set to "${theme}"`);
2819
3605
  return;
2820
3606
  }
2821
3607
  if (options.token) {
@@ -2838,12 +3624,786 @@ function registerConfigCommand(program2) {
2838
3624
  const githubToken = await tokenManager.getGithubToken();
2839
3625
  const isLoggedIn = await tokenManager.isLoggedIn();
2840
3626
  const email = await tokenManager.getUserEmail();
2841
- logger.log("Current configuration:");
3627
+ showConfig(isLoggedIn, email, githubToken);
3628
+ logger.log("To update token: docyrus config --token");
3629
+ logger.log("To change theme: docyrus config --theme");
3630
+ });
3631
+ }
3632
+
3633
+ // src/commands/add.ts
3634
+ init_esm_shims();
3635
+ function findProjectRoot() {
3636
+ let dir = process.cwd();
3637
+ while (dir !== dirname(dir)) {
3638
+ if (existsSync(join(dir, "package.json"))) {
3639
+ return dir;
3640
+ }
3641
+ dir = dirname(dir);
3642
+ }
3643
+ throw new CliError(
3644
+ "Could not find a package.json in the current directory or any parent directory.",
3645
+ 1,
3646
+ "Run this command from within a JavaScript/TypeScript project."
3647
+ );
3648
+ }
3649
+ async function readComponentsJson(projectRoot) {
3650
+ const configPath = join(projectRoot, "components.json");
3651
+ if (!existsSync(configPath)) {
3652
+ throw new MissingConfigError();
3653
+ }
3654
+ const raw = await readFile(configPath, "utf-8");
3655
+ return JSON.parse(raw);
3656
+ }
3657
+ function detectPackageManager2(projectRoot) {
3658
+ if (existsSync(join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
3659
+ if (existsSync(join(projectRoot, "yarn.lock"))) return "yarn";
3660
+ if (existsSync(join(projectRoot, "bun.lockb")) || existsSync(join(projectRoot, "bun.lock"))) return "bun";
3661
+ return "npm";
3662
+ }
3663
+ function normalizeRegistryName(input4) {
3664
+ if (input4.startsWith("@docyrus/")) return input4;
3665
+ if (input4.startsWith("hooks-")) return `@docyrus/${input4}`;
3666
+ if (input4.startsWith("utils-")) return `@docyrus/${input4}`;
3667
+ return `@docyrus/ui-${input4}`;
3668
+ }
3669
+ async function fetchRegistryItem(name, token) {
3670
+ const url = `${REGISTRY_BASE_URL}/${encodeURIComponent(name)}.json`;
3671
+ const headers = {
3672
+ Accept: "application/json"
3673
+ };
3674
+ if (token) {
3675
+ headers.Authorization = `Bearer ${token}`;
3676
+ }
3677
+ let response;
3678
+ try {
3679
+ response = await fetch(url, { headers });
3680
+ } catch {
3681
+ throw new NetworkError();
3682
+ }
3683
+ if (response.status === 401 || response.status === 403) {
3684
+ throw new PremiumAuthError(name);
3685
+ }
3686
+ if (response.status === 404) {
3687
+ throw new ComponentNotFoundError(name);
3688
+ }
3689
+ if (!response.ok) {
3690
+ throw new NetworkError(`Failed to fetch "${name}" from registry (HTTP ${response.status})`);
3691
+ }
3692
+ return response.json();
3693
+ }
3694
+ async function resolveAllDependencies(names, token) {
3695
+ const resolved = /* @__PURE__ */ new Map();
3696
+ const queue = [...names];
3697
+ while (queue.length > 0) {
3698
+ const name = queue.shift();
3699
+ if (resolved.has(name)) continue;
3700
+ const item = await fetchRegistryItem(name, token);
3701
+ resolved.set(name, item);
3702
+ if (item.registryDependencies?.length > 0) {
3703
+ for (const dep of item.registryDependencies) {
3704
+ if (!resolved.has(dep)) {
3705
+ queue.push(dep);
3706
+ }
3707
+ }
3708
+ }
3709
+ }
3710
+ return resolved;
3711
+ }
3712
+ function resolveTargetDir(projectRoot) {
3713
+ if (existsSync(join(projectRoot, "src"))) {
3714
+ return join(projectRoot, "src");
3715
+ }
3716
+ return projectRoot;
3717
+ }
3718
+ async function writeItemFiles(item, baseDir, overwrite) {
3719
+ const written = [];
3720
+ for (const file of item.files) {
3721
+ const targetPath = join(baseDir, file.path);
3722
+ const targetDir = dirname(targetPath);
3723
+ if (!existsSync(targetDir)) {
3724
+ await mkdir(targetDir, { recursive: true });
3725
+ }
3726
+ if (existsSync(targetPath) && !overwrite) {
3727
+ const shouldOverwrite = await confirm({
3728
+ message: MESSAGES.ADD_FILE_EXISTS(file.path),
3729
+ default: false
3730
+ });
3731
+ if (!shouldOverwrite) {
3732
+ logger.dim(` ${MESSAGES.ADD_FILE_SKIPPED(file.path)}`);
3733
+ continue;
3734
+ }
3735
+ }
3736
+ await writeFile(targetPath, file.content, "utf-8");
3737
+ written.push(file.path);
3738
+ }
3739
+ return written;
3740
+ }
3741
+ async function getExistingDependencies(projectRoot) {
3742
+ const pkgPath = join(projectRoot, "package.json");
3743
+ try {
3744
+ const raw = await readFile(pkgPath, "utf-8");
3745
+ const pkg = JSON.parse(raw);
3746
+ const deps = /* @__PURE__ */ new Set();
3747
+ for (const key of Object.keys(pkg.dependencies || {})) {
3748
+ deps.add(key);
3749
+ }
3750
+ for (const key of Object.keys(pkg.devDependencies || {})) {
3751
+ deps.add(key);
3752
+ }
3753
+ return deps;
3754
+ } catch {
3755
+ return /* @__PURE__ */ new Set();
3756
+ }
3757
+ }
3758
+ function installNpmDependencies(deps, projectRoot) {
3759
+ if (deps.length === 0) return;
3760
+ const pm = detectPackageManager2(projectRoot);
3761
+ const pmArgs = {
3762
+ npm: ["install", ...deps],
3763
+ pnpm: ["add", ...deps],
3764
+ yarn: ["add", ...deps],
3765
+ bun: ["add", ...deps]
3766
+ };
3767
+ execFileSync(pm, pmArgs[pm], { cwd: projectRoot, stdio: "pipe" });
3768
+ }
3769
+ function handleDryRun(items, baseDir, allNewDeps) {
3770
+ logger.newline();
3771
+ logger.bold(MESSAGES.ADD_DRY_RUN_HEADER);
3772
+ logger.newline();
3773
+ for (const [name, item] of items) {
3774
+ logger.info(`${item.title || name}`);
3775
+ for (const file of item.files) {
3776
+ const targetPath = join(baseDir, file.path);
3777
+ const exists = existsSync(targetPath);
3778
+ logger.dim(` ${exists ? "(overwrite)" : "(create)"} ${file.path}`);
3779
+ }
3780
+ }
3781
+ if (allNewDeps.length > 0) {
2842
3782
  logger.newline();
2843
- logger.log(` Docyrus Login: ${isLoggedIn ? `\u2713 ${email}` : "\u2717 Not logged in"}`);
2844
- logger.log(` Docyrus Token: ${githubToken ? "\u2713 Configured" : "\u2717 Not configured"}`);
3783
+ logger.info("Dependencies to install:");
3784
+ logger.dim(` ${allNewDeps.join(", ")}`);
3785
+ }
3786
+ logger.newline();
3787
+ }
3788
+ function registerAddCommand(program2) {
3789
+ 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", `
3790
+ Examples:
3791
+ $ docyrus add button Add a component
3792
+ $ docyrus add button dialog Add multiple components
3793
+ $ docyrus add hooks-debounce Add a hook
3794
+ $ docyrus add utils-cn Add a utility
3795
+ $ docyrus add button --dry-run Preview installation
3796
+ $ docyrus add button -o Overwrite existing files
3797
+ `).action(async (items, options) => {
3798
+ if (!items || items.length === 0) {
3799
+ throw new CliError(
3800
+ MESSAGES.ADD_NO_ITEMS,
3801
+ 1,
3802
+ "Usage: docyrus add <item...>\nExample: docyrus add button dialog hooks-debounce"
3803
+ );
3804
+ }
3805
+ const projectRoot = findProjectRoot();
3806
+ const config = await readComponentsJson(projectRoot);
3807
+ const baseDir = options.path ? join(projectRoot, options.path) : resolveTargetDir(projectRoot);
2845
3808
  logger.newline();
2846
- logger.log("To update token: docyrus config --token");
3809
+ logger.dim(MESSAGES.ADD_DETECTED_STYLE(config.style));
3810
+ if (config.style.startsWith("base")) {
3811
+ logger.warn(MESSAGES.ADD_DEPRECATED_BASE_STYLE);
3812
+ }
3813
+ let token;
3814
+ try {
3815
+ token = await requireAuth();
3816
+ } catch {
3817
+ }
3818
+ const registryNames = items.map((item) => normalizeRegistryName(item));
3819
+ const allItems = await withSpinner(
3820
+ MESSAGES.ADD_RESOLVING,
3821
+ () => resolveAllDependencies(registryNames, token),
3822
+ { successText: `Resolved ${registryNames.length} item(s)` }
3823
+ );
3824
+ const existingDeps = await getExistingDependencies(projectRoot);
3825
+ const allNewDeps = [];
3826
+ for (const item of allItems.values()) {
3827
+ for (const dep of item.dependencies) {
3828
+ if (!existingDeps.has(dep) && !allNewDeps.includes(dep)) {
3829
+ allNewDeps.push(dep);
3830
+ }
3831
+ }
3832
+ }
3833
+ if (options.dryRun) {
3834
+ handleDryRun(allItems, baseDir, allNewDeps);
3835
+ return;
3836
+ }
3837
+ let totalFiles = 0;
3838
+ for (const [name, item] of allItems) {
3839
+ const written = await withSpinner(
3840
+ MESSAGES.ADD_INSTALLING_ITEM(item.title || name),
3841
+ () => writeItemFiles(item, baseDir, !!options.overwrite),
3842
+ { successText: `${item.title || name}` }
3843
+ );
3844
+ totalFiles += written.length;
3845
+ }
3846
+ if (allNewDeps.length > 0) {
3847
+ await withSpinner(
3848
+ MESSAGES.ADD_INSTALLING_DEPS(allNewDeps.length),
3849
+ async () => installNpmDependencies(allNewDeps, projectRoot),
3850
+ { successText: `${allNewDeps.length} dependenc${allNewDeps.length === 1 ? "y" : "ies"} installed` }
3851
+ );
3852
+ }
3853
+ logger.newline();
3854
+ logger.success(MESSAGES.ADD_SUCCESS(allItems.size, totalFiles));
3855
+ if (allNewDeps.length > 0) {
3856
+ logger.dim(` Dependencies: ${allNewDeps.join(", ")}`);
3857
+ }
3858
+ logger.newline();
3859
+ });
3860
+ }
3861
+
3862
+ // src/commands/commitlint.ts
3863
+ init_esm_shims();
3864
+
3865
+ // src/utils/commitlint-setup.ts
3866
+ init_esm_shims();
3867
+ function findProjectRoot2() {
3868
+ let dir = process.cwd();
3869
+ while (dir !== dirname(dir)) {
3870
+ if (existsSync(join(dir, "package.json"))) {
3871
+ return dir;
3872
+ }
3873
+ dir = dirname(dir);
3874
+ }
3875
+ throw new CliError(
3876
+ "Could not find a package.json.",
3877
+ 1,
3878
+ "Run this command from within a JavaScript/TypeScript project."
3879
+ );
3880
+ }
3881
+ function detectPackageManager3(projectRoot) {
3882
+ if (existsSync(join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
3883
+ if (existsSync(join(projectRoot, "yarn.lock"))) return "yarn";
3884
+ if (existsSync(join(projectRoot, "bun.lockb")) || existsSync(join(projectRoot, "bun.lock"))) return "bun";
3885
+ return "npm";
3886
+ }
3887
+ async function detectWorkspaceScopes(projectRoot) {
3888
+ const scopes = /* @__PURE__ */ new Set();
3889
+ const pnpmPath = join(projectRoot, "pnpm-workspace.yaml");
3890
+ if (existsSync(pnpmPath)) {
3891
+ const content = await readFile(pnpmPath, "utf-8");
3892
+ const patterns = content.split("\n").filter((line) => line.trim().startsWith("-")).map((line) => line.trim().replace(/^-\s*['"]?/, "").replace(/['"]?\s*$/, ""));
3893
+ for (const pattern of patterns) {
3894
+ const baseDir = pattern.replace(/\/?\*+$/, "");
3895
+ const fullDir = join(projectRoot, baseDir);
3896
+ if (existsSync(fullDir)) {
3897
+ const entries = readdirSync(fullDir, { withFileTypes: true });
3898
+ for (const entry of entries) {
3899
+ if (entry.isDirectory() && !entry.name.startsWith(".")) {
3900
+ scopes.add(entry.name);
3901
+ }
3902
+ }
3903
+ }
3904
+ }
3905
+ }
3906
+ try {
3907
+ const pkg = JSON.parse(await readFile(join(projectRoot, "package.json"), "utf-8"));
3908
+ const workspaces = Array.isArray(pkg.workspaces) ? pkg.workspaces : pkg.workspaces?.packages ?? [];
3909
+ for (const pattern of workspaces) {
3910
+ const baseDir = pattern.replace(/\/?\*+$/, "");
3911
+ const fullDir = join(projectRoot, baseDir);
3912
+ if (existsSync(fullDir)) {
3913
+ const entries = readdirSync(fullDir, { withFileTypes: true });
3914
+ for (const entry of entries) {
3915
+ if (entry.isDirectory() && !entry.name.startsWith(".")) {
3916
+ scopes.add(entry.name);
3917
+ }
3918
+ }
3919
+ }
3920
+ }
3921
+ } catch {
3922
+ }
3923
+ scopes.add("root");
3924
+ scopes.add("deps");
3925
+ scopes.add("release");
3926
+ return [...scopes].sort();
3927
+ }
3928
+ function generateCommitlintConfig(opts) {
3929
+ const { scopes, scopeStrategy } = opts;
3930
+ let scopeRules = "";
3931
+ if (scopeStrategy === "required") {
3932
+ const scopeList = scopes.map((s) => `'${s}'`).join(", ");
3933
+ scopeRules = `
3934
+ // Scope required and must be from the allowed list
3935
+ 'scope-case': [2, 'always', 'lower-case'],
3936
+ 'scope-empty': [2, 'never'],
3937
+ 'scope-enum': [2, 'always', [${scopeList}]],`;
3938
+ } else if (scopeStrategy === "optional") {
3939
+ const scopeList = scopes.map((s) => `'${s}'`).join(", ");
3940
+ scopeRules = `
3941
+ // Scope optional but if present must be from the allowed list
3942
+ 'scope-case': [2, 'always', 'lower-case'],
3943
+ 'scope-empty': [0],
3944
+ 'scope-enum': [2, 'always', [${scopeList}]],`;
3945
+ }
3946
+ return `import { type UserConfig } from '@commitlint/types';
3947
+
3948
+ const config: UserConfig = {
3949
+ extends: ['@commitlint/config-conventional'],
3950
+ rules: {
3951
+ // Type required
3952
+ 'type-empty': [2, 'never'],
3953
+
3954
+ // Subject required with length constraints
3955
+ 'subject-empty': [2, 'never'],
3956
+ 'subject-min-length': [2, 'always', 5],
3957
+ 'subject-max-length': [2, 'always', 250],
3958
+
3959
+ // Header max length
3960
+ 'header-max-length': [2, 'always', 100],
3961
+ ${scopeRules}
3962
+ // Body (disabled \u2014 no line length limit)
3963
+ 'body-max-line-length': [0, 'always', 200],
3964
+
3965
+ // BREAKING CHANGE (disabled by default)
3966
+ 'trailer-exists': [0, 'always', 'BREAKING CHANGE']
3967
+ },
3968
+
3969
+ parserPreset: {
3970
+ parserOpts: {
3971
+ breakingHeaderPattern: /^(\\w*)(?:\\((.*)\\))?!: (.*)$/,
3972
+ noteKeywords: ['BREAKING CHANGE']
3973
+ }
3974
+ },
3975
+
3976
+ helpUrl:
3977
+ 'https://github.com/conventional-changelog/commitlint/#what-is-commitlint'
3978
+ };
3979
+
3980
+ export default config;
3981
+ `;
3982
+ }
3983
+ function generateCommitMsgHook(packageManager) {
3984
+ const exec4 = packageManager === "pnpm" ? "pnpm exec" : packageManager === "yarn" ? "yarn" : packageManager === "bun" ? "bunx" : "npx --no-install";
3985
+ return `${exec4} commitlint --edit $1
3986
+ `;
3987
+ }
3988
+ function generatePreCommitHook() {
3989
+ return `# Skip if no code files are staged
3990
+ if git diff --cached --name-only | grep -qE '\\.(ts|tsx|js|jsx)$'; then
3991
+ npx --no-install lint-staged
3992
+ fi
3993
+ `;
3994
+ }
3995
+ function generateCommitLinterScript() {
3996
+ return `#!/usr/bin/env bash
3997
+ # Monorepo-aware tsc-files: finds the nearest tsconfig.json for each file,
3998
+ # groups files by their tsconfig directory, and runs tsc per group.
3999
+
4000
+ if [ $# -eq 0 ]; then
4001
+ exit 0
4002
+ fi
4003
+
4004
+ node -e "
4005
+ const fs = require('fs');
4006
+ const path = require('path');
4007
+ const { execFileSync } = require('child_process');
4008
+
4009
+ const files = process.argv.slice(1).map(f => path.resolve(f));
4010
+ const groups = {};
4011
+
4012
+ for (const file of files) {
4013
+ let dir = path.dirname(file);
4014
+ let tsconfigDir = null;
4015
+
4016
+ while (dir !== path.dirname(dir)) {
4017
+ if (fs.existsSync(path.join(dir, 'tsconfig.json'))) {
4018
+ tsconfigDir = dir;
4019
+ break;
4020
+ }
4021
+ dir = path.dirname(dir);
4022
+ }
4023
+
4024
+ if (!tsconfigDir) continue;
4025
+ if (!groups[tsconfigDir]) groups[tsconfigDir] = [];
4026
+ groups[tsconfigDir].push(file);
4027
+ }
4028
+
4029
+ let failed = false;
4030
+
4031
+ for (const [dir, groupFiles] of Object.entries(groups)) {
4032
+ const configPath = path.join(dir, 'tsconfig.json');
4033
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
4034
+ const tmpName = 'tsconfig.staged-' + process.pid + '.json';
4035
+ const tmpPath = path.join(dir, tmpName);
4036
+
4037
+ const excludePatterns = (config.exclude || []).map(p => p.replace(/\\\\/\\\\*\\\\*.*$/, '').replace(/\\\\/$/, ''));
4038
+ const filteredFiles = groupFiles.filter(f => {
4039
+ const rel = path.relative(dir, f).replace(/\\\\\\\\\\\\\\\\/g, '/');
4040
+ return !excludePatterns.some(p => rel === p || rel.startsWith(p + '/'));
4041
+ });
4042
+ if (filteredFiles.length === 0) continue;
4043
+
4044
+ const tmpConfig = {
4045
+ ...config,
4046
+ compilerOptions: { ...config.compilerOptions, skipLibCheck: true, rootDir: undefined },
4047
+ files: filteredFiles,
4048
+ include: []
4049
+ };
4050
+ delete tmpConfig.compilerOptions.rootDir;
4051
+
4052
+ fs.writeFileSync(tmpPath, JSON.stringify(tmpConfig, null, 2));
4053
+
4054
+ try {
4055
+ execFileSync('npx', ['--no-install', 'tsc', '-p', tmpName, '--noEmit'], { cwd: dir, stdio: 'inherit' });
4056
+ } catch {
4057
+ failed = true;
4058
+ } finally {
4059
+ fs.unlinkSync(tmpPath);
4060
+ }
4061
+ }
4062
+
4063
+ if (failed) process.exit(1);
4064
+ " "$@"
4065
+
4066
+ exit $?
4067
+ `;
4068
+ }
4069
+ async function updatePackageJson(projectRoot) {
4070
+ const pkgPath = join(projectRoot, "package.json");
4071
+ const pkg = JSON.parse(await readFile(pkgPath, "utf-8"));
4072
+ pkg.devDependencies = {
4073
+ ...pkg.devDependencies,
4074
+ "@commitlint/cli": "latest",
4075
+ "@commitlint/config-conventional": "latest",
4076
+ "@commitlint/types": "latest",
4077
+ husky: "latest",
4078
+ "lint-staged": "latest"
4079
+ };
4080
+ pkg.scripts = pkg.scripts ?? {};
4081
+ if (pkg.scripts.prepare && !pkg.scripts.prepare.includes("husky")) {
4082
+ pkg.scripts.prepare = `${pkg.scripts.prepare} && husky`;
4083
+ } else if (!pkg.scripts.prepare) {
4084
+ pkg.scripts.prepare = "husky";
4085
+ }
4086
+ pkg["lint-staged"] = {
4087
+ "*.{ts,tsx}": ["bash scripts/commit-linter.sh", "eslint --fix"]
4088
+ };
4089
+ await writeFile(pkgPath, `${JSON.stringify(pkg, null, 2)}
4090
+ `, "utf-8");
4091
+ }
4092
+ async function writeCommitlintFiles(opts) {
4093
+ const {
4094
+ projectRoot,
4095
+ scopes,
4096
+ scopeStrategy,
4097
+ packageManager
4098
+ } = opts;
4099
+ await writeFile(
4100
+ join(projectRoot, "commitlint.config.ts"),
4101
+ generateCommitlintConfig({ scopes, scopeStrategy }),
4102
+ "utf-8"
4103
+ );
4104
+ const huskyDir = join(projectRoot, ".husky");
4105
+ if (!existsSync(huskyDir)) {
4106
+ await mkdir(huskyDir, { recursive: true });
4107
+ }
4108
+ await writeFile(join(huskyDir, "commit-msg"), generateCommitMsgHook(packageManager), "utf-8");
4109
+ await writeFile(join(huskyDir, "pre-commit"), generatePreCommitHook(), "utf-8");
4110
+ const scriptsDir = join(projectRoot, "scripts");
4111
+ if (!existsSync(scriptsDir)) {
4112
+ await mkdir(scriptsDir, { recursive: true });
4113
+ }
4114
+ const scriptPath = join(scriptsDir, "commit-linter.sh");
4115
+ await writeFile(scriptPath, generateCommitLinterScript(), "utf-8");
4116
+ await chmod(scriptPath, "755");
4117
+ }
4118
+ function installDeps(projectRoot, packageManager) {
4119
+ execFileSync(packageManager, ["install"], {
4120
+ cwd: projectRoot,
4121
+ stdio: "pipe"
4122
+ });
4123
+ }
4124
+ function initHusky(projectRoot, packageManager) {
4125
+ const cmds = {
4126
+ pnpm: ["pnpm", ["exec", "husky"]],
4127
+ npm: ["npx", ["husky"]],
4128
+ yarn: ["yarn", ["husky"]],
4129
+ bun: ["bunx", ["husky"]]
4130
+ };
4131
+ const [cmd, args] = cmds[packageManager];
4132
+ execFileSync(cmd, args, {
4133
+ cwd: projectRoot,
4134
+ stdio: "pipe"
4135
+ });
4136
+ }
4137
+
4138
+ // src/commands/commitlint.ts
4139
+ function registerCommitlintCommand(program2) {
4140
+ program2.command("commitlint").description(MESSAGES.COMMITLINT_DESCRIPTION).option("--scopes <scopes>", "Comma-separated list of scopes").option("--monorepo", "Treat as monorepo").option("--no-monorepo", "Treat as single project").option("--scope-strategy <strategy>", "Scope strategy: required, optional, none").option("-y, --yes", "Accept defaults and overwrite existing files").action(async (options) => {
4141
+ const projectRoot = findProjectRoot2();
4142
+ const packageManager = detectPackageManager3(projectRoot);
4143
+ logger.newline();
4144
+ logger.dim(MESSAGES.COMMITLINT_DETECTED_PM(packageManager));
4145
+ const configExists = existsSync(join(projectRoot, "commitlint.config.ts"));
4146
+ const huskyExists = existsSync(join(projectRoot, ".husky"));
4147
+ if (configExists && !options.yes) {
4148
+ const overwrite = await confirm({
4149
+ message: MESSAGES.COMMITLINT_CONFIG_EXISTS,
4150
+ default: false
4151
+ });
4152
+ if (!overwrite) {
4153
+ logger.dim("Aborted.");
4154
+ return;
4155
+ }
4156
+ }
4157
+ let skipHusky = false;
4158
+ if (huskyExists && !options.yes) {
4159
+ const overwrite = await confirm({
4160
+ message: MESSAGES.COMMITLINT_HUSKY_EXISTS,
4161
+ default: true
4162
+ });
4163
+ skipHusky = !overwrite;
4164
+ }
4165
+ const isMonorepo = options.monorepo !== void 0 ? options.monorepo : await confirm({
4166
+ message: MESSAGES.COMMITLINT_IS_MONOREPO,
4167
+ default: false
4168
+ });
4169
+ let scopeStrategy;
4170
+ if (options.scopeStrategy) {
4171
+ scopeStrategy = options.scopeStrategy;
4172
+ } else {
4173
+ scopeStrategy = await select({
4174
+ message: MESSAGES.COMMITLINT_SCOPE_STRATEGY,
4175
+ choices: [
4176
+ {
4177
+ name: isMonorepo ? "Required (recommended)" : "Required",
4178
+ value: "required",
4179
+ description: "fix(auth): ... \u2014 scope is mandatory"
4180
+ },
4181
+ {
4182
+ name: isMonorepo ? "Optional" : "Optional (recommended)",
4183
+ value: "optional",
4184
+ description: "fix: ... or fix(auth): ... \u2014 both valid"
4185
+ },
4186
+ {
4187
+ name: "None",
4188
+ value: "none",
4189
+ description: "fix: ... \u2014 scopes are not used"
4190
+ }
4191
+ ],
4192
+ default: isMonorepo ? "required" : "optional"
4193
+ });
4194
+ }
4195
+ let scopes = [];
4196
+ if (scopeStrategy !== "none") {
4197
+ if (options.scopes) {
4198
+ scopes = options.scopes.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
4199
+ } else {
4200
+ if (isMonorepo) {
4201
+ const detected = await detectWorkspaceScopes(projectRoot);
4202
+ if (detected.length > 0) {
4203
+ logger.dim(MESSAGES.COMMITLINT_DETECTED_WORKSPACES(detected.length));
4204
+ scopes = await checkbox({
4205
+ message: MESSAGES.COMMITLINT_SELECT_SCOPES,
4206
+ choices: detected.map((s) => ({
4207
+ name: s,
4208
+ value: s,
4209
+ checked: true
4210
+ }))
4211
+ });
4212
+ } else {
4213
+ logger.warn(MESSAGES.COMMITLINT_NO_WORKSPACES);
4214
+ scopes = [
4215
+ "root",
4216
+ "deps",
4217
+ "release"
4218
+ ];
4219
+ }
4220
+ } else {
4221
+ scopes = [
4222
+ "root",
4223
+ "deps",
4224
+ "release"
4225
+ ];
4226
+ }
4227
+ const additional = await input({
4228
+ message: MESSAGES.COMMITLINT_ADDITIONAL_SCOPES
4229
+ });
4230
+ if (additional.trim()) {
4231
+ const extra = additional.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
4232
+ for (const s of extra) {
4233
+ if (!scopes.includes(s)) {
4234
+ scopes.push(s);
4235
+ }
4236
+ }
4237
+ }
4238
+ }
4239
+ scopes.sort();
4240
+ logger.newline();
4241
+ logger.info(MESSAGES.COMMITLINT_CONFIRM_SCOPES(scopes));
4242
+ }
4243
+ logger.newline();
4244
+ const progress = createMultiStepProgress("Setting up Commitlint", [
4245
+ MESSAGES.COMMITLINT_STEP_CONFIG,
4246
+ MESSAGES.COMMITLINT_STEP_HUSKY,
4247
+ MESSAGES.COMMITLINT_STEP_SCRIPT,
4248
+ MESSAGES.COMMITLINT_STEP_PACKAGE_JSON,
4249
+ MESSAGES.COMMITLINT_STEP_INSTALL,
4250
+ MESSAGES.COMMITLINT_STEP_INIT_HUSKY
4251
+ ]);
4252
+ progress.start(0);
4253
+ await writeCommitlintFiles({
4254
+ projectRoot,
4255
+ scopes,
4256
+ scopeStrategy,
4257
+ packageManager
4258
+ });
4259
+ progress.complete(0);
4260
+ if (skipHusky) {
4261
+ progress.complete(1);
4262
+ } else {
4263
+ progress.start(1);
4264
+ progress.complete(1);
4265
+ }
4266
+ progress.start(2);
4267
+ progress.complete(2);
4268
+ progress.start(3);
4269
+ await updatePackageJson(projectRoot);
4270
+ progress.complete(3);
4271
+ progress.start(4);
4272
+ try {
4273
+ installDeps(projectRoot, packageManager);
4274
+ progress.complete(4);
4275
+ } catch (error) {
4276
+ progress.fail(4, error instanceof Error ? error.message : "Failed to install dependencies");
4277
+ progress.finish();
4278
+ throw error;
4279
+ }
4280
+ progress.start(5);
4281
+ try {
4282
+ initHusky(projectRoot, packageManager);
4283
+ progress.complete(5);
4284
+ } catch (error) {
4285
+ progress.fail(5, error instanceof Error ? error.message : "Failed to initialize husky");
4286
+ progress.finish();
4287
+ throw error;
4288
+ }
4289
+ progress.finish();
4290
+ logger.newline();
4291
+ logger.success(MESSAGES.COMMITLINT_SUCCESS);
4292
+ logger.dim(MESSAGES.COMMITLINT_SUCCESS_HINT);
4293
+ logger.newline();
4294
+ });
4295
+ }
4296
+
4297
+ // src/commands/list.ts
4298
+ init_esm_shims();
4299
+ async function fetchRegistryIndex() {
4300
+ const url = `${REGISTRY_BASE_URL}/__index.json`;
4301
+ let response;
4302
+ try {
4303
+ response = await fetch(url, { headers: { Accept: "application/json" } });
4304
+ } catch {
4305
+ throw new NetworkError();
4306
+ }
4307
+ if (!response.ok) {
4308
+ throw new NetworkError(`Failed to fetch registry index (HTTP ${response.status})`);
4309
+ }
4310
+ return response.json();
4311
+ }
4312
+ async function fetchNpmPackages() {
4313
+ const url = "https://registry.npmjs.org/-/v1/search?text=%40docyrus&size=100";
4314
+ let response;
4315
+ try {
4316
+ response = await fetch(url, { headers: { Accept: "application/json" } });
4317
+ } catch {
4318
+ throw new NetworkError();
4319
+ }
4320
+ if (!response.ok) {
4321
+ throw new NetworkError(`Failed to fetch npm packages (HTTP ${response.status})`);
4322
+ }
4323
+ const data = await response.json();
4324
+ return data.objects.map((o) => ({
4325
+ name: o.package.name,
4326
+ version: o.package.version,
4327
+ description: o.package.description,
4328
+ date: o.package.date
4329
+ }));
4330
+ }
4331
+ function groupByType(items) {
4332
+ const groups = {};
4333
+ for (const item of items) {
4334
+ const category = item.type.replace("registry:", "");
4335
+ const group = groups[category] ??= [];
4336
+ group.push(item);
4337
+ }
4338
+ return groups;
4339
+ }
4340
+ function printRegistryList(items) {
4341
+ const groups = groupByType(items);
4342
+ const categoryLabels = {
4343
+ ui: "Components",
4344
+ hook: "Hooks",
4345
+ lib: "Utilities"
4346
+ };
4347
+ logger.newline();
4348
+ logger.bold(MESSAGES.LIST_HEADER);
4349
+ logger.newline();
4350
+ for (const [type, group] of Object.entries(groups)) {
4351
+ const label = categoryLabels[type] || type;
4352
+ logger.info(chalk4.bold(label));
4353
+ for (const item of group) {
4354
+ const name = chalk4.cyan(item.name);
4355
+ const desc = item.description ? chalk4.dim(` \u2014 ${item.description}`) : "";
4356
+ logger.log(` ${name}${desc}`);
4357
+ }
4358
+ logger.newline();
4359
+ }
4360
+ logger.dim(MESSAGES.LIST_INSTALL_HINT);
4361
+ logger.newline();
4362
+ }
4363
+ function printNpmPackages(packages) {
4364
+ logger.newline();
4365
+ logger.bold(MESSAGES.LIST_NPM_HEADER);
4366
+ logger.newline();
4367
+ for (const pkg of packages) {
4368
+ const name = chalk4.cyan(pkg.name);
4369
+ const version = chalk4.dim(`@${pkg.version}`);
4370
+ const desc = pkg.description ? chalk4.dim(` \u2014 ${pkg.description}`) : "";
4371
+ logger.log(` ${name}${version}${desc}`);
4372
+ }
4373
+ logger.dim(`
4374
+ ${MESSAGES.LIST_NPM_LINK}`);
4375
+ logger.newline();
4376
+ }
4377
+ function registerListCommand(program2) {
4378
+ program2.command("list").alias("ls").description("List available Docyrus UI components, hooks, and utilities").option("--packages", "List published @docyrus npm packages instead").addHelpText("after", `
4379
+ Examples:
4380
+ $ docyrus list List registry components
4381
+ $ docyrus ls Shorthand alias
4382
+ $ docyrus list --packages List published npm packages
4383
+ $ docyrus list --json Output as JSON
4384
+ `).action(async (options) => {
4385
+ if (options.packages) {
4386
+ const packages = await withSpinner(
4387
+ MESSAGES.LIST_FETCHING_NPM,
4388
+ fetchNpmPackages,
4389
+ { successText: MESSAGES.LIST_NPM_SUCCESS }
4390
+ );
4391
+ if (output.isJson()) {
4392
+ output.success("ok", { packages });
4393
+ } else {
4394
+ printNpmPackages(packages);
4395
+ }
4396
+ } else {
4397
+ const items = await withSpinner(
4398
+ MESSAGES.LIST_FETCHING,
4399
+ fetchRegistryIndex
4400
+ );
4401
+ if (output.isJson()) {
4402
+ output.success("ok", { items });
4403
+ } else {
4404
+ printRegistryList(items);
4405
+ }
4406
+ }
2847
4407
  });
2848
4408
  }
2849
4409
 
@@ -2853,12 +4413,18 @@ function registerCommands(program2) {
2853
4413
  registerLogoutCommand(program2);
2854
4414
  registerWhoamiCommand(program2);
2855
4415
  registerCreateCommand(program2);
4416
+ registerAddCommand(program2);
4417
+ registerListCommand(program2);
4418
+ registerCommitlintCommand(program2);
2856
4419
  registerGenerateCommand(program2);
2857
4420
  registerUpgradeCommand(program2);
2858
4421
  registerCompletionCommand(program2);
2859
4422
  registerInfoCommand(program2);
2860
4423
  registerConfigCommand(program2);
2861
4424
  }
4425
+
4426
+ // src/utils/update-checker.ts
4427
+ init_esm_shims();
2862
4428
  function isNewerVersion2(current, latest) {
2863
4429
  const currentParts = current.replace(/^v/, "").split(".").map(Number);
2864
4430
  const latestParts = latest.replace(/^v/, "").split(".").map(Number);
@@ -2896,7 +4462,7 @@ async function checkForUpdates(packageName, currentVersion) {
2896
4462
 
2897
4463
  // src/cli.ts
2898
4464
  var program = new Command();
2899
- program.name(CLI_NAME).description("Docyrus CLI - Authentication and project management tools").version(CLI_VERSION, "-v, --version", "Display version number").option("--json", "Output results in JSON format").hook("preAction", (thisCommand) => {
4465
+ program.name(CLI_NAME).description("Docyrus CLI - Authentication and project management tools").version(CLI_VERSION, "-v, --version", "Display version number").option("--json", "Output results in JSON format").configureHelp(getHelpConfig(configManager.get("helpTheme"))).hook("preAction", (thisCommand) => {
2900
4466
  const opts = thisCommand.opts();
2901
4467
  if (opts.json) {
2902
4468
  output.setFormat("json");