@docyrus/cli 0.4.0 → 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
@@ -6,15 +6,15 @@ import { RestApiClient, AuthenticationError as AuthenticationError$1 } from '@do
6
6
  import { createServer } from 'http';
7
7
  import { randomBytes, createDecipheriv, createCipheriv, scryptSync, createHash } from 'crypto';
8
8
  import open from 'open';
9
- import { existsSync, readFileSync, mkdirSync, writeFileSync, unlinkSync, constants as constants$1 } from 'fs';
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';
13
+ import ora2 from 'ora';
14
+ import { select, input, password, confirm, checkbox } from '@inquirer/prompts';
15
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
 
@@ -57,12 +57,12 @@ var init_esm_shims = __esm({
57
57
  }
58
58
  });
59
59
 
60
- // ../../node_modules/.pnpm/dotenv@17.2.4/node_modules/dotenv/package.json
60
+ // ../../node_modules/.pnpm/dotenv@17.3.1/node_modules/dotenv/package.json
61
61
  var require_package = __commonJS({
62
- "../../node_modules/.pnpm/dotenv@17.2.4/node_modules/dotenv/package.json"(exports$1, module) {
62
+ "../../node_modules/.pnpm/dotenv@17.3.1/node_modules/dotenv/package.json"(exports$1, module) {
63
63
  module.exports = {
64
64
  name: "dotenv",
65
- version: "17.2.4",
65
+ version: "17.3.1",
66
66
  description: "Loads environment variables from .env file",
67
67
  main: "lib/main.js",
68
68
  types: "lib/main.d.ts",
@@ -125,9 +125,9 @@ var require_package = __commonJS({
125
125
  }
126
126
  });
127
127
 
128
- // ../../node_modules/.pnpm/dotenv@17.2.4/node_modules/dotenv/lib/main.js
128
+ // ../../node_modules/.pnpm/dotenv@17.3.1/node_modules/dotenv/lib/main.js
129
129
  var require_main = __commonJS({
130
- "../../node_modules/.pnpm/dotenv@17.2.4/node_modules/dotenv/lib/main.js"(exports$1, module) {
130
+ "../../node_modules/.pnpm/dotenv@17.3.1/node_modules/dotenv/lib/main.js"(exports$1, module) {
131
131
  init_esm_shims();
132
132
  var fs = __require("fs");
133
133
  var path2 = __require("path");
@@ -139,12 +139,9 @@ var require_main = __commonJS({
139
139
  "\u{1F510} encrypt with Dotenvx: https://dotenvx.com",
140
140
  "\u{1F510} prevent committing .env to code: https://dotenvx.com/precommit",
141
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",
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",
148
145
  "\u{1F6E0}\uFE0F run anywhere with `dotenvx run -- yourcommand`",
149
146
  "\u2699\uFE0F specify custom .env file path with { path: '/custom/path/.env' }",
150
147
  "\u2699\uFE0F enable debug logging with { debug: true }",
@@ -1310,7 +1307,8 @@ init_esm_shims();
1310
1307
  // src/config/config-manager.ts
1311
1308
  init_esm_shims();
1312
1309
  var defaults = {
1313
- telemetryEnabled: true
1310
+ telemetryEnabled: true,
1311
+ helpTheme: "default"
1314
1312
  };
1315
1313
  var ConfigManager = class {
1316
1314
  config;
@@ -1551,13 +1549,43 @@ var MESSAGES = {
1551
1549
  ADD_MISSING_CONFIG: "components.json not found.",
1552
1550
  ADD_MISSING_CONFIG_HINT: "Run `npx shadcn@latest init` first to initialize your project.",
1553
1551
  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-*).'
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}`
1555
1583
  };
1556
1584
 
1557
1585
  // src/ui/spinner.ts
1558
1586
  init_esm_shims();
1559
1587
  function createSpinner(text) {
1560
- return ora({
1588
+ return ora2({
1561
1589
  text,
1562
1590
  spinner: "dots"
1563
1591
  });
@@ -1614,12 +1642,77 @@ async function promptPassword() {
1614
1642
 
1615
1643
  // src/ui/progress.ts
1616
1644
  init_esm_shims();
1617
- ({
1645
+ var SYMBOLS = {
1618
1646
  pending: chalk4.dim("\u25CB"),
1619
1647
  in_progress: chalk4.cyan("\u25D0"),
1620
1648
  completed: chalk4.green("\u2713"),
1621
1649
  failed: chalk4.red("\u2717")
1622
- });
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
+ }
1623
1716
  function createSimpleProgress() {
1624
1717
  let spinner = null;
1625
1718
  return (step, current, total) => {
@@ -1628,7 +1721,7 @@ function createSimpleProgress() {
1628
1721
  spinner = null;
1629
1722
  }
1630
1723
  const text = current && total ? `${step} (${current}/${total})` : step;
1631
- spinner = ora({
1724
+ spinner = ora2({
1632
1725
  text,
1633
1726
  color: "cyan"
1634
1727
  }).start();
@@ -1639,6 +1732,71 @@ function createSimpleProgress() {
1639
1732
  };
1640
1733
  }
1641
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
+
1642
1800
  // src/commands/login.ts
1643
1801
  async function handleCredentialLogin(email) {
1644
1802
  const lastEmail = configManager.get("lastLoginEmail");
@@ -3049,7 +3207,7 @@ ${errorMessage}`
3049
3207
  // src/commands/generate.ts
3050
3208
  init_esm_shims();
3051
3209
  function registerGenerateCommand(program2) {
3052
- 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)");
3053
3211
  generate.command("api-spec").description("Download OpenAPI specification from Docyrus API").option("-o, --output <path>", "Output file path", "openapi.json").action(async (options) => {
3054
3212
  const token = await requireAuth();
3055
3213
  const outputPath = resolve(options.output);
@@ -3238,11 +3396,11 @@ _${CLI_NAME}_completions() {
3238
3396
  args=("\${COMP_WORDS[@]}")
3239
3397
 
3240
3398
  # Commands
3241
- 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"
3242
3400
 
3243
3401
  # Subcommands for generate
3244
3402
  if [[ \${args[1]} == "generate" ]]; then
3245
- type_list="db"
3403
+ type_list="api-spec db"
3246
3404
  fi
3247
3405
 
3248
3406
  COMPREPLY=($(compgen -W "\${type_list}" -- \${cur_word}))
@@ -3260,15 +3418,18 @@ _${CLI_NAME}() {
3260
3418
  'logout:Log out from Docyrus'
3261
3419
  'whoami:Display the current logged-in user'
3262
3420
  'create:Create a new Docyrus project from template'
3263
- 'generate:Code generation commands'
3421
+ 'generate:Generate code from OpenAPI specs'
3264
3422
  'upgrade:Upgrade Docyrus CLI to the latest version'
3265
3423
  'completion:Generate shell completion script'
3424
+ 'add:Add a component from the registry'
3425
+ 'commitlint:Set up commitlint, husky, and lint-staged'
3266
3426
  'info:Display CLI and environment information'
3267
3427
  'help:Display help for command'
3268
3428
  )
3269
3429
 
3270
3430
  local -a generate_commands
3271
3431
  generate_commands=(
3432
+ 'api-spec:Download OpenAPI specification from Docyrus API'
3272
3433
  'db:Generate TanStack Query collections from OpenAPI spec'
3273
3434
  )
3274
3435
 
@@ -3301,13 +3462,16 @@ complete -c ${CLI_NAME} -n "__fish_use_subcommand" -a "login" -d "Log in to Docy
3301
3462
  complete -c ${CLI_NAME} -n "__fish_use_subcommand" -a "logout" -d "Log out from Docyrus"
3302
3463
  complete -c ${CLI_NAME} -n "__fish_use_subcommand" -a "whoami" -d "Display the current logged-in user"
3303
3464
  complete -c ${CLI_NAME} -n "__fish_use_subcommand" -a "create" -d "Create a new Docyrus project"
3304
- 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"
3305
3466
  complete -c ${CLI_NAME} -n "__fish_use_subcommand" -a "upgrade" -d "Upgrade Docyrus CLI"
3306
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"
3307
3470
  complete -c ${CLI_NAME} -n "__fish_use_subcommand" -a "info" -d "Display CLI and environment information"
3308
3471
  complete -c ${CLI_NAME} -n "__fish_use_subcommand" -a "help" -d "Display help"
3309
3472
 
3310
3473
  # generate subcommands
3474
+ complete -c ${CLI_NAME} -n "__fish_seen_subcommand_from generate" -a "api-spec" -d "Download OpenAPI specification"
3311
3475
  complete -c ${CLI_NAME} -n "__fish_seen_subcommand_from generate" -a "db" -d "Generate TanStack Query collections"
3312
3476
  ###-end-${CLI_NAME}-completions-###
3313
3477
  `.trim();
@@ -3407,18 +3571,37 @@ function registerInfoCommand(program2) {
3407
3571
 
3408
3572
  // src/commands/config.ts
3409
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
+ }
3410
3583
  function registerConfigCommand(program2) {
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) => {
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) => {
3412
3585
  const tokenManager = getTokenManager();
3413
3586
  if (options.show) {
3414
3587
  const githubToken2 = await tokenManager.getGithubToken();
3415
3588
  const isLoggedIn2 = await tokenManager.isLoggedIn();
3416
3589
  const email2 = await tokenManager.getUserEmail();
3417
- logger.log("Current configuration:");
3418
- logger.newline();
3419
- logger.log(` Docyrus Login: ${isLoggedIn2 ? `\u2713 ${email2}` : "\u2717 Not logged in"}`);
3420
- logger.log(` Docyrus Token: ${githubToken2 ? "\u2713 Configured" : "\u2717 Not configured"}`);
3421
- 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}"`);
3422
3605
  return;
3423
3606
  }
3424
3607
  if (options.token) {
@@ -3441,12 +3624,9 @@ function registerConfigCommand(program2) {
3441
3624
  const githubToken = await tokenManager.getGithubToken();
3442
3625
  const isLoggedIn = await tokenManager.isLoggedIn();
3443
3626
  const email = await tokenManager.getUserEmail();
3444
- logger.log("Current configuration:");
3445
- logger.newline();
3446
- logger.log(` Docyrus Login: ${isLoggedIn ? `\u2713 ${email}` : "\u2717 Not logged in"}`);
3447
- logger.log(` Docyrus Token: ${githubToken ? "\u2713 Configured" : "\u2717 Not configured"}`);
3448
- logger.newline();
3627
+ showConfig(isLoggedIn, email, githubToken);
3449
3628
  logger.log("To update token: docyrus config --token");
3629
+ logger.log("To change theme: docyrus config --theme");
3450
3630
  });
3451
3631
  }
3452
3632
 
@@ -3480,11 +3660,11 @@ function detectPackageManager2(projectRoot) {
3480
3660
  if (existsSync(join(projectRoot, "bun.lockb")) || existsSync(join(projectRoot, "bun.lock"))) return "bun";
3481
3661
  return "npm";
3482
3662
  }
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}`;
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}`;
3488
3668
  }
3489
3669
  async function fetchRegistryItem(name, token) {
3490
3670
  const url = `${REGISTRY_BASE_URL}/${encodeURIComponent(name)}.json`;
@@ -3679,6 +3859,554 @@ Examples:
3679
3859
  });
3680
3860
  }
3681
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
+ }
4407
+ });
4408
+ }
4409
+
3682
4410
  // src/commands/index.ts
3683
4411
  function registerCommands(program2) {
3684
4412
  registerLoginCommand(program2);
@@ -3686,6 +4414,8 @@ function registerCommands(program2) {
3686
4414
  registerWhoamiCommand(program2);
3687
4415
  registerCreateCommand(program2);
3688
4416
  registerAddCommand(program2);
4417
+ registerListCommand(program2);
4418
+ registerCommitlintCommand(program2);
3689
4419
  registerGenerateCommand(program2);
3690
4420
  registerUpgradeCommand(program2);
3691
4421
  registerCompletionCommand(program2);
@@ -3732,7 +4462,7 @@ async function checkForUpdates(packageName, currentVersion) {
3732
4462
 
3733
4463
  // src/cli.ts
3734
4464
  var program = new Command();
3735
- 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) => {
3736
4466
  const opts = thisCommand.opts();
3737
4467
  if (opts.json) {
3738
4468
  output.setFormat("json");