npm package: @northern/cli
Description: A lightweight CLI program framework.
Author: northern
Latest version: 1.0.2

# @northern/cli

A lightweight, typed CLI program framework for building command-driven tools with consistent help output and parameter parsing.

## Feature overview

- **Typed program model** with `Program`, `Command`, `Argument`, and `Option` definitions.
- **Built-in parsing** for required arguments and optional flags.
- **Three parameter modes**: `value`, `switch`, and `enum`.
- **Automatic help output** for global and command-specific options.
- **Validation and errors** via `ParameterError` when arguments are invalid or duplicated.
- **Pluggable console** powered by `@northern/console` for consistent styled output.

## Install

```bash
npm install @northern/cli
```

## Example

Run the included example:
```
npm run example -- greet --name "Freddy Krueger"
```

Note: The first `--` is to indicate that what follows are parameters for the command being executed, not parameters for `npm run`.

We can also turn off color output by adding the `--nocolor` parameter:
```
npm run example -- greet --name "Freddy Krueger" --nocolor
```

## How to use

### 1) Define your program

```ts
import { Interpreter, ParameterType, Program } from '@northern/cli';

const program: Program = {
	title: 'Acme Tool',
	version: '1.2.3',
	name: 'acme',
	options: [
		{
			name: 'verbose',
			description: 'Enable verbose output',
			type: ParameterType.SWITCH,
			directives: ['-v', '--verbose'],
			example: '--verbose',
			default: false,
		},
	],
	commands: [
		{
			name: 'build',
			description: 'Build the project',
			arguments: [
				{
					name: 'source',
					description: 'Source file',
					type: ParameterType.VALUE,
					directives: ['-s', '--source'],
					example: '--source ./app.yaml',
				},
			],
			options: [
				{
					name: 'mode',
					description: 'Build mode',
					type: ParameterType.ENUM,
					directives: ['-m', '--mode'],
					example: '--mode production',
					values: ['development', 'production'],
					default: 'development',
				},
			],
		},
	],
};
```

### 2) Parse arguments and execute

```ts
const interpreter = new Interpreter(program, process.argv.slice(2));

if (!interpreter.init()) {
	process.exit(1);
}

// Global options are parsed independently from commands
const globalOptions = interpreter.getGlobalOptions() ?? {};

const command = interpreter.getCommand();
if (!command) {
	interpreter.displayHelp(process.argv[2]);
	process.exit(1);
}

const params = interpreter.getParameters(command);
if (!params) {
	interpreter.displayCommandHelp(command);
	process.exit(1);
}

// Execute your command using parsed values
// e.g. runBuild(params, globalOptions)
```

### 3) What parsing does

- **Required arguments** must be present or `getParameters()` returns `null`.
- **Options** default to their `default` value when not specified.
- **Switch options** (`ParameterType.SWITCH`) resolve to `true` if present.
- **Enum options** (`ParameterType.ENUM`) must match one of the configured `values`.
- **Duplicate directives** (e.g. `--verbose --verbose`) trigger a parse error.
- **Global options** can be retrieved even when no command is specified.

## Reference documentation

### Types

#### `ParameterType`

- `ParameterType.VALUE` — a directive that requires a value (e.g. `--config path`).
- `ParameterType.SWITCH` — a boolean flag (e.g. `--verbose`).
- `ParameterType.ENUM` — a directive that accepts one of a predefined set of values.

#### `Argument`

Required parameter definition:

| Field | Type | Description |
| --- | --- | --- |
| `name` | `string` | Internal key used in parsed output. |
| `description` | `string` | Human-readable help text. |
| `type` | `ParameterType` | Parameter type. |
| `directives` | `string[]` | Accepted flags (e.g. `['-s', '--source']`). |
| `example` | `string` | Example usage shown in help. |
| `values?` | `string[]` | Allowed values for `ENUM` parameters. |

#### `Option`

Optional parameter definition. Same as `Argument` plus:

| Field | Type | Description |
| --- | --- | --- |
| `default` | `unknown` | Default value when option is omitted. |

#### `Command`

| Field | Type | Description |
| --- | --- | --- |
| `name` | `string` | Command name (e.g. `build`). |
| `description` | `string` | Help summary. |
| `arguments?` | `Argument[]` | Required parameters. |
| `options?` | `Option[]` | Command-specific options. |

#### `Program`

| Field | Type | Description |
| --- | --- | --- |
| `title` | `string` | Display title shown in the banner. |
| `version` | `string` | Version string. |
| `name` | `string` | Executable name for usage output. |
| `options` | `Option[]` | Global options. |
| `commands` | `Command[]` | Available commands. |

#### `Parameters`

`Record<string, unknown>` containing parsed values keyed by `Argument`/`Option` `name`.

#### `ICommand`

```
interface ICommand {
	run(parameters: Parameters): Promise<unknown>
}
```

Use this to define an execution contract for command handlers.

### Errors

#### `ParameterError`

Thrown internally when parsing fails (missing values, invalid enums, duplicate directives).
The interpreter returns `null` from `getParameters()`/`getGlobalOptions()` to preserve backward compatibility.

### `Interpreter`

#### Constructor

```
new Interpreter(program: Program, args: string[], console?: Console)
new Interpreter(program: Program, console?: Console, args?: string[])
```

Creates a parser instance. You can pass `args` (typically `process.argv.slice(2)`) and/or a custom console. Both argument orders are supported for backward compatibility.

#### `init()`

```
init(): boolean
```

Displays general help when no arguments are provided and returns `false`.

#### `getCommand()`

```
getCommand(): Command | null
```

Returns the command matching the first argument (case-insensitive), or `null`.

#### `getBanner()`

```
getBanner(): string
```

Returns a formatted banner string with title and version.

#### `displayHelp(command?: string)`

Displays general help and optionally an “unknown command” message.

#### `displayCommandHelp(command: Command)`

Shows command-specific usage, required arguments, options, and global options.

#### `getParameters(command: Command)`

```
getParameters(command: Command): Parameters | null
```

Parses required arguments and command options. Returns `null` on validation failure.

#### `getGlobalOptions()`

```
getGlobalOptions(): Parameters | null
```

Parses global options only, independently from command parsing. This means you can call it even when no command was provided. Returns `null` on validation failure.

#### `setConsole(console: Console)`

Replaces the internal console instance (useful for testing).

#### `getConsole()`

```
getConsole(): Console
```

Returns the current console instance (useful for testing).