@ooneex/command component is a lightweight CLI command framework. Each command implements the ICommand interface — getName, getDescription, and run — and registers itself with @decorator.command(). The run() entry point parses Bun.argv, resolves the matching command from the container by name, and executes it with fully typed options. Commands are resolved through @ooneex/container, so they support dependency injection and the full range of scopes.
Why this component
- Decorator-based registration.
@decorator.command()registers a class with the DI container and the global command registry in one step. - Type-safe options. The generic
ICommand<Options>interface carries your option type throughrun(). - Automatic argument parsing.
run()parses named options, booleans, and positionals fromBun.argvand forwards them to the command. - Container-managed. Commands resolve through
@ooneex/container, supporting Singleton, Request, and Transient scopes. - Structured errors.
CommandExceptioncarries a machine-readablekey, a message, and adataobject, logged automatically on failure. - Code generation.
commandCreatescaffolds a command class, its test stub, and a barrel export from built-in templates.
How it works
You implementICommand, register it with @decorator.command(), then import the file so the decorator runs before you call run(). The entry point reads the command name from the third positional argument, looks it up by getName(), and invokes run() with the parsed options.
| Member | Purpose |
|---|---|
getName() | Returns the command name used to match CLI input (e.g. db:seed). |
getDescription() | Returns a human-readable description of the command. |
run(options) | Executes the command with the parsed options. |
| Export | Role |
|---|---|
decorator.command(scope?) | Registers a command class with the container and COMMANDS_CONTAINER. |
run() | Parses Bun.argv, resolves the command by name, and runs it. |
getCommand(name) | Resolves a single registered command by name, or null. |
commandCreate(config) | Scaffolds a command file and test file from templates. |
COMMANDS_CONTAINER | Global array of every registered command constructor. |
run() logs the error via TerminalLogger and exits with code 1.
Decorator and usage
@decorator.command()
Registers a command class with the container and pushes it onto the global COMMANDS_CONTAINER. It accepts an optional EContainerScope (defaults to Singleton). Implement ICommand and register the class:
Running the entry point
Callrun() in your CLI entry file. Import the files that register commands first so their decorators execute:
Resolving a command manually
getCommand() returns a registered command by name, or null when none matches:
Injecting dependencies
Because commands resolve through@ooneex/container, you can inject services in the constructor:
Exceptions
The component throwsCommandException for command-related errors. It extends Exception from @ooneex/exception, carries a machine-readable key, a human-readable message, and a data object, and reports an InternalServerError HTTP status. The key is supplied by you at throw time — choose a stable, descriptive value per failure.
run() with a stable key and contextual data:
run() catches an exception it logs it via TerminalLogger and exits with code 1, so uncaught CommandExceptions surface automatically.
Best practices
- Name commands
namespace:action. Use a colon-separated scheme likedb:migrateoruser:importso related commands group naturally. - Type your options. Pass an options type to
ICommand<Options>and define every required and optional flag, instead of relying onRecord<string, unknown>. - Import command files before
run(). Decorators only register a command when its module is loaded; keep a barrel file and import it first. - Inject dependencies, don’t construct them. Resolve services through the container constructor so commands stay testable.
- Throw
CommandExceptionwith a stablekey. Keep the key constant per failure and put variable detail indata. - Keep
run()focused. Validate options up front, fail fast with a clear exception, then perform the work. - Pick a scope deliberately. Default to
Singleton; useTransientonly when a command must not share state across resolutions.
CLI command
Scaffold a command class and its test file with the generator. It writes the class undermodules/<module>/src/commands/<Name>Command.ts, a test under modules/<module>/tests/commands/<Name>Command.spec.ts, updates the commands.ts barrel export, and creates bin/command/run.ts for the module if it is missing.
| Option | Description | Default |
|---|---|---|
--name | Command class name. Pass any casing; it is normalized to PascalCase and the Command suffix is appended automatically. | Prompted if omitted |
--module | Target module the class is generated into. | shared |
--override | Overwrite an existing class without prompting. | false |
ICommand stub, ready for you to define its options and implement run():
getName() value — extra arguments are forwarded to the command:
command:run scans every module under modules/ for a bin/command/run.ts, locates the command whose getName() matches, and spawns it. It exits with code 1 when the command is not found in any module or when it fails.
See command:create and command:run for the full command references.
Use with Claude and Codex
The generator ships a matchingcommand:create skill. It runs the scaffold and then guides your AI agent through completing the command — defining the options type and implementing run(). Initialize the skills once for your agent:
- Claude
- Codex
Prompt
command:create --name=ImportUser, then implements the run() method to read the CSV and persist the users.