Examples
Explore working examples demonstrating CLI Forge features.
Arguments Of
When building a CLI, and especially if taking advantage of [composition](./composable-options), it can be necessary to have a typescript type that respresents the resolved arguments of a CLI command. For example, if you abstract the handler of a command into a separate function, the argument of that function would be typed as the arguments of the CLI command. CLI Forge provides the `ArgumentsOf` type to help with this. It takes a CLI instance or a function that returns a CLI instance and returns the type of the arguments that the CLI command handler will receive. There are some difficulties with typescript support for circular references, so its usage isn't perfect, but if used with composable builders directly you avoid these problems.
Basic CLI
This is a simple example that demonstrates how to create a basic CLI using cli-forge
Option Choices
This is a simple example that demonstrates how to limit choices for a given option. Choices are checked after `coerce` if it is also provided, so be sure that the `coerce` function returns a value that is in the choices array. Choices can be provided as an array of valid values or as a function that returns an array of valid values. Note that when returning the array from a function, providing "as const" is necessary to narrow the typing of the argument. This may not be possible if the choices are dynamic or need to be calculated at runtime, in which case the typing will remain as a broader type (e.g. `string` instead of `'a' | 'b'`).
Composable Options
Extract common options into reusable functions and compose them across multiple commands. Shows two approaches: manual generics and the `makeComposableBuilder` helper. Also demonstrates accessing child commands programmatically via `getChildren()` and invoking their handlers with full type inference.
Conflicts and Implications
This example illustrates how `.conflicts()` and `.implies()` can be used to enforce mutually exclusive options and mutually required options, respectively.
Default Values
This is a simple example that demonstrates the various ways you can set default values for options. The default value can be set via the `default` property in an option definition. This can be done in three ways: - Setting the `default` property to a value directly - Setting the `default` property to an object containing function that returns a value and a description - Setting the `default` property to an object containing a value and a description Setting the `default` property to a value directly is the simplest way to set a default value, but can lead to some odd behavior if the value isn't consistent. For example, if the default value is the value of an environment variable that may differ among users then the actual default value will be different for each user. In this case in documentation, it would be better to tell users a description of the default value rather than the actual value.
Execution Lifecycle
Demonstrates the full execution lifecycle of a cli-forge CLI, including the order in which builders, parsing, middleware, init hooks, validation, coerce, defaults, conflicts, implies, and handlers execute. When `forge()` is called, execution proceeds in two phases: **Phase 1 — Discovery loop** (per command level): builder → parse (best-effort) → merge → middleware → init hooks → find next subcommand → repeat **Phase 2 — Final parse + execution:** parse (with validation) → help/version check → handler This example uses `--format json` with the `deploy` subcommand to walk through every stage. The console output shows the exact order.
i18next Integration
Demonstrates how to integrate cli-forge with i18next localization library using the function-based localization API.
Interactive Subshell
This example demonstrates how to create an interactive subshell using cli-forge. The subshell is a simple REPL that can execute commands or shell commands. To launch the subshell, run the script with no arguments, or a command that contains subcommands and no handler. The subshell presents a prompt that includes the current command chain, and executes the command when the user presses enter. If the command is not recognized, it is executed as a shell command. Notably, the subshell is very basic. It does not currently support command history, tab completion, or other advanced features.
Localization Support
Demonstrates how to use the localization feature to support multiple languages for option keys and command names.
Middleware
This is a simple example that demonstrates how to register middleware that run before the command handler. Middleware can do **a lot** of things. Almost everything that middleware can do could be done at the beginning of the handler function, but middleware keeps the handler clean and focused on the command's behavior as well as being much more composable. Some things middleware can do: - Modify the arguments object - Perform validation that takes multiple arguments into account - Perform side effects
Non-Strict Mode (Default)
This example demonstrates the default behavior without strict mode. Unmatched arguments are collected in the `unmatched` array and don't cause errors.
Object Arguments (Simple)
This is a simple example that demonstrates passing object-valued options to a command. Note that the object-valued options are passed as dot-notation strings. These can be nested for complex option structures that contain object properties that are themselves objects. > For a more detailed example showcasing defaults, required properties, validation, and JSON input, > see the `object-arguments` multi-file example. > Note: This example is a bit more abstract than the others, as real world use cases for object-valued > options and especially nested objects are less common. This example is included to demonstrate the > flexibility of the CLI Forge APIs and the ability to handle complex option structures with type safety.
Option Groups
Options can be grouped together in the help output by using the `group` method. This will also affect generated documentation. The `sortOrder` parameter can be used to control the order in which groups are displayed. Lower values will be displayed first. If two groups have the same `sortOrder`, they will be sorted alphabetically. Options can be placed in a group in one of 2 ways: - An explicit call to `.group` as part of the command's builder function. - By passing a `group` property in the option definition.
Orchestrated Workflow
A parent command that executes child commands in sequence, passing data between steps. Useful for release pipelines, build systems, or deployments.
Parser Only
This example demonstrates how to use [@cli-forge/parser](https://npmjs.com/@cli-forge/parser) to interpret CLI arguments without the need for a CLI framework. For single-command CLIs, this may be enough.
Prompting for Missing Values
This example demonstrates how to use cli-forge's **prompt layer** to interactively collect missing option values. When a prompt provider is registered, required options that were not supplied on the command line are automatically prompted for. Prompt providers are pluggable — register one via `.withPromptProvider()`. This example uses a simple `readline`-based provider for portability. For a richer terminal experience, cli-forge ships a ready-made `@clack/prompts` provider via `cli-forge/prompt-providers/clack`. **Key behaviors:** - `required: true` options auto-prompt when missing (if a provider exists) - `prompt: true` forces prompting even when a value was already given - `prompt: "label"` uses a custom label instead of the description - `prompt: (args) => ...` enables conditional/dynamic prompting
Generated SDK
This example demonstrates the basic usage of cli-forge to create a simple CLI with two commands and various options, and how that CLI can be used programmatically.
Shell Completion
This example demonstrates how to enable shell completion for your CLI. The `.completion()` method registers a `completion` subcommand and a hidden `--get-completions` flag that shells use to request dynamic suggestions. You can customize completions at the command level or per-option. The `completionHelpers.files()` helper suggests filesystem paths.
Strict Mode
This example demonstrates how to use strict mode to ensure that all arguments are recognized. Strict mode throws a validation error when unmatched arguments are encountered.
Using the Test Harness
This is a simple example that demonstrates how to create a basic CLI using cli-forge
Zod Middleware
As another example of middleware, we can look at how to integrate [Zod](https://npmjs.com/zod). CLI Forge provides a middleware function under `cli-forge/middleware/zod` that can be used to validate, parse, transform, and otherwise manipulate command arguments using Zod schemas.
Bundling
Validates that cli-forge works correctly when bundled into a single file by popular bundlers, in both ESM and CJS output formats.
Composable Builders
Shows how to create reusable option builders that can be shared across commands. This pattern reduces duplication when multiple commands need the same options like verbosity, output format, or configuration paths.
Circular Config Detection
Configuration Files
This example demonstrates how to enable loading arguments from a configuration file. The `.config()` method configures the CLI to load arguments from a configuration file. The method takes a single argument, a `ConfigurationProvider`. [See the docs for `ConfigurationProvider` here](/api/parser/@cli-forge/namespaces/ConfigurationFiles/type-aliases/ConfigurationProvider). For convenience, `cli-forge` exports some built-in configuration providers. Currently, there are two built-in configuration providers: `JsonFile` and `PackageJson`. Usage of each of these is demonstrated below. Note that when using multiple configuration files, the order in which they are loaded is important. The last configuration file loaded will override any previously loaded configuration files. When both environment variables and configuration files are used, the order of precedence is as follows: - CLI Arguments - Environment Variables - Configuration Files - Default Values This is based on the idea of highest specificity. CLI Arguments are always provided directly by the user. Environment variables can change system-to-system. Configuration files are specific to the project. Default values are equal for all instances of the CLI.
Dependency Injection - Logger with Log Level
Registers a `logger` provider whose factory inspects `args.logLevel`. Any command handler can `inject('logger')` via `getCommandContext()` — no prop drilling, and the logger's level filtering automatically follows whatever the user passed on the command line. The handler lives in its own module and imports the CLI instance. That way `typeof app` is fully resolved where `getCommandContext(app)` is called, so `inject('logger')` is properly typed as `Logger` instead of `unknown`.
Environment Variable Options
Options can be populated from environment variables in two ways: **Per-option `env` key** — set `env: 'MY_VAR'` on any individual option. No `.env()` call is needed. Only the options you annotate can read from the environment; everything else stays CLI-flag-only. **Global `.env()` call** — enables environment variable support for every option at once, automatically deriving the variable name from the option name and using the CLI name as a prefix (e.g. `greet-app` → `GREET_APP`). In both cases, command-line flags take precedence over environment variables, which in turn take precedence over config files and defaults.
Framework Comparison
The same CLI implemented in multiple frameworks. A `greet` command with two subcommands (`hello` and `goodbye`), string options, and boolean flags — showing how each library approaches the same problem.
Middleware Composition
Demonstrates how to compose middleware from separate modules. Each middleware adds properties to the args object, and TypeScript correctly infers the accumulated type in the final handler.
Multi-Command CLI Structure
Organize a CLI with multiple commands in separate files using `makeComposableBuilder` and `chain`. Each command returns typed results that can be accessed programmatically via `getChildren()`.
Object Arguments with Defaults and Validation
This example demonstrates how to work with object-valued options using dot-notation or JSON strings. Object options support: - **Nested properties**: Define complex configuration structures with type-safe nested objects - **Default values**: Provide defaults at any level (entire object or individual nested properties) - **Required properties**: Mark nested properties as required (only validated when parent object is present) - **Validation**: Validate the entire object or nested properties - **Coercion**: Transform the final object after parsing - **Additional properties**: Allow arbitrary properties with a specific type ## Dot Notation Syntax Object properties are accessed using dot notation on the command line: - `--config.server.host localhost` sets `config.server.host` to `"localhost"` - `--config.server.port 8080` sets `config.server.port` to `8080` - Multiple properties can be set independently ## JSON String Input Alternatively, you can pass the entire object as a JSON string: - `--config '{"server": {"host": "example.com", "port": 8080}}'` - JSON input can be mixed with dot notation - When mixed, the order matters (later values override earlier ones) ## Default Value Behavior - If no `config` properties are provided, the entire default object is used - If some properties are provided, defaults are applied for missing nested properties - Default values work at any nesting level ## Required Properties Required properties within objects are conditionally required: - If the parent object is not present at all, nested required properties are not checked - If the parent object is present (any property is set), nested required properties are validated For example, if `database.host` is marked as required: - `--config.server.port 8080` is valid (database config not present) - `--config.database.port 5432` is invalid (database config present but `host` is missing)
OneOf Option
The `oneOf` option type lets a single flag accept values of different types. This is common in CLI tools where a flag doubles as a boolean toggle and a typed selector — for example `--color` / `--no-color` / `--color=always`. When boolean is one of the value types, bare flags (`--flag`), negation (`--no-flag`), and the literals `true` / `false` are claimed exclusively by the boolean parser. All other values are tried against the remaining types in array order.
Dependency Injection with Providers
Demonstrates using `.provide()` and `getCommandContext()` to register and inject services without threading them through function calls. The logger provider is registered once on the CLI with a factory that receives parsed args, then any command handler can access it via `getCommandContext(app).inject('logger')` — no prop drilling needed.
Zod Schema Validation
Demonstrates how to use Zod schemas for argument validation and transformation. Schemas are defined in separate files, making them reusable across commands and testable in isolation.