@visulima/cerebro 2.1.5 → 3.0.0-alpha.10

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.
Files changed (41) hide show
  1. package/CHANGELOG.md +228 -0
  2. package/LICENSE.md +1810 -98
  3. package/README.md +28 -21
  4. package/dist/cli.d.ts +22 -0
  5. package/dist/commands/completion-command.js +204 -5
  6. package/dist/commands/help-command.js +200 -1
  7. package/dist/commands/readme-command.js +326 -32
  8. package/dist/commands/version-command.js +18 -1
  9. package/dist/default-env.d.ts +1 -1
  10. package/dist/index.js +7 -1
  11. package/dist/logger/create-pail-logger.js +34 -1
  12. package/dist/packem_chunks/has-new-version.js +264 -1
  13. package/dist/packem_shared/Cerebro-bgCm5Tb1.js +3444 -0
  14. package/dist/packem_shared/VERBOSITY_QUIET-Dp46zlLW.js +10 -0
  15. package/dist/packem_shared/VisulimaError-DA7QsCxH.js +34 -0
  16. package/dist/packem_shared/cerebro-error-GmJ3jN7Q.js +16 -0
  17. package/dist/packem_shared/index-CS31xKFe.js +264 -0
  18. package/dist/packem_shared/runtime-process-B6ZplyWn.js +187 -0
  19. package/dist/plugins/error-handler-plugin.js +648 -1
  20. package/dist/plugins/runtime-version-check-plugin.js +77 -1
  21. package/dist/plugins/update-notifier/update-notifier-plugin.js +517 -1
  22. package/dist/types/cli.d.ts +11 -0
  23. package/dist/types/command-line-usage.d.ts +1 -1
  24. package/dist/types/command.d.ts +10 -10
  25. package/dist/util/command-processing/option-processor.d.ts +8 -8
  26. package/dist/util/general/compile-cache.d.ts +41 -0
  27. package/dist/util/general/compile-cache.js +16 -0
  28. package/dist/util/general/heap-tuning.d.ts +81 -0
  29. package/dist/util/general/heap-tuning.js +91 -0
  30. package/dist/util/general/register-exception-handler.d.ts +1 -1
  31. package/dist/util/process-env-variables.d.ts +1 -1
  32. package/package.json +18 -9
  33. package/dist/packem_shared/Cerebro-CQZ9sj4S.js +0 -4
  34. package/dist/packem_shared/VERBOSITY_QUIET-XPultrIA.js +0 -1
  35. package/dist/packem_shared/VisulimaError--04oA1Oy.js +0 -76
  36. package/dist/packem_shared/cerebro-error-BnJTixb2.js +0 -1
  37. package/dist/packem_shared/help-command-CIRIXN03.js +0 -1
  38. package/dist/packem_shared/index-DQ3pvLQH.js +0 -6
  39. package/dist/packem_shared/isVisulimaError-jVZgumOU-C4fgdbWg.js +0 -1
  40. package/dist/packem_shared/renderError-ZMlMvw1N-eVUSdl6c.js +0 -24
  41. package/dist/packem_shared/runtime-process-G-n-wOub.js +0 -1
package/README.md CHANGED
@@ -1,26 +1,24 @@
1
- <div align="center">
2
- <h3>Visulima Cerebro</h3>
3
- <p>
4
- Cerebro is a delightful toolkit for building cross-runtime command-line interfaces (CLIs) for Node.js, Deno, and Bun, built on top of
1
+ <!-- START_PACKAGE_OG_IMAGE_PLACEHOLDER -->
5
2
 
6
- [@visulima/boxen](https://github.com/visulima/visulima/tree/main/packages/boxen),
7
- [@visulima/colorize](https://github.com/visulima/visulima/tree/main/packages/colorize),
8
- [@visulima/command-line-args](https://github.com/visulima/visulima/tree/main/packages/command-line-args),
9
- [@visulima/tabular](https://github.com/visulima/visulima/tree/main/packages/tabular) and
10
- [fastest-levenshtein](https://github.com/ka-weihe/fastest-levenshtein)
3
+ <a href="https://www.anolilab.com/open-source" align="center">
11
4
 
12
- <br />
5
+ <img src="__assets__/package-og.svg" alt="cerebro" />
13
6
 
14
- I would recommend reading this [guide](https://clig.dev/) on how to make user-friendly command-line tools.
7
+ </a>
15
8
 
16
- </p>
17
- </div>
9
+ <h3 align="center">A delightful toolkit for building cross-runtime CLIs for Node.js, Deno, and Bun.</h3>
10
+
11
+ <!-- END_PACKAGE_OG_IMAGE_PLACEHOLDER -->
18
12
 
19
13
  <br />
20
14
 
21
15
  <div align="center">
22
16
 
23
- [![typescript-image]][typescript-url] [![npm-image]][npm-url] [![license-image]][license-url]
17
+ [![typescript-image][typescript-badge]][typescript-url]
18
+ [![mit licence][license-badge]][license]
19
+ [![npm downloads][npm-downloads-badge]][npm-downloads]
20
+ [![Chat][chat-badge]][chat]
21
+ [![PRs Welcome][prs-welcome-badge]][prs-welcome]
24
22
 
25
23
  </div>
26
24
 
@@ -428,14 +426,23 @@ If you would like to help take a look at the [list of issues](https://github.com
428
426
  - [commander.js](https://github.com/tj/commander.js) - node.js command-line interfaces made easy
429
427
  - [yargs](https://www.npmjs.com/package/yargs) - yargs the modern, pirate-themed successor to optimist.
430
428
 
429
+ ## Made with ❤️ at Anolilab
430
+
431
+ This is an open source project and will always remain free to use. If you think it's cool, please star it 🌟. [Anolilab](https://www.anolilab.com/open-source) is a Development and AI Studio. Contact us at [hello@anolilab.com](mailto:hello@anolilab.com) if you need any help with these technologies or just want to say hi!
432
+
431
433
  ## License
432
434
 
433
- The visulima package is open-sourced software licensed under the [MIT][license-url]
435
+ The visulima package is open-sourced software licensed under the [MIT][license]
434
436
 
435
- [typescript-image]: https://img.shields.io/badge/Typescript-294E80.svg?style=for-the-badge&logo=typescript
437
+ <!-- badges -->
436
438
 
437
- [typescript-url]: https://www.typescriptlang.org/ "TypeScript" "typescript"
438
- [license-image]: https://img.shields.io/npm/l/@visulima/cerebro?color=blueviolet&style=for-the-badge
439
- [license-url]: LICENSE.md "license"
440
- [npm-image]: https://img.shields.io/npm/v/@visulima/cerebro/latest.svg?style=for-the-badge&logo=npm
441
- [npm-url]: https://www.npmjs.com/package/@visulima/cerebro/v/latest "npm"
439
+ [license-badge]: https://img.shields.io/npm/l/@visulima/cerebro?style=for-the-badge
440
+ [license]: https://github.com/visulima/visulima/blob/main/LICENSE
441
+ [npm-downloads-badge]: https://img.shields.io/npm/dm/@visulima/cerebro?style=for-the-badge
442
+ [npm-downloads]: https://www.npmjs.com/package/@visulima/cerebro
443
+ [prs-welcome-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=for-the-badge
444
+ [prs-welcome]: https://github.com/visulima/visulima/blob/main/.github/CONTRIBUTING.md
445
+ [chat-badge]: https://img.shields.io/discord/932323359193186354.svg?style=for-the-badge
446
+ [chat]: https://discord.gg/TtFJY8xkFK
447
+ [typescript-badge]: https://img.shields.io/badge/Typescript-294E80.svg?style=for-the-badge&logo=typescript
448
+ [typescript-url]: https://www.typescriptlang.org/
package/dist/cli.d.ts CHANGED
@@ -89,6 +89,28 @@ export declare class Cli<T extends Console = Console> implements ICli<T> {
89
89
  * ```
90
90
  */
91
91
  addCommand<OD extends OptionDefinition<unknown> = OptionDefinition<unknown>>(command: ICommand<OD, T>): this;
92
+ /**
93
+ * Adds a global option available to all commands.
94
+ *
95
+ * Global options are parsed alongside command-specific options and displayed
96
+ * in the help output under the "Global Options" section.
97
+ * @param option The option definition
98
+ * @returns The CLI instance for method chaining
99
+ * @example
100
+ * ```typescript
101
+ * cli.addGlobalOption({
102
+ * name: 'cwd',
103
+ * type: String,
104
+ * description: 'Override working directory',
105
+ * });
106
+ * ```
107
+ */
108
+ addGlobalOption<V = unknown>(option: OptionDefinition<V>): this;
109
+ /**
110
+ * Gets all global options (built-in + custom).
111
+ * @returns Array of all global option definitions
112
+ */
113
+ getGlobalOptions(): OptionDefinition<unknown>[];
92
114
  /**
93
115
  * Adds a plugin to extend the CLI functionality.
94
116
  *
@@ -1,5 +1,204 @@
1
- var g=Object.defineProperty;var a=(e,t)=>g(e,"name",{value:t,configurable:!0});import S from"@bomb.sh/tab";import{c as $}from"../packem_shared/cerebro-error-BnJTixb2.js";var w=Object.defineProperty,y=a((e,t)=>w(e,"name",{value:t,configurable:!0}),"n");class p extends ${static{a(this,"l")}static{y(this,"CompletionError")}troubleshooting;constructor(t,o,n=[]){super(t,o,{troubleshooting:n}),this.name="CompletionError",this.troubleshooting=n,n.length>0&&(this.hint=n.join(`
2
- `))}}var C=Object.defineProperty,s=a((e,t)=>C(e,"name",{value:t,configurable:!0}),"s");const h=["bash","zsh","fish","powershell"],d=["node","bun","deno"],v=s(e=>"Deno"in e,"hasDeno"),L=s(e=>"Bun"in e,"hasBun"),u=s(()=>v(globalThis)?"deno":L(globalThis)?"bun":"node","detectRuntime"),m=s(e=>{const t=e?.starshipShell??e?.shell;if(t){const r=t.toLowerCase();if(r.includes("zsh"))return"zsh";if(r.includes("bash"))return"bash";if(r.includes("fish"))return"fish"}const o=e?.psModulePath,n=e?.prompt;if(o||n?.includes("PS"))return"powershell";if(e?.comSpec?.toLowerCase().includes("cmd.exe"))return"bash"},"detectShell"),f=S,I=s((e,t)=>{for(const o of t)o.hidden||(o.name&&e.option(o.name,o.description||""),o.alias&&e.option(o.alias,o.description||""))},"registerCommandOptions"),P=s((e,t)=>{for(const[o,n]of t){if(n.name!==o||n.hidden)continue;const r=e.command(n.name,n.description||"");n.options&&I(r,n.options)}},"registerCommands"),E=s(e=>{if(!h.includes(e))throw new p(`Invalid shell type: ${e}`,"INVALID_SHELL",[`Valid shells are: ${h.join(", ")}`,"Shell will be auto-detected if not specified"])},"validateShell"),j=s(e=>{if(e&&!d.includes(e))throw new p(`Invalid runtime: ${e}`,"INVALID_RUNTIME",[`Valid runtimes are: ${d.join(", ")}`,"Runtime will be auto-detected if not specified"])},"validateRuntime"),z=s((e,t)=>{e.error("Could not detect current shell");const o=[`Usage: ${t} completion --shell=<bash|zsh|fish|powershell> [--runtime=<node|bun|deno>]`,"","Examples:"," # Install completions for zsh:",` ${t} completion --shell=zsh > ~/.${t}-completion.zsh`,` echo 'source ~/.${t}-completion.zsh' >> ~/.zshrc`,""," # Install completions for bash with custom runtime:",` ${t} completion --shell=bash --runtime=bun > ~/.${t}-completion.bash`,` echo 'source ~/.${t}-completion.bash' >> ~/.bashrc`,""," # Install completions for fish:",` ${t} completion --shell=fish > ~/.config/fish/completions/${t}.fish`].join(`
3
- `);e.info(o)},"printUsageInstructions"),x={description:"Generate shell completion scripts",env:[{description:"Shell path (Unix-like systems). Used for shell detection.",name:"SHELL",type:String},{description:"Starship shell configuration. Takes precedence over SHELL for detection.",name:"STARSHIP_SHELL",type:String},{description:"PowerShell module path (Windows). Used for PowerShell detection.",name:"PSModulePath",type:String},{description:"Command prompt variable (Windows). Used for PowerShell detection.",name:"PROMPT",type:String},{description:"Command processor (Windows). Used for Windows Command Prompt detection.",name:"ComSpec",type:String}],execute:s(async({env:e,logger:t,options:o,runtime:n})=>{const r=n.getCliName(),c=o?.shell||m(e);if(!c){z(t,r);return}try{E(c),j(o?.runtime),P(f,n.getCommands());const i=`${o?.runtime||u()} ${r}`;f.setup(r,i,c)}catch(i){if(i instanceof p){const l=[`Failed to generate completion script: ${i.message}`,`Error code: ${i.code}`];throw i.troubleshooting.length>0&&l.push("","Troubleshooting:",...i.troubleshooting.map(b=>` • ${b}`)),t.error(l.join(`
4
- `)),i}else{const l=["Failed to generate completion script",`Error: ${i instanceof Error?i.message:String(i)}`,"","Troubleshooting:"," • Ensure @bomb.sh/tab is installed: pnpm add @bomb.sh/tab",` • Verify shell is supported: ${h.join(", ")}`,` • Verify runtime is supported: ${d.join(", ")}`," • Check that your CLI name is correct"];t.error(l.join(`
5
- `))}}},"execute"),name:"completion",options:[{defaultOption:!0,defaultValue:m(),description:"Shell type (bash, zsh, fish, powershell). Defaults to current shell if detected.",name:"shell",type:String,typeLabel:"{underline shell}"},{defaultOption:!0,defaultValue:u(),description:"JavaScript runtime (node, bun, deno). Defaults to current runtime if detected.",name:"runtime",type:String,typeLabel:"{underline runtime}"}]};export{x as default};
1
+ import tab from '@bomb.sh/tab';
2
+ import { C as CerebroError } from '../packem_shared/cerebro-error-GmJ3jN7Q.js';
3
+
4
+ class CompletionError extends CerebroError {
5
+ troubleshooting;
6
+ constructor(message, code, troubleshooting = []) {
7
+ super(message, code, { troubleshooting });
8
+ this.name = "CompletionError";
9
+ this.troubleshooting = troubleshooting;
10
+ if (troubleshooting.length > 0) {
11
+ this.hint = troubleshooting.join("\n");
12
+ }
13
+ }
14
+ }
15
+
16
+ const validShells = ["bash", "zsh", "fish", "powershell"];
17
+ const validRuntimes = ["node", "bun", "deno"];
18
+ const hasDeno = (global) => "Deno" in global;
19
+ const hasBun = (global) => "Bun" in global;
20
+ const detectRuntime = () => {
21
+ if (hasDeno(globalThis)) {
22
+ return "deno";
23
+ }
24
+ if (hasBun(globalThis)) {
25
+ return "bun";
26
+ }
27
+ return "node";
28
+ };
29
+ const detectShell = (toolboxEnv) => {
30
+ const starshipShell = toolboxEnv?.starshipShell;
31
+ const shell = starshipShell ?? toolboxEnv?.shell;
32
+ if (shell) {
33
+ const shellPath = shell.toLowerCase();
34
+ if (shellPath.includes("zsh")) {
35
+ return "zsh";
36
+ }
37
+ if (shellPath.includes("bash")) {
38
+ return "bash";
39
+ }
40
+ if (shellPath.includes("fish")) {
41
+ return "fish";
42
+ }
43
+ }
44
+ const psModulePath = toolboxEnv?.psModulePath;
45
+ const prompt = toolboxEnv?.prompt;
46
+ if (psModulePath || prompt?.includes("PS")) {
47
+ return "powershell";
48
+ }
49
+ const comSpec = toolboxEnv?.comSpec;
50
+ if (comSpec?.toLowerCase().includes("cmd.exe")) {
51
+ return "bash";
52
+ }
53
+ return void 0;
54
+ };
55
+ const tabInstance = tab;
56
+ const registerCommandOptions = (cmd, options) => {
57
+ for (const option of options) {
58
+ if (option.hidden) {
59
+ continue;
60
+ }
61
+ if (option.name) {
62
+ cmd.option(option.name, option.description ?? "");
63
+ }
64
+ if (option.alias) {
65
+ cmd.option(option.alias, option.description ?? "");
66
+ }
67
+ }
68
+ };
69
+ const registerCommands = (tabInstance2, commands) => {
70
+ for (const [commandName, command] of commands) {
71
+ if (command.name !== commandName || command.hidden) {
72
+ continue;
73
+ }
74
+ const cmd = tabInstance2.command(command.name, command.description ?? "");
75
+ if (command.options) {
76
+ registerCommandOptions(cmd, command.options);
77
+ }
78
+ }
79
+ };
80
+ const validateShell = (shell) => {
81
+ if (!validShells.includes(shell)) {
82
+ throw new CompletionError(`Invalid shell type: ${shell}`, "INVALID_SHELL", [
83
+ `Valid shells are: ${validShells.join(", ")}`,
84
+ "Shell will be auto-detected if not specified"
85
+ ]);
86
+ }
87
+ };
88
+ const validateRuntime = (runtime) => {
89
+ if (runtime && !validRuntimes.includes(runtime)) {
90
+ throw new CompletionError(`Invalid runtime: ${runtime}`, "INVALID_RUNTIME", [
91
+ `Valid runtimes are: ${validRuntimes.join(", ")}`,
92
+ "Runtime will be auto-detected if not specified"
93
+ ]);
94
+ }
95
+ };
96
+ const printUsageInstructions = (logger, cliName) => {
97
+ logger.error("Could not detect current shell");
98
+ const usageInfo = [
99
+ `Usage: ${cliName} completion --shell=<bash|zsh|fish|powershell> [--runtime=<node|bun|deno>]`,
100
+ "",
101
+ "Examples:",
102
+ ` # Install completions for zsh:`,
103
+ ` ${cliName} completion --shell=zsh > ~/.${cliName}-completion.zsh`,
104
+ ` echo 'source ~/.${cliName}-completion.zsh' >> ~/.zshrc`,
105
+ "",
106
+ ` # Install completions for bash with custom runtime:`,
107
+ ` ${cliName} completion --shell=bash --runtime=bun > ~/.${cliName}-completion.bash`,
108
+ ` echo 'source ~/.${cliName}-completion.bash' >> ~/.bashrc`,
109
+ "",
110
+ ` # Install completions for fish:`,
111
+ ` ${cliName} completion --shell=fish > ~/.config/fish/completions/${cliName}.fish`
112
+ ].join("\n");
113
+ logger.info(usageInfo);
114
+ };
115
+ const completionCommand = {
116
+ description: "Generate shell completion scripts",
117
+ env: [
118
+ {
119
+ description: "Shell path (Unix-like systems). Used for shell detection.",
120
+ name: "SHELL",
121
+ type: String
122
+ },
123
+ {
124
+ description: "Starship shell configuration. Takes precedence over SHELL for detection.",
125
+ name: "STARSHIP_SHELL",
126
+ type: String
127
+ },
128
+ {
129
+ description: "PowerShell module path (Windows). Used for PowerShell detection.",
130
+ name: "PSModulePath",
131
+ type: String
132
+ },
133
+ {
134
+ description: "Command prompt variable (Windows). Used for PowerShell detection.",
135
+ name: "PROMPT",
136
+ type: String
137
+ },
138
+ {
139
+ description: "Command processor (Windows). Used for Windows Command Prompt detection.",
140
+ name: "ComSpec",
141
+ type: String
142
+ }
143
+ ],
144
+ // eslint-disable-next-line @typescript-eslint/require-await
145
+ execute: async ({ env: toolboxEnv, logger, options, runtime }) => {
146
+ const cliName = runtime.getCliName();
147
+ const shell = options.shell ?? detectShell(toolboxEnv);
148
+ if (!shell) {
149
+ printUsageInstructions(logger, cliName);
150
+ return;
151
+ }
152
+ try {
153
+ validateShell(shell);
154
+ validateRuntime(options.runtime);
155
+ registerCommands(tabInstance, runtime.getCommands());
156
+ const jsRuntime = options.runtime ?? detectRuntime();
157
+ const scriptPath = `${jsRuntime} ${cliName}`;
158
+ tabInstance.setup(cliName, scriptPath, shell);
159
+ } catch (error) {
160
+ if (error instanceof CompletionError) {
161
+ const errorMessages = [`Failed to generate completion script: ${error.message}`, `Error code: ${error.code}`];
162
+ if (error.troubleshooting.length > 0) {
163
+ errorMessages.push("", "Troubleshooting:", ...error.troubleshooting.map((tip) => ` • ${tip}`));
164
+ }
165
+ logger.error(errorMessages.join("\n"));
166
+ throw error;
167
+ } else {
168
+ const errorMessage = error instanceof Error ? error.message : String(error);
169
+ const errorMessages = [
170
+ "Failed to generate completion script",
171
+ `Error: ${errorMessage}`,
172
+ "",
173
+ "Troubleshooting:",
174
+ " • Ensure @bomb.sh/tab is installed: pnpm add @bomb.sh/tab",
175
+ ` • Verify shell is supported: ${validShells.join(", ")}`,
176
+ ` • Verify runtime is supported: ${validRuntimes.join(", ")}`,
177
+ " • Check that your CLI name is correct"
178
+ ];
179
+ logger.error(errorMessages.join("\n"));
180
+ }
181
+ }
182
+ },
183
+ name: "completion",
184
+ options: [
185
+ {
186
+ defaultOption: true,
187
+ defaultValue: detectShell(),
188
+ description: "Shell type (bash, zsh, fish, powershell). Defaults to current shell if detected.",
189
+ name: "shell",
190
+ type: String,
191
+ typeLabel: "{underline shell}"
192
+ },
193
+ {
194
+ defaultOption: true,
195
+ defaultValue: detectRuntime(),
196
+ description: "JavaScript runtime (node, bun, deno). Defaults to current runtime if detected.",
197
+ name: "runtime",
198
+ type: String,
199
+ typeLabel: "{underline runtime}"
200
+ }
201
+ ]
202
+ };
203
+
204
+ export { completionCommand as default };
@@ -1 +1,200 @@
1
- import"@visulima/colorize";import{D as p}from"../packem_shared/help-command-CIRIXN03.js";import"../packem_shared/index-DQ3pvLQH.js";export{p as default};
1
+ import { inverse, cyan, green, yellow } from '@visulima/colorize';
2
+ import { t as templateFormat, c as commandLineUsage } from '../packem_shared/index-CS31xKFe.js';
3
+
4
+ const defaultEnv = [
5
+ {
6
+ defaultValue: "32",
7
+ description: "Controls the verbosity level of output. Valid values: '16' (quiet), '32' (normal), '64' (verbose), '128' (debug)",
8
+ name: "CEREBRO_OUTPUT_LEVEL",
9
+ type: String
10
+ },
11
+ {
12
+ description: "Sets the minimum required Node.js version. Overrides the default minimum version check",
13
+ name: "CEREBRO_MIN_NODE_VERSION",
14
+ type: Number
15
+ },
16
+ {
17
+ defaultValue: false,
18
+ description: "When set, disables the update notifier check",
19
+ name: "NO_UPDATE_NOTIFIER",
20
+ type: Boolean
21
+ },
22
+ {
23
+ description: "Standard Node.js environment variable. When set to 'test', disables update notifier",
24
+ name: "NODE_ENV",
25
+ type: String
26
+ },
27
+ {
28
+ defaultValue: false,
29
+ description: "When set, enables debug output (same as --debug flag)",
30
+ name: "DEBUG",
31
+ type: Boolean
32
+ },
33
+ {
34
+ description: "Sets the terminal width for table rendering. Useful for testing and consistent output",
35
+ name: "CEREBRO_TERMINAL_WIDTH",
36
+ type: Number
37
+ }
38
+ ];
39
+
40
+ const EMPTY_GROUP_KEY = "__Other";
41
+ const upperFirstChar = (string_) => string_.charAt(0).toUpperCase() + string_.slice(1);
42
+ const printGeneralHelp = (logger, runtime, commands, groupOption) => {
43
+ logger.debug("no command given, printing general help...");
44
+ let filteredCommands = [...new Set(commands.values())].filter((command) => !command.hidden);
45
+ if (groupOption) {
46
+ filteredCommands = filteredCommands.filter((command) => command.group === groupOption);
47
+ }
48
+ const groupedCommands = filteredCommands.reduce((accumulator, command) => {
49
+ const group = command.group ?? EMPTY_GROUP_KEY;
50
+ accumulator[group] ??= [];
51
+ accumulator[group].push(command);
52
+ return accumulator;
53
+ }, {});
54
+ const buildCommandList = (commandList) => commandList.map((command) => {
55
+ let aliases = "";
56
+ if (typeof command.alias === "string") {
57
+ aliases = command.alias;
58
+ } else if (Array.isArray(command.alias)) {
59
+ aliases = command.alias.join(", ");
60
+ }
61
+ if (aliases !== "") {
62
+ aliases = ` [${aliases}]`;
63
+ }
64
+ let commandDisplay = command.name;
65
+ if (command.commandPath && command.commandPath.length > 0) {
66
+ commandDisplay = `${command.commandPath.join(" ")} ${command.name}`;
67
+ }
68
+ return [`${green(commandDisplay)}${aliases}`, command.description ?? ""];
69
+ });
70
+ (logger.raw ?? logger.log)(
71
+ commandLineUsage(
72
+ [
73
+ {
74
+ content: `${cyan(runtime.getCliName())} ${green("<command>")} [positional arguments] ${yellow("[options]")}`,
75
+ header: inverse.cyan(" Usage ")
76
+ },
77
+ ...Object.keys(groupedCommands).map((key) => {
78
+ const groupOptionName = groupOption ? ` ${upperFirstChar(groupOption)}` : "";
79
+ return {
80
+ content: buildCommandList(groupedCommands[key]),
81
+ header: key === EMPTY_GROUP_KEY || groupOption ? inverse.green(` Available${groupOptionName} Commands `) : ` ${inverse.green(` ${upperFirstChar(key)} `)}`
82
+ };
83
+ }),
84
+ commands.has("help") ? {
85
+ header: inverse.yellow(" Command Options "),
86
+ optionList: commands.get("help").options?.filter((option) => !option.hidden)
87
+ } : void 0,
88
+ { header: inverse.yellow(" Global Options "), optionList: runtime.getGlobalOptions() },
89
+ {
90
+ content: defaultEnv.filter((envVariable) => !envVariable.hidden).map((envVariable) => [envVariable.name, envVariable.description ?? ""]),
91
+ header: inverse.magenta(" Environment Variables ")
92
+ },
93
+ {
94
+ content: `Run "${cyan(runtime.getCliName())} ${green("help <command>")}" or "${cyan(runtime.getCliName())} ${green("<command>")} ${yellow("--help")}" for help with a specific command.`,
95
+ raw: true
96
+ }
97
+ ].filter(Boolean)
98
+ )
99
+ );
100
+ };
101
+ const printCommandHelp = (logger, runtime, commands, name) => {
102
+ let command = commands.get(name);
103
+ if (!command) {
104
+ for (const cmd of commands.values()) {
105
+ const fullPath = cmd.commandPath ? [...cmd.commandPath, cmd.name] : [cmd.name];
106
+ if (fullPath.at(-1) === name || fullPath.join(" ") === name) {
107
+ command = cmd;
108
+ break;
109
+ }
110
+ }
111
+ }
112
+ if (!command) {
113
+ logger.error(`Command "${name}" not found`);
114
+ return;
115
+ }
116
+ const usageGroups = [];
117
+ const fullCommandPath = command.commandPath ? [...command.commandPath, command.name] : [command.name];
118
+ const commandDisplay = fullCommandPath.join(" ");
119
+ usageGroups.push({
120
+ content: `${cyan(runtime.getCliName())} ${green(commandDisplay)}${command.argument ? " [positional arguments]" : ""}${command.options ? " [options]" : ""}`,
121
+ header: inverse.cyan(" Usage ")
122
+ });
123
+ if (command.description) {
124
+ usageGroups.push({ content: command.description, header: inverse.green(" Description ") });
125
+ }
126
+ if (command.argument) {
127
+ usageGroups.push({ header: "Command Positional Arguments", isArgument: true, optionList: [command.argument] });
128
+ }
129
+ if (Array.isArray(command.options) && command.options.length > 0) {
130
+ usageGroups.push({
131
+ header: inverse.yellow(" Command Options "),
132
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
133
+ optionList: command.options.filter((option) => !option.hidden)
134
+ });
135
+ }
136
+ usageGroups.push({ header: inverse.yellow(" Global Options "), optionList: runtime.getGlobalOptions() });
137
+ if (Array.isArray(command.env) && command.env.length > 0) {
138
+ const visibleEnvVariables = command.env.filter((envVariable) => !envVariable.hidden);
139
+ if (visibleEnvVariables.length > 0) {
140
+ usageGroups.push({
141
+ content: visibleEnvVariables.map((envVariable) => [envVariable.name, envVariable.description ?? ""]),
142
+ header: inverse.magenta(" Environment Variables ")
143
+ });
144
+ }
145
+ }
146
+ if (command.alias !== void 0 && command.alias.length > 0) {
147
+ let alias = command.alias;
148
+ if (typeof command.alias === "string") {
149
+ alias = [command.alias];
150
+ }
151
+ usageGroups.splice(1, 0, {
152
+ content: alias,
153
+ header: "Alias(es)"
154
+ });
155
+ }
156
+ if (Array.isArray(command.examples) && command.examples.length > 0) {
157
+ usageGroups.push({
158
+ content: command.examples,
159
+ header: "Examples"
160
+ });
161
+ }
162
+ (logger.raw ?? logger.log)(commandLineUsage(usageGroups));
163
+ };
164
+ class HelpCommand {
165
+ name = "help";
166
+ options = [
167
+ {
168
+ description: "Display only the specified group",
169
+ name: "group",
170
+ type: String
171
+ }
172
+ ];
173
+ commands;
174
+ constructor(commands) {
175
+ this.commands = commands;
176
+ }
177
+ execute(toolbox) {
178
+ const { commandName, logger, options, runtime } = toolbox;
179
+ const { footer, header } = runtime.getCommandSection();
180
+ if (header) {
181
+ (logger.raw ?? logger.log)(templateFormat(header));
182
+ }
183
+ if (commandName === "help") {
184
+ printGeneralHelp(
185
+ logger,
186
+ runtime,
187
+ this.commands,
188
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- options may be undefined at runtime
189
+ typeof options?.group === "string" ? options.group : void 0
190
+ );
191
+ } else {
192
+ printCommandHelp(logger, runtime, this.commands, commandName);
193
+ }
194
+ if (footer) {
195
+ (logger.raw ?? logger.log)(templateFormat(footer));
196
+ }
197
+ }
198
+ }
199
+
200
+ export { HelpCommand as default };