Arguments & flags
Declare typed positional arguments and flags, give them defaults, make them optional or variadic, and validate their values. Read more
The effect/unstable/cli modules let you build command-line applications the
same way you build the rest of an Effect program: declaratively, with typed
inputs and effectful handlers. You describe a command’s positional arguments
and named flags as Param values, attach a handler written with
Effect.fn, and Effect takes care of parsing process.argv, validating inputs,
generating help text, and reporting friendly errors.
Because handlers are ordinary Effects, everything you already know carries over: you can access services, acquire scoped resources, fail with typed errors, and provide Layers — all from inside a command.
import { NodeRuntime, NodeServices } from "@effect/platform-node"import { Console, Effect } from "effect"import { Argument, Command, Flag } from "effect/unstable/cli"
// A command pairs a typed config (its arguments and flags) with a handler.const greet = Command.make( "greet", { // A required positional argument: `greet Alice` name: Argument.string("name").pipe( Argument.withDescription("Who to greet") ), // A flag with a default, so `--count` is optional: `greet Alice --count 3` count: Flag.integer("count").pipe( Flag.withAlias("c"), Flag.withDescription("How many times to greet"), Flag.withDefault(1) ) }, // The handler receives the parsed, fully-typed config. Writing it with // `Effect.fn` means it is a normal Effect — it can use services and fail. Effect.fn(function*({ name, count }) { for (let i = 0; i < count; i++) { yield* Console.log(`Hello, ${name}!`) } }))
// `Command.run` turns a command into an Effect that reads argv, parses it, and// dispatches to the handler. `--help` and `--version` are wired up for free.greet.pipe( Command.run({ version: "1.0.0" }), // Provide the services for your target platform (file system, stdin/stdout…). Effect.provide(NodeServices.layer), NodeRuntime.runMain)Run it with node app.ts Alice --count 2 and it prints Hello, Alice! twice.
Run node app.ts --help and Effect prints generated usage and option docs.
Flag — named options like --count 3 or -c 3. Flags are optional by
default once you give them a default; otherwise they are required (boolean
flags are an exception: a bare boolean flag defaults to false).Argument — positional operands like the Alice above. Order matters and
they are matched left to right.Command — bundles a config of flags and arguments with a handler, and
can compose child commands into a tree of subcommands.Prompt — interactive input read from the terminal (text, password,
confirm, select, multi-select, and more) for when a value was not supplied on
the command line.GlobalFlag — the built-in --help, --version, and --completions
flags, plus a way to define your own global flags and settings (like
--log-level) that apply across the whole command tree.Completions — generates shell completion scripts (bash, zsh, fish) from
your command tree, surfaced through the --completions global flag.CliOutput — the formatter that renders help text and error messages; swap
it out to customize coloring and layout.Both Flag and Argument are built on the shared Param type, so the same
combinators (withDefault, optional, map, withSchema, …) work on either.
A command moves through three steps, each backed by a Command API:
Command.make pairs a config (its flags and arguments) with a handler.
Write the handler with Effect.fn so it stays a normal Effect — it can use
services, acquire resources, and fail with typed errors. You can also attach
the handler later with Command.withHandler.Command.run turns the command into an Effect. It reads the process
argument vector from the Stdio service, lexes and parses it, applies global
flags like --help/--version, and dispatches to the matching handler.Command.runWith does the same but takes an explicit ReadonlyArray<string>
of arguments instead of reading Stdio. This is what you use in tests to
exercise a command with specific argv without a real terminal.import { Effect } from "effect"import { Command, Flag } from "effect/unstable/cli"
const greet = Command.make("greet", { name: Flag.string("name")}, ({ name }) => Effect.log(`Hello, ${name}!`))
// In a test: feed argv directly, no Stdio needed.const run = Command.runWith(greet, { version: "1.0.0" })// `program` is an Effect; running it logs "Hello, Alice!"const program = run(["--name", "Alice"])Arguments & flags
Declare typed positional arguments and flags, give them defaults, make them optional or variadic, and validate their values. Read more
Subcommands
Compose child commands into a single executable, share parent flags, and read parent input from inside a subcommand. Read more
Prompts
Ask for input interactively — text, password, confirm, select, multi-select, and more — when a value was not passed on the command line. Read more
Global flags & completions
The built-in --help, --version, and --completions flags, custom global
flags and settings, and generating shell completion scripts.
Read more
Output & errors
How help text and parse errors are formatted, and how to customize the output formatter. Read more
Command.run produces a plain Effect. To execute it you provide the platform
services it needs (Stdio, FileSystem, …) and hand it to a runtime. On
Node.js that is NodeServices.layer plus NodeRuntime.runMain, as shown above.
See Platform for other runtimes and what each layer provides.