UNPKG

@adonisjs/ace

Version:

A CLI framework for Node.js

3,025 lines 87.3 kB
import { cliui, cliui as cliui$1 } from "@poppinss/cliui";
import yargsParser from "yargs-parser";
import Hooks from "@poppinss/hooks";
import { Prompt, errors } from "@poppinss/prompts";
import { distance } from "fastest-levenshtein";
import { Exception, InvalidArgumentsException, RuntimeException, createError } from "@poppinss/utils/exception";
import { debuglog, inspect } from "node:util";
import string from "@poppinss/utils/string";
import Macroable from "@poppinss/macroable";
import lodash from "@poppinss/utils/lodash";
import { AssertionError } from "node:assert";
import { defineStaticProperty, importDefault } from "@poppinss/utils";
import stringWidth from "string-width";
import * as cliHelpers from "@poppinss/cliui/helpers";
import { TERMINAL_SIZE, justify, wrap } from "@poppinss/cliui/helpers";
import { Validator } from "jsonschema";
import diagnostics_channel from "node:diagnostics_channel";
import { fileURLToPath } from "node:url";
import { fsReadAll } from "@poppinss/utils/fs";
import { basename, extname, join, relative } from "node:path";
import { copyFile, mkdir, writeFile } from "node:fs/promises";
//#region \0rolldown/runtime.js
var __defProp = Object.defineProperty;
var __exportAll = (all, no_symbols) => {
	let target = {};
	for (var name in all) __defProp(target, name, {
		get: all[name],
		enumerable: true
	});
	if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
	return target;
};
//#endregion
//#region src/yars_config.ts
/**
* The fixed config used to parse command line arguments using yargs. We
* do not allow changing these options, since some of the internal
* checks and features rely on this specific config
*/
const yarsConfig = {
	"camel-case-expansion": false,
	"combine-arrays": true,
	"short-option-groups": true,
	"dot-notation": false,
	"parse-numbers": true,
	"parse-positional-numbers": false,
	"boolean-negation": true,
	"flatten-duplicate-arrays": true,
	"greedy-arrays": false,
	"strip-aliased": true,
	"nargs-eats-options": false,
	"unknown-options-as-args": false
};
//#endregion
//#region src/parser.ts
/**
* Parses the command line arguments. The flags are parsed using yargs-parser
*
* @example
* ```ts
* const parser = new Parser({ flagsParserOptions, argumentsParserOptions })
* const parsed = parser.parse(['--verbose', 'create', 'User'])
* console.log(parsed.args) // ['User']
* console.log(parsed.flags) // { verbose: true }
* ```
*/
var Parser = class {
	/**
	* Parser options for flags and arguments
	*/
	#options;
	/**
	* Create a new parser instance
	*
	* @param options - Parser configuration options
	*/
	constructor(options) {
		this.#options = options;
	}
	/**
	* Parse flags using yargs parser
	*
	* @param argv - Command line arguments
	*/
	#parseFlags(argv) {
		return yargsParser(argv, {
			...this.#options.flagsParserOptions,
			configuration: yarsConfig
		});
	}
	/**
	* Scans for unknown flags in yargs output
	*
	* @param parsed - Parsed flags object
	*/
	#scanUnknownFlags(parsed) {
		const unknownFlags = [];
		for (let key of Object.keys(parsed)) if (!this.#options.flagsParserOptions.all.includes(key)) unknownFlags.push(key);
		return unknownFlags;
	}
	/**
	* Parse arguments by mimicking the yargs behavior
	*
	* @param parsedOutput - Output from yargs parser
	*/
	#parseArguments(parsedOutput) {
		let lastParsedIndex = -1;
		const output = this.#options.argumentsParserOptions.map((option, index) => {
			if (option.type === "spread") {
				let value = parsedOutput._.slice(index);
				lastParsedIndex = parsedOutput._.length;
				/**
				* Step 1
				*
				* Use default value when original value is not defined.
				*/
				if (!value.length) value = Array.isArray(option.default) ? option.default : option.default === void 0 ? void 0 : [option.default];
				/**
				* Step 2
				*
				* Call parse method when value is not undefined
				*/
				if (value !== void 0 && option.parse) value = option.parse(value);
				return value;
			}
			let value = parsedOutput._[index];
			lastParsedIndex = index + 1;
			/**
			* Step 1:
			*
			* Use default value when original value is undefined
			* Original value set to empty string will be used
			* as real value. The behavior is same as yargs
			* flags parser `--connection=`
			*/
			if (value === void 0) value = option.default;
			/**
			* Step 2
			*
			* Call parse method when value is not undefined
			*/
			if (value !== void 0 && option.parse) value = option.parse(value);
			return value;
		});
		const { "_": args, "--": o, ...rest } = parsedOutput;
		return {
			args: output,
			nodeArgs: [],
			_: args.slice(lastParsedIndex === -1 ? 0 : lastParsedIndex),
			unknownFlags: this.#scanUnknownFlags(rest),
			flags: rest
		};
	}
	/**
	* Parse command-line arguments into structured format
	*
	* @param argv - Command line arguments as string or array
	*
	* @example
	* ```ts
	* const parsed = parser.parse(['--verbose', 'create', 'User'])
	* // Returns: { args: ['User'], flags: { verbose: true }, ... }
	* ```
	*/
	parse(argv) {
		return this.#parseArguments(this.#parseFlags(argv));
	}
};
//#endregion
//#region src/debug.ts
var debug_default = debuglog("adonisjs:ace");
//#endregion
//#region src/errors.ts
var errors_exports = /* @__PURE__ */ __exportAll({
	E_COMMAND_NOT_FOUND: () => E_COMMAND_NOT_FOUND,
	E_INVALID_FLAG: () => E_INVALID_FLAG,
	E_MISSING_ARG: () => E_MISSING_ARG,
	E_MISSING_ARG_VALUE: () => E_MISSING_ARG_VALUE,
	E_MISSING_COMMAND_NAME: () => E_MISSING_COMMAND_NAME,
	E_MISSING_FLAG: () => E_MISSING_FLAG,
	E_MISSING_FLAG_VALUE: () => E_MISSING_FLAG_VALUE,
	E_PROMPT_CANCELLED: () => E_PROMPT_CANCELLED,
	E_UNKNOWN_FLAG: () => E_UNKNOWN_FLAG
});
const E_PROMPT_CANCELLED = errors.E_PROMPT_CANCELLED;
/**
* Command is missing the static property command name
*/
const E_MISSING_COMMAND_NAME = createError("Cannot serialize command \"%s\". Missing static property \"commandName\"", "E_MISSING_COMMAND_NAME");
/**
* Cannot find a command for the given name
*/
const E_COMMAND_NOT_FOUND = class CommandNotFound extends Exception {
	static status = 404;
	commandName;
	constructor(args) {
		super(`Command "${args[0]}" is not defined`, { code: "E_COMMAND_NOT_FOUND" });
		this.commandName = args[0];
	}
};
/**
* Missing a required flag when running the command
*/
const E_MISSING_FLAG = createError("Missing required option \"%s\"", "E_MISSING_FLAG");
/**
* Missing value for a flag that accepts values
*/
const E_MISSING_FLAG_VALUE = createError("Missing value for option \"%s\"", "E_MISSING_FLAG_VALUE");
/**
* Missing a required argument when running the command
*/
const E_MISSING_ARG = createError("Missing required argument \"%s\"", "E_MISSING_ARG");
/**
* Missing value for an argument
*/
const E_MISSING_ARG_VALUE = createError("Missing value for argument \"%s\"", "E_MISSING_ARG_VALUE");
/**
* An unknown flag was mentioned
*/
const E_UNKNOWN_FLAG = createError("Unknown flag \"%s\". The mentioned flag is not accepted by the command", "E_UNKNOWN_FLAG");
/**
* Invalid value provided for the flag
*/
const E_INVALID_FLAG = createError("Invalid value. The \"%s\" flag accepts a \"%s\" value", "E_INVALID_FLAG");
//#endregion
//#region src/commands/base.ts
/**
* The base command sets the foundation for defining ace commands.
* Every command should inherit from the base command.
*
* @example
* ```ts
* export class MyCommand extends BaseCommand {
*   static commandName = 'my:command'
*   static description = 'My custom command'
*
*   async run() {
*     this.logger.info('Hello from my command!')
*   }
* }
* ```
*/
var BaseCommand = class extends Macroable {
	/**
	* Whether the command class has been booted
	*/
	static booted = false;
	/**
	* Configuration options accepted by the command
	*/
	static options;
	/**
	* A collection of aliases for the command
	*/
	static aliases;
	/**
	* The command name one can type to run the command
	*/
	static commandName;
	/**
	* The command description
	*/
	static description;
	/**
	* The help text for the command. Help text can be a multiline
	* string explaining the usage of command
	*/
	static help;
	/**
	* Registered arguments
	*/
	static args;
	/**
	* Registered flags
	*/
	static flags;
	/**
	* Define static properties on the class. During inheritance, certain
	* properties must inherit from the parent.
	*
	* @example
	* ```ts
	* MyCommand.boot()
	* ```
	*/
	static boot() {
		if (Object.hasOwn(this, "booted") && this.booted === true) return;
		this.booted = true;
		defineStaticProperty(this, "args", {
			initialValue: [],
			strategy: "inherit"
		});
		defineStaticProperty(this, "flags", {
			initialValue: [],
			strategy: "inherit"
		});
		defineStaticProperty(this, "aliases", {
			initialValue: [],
			strategy: "inherit"
		});
		defineStaticProperty(this, "commandName", {
			initialValue: "",
			strategy: "inherit"
		});
		defineStaticProperty(this, "description", {
			initialValue: "",
			strategy: "inherit"
		});
		defineStaticProperty(this, "help", {
			initialValue: "",
			strategy: "inherit"
		});
		defineStaticProperty(this, "options", {
			initialValue: {
				staysAlive: false,
				allowUnknownFlags: false
			},
			strategy: "inherit"
		});
	}
	/**
	* Specify the argument the command accepts. The arguments via the CLI
	* will be accepted in the same order as they are defined.
	*
	* Mostly, you will be using the `@args` decorator to define the arguments.
	*
	* @param name - The name of the argument
	* @param options - Configuration options for the argument
	*
	* @example
	* ```ts
	* Command.defineArgument('entity', { type: 'string' })
	* Command.defineArgument('files', { type: 'spread', required: false })
	* ```
	*/
	static defineArgument(name, options) {
		this.boot();
		const arg = {
			name,
			argumentName: string.dashCase(name),
			required: true,
			...options
		};
		const lastArg = this.args[this.args.length - 1];
		/**
		* Ensure the arg type is specified
		*/
		if (!arg.type) throw new InvalidArgumentsException(`Cannot define argument "${this.name}.${name}". Specify the argument type`);
		/**
		* Ensure we are not adding arguments after a spread argument
		*/
		if (lastArg && lastArg.type === "spread") throw new InvalidArgumentsException(`Cannot define argument "${this.name}.${name}" after spread argument "${this.name}.${lastArg.name}". Spread argument should be the last one`);
		/**
		* Ensure we are not adding a required argument after an optional
		* argument
		*/
		if (arg.required && lastArg && lastArg.required === false) throw new InvalidArgumentsException(`Cannot define required argument "${this.name}.${name}" after optional argument "${this.name}.${lastArg.name}"`);
		if (debug_default.enabled) debug_default("defining arg %O, command: %O", arg, `[class: ${this.name}]`);
		this.args.push(arg);
	}
	/**
	* Specify a flag the command accepts.
	*
	* Mostly, you will be using the `@flags` decorator to define a flag.
	*
	* @param name - The name of the flag
	* @param options - Configuration options for the flag
	*
	* @example
	* ```ts
	* Command.defineFlag('connection', { type: 'string', required: true })
	* Command.defineFlag('force', { type: 'boolean' })
	* Command.defineFlag('tags', { type: 'array' })
	* ```
	*/
	static defineFlag(name, options) {
		this.boot();
		const flag = {
			name,
			flagName: string.dashCase(name),
			required: false,
			...options
		};
		/**
		* Ensure the arg type is specified
		*/
		if (!flag.type) throw new InvalidArgumentsException(`Cannot define flag "${this.name}.${name}". Specify the flag type`);
		if (debug_default.enabled) debug_default("defining flag %O, command: %O", flag, `[class: ${this.name}]`);
		this.flags.push(flag);
	}
	/**
	* Returns the options for parsing flags and arguments
	*
	* @param options - Optional parser options to merge
	*/
	static getParserOptions(options) {
		this.boot();
		const argumentsParserOptions = this.args.map((arg) => {
			return {
				type: arg.type,
				default: arg.default,
				parse: arg.parse
			};
		});
		const flagsParserOptions = lodash.merge({
			all: [],
			string: [],
			boolean: [],
			array: [],
			number: [],
			alias: {},
			count: [],
			coerce: {},
			default: {}
		}, options);
		this.flags.forEach((flag) => {
			flagsParserOptions.all.push(flag.flagName);
			if (flag.alias) flagsParserOptions.alias[flag.flagName] = flag.alias;
			if (flag.parse) flagsParserOptions.coerce[flag.flagName] = flag.parse;
			if (flag.default !== void 0) flagsParserOptions.default[flag.flagName] = flag.default;
			switch (flag.type) {
				case "string":
					flagsParserOptions.string.push(flag.flagName);
					break;
				case "boolean":
					flagsParserOptions.boolean.push(flag.flagName);
					break;
				case "number":
					flagsParserOptions.number.push(flag.flagName);
					break;
				case "array":
					flagsParserOptions.array.push(flag.flagName);
					break;
			}
		});
		return {
			flagsParserOptions,
			argumentsParserOptions
		};
	}
	/**
	* Serializes the command to JSON. The return value satisfies the
	* {@link CommandMetaData}
	*
	* @example
	* ```ts
	* const metadata = MyCommand.serialize()
	* console.log(metadata.commandName) // 'my:command'
	* ```
	*/
	static serialize() {
		this.boot();
		if (!this.commandName) throw new E_MISSING_COMMAND_NAME([this.name]);
		const [namespace, name] = this.commandName.split(":");
		return {
			commandName: this.commandName,
			description: this.description,
			help: this.help,
			namespace: name ? namespace : null,
			aliases: this.aliases,
			flags: this.flags.map((flag) => {
				const { parse, ...rest } = flag;
				return rest;
			}),
			args: this.args.map((arg) => {
				const { parse, ...rest } = arg;
				return rest;
			}),
			options: this.options
		};
	}
	/**
	* Validate the yargs parsed output against the command.
	*
	* @param parsedOutput - The parsed CLI input to validate
	*
	* @example
	* ```ts
	* const parsed = { args: ['value'], flags: { force: true }, unknownFlags: [] }
	* MyCommand.validate(parsed)
	* ```
	*/
	static validate(parsedOutput) {
		this.boot();
		/**
		* Validates args and their values
		*/
		this.args.forEach((arg, index) => {
			const value = parsedOutput.args[index];
			const hasDefinedArgument = value !== void 0;
			if (arg.required && !hasDefinedArgument) throw new E_MISSING_ARG([arg.name]);
			if (hasDefinedArgument && !arg.allowEmptyValue && (value === "" || !value.length)) {
				if (debug_default.enabled) debug_default("disallowing empty value \"%s\" for arg: \"%s\"", value, arg.name);
				throw new E_MISSING_ARG_VALUE([arg.name]);
			}
		});
		/**
		* Disallow unknown flags
		*/
		if (!this.options.allowUnknownFlags && parsedOutput.unknownFlags.length) {
			const unknowFlag = parsedOutput.unknownFlags[0];
			throw new E_UNKNOWN_FLAG([unknowFlag.length === 1 ? `-${unknowFlag}` : `--${unknowFlag}`]);
		}
		/**
		* Validate flags
		*/
		this.flags.forEach((flag) => {
			const hasMentionedFlag = Object.hasOwn(parsedOutput.flags, flag.flagName);
			const value = parsedOutput.flags[flag.flagName];
			/**
			* Validate the value by flag type
			*/
			switch (flag.type) {
				case "boolean":
					/**
					* If flag is required, then it should be mentioned
					*/
					if (flag.required && !hasMentionedFlag) throw new E_MISSING_FLAG([flag.flagName]);
					break;
				case "number":
					/**
					* If flag is required, then it should be mentioned
					*/
					if (flag.required && !hasMentionedFlag) throw new E_MISSING_FLAG([flag.flagName]);
					/**
					* Regardless of whether flag is required or not. If it is mentioned,
					* then some value should be provided.
					*
					* In case of number input, yargs sends undefined
					*/
					if (hasMentionedFlag && value === void 0) throw new E_MISSING_FLAG_VALUE([flag.flagName]);
					if (Number.isNaN(value)) throw new E_INVALID_FLAG([flag.flagName, "numeric"]);
					break;
				case "string":
				case "array":
					/**
					* If flag is required, then it should be mentioned
					*/
					if (flag.required && !hasMentionedFlag) throw new E_MISSING_FLAG([flag.flagName]);
					/**
					* Regardless of whether flag is required or not. If it is mentioned,
					* then some value should be provided, unless empty values are
					* allowed.
					*
					* In case of string, flag with no value receives an empty string
					* In case of array, flag with no value receives an empty array
					*/
					if (hasMentionedFlag && !flag.allowEmptyValue && (value === "" || !value.length)) {
						if (debug_default.enabled) debug_default("disallowing empty value \"%s\" for flag: \"%s\"", value, flag.name);
						throw new E_MISSING_FLAG_VALUE([flag.flagName]);
					}
			}
		});
	}
	/**
	* Check if a command has been hydrated
	*/
	hydrated = false;
	/**
	* The exit code for the command
	*/
	exitCode;
	/**
	* The error raised at the time of executing the command.
	* The value is undefined if no error is raised.
	*/
	error;
	/**
	* The result property stores the return value of the "run"
	* method (unless command sets it explicitly)
	*/
	result;
	/**
	* Logger to log messages
	*
	* @example
	* ```ts
	* this.logger.info('Command executed successfully')
	* this.logger.error('Something went wrong')
	* ```
	*/
	get logger() {
		return this.ui.logger;
	}
	/**
	* Add colors to console messages
	*
	* @example
	* ```ts
	* this.logger.info(this.colors.green('Success!'))
	* this.logger.error(this.colors.red('Error!'))
	* ```
	*/
	get colors() {
		return this.ui.colors;
	}
	/**
	* Is the current command the main command executed from the CLI
	*
	* @example
	* ```ts
	* if (this.isMain) {
	*   this.logger.info('This is the main command')
	* }
	* ```
	*/
	get isMain() {
		return this.kernel.getMainCommand() === this;
	}
	/**
	* Reference to the command name
	*/
	get commandName() {
		return this.constructor.commandName;
	}
	/**
	* Reference to the command options
	*/
	get options() {
		return this.constructor.options;
	}
	/**
	* Reference to the command args
	*/
	get args() {
		return this.constructor.args;
	}
	/**
	* Reference to the command flags
	*/
	get flags() {
		return this.constructor.flags;
	}
	/**
	* Create a new base command instance
	*
	* @param kernel - The Ace kernel instance
	* @param parsed - The parsed CLI input
	* @param ui - UI primitives for output
	* @param prompt - Prompt utilities for user interaction
	*/
	constructor(kernel, parsed, ui, prompt) {
		super();
		this.kernel = kernel;
		this.parsed = parsed;
		this.ui = ui;
		this.prompt = prompt;
	}
	/**
	* Hydrate command by setting class properties from the parsed output
	*
	* @example
	* ```ts
	* command.hydrate()
	* console.log(command.name) // Argument value
	* console.log(command.force) // Flag value
	* ```
	*/
	hydrate() {
		if (this.hydrated) return;
		const CommandConstructor = this.constructor;
		/**
		* Set args as properties on the command instance
		*/
		CommandConstructor.args.forEach((arg, index) => {
			Object.defineProperty(this, arg.name, {
				value: this.parsed.args[index],
				enumerable: true,
				writable: true,
				configurable: true
			});
		});
		/**
		* Set flags as properties on the command instance
		*/
		CommandConstructor.flags.forEach((flag) => {
			Object.defineProperty(this, flag.name, {
				value: this.parsed.flags[flag.flagName],
				enumerable: true,
				writable: true,
				configurable: true
			});
		});
		this.hydrated = true;
	}
	/**
	* The run method should include the implementation for the command.
	*
	* @param _ - Additional arguments (not used in base implementation)
	*
	* @example
	* ```ts
	* async run() {
	*   this.logger.info('Running my command')
	*   return 'Command completed'
	* }
	* ```
	*/
	async run(..._) {}
	/**
	* Executes the command by running the command's run method.
	*
	* @example
	* ```ts
	* const result = await command.exec()
	* console.log('Exit code:', command.exitCode)
	* ```
	*/
	async exec() {
		this.hydrate();
		try {
			this.result = await this.run();
			this.exitCode = this.exitCode ?? 0;
			return this.result;
		} catch (error) {
			this.error = error;
			this.exitCode = this.exitCode ?? 1;
			throw error;
		}
	}
	/**
	* JSON representation of the command
	*
	* @example
	* ```ts
	* const json = command.toJSON()
	* console.log(json.commandName, json.exitCode)
	* ```
	*/
	toJSON() {
		return {
			commandName: this.constructor.commandName,
			options: this.constructor.options,
			args: this.parsed.args,
			flags: this.parsed.flags,
			error: this.error,
			result: this.result,
			exitCode: this.exitCode
		};
	}
	/**
	* Assert the command exits with a given exit code
	*
	* @param code - The expected exit code
	*
	* @example
	* ```ts
	* command.assertExitCode(0) // Assert successful execution
	* command.assertExitCode(1) // Assert failure
	* ```
	*/
	assertExitCode(code) {
		if (this.exitCode !== code) throw new AssertionError({
			message: `Expected '${this.commandName}' command to finish with exit code '${code}'`,
			actual: this.exitCode,
			expected: code,
			operator: "strictEqual",
			stackStartFn: this.assertExitCode
		});
	}
	/**
	* Assert the command does not exit with a given exit code
	*
	* @param code - The exit code that should not be used
	*
	* @example
	* ```ts
	* command.assertNotExitCode(1) // Assert no failure
	* ```
	*/
	assertNotExitCode(code) {
		if (this.exitCode === code) throw new AssertionError({
			message: `Expected '${this.commandName}' command to finish without exit code '${this.exitCode}'`,
			stackStartFn: this.assertNotExitCode
		});
	}
	/**
	* Assert the command exits with zero exit code
	*
	* @example
	* ```ts
	* command.assertSucceeded() // Assert success
	* ```
	*/
	assertSucceeded() {
		return this.assertExitCode(0);
	}
	/**
	* Assert the command exits with non-zero exit code
	*
	* @example
	* ```ts
	* command.assertFailed() // Assert failure
	* ```
	*/
	assertFailed() {
		return this.assertNotExitCode(0);
	}
	/**
	* Assert command logs the expected message
	*
	* @param message - The expected log message
	* @param stream - Optional stream to check ('stdout' or 'stderr')
	*
	* @example
	* ```ts
	* command.assertLog('Command executed successfully')
	* command.assertLog('Error occurred', 'stderr')
	* ```
	*/
	assertLog(message, stream) {
		const logs = this.logger.getLogs();
		const logMessages = logs.map((log) => log.message);
		const matchingLog = logs.find((log) => log.message === message);
		/**
		* No log found
		*/
		if (!matchingLog) throw new AssertionError({
			message: `Expected log messages to include ${inspect(message)}`,
			actual: logMessages,
			expected: [message],
			operator: "strictEqual",
			stackStartFn: this.assertLog
		});
		/**
		* Log is on a different stream
		*/
		if (stream && matchingLog.stream !== stream) throw new AssertionError({
			message: `Expected log message stream to be ${inspect(stream)}, instead received ${inspect(matchingLog.stream)}`,
			actual: matchingLog.stream,
			expected: stream,
			operator: "strictEqual",
			stackStartFn: this.assertLog
		});
	}
	/**
	* Assert command logs a message matching the given regex
	*
	* @param matchingRegex - The regex pattern to match against log messages
	* @param stream - Optional stream to check ('stdout' or 'stderr')
	*
	* @example
	* ```ts
	* command.assertLogMatches(/^Command.*completed$/)
	* command.assertLogMatches(/error/i, 'stderr')
	* ```
	*/
	assertLogMatches(matchingRegex, stream) {
		const matchingLog = this.logger.getLogs().find((log) => matchingRegex.test(log.message));
		/**
		* No log found
		*/
		if (!matchingLog) throw new AssertionError({
			message: `Expected log messages to match ${inspect(matchingRegex)}`,
			stackStartFn: this.assertLogMatches
		});
		/**
		* Log is on a different stream
		*/
		if (stream && matchingLog.stream !== stream) throw new AssertionError({
			message: `Expected log message stream to be ${inspect(stream)}, instead received ${inspect(matchingLog.stream)}`,
			actual: matchingLog.stream,
			expected: stream,
			operator: "strictEqual",
			stackStartFn: this.assertLogMatches
		});
	}
	/**
	* Assert the command prints a table with the expected rows to stdout
	*
	* @param rows - The expected table rows as arrays of strings
	*
	* @example
	* ```ts
	* command.assertTableRows([
	*   ['Name', 'Age'],
	*   ['John', '25'],
	*   ['Jane', '30']
	* ])
	* ```
	*/
	assertTableRows(rows) {
		const logs = this.logger.getLogs();
		if (!rows.every((row) => {
			const columnsContent = row.join("|");
			return !!logs.find((log) => log.message === columnsContent);
		})) throw new AssertionError({
			message: `Expected log messages to include a table with the expected rows`,
			operator: "strictEqual",
			stackStartFn: this.assertTableRows
		});
	}
};
//#endregion
//#region src/decorators/args.ts
/**
* Namespace for defining arguments using decorators.
*
* Arguments are positional parameters that commands accept from the CLI.
* They are parsed in the order they are defined and made available as
* properties on the command instance.
*
* @example
* ```ts
* export class MakeCommand extends BaseCommand {
*   @args.string({ description: 'Entity name' })
*   declare name: string
*
*   @args.spread({ description: 'Additional files', required: false })
*   declare files?: string[]
* }
* ```
*/
const args = {
	/**
	* Define argument that accepts a string value
	*
	* @param options - Configuration options for the string argument
	*
	* @example
	* ```ts
	* export class MakeCommand extends BaseCommand {
	*   @args.string({ description: 'The entity name' })
	*   declare name: string
	*
	*   @args.string({ description: 'Template type', required: false, default: 'default' })
	*   declare template?: string
	* }
	* ```
	*/
	string(options) {
		return function addArg(target, propertyName) {
			target.constructor.defineArgument(propertyName, {
				...options,
				type: "string"
			});
		};
	},
	/**
	* Define argument that accepts a spread of values (variable number of arguments)
	*
	* Spread arguments collect all remaining positional arguments into an array.
	* Only one spread argument is allowed per command and it must be the last argument.
	*
	* @param options - Configuration options for the spread argument
	*
	* @example
	* ```ts
	* export class InstallCommand extends BaseCommand {
	*   @args.string({ description: 'Package manager' })
	*   declare manager: string
	*
	*   @args.spread({ description: 'Package names to install', required: false })
	*   declare packages?: string[]
	* }
	* // Usage: node ace install npm lodash axios moment
	* // manager = 'npm', packages = ['lodash', 'axios', 'moment']
	* ```
	*/
	spread(options) {
		return function addArg(target, propertyName) {
			target.constructor.defineArgument(propertyName, {
				...options,
				type: "spread"
			});
		};
	}
};
//#endregion
//#region src/decorators/flags.ts
/**
* Namespace for defining flags using decorators.
*
* Flags are named options that commands can accept from the CLI.
* They can be passed using --flag-name or -alias syntax and are
* made available as properties on the command instance.
*
* @example
* ```ts
* export class MakeCommand extends BaseCommand {
*   @flags.boolean({ description: 'Skip confirmation prompts' })
*   declare force: boolean
*
*   @flags.string({ description: 'Database connection', alias: 'c' })
*   declare connection?: string
*
*   @flags.array({ description: 'Additional tags' })
*   declare tags?: string[]
* }
* ```
*/
const flags = {
	/**
	* Define flag that accepts a string value
	*
	* @param options - Configuration options for the string flag
	*
	* @example
	* ```ts
	* export class MakeCommand extends BaseCommand {
	*   @flags.string({ description: 'Database connection name', alias: 'c' })
	*   declare connection?: string
	*
	*   @flags.string({ description: 'Template name', required: true, default: 'default' })
	*   declare template: string
	* }
	* // Usage: node ace make --connection=mysql --template=api
	* // Usage: node ace make -c mysql --template=api
	* ```
	*/
	string(options) {
		return function addArg(target, propertyName) {
			target.constructor.defineFlag(propertyName, {
				type: "string",
				...options
			});
		};
	},
	/**
	* Define flag that accepts a boolean value
	*
	* Boolean flags don't require a value - their presence indicates true.
	* They can optionally support negated variants with --no-flag-name.
	*
	* @param options - Configuration options for the boolean flag
	*
	* @example
	* ```ts
	* export class MigrateCommand extends BaseCommand {
	*   @flags.boolean({ description: 'Skip confirmation prompts', alias: 'f' })
	*   declare force: boolean
	*
	*   @flags.boolean({ description: 'Run in dry mode', showNegatedVariantInHelp: true })
	*   declare dryRun: boolean
	* }
	* // Usage: node ace migrate --force
	* // Usage: node ace migrate -f
	* // Usage: node ace migrate --dry-run or --no-dry-run
	* ```
	*/
	boolean(options) {
		return function addArg(target, propertyName) {
			target.constructor.defineFlag(propertyName, {
				type: "boolean",
				...options
			});
		};
	},
	/**
	* Define flag that accepts a numeric value
	*
	* @param options - Configuration options for the number flag
	*
	* @example
	* ```ts
	* export class ServeCommand extends BaseCommand {
	*   @flags.number({ description: 'Port number', alias: 'p', default: 3000 })
	*   declare port: number
	*
	*   @flags.number({ description: 'Number of workers', required: false })
	*   declare workers?: number
	* }
	* // Usage: node ace serve --port=8080
	* // Usage: node ace serve -p 8080 --workers=4
	* ```
	*/
	number(options) {
		return function addArg(target, propertyName) {
			target.constructor.defineFlag(propertyName, {
				type: "number",
				...options
			});
		};
	},
	/**
	* Define flag that accepts an array of values
	*
	* Array flags can be specified multiple times to build up an array of values.
	*
	* @param options - Configuration options for the array flag
	*
	* @example
	* ```ts
	* export class BuildCommand extends BaseCommand {
	*   @flags.array({ description: 'Include additional files' })
	*   declare include?: string[]
	*
	*   @flags.array({ description: 'Environment variables', alias: 'e' })
	*   declare env?: string[]
	* }
	* // Usage: node ace build --include=*.js --include=*.css
	* // Usage: node ace build -e NODE_ENV=production -e DEBUG=false
	* // Results: include = ['*.js', '*.css'], env = ['NODE_ENV=production', 'DEBUG=false']
	* ```
	*/
	array(options) {
		return function addArg(target, propertyName) {
			target.constructor.defineFlag(propertyName, {
				type: "array",
				...options
			});
		};
	}
};
//#endregion
//#region src/formatters/flag.ts
/**
* The flag formatter formats a flag as per the http://docopt.org/ specification
*
* @example
* ```ts
* const formatter = new FlagFormatter(flag, colors)
* const formatted = formatter.formatOption() // '--connection[=CONNECTION]'
* const description = formatter.formatDescription() // 'Database connection'
* ```
*/
var FlagFormatter = class {
	/**
	* The flag configuration
	*/
	#flag;
	/**
	* Color utilities for formatting output
	*/
	#colors;
	/**
	* Create a new flag formatter
	*
	* @param flag - The flag configuration to format
	* @param colors - Color utilities for output formatting
	*/
	constructor(flag, colors) {
		this.#flag = flag;
		this.#colors = colors;
	}
	/**
	* Formats the value flag with proper placeholder syntax
	*
	* @param flag - The flag configuration
	* @param valuePlaceholder - The placeholder text for the flag value
	*/
	#formatValueFlag(flag, valuePlaceholder) {
		return flag.required ? `=${valuePlaceholder}` : `[=${valuePlaceholder}]`;
	}
	/**
	* Formats the aliases for the flag
	*
	* @param flag - The flag configuration
	*/
	#formatAliases(flag) {
		if (!flag.alias) return [];
		if (typeof flag.alias === "string") return [`-${flag.alias}`];
		return flag.alias.map((alias) => `-${alias}`);
	}
	/**
	* Formats the array flag by appending ellipsis and wrapping the value
	*
	* @param flag - The array flag configuration
	*/
	#formatArrayFlag(flag) {
		const value = this.#formatValueFlag(flag, `${flag.flagName.toUpperCase()}...`);
		const aliases = this.#formatAliases(flag);
		const flagWithValue = `--${flag.flagName}${value}`;
		if (aliases.length) return `  ${this.#colors.green(`${aliases.join(",")}, ${flagWithValue}`)}  `;
		return `  ${this.#colors.green(flagWithValue)}  `;
	}
	/**
	* Formats the string flag by wrapping the value to indicate if required
	*
	* @param flag - The string flag configuration
	*/
	#formatStringFlag(flag) {
		const value = this.#formatValueFlag(flag, `${flag.flagName.toUpperCase()}`);
		const aliases = this.#formatAliases(flag);
		const flagWithValue = `--${flag.flagName}${value}`;
		if (aliases.length) return `  ${this.#colors.green(`${aliases.join(",")}, ${flagWithValue}`)}  `;
		return `  ${this.#colors.green(flagWithValue)}  `;
	}
	/**
	* Formats the numeric flag by wrapping the value to indicate if required
	*
	* @param flag - The numeric flag configuration
	*/
	#formatNumericFlag(flag) {
		const value = this.#formatValueFlag(flag, `${flag.flagName.toUpperCase()}`);
		const aliases = this.#formatAliases(flag);
		const flagWithValue = `--${flag.flagName}${value}`;
		if (aliases.length) return `  ${this.#colors.green(`${aliases.join(",")}, ${flagWithValue}`)}  `;
		return `  ${this.#colors.green(flagWithValue)}  `;
	}
	/**
	* Formats the boolean flag. Boolean flags need no value wrapping
	*
	* @param flag - The boolean flag configuration
	*/
	#formatBooleanFlag(flag) {
		const aliases = this.#formatAliases(flag);
		const negatedVariant = flag.showNegatedVariantInHelp ? `|--no-${flag.flagName}` : "";
		const flagWithVariant = `--${flag.flagName}${negatedVariant}`;
		if (aliases.length) return `  ${this.#colors.green(`${aliases.join(",")}, ${flagWithVariant}`)}  `;
		return `  ${this.#colors.green(flagWithVariant)}  `;
	}
	/**
	* Returns formatted description for the flag
	*
	* @example
	* ```ts
	* formatter.formatDescription() // 'Database connection [default: mysql]'
	* ```
	*/
	formatDescription() {
		const defaultValue = this.#flag.default !== void 0 ? `[default: ${this.#flag.default}]` : "";
		const separator = defaultValue && this.#flag.description ? " " : "";
		return this.#colors.dim(`${this.#flag.description || ""}${separator}${defaultValue}`);
	}
	/**
	* Returns a formatted version of the flag name and aliases
	*
	* @example
	* ```ts
	* formatter.formatOption() // '--connection[=CONNECTION]' or '--force, -f'
	* ```
	*/
	formatOption() {
		switch (this.#flag.type) {
			case "array": return this.#formatArrayFlag(this.#flag);
			case "string": return this.#formatStringFlag(this.#flag);
			case "number": return this.#formatNumericFlag(this.#flag);
			case "boolean": return this.#formatBooleanFlag(this.#flag);
		}
	}
};
//#endregion
//#region src/formatters/list.ts
/**
* The list formatter formats the list of commands and flags. The option column
* is justified to have same width across all the rows.
*
* @example
* ```ts
* const formatter = new ListFormatter(tables)
* const formatted = formatter.format() // Array of formatted tables
* ```
*/
var ListFormatter = class {
	/**
	* Array of tables to format
	*/
	#tables;
	/**
	* The width of the largest option column across all tables
	*/
	#largestOptionColumnWidth;
	/**
	* Create a new list formatter
	*
	* @param tables - Array of tables to format
	*/
	constructor(tables) {
		this.#tables = tables;
		this.#largestOptionColumnWidth = Math.max(...this.#tables.map((table) => table.columns.map((column) => stringWidth(column.option))).flat());
	}
	/**
	* Formats a single table to an array of plain text rows
	*
	* @param table - The table to format
	* @param terminalWidth - Width of the terminal for text wrapping
	*/
	#formatTable(table, terminalWidth) {
		const options = justify(table.columns.map(({ option }) => option), { maxWidth: this.#largestOptionColumnWidth });
		const descriptions = wrap(table.columns.map(({ description }) => description), {
			startColumn: this.#largestOptionColumnWidth,
			endColumn: terminalWidth,
			trimStart: true
		});
		return table.columns.map((_, index) => `${options[index]}${descriptions[index]}`);
	}
	/**
	* Format all tables into an array of formatted table objects
	*
	* @param terminalWidth - Width of the terminal for text wrapping
	*
	* @example
	* ```ts
	* formatter.format() // [{ heading: 'Commands:', rows: [...] }]
	* ```
	*/
	format(terminalWidth = TERMINAL_SIZE) {
		return this.#tables.map((table) => {
			return {
				heading: table.heading,
				rows: this.#formatTable(table, terminalWidth)
			};
		});
	}
};
//#endregion
//#region schemas/main.ts
const schema = {
	$id: "adonisjs://ace/command-metadata",
	$ref: "#/definitions/CommandMetaData",
	$schema: "http://json-schema.org/draft-07/schema#",
	definitions: {
		CommandMetaData: {
			description: "Command metdata required to display command help.",
			properties: {
				aliases: {
					description: "Command aliases. The same command can be run using these aliases as well.",
					items: { type: "string" },
					type: "array"
				},
				args: {
					description: "Args accepted by the command",
					items: {
						additionalProperties: false,
						properties: {
							allowEmptyValue: {
								description: "Whether or not to allow empty values. When set to false, the validation will fail if the argument is provided an empty string\n\nDefaults to false",
								type: "boolean"
							},
							argumentName: { type: "string" },
							default: {},
							description: { type: "string" },
							name: { type: "string" },
							required: { type: "boolean" },
							type: {
								enum: ["string", "spread"],
								type: "string"
							}
						},
						required: [
							"name",
							"argumentName",
							"type"
						],
						type: "object"
					},
					type: "array"
				},
				commandName: {
					description: "The name of the command",
					type: "string"
				},
				description: {
					description: "The command description to show on the help screen",
					type: "string"
				},
				flags: {
					description: "Flags accepted by the command",
					items: {
						additionalProperties: false,
						properties: {
							alias: { anyOf: [{ type: "string" }, {
								items: { type: "string" },
								type: "array"
							}] },
							allowEmptyValue: {
								description: "Whether or not to allow empty values. When set to false, the validation will fail if the flag is mentioned but no value is provided\n\nDefaults to false",
								type: "boolean"
							},
							default: {},
							description: { type: "string" },
							flagName: { type: "string" },
							name: { type: "string" },
							required: { type: "boolean" },
							showNegatedVariantInHelp: {
								description: "Whether or not to display the negated variant in the help output.\n\nApplicable for boolean flags only\n\nDefaults to false",
								type: "boolean"
							},
							type: {
								enum: [
									"string",
									"boolean",
									"number",
									"array"
								],
								type: "string"
							}
						},
						required: [
							"name",
							"flagName",
							"type"
						],
						type: "object"
					},
					type: "array"
				},
				help: {
					anyOf: [{ type: "string" }, {
						items: { type: "string" },
						type: "array"
					}],
					description: "Help text for the command"
				},
				namespace: {
					description: "Command namespace. The namespace is extracted from the command name",
					type: ["string", "null"]
				},
				options: {
					$ref: "#/definitions/CommandOptions",
					description: "Command configuration options"
				}
			},
			required: [
				"aliases",
				"args",
				"commandName",
				"description",
				"flags",
				"namespace",
				"options"
			],
			type: "object"
		},
		CommandOptions: {
			description: "Static set of command options",
			properties: {
				allowUnknownFlags: {
					description: "Whether or not to allow for unknown flags. If set to false, the command will not run when unknown flags are provided through the CLI\n\nDefaults to false",
					type: "boolean"
				},
				staysAlive: {
					description: "When flag set to true, the kernel will not trigger the termination process unless the command explicitly calls the terminate method.\n\nDefaults to false",
					type: "boolean"
				}
			},
			type: "object"
		}
	}
};
//#endregion
//#region src/utils.ts
/**
* Helper to sort array of strings alphabetically.
*/
function sortAlphabetically(prev, curr) {
	if (curr > prev) return -1;
	if (curr < prev) return 1;
	return 0;
}
/**
* Renders an error message and lists suggestions.
*/
function renderErrorWithSuggestions(ui, message, suggestions) {
	const instructions = ui.sticker().fullScreen().drawBorder((borderChar, colors) => colors.red(borderChar));
	instructions.add(ui.colors.red(message));
	if (suggestions.length) {
		instructions.add("");
		instructions.add(`${ui.colors.dim("Did you mean?")} ${suggestions.slice(0, 4).join(", ")}`);
	}
	instructions.getRenderer().logError(instructions.prepare());
}
/**
* Validates the metadata of a command to ensure it has all the neccessary
* properties
*/
function validateCommandMetaData(command, exportPath) {
	if (!command || typeof command !== "object") throw new RuntimeException(`Invalid command metadata exported from ${exportPath}`);
	try {
		new Validator().validate(command, schema, { throwError: true });
	} catch (error) {
		if (error && typeof error === "object" && "message" in error) throw new RuntimeException(`Invalid command exported from ${exportPath}. ${error.message}`);
	}
}
/**
* Validates the command class. We do not check it against the "BaseCommand"
* class, because the ace version mis-match could make the validation
* fail.
*/
function validateCommand(command, exportPath) {
	if (typeof command !== "function" || !command.toString().startsWith("class ")) throw new RuntimeException(`Invalid command exported from ${exportPath}. Expected command to be a class`);
	const commandConstructor = command;
	if (typeof commandConstructor.serialize !== "function") throw new RuntimeException(`Invalid command exported from ${exportPath}. Expected command to extend the "BaseCommand"`);
	validateCommandMetaData(commandConstructor.serialize(), exportPath);
}
//#endregion
//#region src/formatters/argument.ts
/**
* The argument formatter formats an argument as per the http://docopt.org/ specification
*
* @example
* ```ts
* const formatter = new ArgumentFormatter(argument, colors)
* const formatted = formatter.formatOption() // '<entity>'
* const listOption = formatter.formatListOption() // '  entity  '
* ```
*/
var ArgumentFormatter = class {
	/**
	* The argument configuration
	*/
	#argument;
	/**
	* Color utilities for formatting output
	*/
	#colors;
	/**
	* Create a new argument formatter
	*
	* @param argument - The argument configuration to format
	* @param colors - Color utilities for output formatting
	*/
	constructor(argument, colors) {
		this.#argument = argument;
		this.#colors = colors;
	}
	/**
	* Wraps the optional placeholder on option arguments
	*
	* @param argument - The argument configuration
	* @param valuePlaceholder - The placeholder text for the argument value
	*/
	#formatArgument(argument, valuePlaceholder) {
		return argument.required ? `${valuePlaceholder}` : `[${valuePlaceholder}]`;
	}
	/**
	* Returns formatted description for the argument
	*
	* @example
	* ```ts
	* formatter.formatDescription() // 'The entity name [default: user]'
	* ```
	*/
	formatDescription() {
		const defaultValue = this.#argument.default ? `[default: ${this.#argument.default}]` : "";
		const separator = defaultValue && this.#argument.description ? " " : "";
		return this.#colors.dim(`${this.#argument.description || ""}${separator}${defaultValue}`);
	}
	/**
	* Returns a formatted version of the argument name to be displayed inside a list
	*
	* @example
	* ```ts
	* formatter.formatListOption() // '  entity  ' or '  [entity]  ' for optional
	* ```
	*/
	formatListOption() {
		switch (this.#argument.type) {
			case "spread": return `  ${this.#colors.green(this.#formatArgument(this.#argument, `${this.#argument.argumentName}...`))}  `;
			case "string": return `  ${this.#colors.green(this.#formatArgument(this.#argument, `${this.#argument.argumentName}`))}  `;
		}
	}
	/**
	* Returns a formatted version of the argument name to be displayed next to usage
	*
	* @example
	* ```ts
	* formatter.formatOption() // '<entity>' or '[<entity>]' for optional
	* ```
	*/
	formatOption() {
		switch (this.#argument.type) {
			case "spread": return this.#colors.dim(`${this.#formatArgument(this.#argument, `<${this.#argument.argumentName}...>`)}`);
			case "string": return this.#colors.dim(`${this.#formatArgument(this.#argument, `<${this.#argument.argumentName}>`)}`);
		}
	}
};
//#endregion
//#region src/formatters/command.ts
/**
* The command formatter exposes API to format command data for the
* commands list and the command help
*
* @example
* ```ts
* const formatter = new CommandFormatter(command, colors)
* const usage = formatter.formatUsage(['alias'], 'node ace')
* const description = formatter.formatDescription()
* ```
*/
var CommandFormatter = class {
	/**
	* The command metadata
	*/
	#command;
	/**
	* Color utilities for formatting output
	*/
	#colors;
	/**
	* Create a new command formatter
	*
	* @param command - The command metadata to format
	* @param colors - Color utilities for output formatting
	*/
	constructor(command, colors) {
		this.#command = command;
		this.#colors = colors;
	}
	/**
	* Returns the formatted command name to be displayed in the list of commands
	*
	* @param aliases - Array of command aliases
	*
	* @example
	* ```ts
	* formatter.formatListName(['make']) // '  generate:model (make)  '
	* ```
	*/
	formatListName(aliases) {
		const formattedAliases = aliases.length ? ` ${this.#colors.dim(`(${aliases.join(", ")})`)}` : "";
		return `  ${this.#colors.green(this.#command.commandName)}${formattedAliases}  `;
	}
	/**
	* Returns the formatted description of the command
	*
	* @example
	* ```ts
	* formatter.formatDescription() // 'Generate a new model'
	* ```
	*/
	formatDescription() {
		return this.#command.description || "";
	}
	/**
	* Returns multiline command help with proper text wrapping
	*
	* @param binaryName - The binary name for interpolation
	* @param terminalWidth - Terminal width for text wrapping
	*
	* @example
	* ```ts
	* formatter.formatHelp('node ace') // Formatted help text
	* ```
	*/
	formatHelp(binaryName, terminalWidth = TERMINAL_SIZE) {
		const binary = binaryName ? `${binaryName}` : "";
		if (!this.#command.help) return "";
		/**
		* Wrap text when goes over the terminal size
		*/
		return wrap((Array.isArray(this.#command.help) ? this.#command.help : [this.#command.help]).map((line) => string.interpolate(line, { binaryName: binary })), {
			startColumn: 2,
			trimStart: false,
			endColumn: terminalWidth
		}).join("\n");
	}
	/**
	* Returns the formatted description to be displayed in the list of commands
	*
	* @example
	* ```ts
	* formatter.formatListDescription() // Dimmed description text
	* ```
	*/
	formatListDescription() {
		if (!this.#command.description) return "";
		return this.#colors.dim(this.#command.description);
	}
	/**
	* Returns an array of strings, each line contains an individual usage example
	*
	* @param aliases - Array of command aliases
	* @param binaryName - The binary name for usage examples
	*
	* @example
	* ```ts
	* formatter.formatUsage(['make'], 'node ace')
	* // ['  node ace generate:model [options] <name>', '  node ace make [options] <name>']
	* ```
	*/
	formatUsage(aliases, binaryName) {
		const binary = binaryName ? `${binaryName} ` : "";
		/**
		* Display options placeholder for flags
		*/
		const flags = this.#command.flags.length ? this.#colors.dim("[options]") : "";
		/**
		* Display a list of named args
		*/
		const args = this.#command.args.map((arg) => new ArgumentFormatter(arg, this.#colors).formatOption()).join(" ");
		/**
		* Separator between options placeholder and args
		*/
		const separator = flags && args ? ` ${this.#colors.dim("[--]")} ` : "";
		return [`  ${binary}${this.#command.commandName} ${flags}${separator}${args}`].concat(aliases.map((alias) => `  ${binary}${alias} ${flags}${separator}${args}`));
	}
};
//#endregion
//#region \0@oxc-project+runtime@0.127.0/helpers/decorate.js
function __decorate(decorators, target, key, desc) {
	var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
	if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
	else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
	return c > 3 && r && Object.defineProperty(target, key, r), r;
}
//#endregion
//#region src/commands/list.ts
/**
* The list command is used to view a list of commands
*
* @example
* ```ts
* // Usage from CLI: node ace list
* // Or with namespace filter: node ace list migrate
* // Or as JSON: node ace list --json
* const listCommand = new ListCommand(kernel, parsed, ui, prompt)
* await listCommand.run()
* ```
*/
var ListCommand = class extends BaseCommand {
	/**
	* Command metadata
	*/
	static commandName = "list";
	static description = "View list of available commands";
	static help = [
		"The list command displays a list of all the commands:",
		"  {{ binaryName }} list",
		"",
		"You can also display the commands for a specific namespace:",
		"  {{ binaryName }} list <namespace...>"
	];
	/**
	* Returns a table for an array of commands
	*
	* @param heading - The table heading text
	* @param commands - Array of command metadata
	*/
	#makeCommandsTable(heading, commands) {
		return {
			heading: this.colors.yellow(heading),
			columns: commands.map((command) => {
				const aliases = this.kernel.getCommandAliases(command.commandName);
				const commandFormatter = new CommandFormatter(command, this.colors);
				return {
					option: commandFormatter.formatListName(aliases),
					description: commandFormatter.formatListDescription()
				};
			})
		};
	}
	/**
	* Returns a table for an array of global options
	*
	* @param heading - The table heading text
	* @param flagsList - Array of global flags
	*/
	#makeOptionsTable(heading, flagsList) {
		return {
			heading: this.colors.yellow(heading),
			columns: flagsList.map((flag) => {
				const flagFormatter = new FlagFormatter(flag, this.colors);
				return {
					option: flagFormatter.formatOption(),
					description: flagFormatter.formatDescription()
				};
			})
		};
	}
	/**
	* Returns an array of tables for all commands or for mentioned namespaces only
	*
	* @param namespaces - Optional array of namespaces to filter by
	*/
	#getCommandsTables(namespaces) {
		if (namespaces && namespaces.length) return namespaces.map((namespace) => {
			return this.#makeCommandsTable(namespace, this.kernel.getNamespaceCommands(namespace));
		});
		return [this.#makeCommandsTable("Available commands:", this.kernel.getNamespaceCommands()), ...this.kernel.getNamespaces().map((namespace) => this.#makeCommandsTable(namespace, this.kernel.getNamespaceCommands(namespace)))];
	}
	/**
	* Returns table for the global flags
	*/
	#getOptionsTable() {
		if (!this.kernel.flags.length) return [];
		return [this.#makeOptionsTable("Options:", this.kernel.flags)];
	}
	/**
	* Validates the namespaces mentioned via the namespaces argument
	*/
	#validateNamespace() {
		if (!this.namespaces) return true;
		const namespaces = this.kernel.getNamespaces();
		const unknownNamespace = this.namespaces.find((namespace) => !namespaces.includes(namespace));
		/**
		* Show error when the namespace is not known
		*/
		if (unknownNamespace) {
			renderErrorWithSuggestions(this.ui, `Namespace "${unknownNamespace}" is not defined`, this.kernel.getNamespaceSuggestions(unknownNamespace));
			return false;
		}
		return true;
	}
	/**
	* Renders a formatted list of options and commands to the console
	*/
	renderList() {
		new ListFormatter(this.#getOptionsTable().concat(this.#getCommandsTables(this.namespaces))).format().forEach((table) => {
			this.logger.log("");
			this.logger.log(table.heading);
			this.logger.log(table.rows.join("\n"));
		});
	}
	/**
	* Returns command data as JSON for the --json flag
	*/
	renderToJSON() {
		if (this.namespaces && this.namespaces.length) return this.namespaces.map((namespace) => {
			return this.kernel.getNamespaceCommands(namespace);
		}).flat(1);
		return this.kernel.getNamespaceCommands().concat(this.kernel.getNamespaces().map((namespace) => this.kernel.getNamespaceCommands(namespace)).flat(1));
	}
	/**
	* Executes the list command to display available commands
	*
	* @example
	* ```ts
	* await listCommand.run()
	* ```
	*/
	async run() {
		if (!this.#validateNamespace()) {
			this.exitCode = 1;
			return;
		}
		if (this.json) {
			this.logger.log(JSON.stringify(this.renderToJSON(), null, 2));
			return;
		}
		this.renderList();
	}
};
__decorate([args.spread({
	description: "Filter list by namespace",
	required: false
})], ListCommand.prototype, "namespaces", void 0);
__decorate([flags.boolean({ description: "Get list of commands as JSON" })], ListCommand.prototype, "json", void 0);
//#endregion
//#region src/loaders/list_loader.ts
/**
* List loader exposes the API to register commands as classes
*
* @example
* ```ts
* const loader = new ListLoader([MyCommand, AnotherCommand])
* const metadata = await loader.getMetaData()
* const command = await loader.getCommand(metadata[0])
* ```
*/
var ListLoader = class {
	/**
	* Array of command classes
	*/
	#commands;
	/**
	* Create a new list loader
	*
	* @param commands - Array of command classes to register
	*/
	constructor(commands) {
		this.#commands = commands;
	}
	/**
	* Returns an array of command metadata for all registered commands
	*
	* @example
	* ```ts
	* const metadata = await loader.getMetaData()
	* metadata.forEach(cmd => console.log(cmd.commandName))
	* ```
	*/
	async getMetaData() {
		return this.#commands.map((command) => command.serialize());
	}
	/**
	* Returns the command class constructor for a given command. Null
	* is returned when unable to lookup the command
	*
	* @param metaData - The command metadata to find
	*
	* @example
	* ```ts
	* const command = await loader.getCommand(metadata)
	* if (command) {
	*   const instance = new command(kernel, parsed, ui, prompt)
	* }
	* ```
	*/
	async getCommand(metaData) {
		return this.#commands.find((command) => command.commandName === metaData.commandName) || null;
	}
};
//#endregion
//#region src/exception_handler.ts
/**
* The base exception handler that is used by default to handle
* Ace exceptions.
*
* You can extend this class to customize the exception rendering
* behavior.
*
* @example
* ```ts
* export class MyExceptionHandler extends ExceptionHandler {
*   async render(error: unknown, kernel: Kernel<any>) {
*     // Custom error handling
*     await super.render(error, kernel)
*   }
* }
* ```
*/
var ExceptionHandler = class {
	/**
	* Enable debug mode for detailed error reporting
	*/
	debug = true;
	/**
	* Known error codes. For these errors, only the error message is
	* reported using the logger
	*/
	knownErrorCodes = [];
	/**
	* Internal set of known error codes
	*/
	internalKnownErrorCode = Object.keys(errors_exports);
	/**
	* Logs error to stderr using logger
	*
	* @param error - The error object with a message property
	* @param kernel - The Ace kernel instance
	*/
	logError(error, kernel) {
		kernel.ui.logger.logError(`${kernel.ui.colors.bgRed().white("  ERROR  ")} ${error.message}`);
	}
	/**
	* Pretty prints uncaught error in debug mode using Youch
	*
	* @param error - The error object to pretty print
	*/
	async prettyPrintError(error) {
		const { Youch } = await import("youch");
		const youch = new Youch();
		console.log(await youch.toANSI(error));
	}
	/**
	* Renders an exception for the console with appropriate formatting
	*
	* @param error - The error to render
	* @param kernel - The Ace kernel instance
	*
	* @example
	* ```ts
	* const handler = new ExceptionHandler()
	* await handler.render(new Error('Something went wrong'), kernel)
	* ```
	*/
	async render(error, kernel) {
		/**
		* Render non object errors or errors without message property
		* as a string using the logger
		*/
		if (typeof error !== "object" || error === null || !("message" in error)) {
			this.logError({ message: String(error) }, kernel);
			return;
		}
		/**
		* Report command not found error with command suggestions
		*/
		if (error instanceof E_COMMAND_NOT_FOUND) {
			renderErrorWithSuggestions(kernel.ui, error.message, kernel.getCommandSuggestions(error.commandName));
			return;
		}
		/**
		* Display prompt cancellation error
		*/
		if (error instanceof errors.E_PROMPT_CANCELLED) {
			this.logError({ message: "Prompt cancelled" }, kernel);
			return;
		}
		/**
		* Known errors should always be reported with a message
		*/
		if ("code" in error && typeof error.code === "string" && (this.internalKnownErrorCode.includes(error.code) || this.knownErrorCodes.includes(error.code))) {
			this.logError({ message: error.message }, kernel);
			return;
		}
		/**
		* Allow errors to be self handled.
		*/
		if ("render" in error && typeof error.render === "function") return error.render(error, kernel);
		/**
		* Log error message and stack only when not in debug mode
		*/
		if (!this.debug) {
			kernel.ui.logger.fatal(error);
			return;
		}
		try {
			await this.prettyPrintError(error);
		} catch {
			kernel.ui.logger.fatal(error);
		}
	}
};
//#endregion
//#region src/tracing_channels.ts
var tracing_channels_exports = /* @__PURE__ */ __exportAll({ commandExec: () => commandExec });
/**
* Traces every command execution handled by the {@link Kernel} class.
*/
const commandExec = diagnostics_channel.tracingChannel("adonisjs.command.exec");
//#endregion
//#region src/kernel.ts
/**
* The Ace kernel manages the registration and execution of commands.
*
* The kernel is the main entry point of a console application, and
* is tailored for a standard CLI environment.
*
* @example
* ```ts
* const kernel = Kernel.create()
*
* kernel.defineFlag('help', {
*  type: 'boolean',
*  alias: 'h',
*  description: 'Display help for the given command. When no command is given display help for the list command'
* })
* kernel.on('help', async (command, $kernel, options) => {
*   options.args.unshift(command.commandName)
*   await new HelpCommand($kernel, options, kernel.ui, kernel.prompt).exec()
*   return true
* })
*
* kernel.addLoader(new FsLoader('./commands'))
*
* kernel.info.set('binary', 'node ace')
* kernel.info.set('Framework version', '9.1')
* kernel.info.set('App version', '1.1.1')
*
* await kernel.handle(process.argv.slice(2))
* ```
*/
var Kernel = class Kernel {
	/**
	* The error handler for rendering exceptions
	*/
	errorHandler = new ExceptionHandler();
	/**
	* The default executor for creating command instances and running them
	*/
	static commandExecutor = {
		create(command, parsedArgs, kernel) {
			return new command(kernel, parsedArgs, kernel.ui, kernel.prompt);
		},
		run(command) {
			return command.exec();
		}
	};
	/**
	* The default command to use when creating kernel instance via static create method
	*/
	static defaultCommand = ListCommand;
	/**
	* Creates an instance of kernel with the default executor and default command
	*
	* @example
	* ```ts
	* const kernel = Kernel.create()
	* ```
	*/
	static create() {
		return new Kernel(this.defaultCommand, this.commandExecutor);
	}
	/**
	* Listeners for CLI options. Executed for the main command
	* only
	*/
	#optionListeners = /* @__PURE__ */ new Map();
	/**
	* The global command is used to register global flags applicable
	* on all the commands
	*/
	#globalCommand = class extends BaseCommand {
		static options = { allowUnknownFlags: true };
	};
	/**
	* The default command to run when no command is mentioned. The default
	* command will also run when only flags are mentioned.
	*/
	#defaultCommand;
	/**
	* Available hooks
	*/
	#hooks = new Hooks();
	/**
	* Executors are used to instantiate a command and execute
	* the run method.
	*/
	#executor;
	/**
	* Keeping track of the main command. There are some action (like termination)
	* that only the main command can perform
	*/
	#mainCommand;
	/**
	* The current state of kernel. The `running` and `terminated`
	* states are only set when kernel takes over the process.
	*/
	#state = "idle";
	/**
	* Collection of loaders to use for loading commands
	*/
	#loaders = [];
	/**
	* An array of registered namespaces. Sorted alphabetically
	*/
	#namespaces = [];
	/**
	* A collection of aliases for the commands. The key is the alias name
	* and the value is the command name.
	*
	* In case of duplicate aliases, the most recent alias will override
	* the previous existing alias.
	*/
	#aliases = /* @__PURE__ */ new Map();
	/**
	* An collection of expansion arguments and flags set on
	* an alias. The key is the alias name and the value is
	* everything after it.
	*/
	#aliasExpansions = /* @__PURE__ */ new Map();
	/**
	* A collection of commands by the command name. This allows us to keep only
	* the unique commands and also keep the loader reference to know which
	* loader to ask for loading the command.
	*/
	#commands = /* @__PURE__ */ new Map();
	/**
	* The exit code for the kernel. The exit code is inferred
	* from the main command when not set explicitly
	*/
	exitCode;
	/**
	* The UI primitives to use within commands
	*/
	ui = cliui$1();
	/**
	* Instance of prompt to display CLI prompts. We share
	* a single instance with all the commands. This
	* allows trapping prompts for commands executed internally
	*/
	prompt = new Prompt();
	/**
	* CLI info map containing metadata about the application
	*/
	info = /* @__PURE__ */ new Map();
	/**
	* List of global flags available across all commands
	*
	* @example
	* ```ts
	* kernel.flags // [{ name: 'help', type: 'boolean', ... }]
	* ```
	*/
	get flags() {
		return this.#globalCommand.flags;
	}
	/**
	* Create a new Kernel instance
	*
	* @param defaultCommand - The default command to run when no command is specified
	* @param executor - The executor for creating and running command instances
	*/
	constructor(defaultCommand, executor) {
		this.#defaultCommand = defaultCommand;
		this.#executor = executor;
	}
	/**
	* Process command line arguments. All flags before the command
	* name are considered as Node.js argv and all flags after
	* the command name are considered as command argv.
	*
	* The behavior is same as Node.js CLI, where all flags before the
	* script file name are Node.js argv.
	*/
	#processArgv(argv) {
		const commandNameIndex = argv.findIndex((value) => !value.startsWith("-"));
		if (commandNameIndex === -1) return {
			nodeArgv: [],
			commandName: null,
			commandArgv: argv
		};
		return {
			nodeArgv: argv.slice(0, commandNameIndex),
			commandName: argv[commandNameIndex],
			commandArgv: argv.slice(commandNameIndex + 1)
		};
	}
	/**
	* Creates an instance of a command by parsing and validating
	* the command line arguments.
	*/
	async #create(Command, argv) {
		/**
		* Parse CLI argv without global flags. When running commands directly, we
		* should not be using global flags anyways
		*/
		const parsed = new Parser(Command.getParserOptions()).parse(argv);
		/**
		* Validate the parsed output
		*/
		Command.validate(parsed);
		/**
		* Construct command instance using the executor
		*/
		const commandInstance = await this.#executor.create(Command, parsed, this);
		commandInstance.hydrate();
		return commandInstance;
	}
	/**
	* Executes a given command. The main commands are executed using the
	* "execMain" method.
	*/
	async #exec(commandName, argv, options) {
		const Command = await this.find(commandName);
		/**
		* Expand aliases
		*/
		const aliasExpansions = this.#aliasExpansions.get(commandName);
		if (aliasExpansions) {
			argv = aliasExpansions.concat(argv);
			debug_default("expanding alias %O, cli args %O", commandName, argv);
		}
		const commandInstance = await this.#create(Command, argv);
		/**
		* Override the UI primitives when a custom instance is provided.
		* This allows callers to silence command output by passing a
		* UI backed by a memory renderer.
		*/
		if (options?.ui && "ui" in commandInstance) commandInstance.ui = options.ui;
		/**
		* Execute the command using the executor
		*/
		await this.#hooks.runner("executing").run(commandInstance, false);
		await commandExec.tracePromise(this.#executor.run, commandExec.hasSubscribers ? {
			command: Command,
			commandInstance,
			argv
		} : void 0, this.#executor, commandInstance, this);
		await this.#hooks.runner("executed").run(commandInstance, false);
		return commandInstance;
	}
	/**
	* Executes the main command and handles the exceptions by
	* reporting them
	*/
	async #execMain(commandName, nodeArgv, argv) {
		try {
			const Command = await this.find(commandName);
			/**
			* Expand aliases
			*/
			const aliasExpansions = this.#aliasExpansions.get(commandName);
			if (aliasExpansions) {
				argv = aliasExpansions.concat(argv);
				debug_default("expanding alias %O, cli args %O", commandName, argv);
			}
			/**
			* Parse CLI argv and also merge global flags parser options.
			*/
			const parsed = new Parser(Command.getParserOptions(this.#globalCommand.getParserOptions().flagsParserOptions)).parse(argv);
			/**
			* Defined only for the main command
			*/
			parsed.nodeArgs = nodeArgv;
			/**
			* Validate the flags against the global list as well
			*/
			this.#globalCommand.validate(parsed);
			/**
			* Run options listeners. Option listeners can terminate
			* the process early
			*/
			let shortcircuit = false;
			for (let [option, listener] of this.#optionListeners) if (parsed.flags[option] !== void 0) {
				debug_default("running listener for \"%s\" flag", option);
				shortcircuit = await listener(Command, this, parsed);
				if (shortcircuit) break;
			}
			/**
			* Validate the parsed output
			*/
			Command.validate(parsed);
			/**
			* Return early if a flag listener shortcircuits
			*/
			if (shortcircuit) {
				debug_default("short circuiting from flag listener");
				this.exitCode = this.exitCode ?? 0;
				this.#state = "completed";
				return;
			}
			/**
			* Keep a note of the main command
			*/
			this.#mainCommand = await this.#executor.create(Command, parsed, this);
			this.#mainCommand.hydrate();
			/**
			* Execute the command using the executor
			*/
			await this.#hooks.runner("executing").run(this.#mainCommand, true);
			await commandExec.tracePromise(this.#executor.run, commandExec.hasSubscribers ? {
				command: Command,
				commandInstance: this.#mainCommand,
				argv
			} : void 0, this.#executor, this.#mainCommand, this);
			await this.#hooks.runner("executed").run(this.#mainCommand, true);
			this.exitCode = this.exitCode ?? this.#mainCommand.exitCode;
			this.#state = "completed";
		} catch (error) {
			this.exitCode = 1;
			this.#state = "completed";
			await this.errorHandler.render(error, this);
		}
	}
	/**
	* Listen for CLI options and execute an action. Only one listener
	* can be defined per option.
	*
	* The callbacks are only executed for the main command
	*/
	on(option, callback) {
		debug_default("registering flag listener for \"%s\" flag", option);
		this.#optionListeners.set(option, callback);
		return this;
	}
	/**
	* Define a global flag that is applicable for all commands
	*
	* @param name - The flag name
	* @param options - Flag configuration options
	*
	* @example
	* ```ts
	* kernel.defineFlag('verbose', { type: 'boolean', description: 'Enable verbose output' })
	* ```
	*/
	defineFlag(name, options) {
		if (this.#state !== "idle") throw new RuntimeException(`Cannot register global flag in "${this.#state}" state`);
		this.#globalCommand.defineFlag(name, options);
	}
	/**
	* Register a commands loader. The commands will be collected by all loaders.
	*
	* In case multiple loaders return a single command, the command from the
	* most recent loader will be used.
	*
	* @param loader - The loader instance or a function that returns a loader
	*
	* @example
	* ```ts
	* kernel.addLoader(new FsLoader('./commands'))
	* kernel.addLoader(() => import('./lazy-loader').then(m => new m.LazyLoader()))
	* ```
	*/
	addLoader(loader) {
		if (this.#state !== "idle") throw new RuntimeException(`Cannot add loader in "${this.#state}" state`);
		this.#loaders.push(loader);
		return this;
	}
	/**
	* Register alias for a command name
	*
	* @param alias - The alias name
	* @param command - The command name (can include arguments)
	*
	* @example
	* ```ts
	* kernel.addAlias('m', 'make:model')
	* kernel.addAlias('migrate:fresh', 'migration:rollback --to=0 && migration:run')
	* ```
	*/
	addAlias(alias, command) {
		const [commandName, ...expansions] = command.split(" ");
		this.#aliases.set(alias, commandName);
		if (expansions.length) {
			debug_default("registering alias %O for command %O with options %O", alias, commandName, expansions);
			this.#aliasExpansions.set(alias, expansions);
		} else debug_default("registering alias %O for command %O", alias, commandName);
		return this;
	}
	/**
	* Check if a command or an alias is registered with kernel
	*/
	hasCommand(commandName) {
		commandName = this.#aliases.get(commandName) || commandName;
		return this.#commands.has(commandName);
	}
	/**
	* Get the current state of the kernel.
	*/
	getState() {
		return this.#state;
	}
	/**
	* Returns a flat list of commands metadata registered with the kernel.
	* The list is sorted alphabetically by the command name.
	*/
	getCommands() {
		return [...this.#commands.keys()].sort(sortAlphabetically).map((name) => this.#commands.get(name).metaData);
	}
	/**
	* Get a list of commands for a specific namespace. All non-namespaces
	* commands will be returned if no namespace is defined.
	*/
	getNamespaceCommands(namespace) {
		let commandNames = [...this.#commands.keys()];
		/**
		* Filter a list of commands by the namespace
		*/
		if (namespace) commandNames = commandNames.filter((name) => this.#commands.get(name).metaData.namespace === namespace);
		else commandNames = commandNames.filter((name) => !this.#commands.get(name).metaData.namespace);
		return commandNames.sort(sortAlphabetically).map((name) => this.#commands.get(name).metaData);
	}
	/**
	* Returns the command metadata by its name. Returns null when the
	* command is missing.
	*/
	getCommand(commandName) {
		return this.#commands.get(commandName)?.metaData || null;
	}
	/**
	* Returns a reference for the default command. The return value
	* is the default command constructor
	*/
	getDefaultCommand() {
		return this.#defaultCommand;
	}
	/**
	* Returns reference to the main command
	*/
	getMainCommand() {
		return this.#mainCommand;
	}
	/**
	* Returns an array of aliases registered.
	*
	* - Call `getCommandAliases` method to get aliases for a given command
	* - Call `getAliasCommand` to get the command or a given alias
	*/
	getAliases() {
		return [...this.#aliases.keys()];
	}
	/**
	* Returns the command metata for a given alias. Returns null
	* if alias is not recognized.
	*/
	getAliasCommand(alias) {
		const aliasCommand = this.#aliases.get(alias);
		if (!aliasCommand) return null;
		return this.#commands.get(aliasCommand)?.metaData || null;
	}
	/**
	* Returns an array of aliases for a given command
	*/
	getCommandAliases(commandName) {
		return [...this.#aliases.entries()].filter(([, command]) => {
			return command === commandName;
		}).map(([alias]) => alias);
	}
	/**
	* Returns a list of namespaces. The list is sorted alphabetically
	* by the namespace name
	*/
	getNamespaces() {
		return this.#namespaces;
	}
	/**
	* Returns an array of command and aliases name suggestions for
	* a given keyword.
	*/
	getCommandSuggestions(keyword) {
		/**
		* Priortize namespace commands when the keyword matches the
		* namespace
		*/
		if (this.#namespaces.includes(keyword)) return this.getNamespaceCommands(keyword).map((command) => command.commandName);
		return [...this.#commands.keys()].concat([...this.#aliases.keys()]).map((value) => {
			return {
				value,
				distance: distance(keyword, value)
			};
		}).sort((current, next) => next.distance - current.distance).filter((rating) => {
			return rating.distance <= 3;
		}).map((rating) => rating.value);
	}
	/**
	* Returns an array of namespaces suggestions for a given keyword.
	*/
	getNamespaceSuggestions(keyword) {
		return this.#namespaces.map((value) => {
			return {
				value,
				distance: distance(keyword, value)
			};
		}).sort((current, next) => next.distance - current.distance).filter((rating) => {
			return rating.distance <= 3;
		}).map((rating) => rating.value);
	}
	/**
	* Listen for the event before we begin the process of finding
	* the command.
	*/
	finding(callback) {
		this.#hooks.add("finding", callback);
		return this;
	}
	/**
	* Listen for the event when importing the command
	*/
	loading(callback) {
		this.#hooks.add("loading", callback);
		return this;
	}
	/**
	* Listen for the event when the command has been imported
	*/
	loaded(callback) {
		this.#hooks.add("loaded", callback);
		return this;
	}
	/**
	* Listen for the event before we start to execute the command.
	*/
	executing(callback) {
		this.#hooks.add("executing", callback);
		return this;
	}
	/**
	* Listen for the event after the command has been executed
	*/
	executed(callback) {
		this.#hooks.add("executed", callback);
		return this;
	}
	/**
	* Loads commands from all the registered loaders. The "addLoader" method
	* must be called before calling the "load" method.
	*/
	async boot() {
		if (this.#state !== "idle") return;
		/**
		* Boot global command is not already booted
		*/
		this.#globalCommand.boot();
		/**
		* Registering the default command
		*/
		this.addLoader(new ListLoader([this.#defaultCommand]));
		/**
		* Set state to booted
		*/
		this.#state = "booted";
		/**
		* A set of unique namespaces. Later, we will store them on kernel
		* directly as an alphabetically sorted array.
		*/
		const namespaces = /* @__PURE__ */ new Set();
		/**
		* Load metadata for all commands using the loaders
		*/
		for (let loader of this.#loaders) {
			let loaderInstance;
			/**
			* A loader can be a function that lazily imports and instantiates
			* a loader
			*/
			if (typeof loader === "function") loaderInstance = await loader();
			else loaderInstance = loader;
			(await loaderInstance.getMetaData()).forEach((command) => {
				this.#commands.set(command.commandName, {
					metaData: command,
					loader: loaderInstance
				});
				command.aliases.forEach((alias) => this.addAlias(alias, command.commandName));
				command.namespace && namespaces.add(command.namespace);
			});
		}
		this.#namespaces = [...namespaces].sort(sortAlphabetically);
	}
	/**
	* Find a command by its name
	*/
	async find(commandName) {
		/**
		* Get command name from the alias (if one exists)
		*/
		commandName = this.#aliases.get(commandName) || commandName;
		await this.#hooks.runner("finding").run(commandName);
		/**
		* Find if we have a command registered
		*/
		const command = this.#commands.get(commandName);
		if (!command) throw new E_COMMAND_NOT_FOUND([commandName]);
		await this.#hooks.runner("loading").run(command.metaData);
		/**
		* Find if the loader is able to load the command
		*/
		const commandConstructor = await command.loader.getCommand(command.metaData);
		if (!commandConstructor) throw new E_COMMAND_NOT_FOUND([commandName]);
		await this.#hooks.runner("loaded").run(commandConstructor);
		return commandConstructor;
	}
	/**
	* Execute a command. The second argument is an array of command-line
	* arguments (without the command name)
	*
	* @param commandName - The name of the command to execute
	* @param argv - Array of command-line arguments
	*
	* @example
	* ```ts
	* await kernel.exec('make:model', ['User', '--migration'])
	* ```
	*/
	async exec(commandName, argv, options) {
		/**
		* Boot if not already booted
		*/
		if (this.#state === "idle") await this.boot();
		/**
		* Cannot execute commands after the main command has exited
		*/
		if (this.#state === "completed") throw new RuntimeException("The kernel has been terminated. Create a fresh instance to execute commands");
		return this.#exec(commandName, argv, options);
	}
	/**
	* Creates a command instance by parsing and validating the command-line arguments
	*
	* @param command - The command class to instantiate
	* @param argv - Command-line arguments as string or array
	*
	* @example
	* ```ts
	* const commandInstance = await kernel.create(MyCommand, ['--verbose', 'arg1'])
	* ```
	*/
	async create(command, argv) {
		/**
		* Boot if not already booted
		*/
		if (this.#state === "idle") await this.boot();
		return this.#create(command, argv);
	}
	/**
	* Handle process argv and execute the command. Calling this method
	* makes kernel own the process and register SIGNAL listeners
	*
	* @param argv - Array of command-line arguments from process.argv
	*
	* @example
	* ```ts
	* await kernel.handle(process.argv.slice(2))
	* ```
	*/
	async handle(argv) {
		/**
		* Cannot run multiple main commands from a single process
		*/
		if (this.#state === "running") throw new RuntimeException("Cannot run multiple main commands from a single process");
		/**
		* Cannot run multiple main commands from the same instance
		*/
		if (this.#state === "completed") throw new RuntimeException("The kernel has been terminated. Create a fresh instance to execute commands");
		/**
		* Boot kernel
		*/
		if (this.#state === "idle") await this.boot();
		this.#state = "running";
		const { commandName, nodeArgv, commandArgv } = this.#processArgv(argv);
		/**
		* Run the default command
		*/
		if (!commandName) {
			debug_default("running default command \"%s\"", this.#defaultCommand.commandName);
			return this.#execMain(this.#defaultCommand.commandName, nodeArgv, commandArgv);
		}
		/**
		* Run the mentioned command as the main command
		*/
		debug_default("running main command \"%s\"", commandName);
		return this.#execMain(commandName, nodeArgv, commandArgv);
	}
	/**
	* A named function that returns true. To be used
	* by flag listeners
	*/
	shortcircuit() {
		return true;
	}
};
//#endregion
//#region src/commands/help.ts
/**
* The Help command is used to view help for a given command
*
* @example
* ```ts
* // Usage from CLI: node ace help my:command
* // Or programmatically:
* const helpCommand = new HelpCommand(kernel, parsed, ui, prompt)
* await helpCommand.run()
* ```
*/
var HelpCommand = class extends BaseCommand {
	/**
	* Command metadata
	*/
	static commandName = "help";
	static description = "View help for a given command";
	/**
	* Returns the command arguments table
	*
	* @param heading - The table heading text
	* @param command - The command metadata
	*/
	#makeArgumentsTable(heading, command) {
		if (!command.args.length) return [];
		return [{
			heading: this.colors.yellow(heading),
			columns: command.args.map((arg) => {
				const formatter = new ArgumentFormatter(arg, this.colors);
				return {
					option: formatter.formatListOption(),
					description: formatter.formatDescription()
				};
			})
		}];
	}
	/**
	* Returns the command options table
	*
	* @param heading - The table heading text
	* @param command - The command metadata
	*/
	#makeOptionsTable(heading, command) {
		if (!command.flags.length) return [];
		return [{
			heading: this.colors.yellow(heading),
			columns: command.flags.map((flag) => {
				const formatter = new FlagFormatter(flag, this.colors);
				return {
					option: formatter.formatOption(),
					description: formatter.formatDescription()
				};
			})
		}];
	}
	/**
	* Validates the command name to ensure it exists
	*/
	#validateCommandName() {
		if (!this.kernel.getCommand(this.name)) {
			renderErrorWithSuggestions(this.ui, `Command "${this.name}" is not defined`, this.kernel.getCommandSuggestions(this.name));
			return false;
		}
		return true;
	}
	/**
	* Logs command description
	*
	* @param command - The command metadata
	*/
	renderDescription(command) {
		const description = new CommandFormatter(command, this.colors).formatDescription();
		if (!description) return;
		this.logger.log("");
		this.logger.log(this.colors.yellow("Description:"));
		this.logger.log(wrap([description], {
			startColumn: 2,
			trimStart: false,
			endColumn: TERMINAL_SIZE
		}).join("\n"));
	}
	/**
	* Logs command usage
	*
	* @param command - The command metadata
	*/
	renderUsage(command) {
		const aliases = this.kernel.getCommandAliases(command.commandName);
		const usage = new CommandFormatter(command, this.colors).formatUsage(aliases, this.kernel.info.get("binary")).join("\n");
		this.logger.log("");
		this.logger.log(this.colors.yellow("Usage:"));
		this.logger.log(usage);
	}
	/**
	* Logs command arguments and options tables
	*
	* @param command - The command metadata
	*/
	renderList(command) {
		new ListFormatter(this.#makeArgumentsTable("Arguments:", command).concat(this.#makeOptionsTable("Options:", command))).format().forEach((table) => {
			this.logger.log("");
			this.logger.log(table.heading);
			this.logger.log(table.rows.join("\n"));
		});
	}
	/**
	* Logs command help text
	*
	* @param command - The command metadata
	*/
	renderHelp(command) {
		const help = new CommandFormatter(command, this.colors).formatHelp(this.kernel.info.get("binary"));
		if (!help) return;
		this.logger.log("");
		this.logger.log(this.colors.yellow("Help:"));
		this.logger.log(help);
	}
	/**
	* Executes the help command to display help for the specified command
	*
	* @example
	* ```ts
	* await helpCommand.run()
	* ```
	*/
	async run() {
		if (!this.#validateCommandName()) {
			this.exitCode = 1;
			return;
		}
		const command = this.kernel.getCommand(this.name);
		this.renderDescription(command);
		this.renderUsage(command);
		this.renderList(command);
		this.renderHelp(command);
	}
};
__decorate([args.string({
	description: "Command name",
	argumentName: "command"
})], HelpCommand.prototype, "name", void 0);
//#endregion
//#region src/loaders/fs_loader.ts
const JS_MODULES = [
	".js",
	".cjs",
	".mjs"
];
/**
* Fs loader exposes the API to load commands from a directory. All files
* ending with ".js", ".cjs", ".mjs", ".ts" and ".mts" are considered
* as commands
*
* @example
* ```ts
* const loader = new FsLoader('./commands')
* const metadata = await loader.getMetaData()
* const command = await loader.getCommand(metadata[0])
* ```
*/
var FsLoader = class {
	/**
	* Absolute path to directory from which to load files
	*/
	#comandsDirectory;
	/**
	* Filter function to ignore certain files
	*/
	#filter;
	/**
	* An array of loaded commands with their file paths
	*/
	#commands = [];
	/**
	* Create a new filesystem loader
	*
	* @param comandsDirectory - Path to the directory containing command files
	* @param filter - Optional filter function to exclude certain files
	*/
	constructor(comandsDirectory, filter) {
		this.#comandsDirectory = comandsDirectory;
		this.#filter = filter;
	}
	/**
	* Returns a collection of commands. The command values are unknown and must be validated
	*/
	async #loadCommands() {
		const commands = {};
		/**
		* Scanning all files
		*/
		const commandFiles = await fsReadAll(this.#comandsDirectory, {
			pathType: "url",
			ignoreMissingRoot: true,
			filter: (filePath) => {
				const ext = extname(filePath);
				/**
				* Ignore files prefixed with _
				*/
				if (basename(filePath).startsWith("_")) return false;
				if (JS_MODULES.includes(ext)) return true;
				if (ext === ".ts" && !filePath.endsWith(".d.ts")) return true;
				return false;
			}
		});
		/**
		* Importing files and validating the exports to have a default
		* export
		*/
		for (let file of commandFiles) {
			/**
			* Remapping .ts files to .js, otherwise the file cannot imported
			*/
			if (file.endsWith(".ts")) file = file.replace(/\.ts$/, ".js");
			const relativeFileName = string.toUnixSlash(relative(this.#comandsDirectory, fileURLToPath(file)));
			/**
			* Import file if no filters are defined or the filter
			* allows the file
			*/
			if (!this.#filter || this.#filter(relativeFileName)) commands[relativeFileName] = await importDefault(() => import(file), relativeFileName);
		}
		return commands;
	}
	/**
	* Returns the metadata of all commands in the directory
	*
	* @example
	* ```ts
	* const metadata = await loader.getMetaData()
	* metadata.forEach(cmd => console.log(cmd.commandName))
	* ```
	*/
	async getMetaData() {
		const commandsCollection = await this.#loadCommands();
		Object.keys(commandsCollection).forEach((key) => {
			const command = commandsCollection[key];
			validateCommand(command, `"${key}" file`);
			this.#commands.push({
				command,
				filePath: key
			});
		});
		return this.#commands.map(({ command, filePath }) => {
			return Object.assign({}, command.serialize(), { filePath });
		});
	}
	/**
	* Returns the command class constructor for a given command. Null
	* is returned when unable to lookup the command
	*
	* @param metaData - The command metadata to find
	*
	* @example
	* ```ts
	* const command = await loader.getCommand(metadata)
	* if (command) {
	*   const instance = new command(kernel, parsed, ui, prompt)
	* }
	* ```
	*/
	async getCommand(metaData) {
		return this.#commands.find(({ command }) => {
			return command.commandName === metaData.commandName;
		})?.command || null;
	}
};
//#endregion
//#region stubs/main.ts
const stubsRoot = import.meta.dirname;
//#endregion
//#region src/generators/index_generator.ts
/**
* The index generator creates a commands loader that can be lazily imported.
*
* Also, a commands.json index file is created that has metadata for all
* the files. Doing so speeds up commands lookup, as we do not
* have to import all the classes just to find if a command exists or not.
*
* @example
* ```ts
* const generator = new IndexGenerator('./build/commands')
* await generator.generate()
* ```
*/
var IndexGenerator = class {
	/**
	* The directory where commands are located
	*/
	#commandsDir;
	/**
	* Create a new index generator
	*
	* @param commandsDir - The directory containing command files
	*/
	constructor(commandsDir) {
		this.#commandsDir = commandsDir;
	}
	/**
	* Generate the commands index files (commands.json, main.js, main.d.ts)
	*
	* @example
	* ```ts
	* await generator.generate()
	* ```
	*/
	async generate() {
		const commandsMetaData = await new FsLoader(this.#commandsDir).getMetaData();
		const indexJSON = JSON.stringify({
			commands: commandsMetaData,
			version: 1
		});
		const indexFile = join(this.#commandsDir, "commands.json");
		const loaderFile = join(this.#commandsDir, "main.js");
		const loaderStub = join(stubsRoot, "commands_loader.stub");
		const loaderTypes = join(this.#commandsDir, "main.d.ts");
		const loaderTypesStub = join(stubsRoot, "commands_loader_types.stub");
		await mkdir(this.#commandsDir, { recursive: true });
		console.log(`artifacts directory: ${this.#commandsDir}`);
		await writeFile(indexFile, indexJSON);
		console.log("create commands.json");
		await copyFile(loaderStub, loaderFile);
		console.log("create main.js");
		await copyFile(loaderTypesStub, loaderTypes);
		console.log("create main.d.ts");
	}
};
//#endregion
export { BaseCommand, ExceptionHandler, FsLoader, HelpCommand, IndexGenerator, Kernel, ListCommand, ListLoader, Parser, args, cliHelpers, cliui, errors_exports as errors, flags, tracing_channels_exports as tracingChannels };