> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ooneex.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Command

> Build typed, container-managed CLI commands with decorator registration and automatic argument parsing.

The `@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 through `run()`.
* **Automatic argument parsing.** `run()` parses named options, booleans, and positionals from `Bun.argv` and forwards them to the command.
* **Container-managed.** Commands resolve through `@ooneex/container`, supporting Singleton, Request, and Transient scopes.
* **Structured errors.** `CommandException` carries a machine-readable `key`, a message, and a `data` object, logged automatically on failure.
* **Code generation.** `commandCreate` scaffolds a command class, its test stub, and a barrel export from built-in templates.

## How it works

You implement `ICommand`, 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.                      |

The runtime pieces fit together as follows:

| 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.                  |

When no command matches the requested name, or the command throws, `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:

```typescript theme={null}
import { decorator } from "@ooneex/command";
import type { ICommand } from "@ooneex/command";

interface GreetOptions {
  name?: string;
}

@decorator.command()
class GreetCommand implements ICommand<GreetOptions> {
  public getName(): string {
    return "greet";
  }

  public getDescription(): string {
    return "Greet a user by name";
  }

  public async run(options: GreetOptions): Promise<void> {
    const who = options.name ?? "World";
    console.log(`Hello, ${who}!`);
  }
}
```

Pass a scope when you need a fresh instance per resolution:

```typescript theme={null}
import { decorator } from "@ooneex/command";
import { EContainerScope } from "@ooneex/container";

@decorator.command(EContainerScope.Transient)
class BuildCommand implements ICommand {
  // ...
}
```

### Running the entry point

Call `run()` in your CLI entry file. Import the files that register commands first so their decorators execute:

```typescript theme={null}
import { run } from "@ooneex/command";
import "./commands"; // imports files that register commands via @decorator.command()

await run();
```

Invoke from the terminal — the command name is the third positional argument:

```bash theme={null}
bun run cli.ts greet --name Alice
# Hello, Alice!
```

### Resolving a command manually

`getCommand()` returns a registered command by name, or `null` when none matches:

```typescript theme={null}
import { getCommand } from "@ooneex/command";

const command = getCommand("greet");

if (command) {
  await command.run({ name: "Bob" });
}
```

### Injecting dependencies

Because commands resolve through `@ooneex/container`, you can inject services in the constructor:

```typescript theme={null}
import { decorator } from "@ooneex/command";
import { inject } from "@ooneex/container";

@decorator.command()
class MigrateCommand implements ICommand {
  constructor(@inject(MigrationService) private readonly migrations: MigrationService) {}

  public getName(): string {
    return "db:migrate";
  }

  public getDescription(): string {
    return "Run database migrations";
  }

  public async run(options: { drop?: boolean }): Promise<void> {
    await this.migrations.run({ drop: options.drop });
  }
}
```

## Exceptions

The component throws `CommandException` 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.

```typescript theme={null}
new CommandException(message: string, key: string, data?: Record<string, unknown>)
```

Throw it from `run()` with a stable key and contextual data:

```typescript theme={null}
import { CommandException } from "@ooneex/command";

@decorator.command()
class ValidateCommand implements ICommand {
  public getName(): string {
    return "validate";
  }

  public getDescription(): string {
    return "Validate project configuration";
  }

  public async run(options: { name?: string }): Promise<void> {
    if (!options.name) {
      throw new CommandException("The --name option is required", "MISSING_NAME", {
        received: options,
      });
    }
  }
}
```

Catch it to inspect the structured fields:

```typescript theme={null}
import { CommandException } from "@ooneex/command";

try {
  await command.run(options);
} catch (error) {
  if (error instanceof CommandException) {
    logger.error(`Command error [${error.key}]: ${error.message}`, error.data);
  } else {
    throw error;
  }
}
```

When `run()` catches an exception it logs it via `TerminalLogger` and exits with code `1`, so uncaught `CommandException`s surface automatically.

## Best practices

* **Name commands `namespace:action`.** Use a colon-separated scheme like `db:migrate` or `user:import` so related commands group naturally.
* **Type your options.** Pass an options type to `ICommand<Options>` and define every required and optional flag, instead of relying on `Record<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 `CommandException` with a stable `key`.** Keep the key constant per failure and put variable detail in `data`.
* **Keep `run()` focused.** Validate options up front, fail fast with a clear exception, then perform the work.
* **Pick a scope deliberately.** Default to `Singleton`; use `Transient` only 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 under `modules/<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.

```bash theme={null}
# Interactive: prompts for the name
ooneex command:create

# Provide the name
ooneex command:create --name=ImportUser

# Target a module and overwrite
ooneex command:create --name=ImportUser --module=auth --override
```

| 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`             |

The generated class is a registered `ICommand` stub, ready for you to define its options and implement `run()`:

```typescript theme={null}
import { type ICommand, decorator } from "@ooneex/command";

type CommandOptionsType = {
  name?: string;
};

@decorator.command()
export class ImportUserCommand<T extends CommandOptionsType = CommandOptionsType> implements ICommand<T> {
  public getName(): string {
    return "import:user";
  }

  public getDescription(): string {
    return "Execute import:user command";
  }

  public async run(options: T): Promise<void> {
    // TODO: Implement command logic
  }
}
```

Run a generated command from its module by its `getName()` value — extra arguments are forwarded to the command:

```bash theme={null}
ooneex command:run import:user --name=fixtures
```

`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](/cli/commands/command-create) and [command:run](/cli/commands/command-run) for the full command references.

## Use with Claude and Codex

The generator ships a matching `command: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:

<Tabs>
  <Tab title="Claude">
    ```bash theme={null}
    ooneex claude:init
    ```

    Then ask Claude in natural language — it maps the request to the generator, runs it, and fills in the implementation:

    ```text Prompt icon="terminal" wrap theme={null}
    Create a command that imports users from a CSV file.
    ```
  </Tab>

  <Tab title="Codex">
    ```bash theme={null}
    ooneex codex:init
    ```

    Then ask Codex in natural language — it maps the request to the generator, runs it, and fills in the implementation:

    ```text Prompt icon="terminal" wrap theme={null}
    Create a command that imports users from a CSV file.
    ```
  </Tab>
</Tabs>

For example, the prompt above maps to `command:create --name=ImportUser`, then implements the `run()` method to read the CSV and persist the users.
