Command
Main building block for defining and running Effect-based command-line applications.
A Command combines a name, typed flags and positional arguments, optional
subcommands, help metadata, and an effectful handler. The module includes
builders for command trees and the runners that parse command-line input,
handle built-in help and version behavior, render help through CliOutput,
and execute the selected handler.
Combinators
Adds a custom annotation to a command.
When to use
Use to attach one command-scoped metadata value under a Context.Key,
especially for consumers such as custom help formatters.
Details
Annotations are stored on the command's annotation context and flow into generated help document annotations.
Gotchas
Adding the same Context.Key again replaces the earlier value.
See
- annotateMerge for merging an existing annotation context
Signature
declare const annotate: { <I, S>(service: Key<I, S>, value: NoInfer<S>): <Name extends string, Input, E, R, ContextInput>(self: Command<Name, Input, ContextInput, E, R>) => Command<Name, Input, ContextInput, E, R>; <Name extends string, Input, E, R, ContextInput, I, S>(self: Command<Name, Input, ContextInput, E, R>, service: Key<I, S>, value: NoInfer<S>): Command<Name, Input, ContextInput, E, R>;}annotateMerge
Merges a Context of annotations into a command.
When to use
Use when you need to attach an already-built Context.Context of command
annotations.
Details
Merged annotations are stored on the command and exposed through generated help document annotations.
Gotchas
If both contexts contain the same Context.Key, the incoming annotations
context wins.
See
- annotate for adding a single annotation without constructing a
Context
Signature
declare const annotateMerge: { <I>(annotations: Context<I>): <Name extends string, Input, E, R, ContextInput>(self: Command<Name, Input, ContextInput, E, R>) => Command<Name, Input, ContextInput, E, R>; <Name extends string, Input, E, R, ContextInput, I>(self: Command<Name, Input, ContextInput, E, R>, annotations: Context<I>): Command<Name, Input, ContextInput, E, R>;}Omits a subcommand from parent help output, shell completions, and "did you mean?" suggestions while keeping it fully invocable by exact name.
When to use
Use when experimental or internal subcommands should be accepted but not advertised on the public CLI surface.
Signature
declare function unlisted<Name extends string, Input, E, R, ContextInput>(self: Command<Name, Input, ContextInput, E, R>): Command<Name, Input, ContextInput, E, R>Example
(Unlisting a subcommand)
import { Command } from "effect/unstable/cli"
// `experimental` still runs when invoked as `mycli experimental`,// but it does not appear under SUBCOMMANDS in `mycli --help`.const experimental = Command.make("experimental").pipe( Command.unlisted)
const root = Command.make("mycli").pipe( Command.withSubcommands([experimental]))
root.subcommands[0].commands[0].unlisted // => trueSets an alias for a command.
Details
Aliases are accepted as alternate subcommand names during parsing and are
shown in help output as name, alias.
Signature
declare const withAlias: { (alias: string): <Name extends string, Input, E, R, ContextInput>(self: Command<Name, Input, ContextInput, E, R>) => Command<Name, Input, ContextInput, E, R>; <Name extends string, Input, E, R, ContextInput>(self: Command<Name, Input, ContextInput, E, R>, alias: string): Command<Name, Input, ContextInput, E, R>;}withDescription
Sets the description for a command.
Details
Descriptions provide users with information about what the command does when they view help documentation.
Signature
declare const withDescription: { (description: string): <Name extends string, Input, E, R, ContextInput>(self: Command<Name, Input, ContextInput, E, R>) => Command<Name, Input, ContextInput, E, R>; <Name extends string, Input, E, R, ContextInput>(self: Command<Name, Input, ContextInput, E, R>, description: string): Command<Name, Input, ContextInput, E, R>;}Example
(Setting descriptions)
import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect"import { Command, Flag } from "effect/unstable/cli"import { ChildProcessSpawner } from "effect/unstable/process"
const CliTestLayer = Layer.mergeAll( FileSystem.layerNoop({}), Path.layer, Stdio.layerTest({}), Layer.succeed(Terminal.Terminal, Terminal.make({ columns: Effect.succeed(80), rows: Effect.succeed(24), readInput: Effect.die("unused"), readLine: Effect.die("unused"), display: () => Effect.void })), Layer.succeed( ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make(() => Effect.die("unused")) ))
const output: Array<string> = []const deploy = Command.make("deploy", { environment: Flag.string("env")}, (config) => Effect.gen(function*() { yield* Effect.sync(() => output.push(`Deploying to ${config.environment}`)) })).pipe( Command.withDescription("Deploy the application to a specified environment") )
await Effect.runPromise( Command.runWith(deploy, { version: "1.0.0" })(["--env", "staging"]).pipe(Effect.provide(CliTestLayer)))output // => ["Deploying to staging"]withExamples
Sets usage examples for a command.
Details
Examples are exposed in structured HelpDoc data and rendered by the
default formatter in an EXAMPLES section.
Signature
declare const withExamples: { (examples: readonly Array<Example>): <Name extends string, Input, E, R, ContextInput>(self: Command<Name, Input, ContextInput, E, R>) => Command<Name, Input, ContextInput, E, R>; <Name extends string, Input, E, R, ContextInput>(self: Command<Name, Input, ContextInput, E, R>, examples: readonly Array<Example>): Command<Name, Input, ContextInput, E, R>;}Example
(Adding usage examples)
import { Command } from "effect/unstable/cli"
const login = Command.make("login").pipe( Command.withExamples([ { command: "myapp login", description: "Log in with browser OAuth" }, { command: "myapp login --token sbp_abc123", description: "Log in with a token" } ]))
login.examples.map((example) => example.command) // => ["myapp login", "myapp login --token sbp_abc123"]withGlobalFlags
Adds global flags to a command scope.
Details
Declared global flags apply to the command and all of its descendants.
Signature
declare const withGlobalFlags: { <GlobalFlags extends readonly Array<GlobalFlag<any>>>(globalFlags: GlobalFlags): <Name extends string, Input, E, R, ContextInput>(self: Command<Name, Input, ContextInput, E, R>) => Command<Name, Input, ContextInput, E, Exclude<R, ExtractGlobalFlagContext<GlobalFlags>>>; <Name extends string, Input, E, R, ContextInput, GlobalFlags extends readonly Array<GlobalFlag<any>>>(self: Command<Name, Input, ContextInput, E, R>, globalFlags: GlobalFlags): Command<Name, Input, ContextInput, E, Exclude<R, ExtractGlobalFlagContext<GlobalFlags>>>;}withHandler
Adds or replaces the handler for a command.
Signature
declare const withHandler: { <A, R, E>(handler: (value: A) => Effect<void, E, R>): <Name extends string, XR, XE, ContextInput>(self: Command<Name, A, ContextInput, XE, XR>) => Command<Name, A, ContextInput, E, Exclude<R, "effect/unstable/cli/GlobalFlag/log-level">>; <Name extends string, A, XR, XE, R, E, ContextInput>(self: Command<Name, A, ContextInput, XE, XR>, handler: (value: A) => Effect<void, E, R>): Command<Name, A, ContextInput, E, Exclude<R, "effect/unstable/cli/GlobalFlag/log-level">>;}Example
(Adding command handlers)
import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect"import { Command, Flag } from "effect/unstable/cli"import { ChildProcessSpawner } from "effect/unstable/process"
const CliTestLayer = Layer.mergeAll( FileSystem.layerNoop({}), Path.layer, Stdio.layerTest({}), Layer.succeed(Terminal.Terminal, Terminal.make({ columns: Effect.succeed(80), rows: Effect.succeed(24), readInput: Effect.die("unused"), readLine: Effect.die("unused"), display: () => Effect.void })), Layer.succeed( ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make(() => Effect.die("unused")) ))
// Command without initial handlerconst greet = Command.make("greet", { name: Flag.string("name")})
// Add handler laterconst output: Array<string> = []const greetWithHandler = greet.pipe( Command.withHandler((config: { readonly name: string }) => Effect.sync(() => output.push(`Hello, ${config.name}!`)).pipe(Effect.asVoid) ))
await Effect.runPromise( Command.runWith(greetWithHandler, { version: "1.0.0" })(["--name", "Alice"]).pipe( Effect.provide(CliTestLayer) ))output // => ["Hello, Alice!"]withShortDescription
Sets a short description for a command.
Details
Short descriptions are used when listing subcommands in help output and
shell completions. If no short description is provided, the full
description is used as a fallback.
Signature
declare const withShortDescription: { (shortDescription: string): <Name extends string, Input, E, R, ContextInput>(self: Command<Name, Input, ContextInput, E, R>) => Command<Name, Input, ContextInput, E, R>; <Name extends string, Input, E, R, ContextInput>(self: Command<Name, Input, ContextInput, E, R>, shortDescription: string): Command<Name, Input, ContextInput, E, R>;}withSubcommands
Adds subcommands to a command, creating a hierarchical command structure.
Details
Subcommands can access their parent's parsed configuration by yielding the parent command within their handler. This enables shared parent flags that affect all subcommands.
Signature
declare const withSubcommands: { <Subcommands extends readonly Array<SubcommandEntry>>(subcommands: Subcommands): <Name extends string, Input, E, R, ContextInput>(self: Command<Name, Input, ContextInput, E, R>) => Command<Name, Simplify<Input | ContextInput>, ContextInput, E | Error<ExtractSubcommand<Subcommands[number]>>, R | Exclude<Services<ExtractSubcommand<Subcommands[number]>>, CommandContext<Name>>>; <Name extends string, Input, E, R, ContextInput, Subcommands extends readonly Array<SubcommandEntry>>(self: Command<Name, Input, ContextInput, E, R>, subcommands: Subcommands): Command<Name, Simplify<Input | ContextInput>, ContextInput, E | Error<ExtractSubcommand<Subcommands[number]>>, R | Exclude<Services<ExtractSubcommand<Subcommands[number]>>, CommandContext<Name>>>;}Example
(Adding subcommands)
import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect"import { Command, Flag } from "effect/unstable/cli"import { ChildProcessSpawner } from "effect/unstable/process"
const CliTestLayer = Layer.mergeAll( FileSystem.layerNoop({}), Path.layer, Stdio.layerTest({}), Layer.succeed(Terminal.Terminal, Terminal.make({ columns: Effect.succeed(80), rows: Effect.succeed(24), readInput: Effect.die("unused"), readLine: Effect.die("unused"), display: () => Effect.void })), Layer.succeed( ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make(() => Effect.die("unused")) ))
// Parent command with shared flagsconst git = Command.make("git").pipe( Command.withSharedFlags({ verbose: Flag.boolean("verbose").pipe(Flag.withDefault(false)) }))
// Subcommand that accesses parent configconst output: Array<string> = []const clone = Command.make("clone", { repository: Flag.string("repo")}, (config) => Effect.gen(function*() { const parent = yield* git // Access parent's parsed config if (parent.verbose) { yield* Effect.sync(() => output.push("Verbose mode enabled")) } yield* Effect.sync(() => output.push(`Cloning ${config.repository}`)) }))
const app = git.pipe(Command.withSubcommands([clone]))
await Effect.runPromise( Command.runWith(app, { version: "1.0.0" })([ "--verbose", "clone", "--repo", "github.com/foo/bar" ]).pipe(Effect.provide(CliTestLayer)))output // => ["Verbose mode enabled", "Cloning github.com/foo/bar"]Constructors
Creates a Command from a name, an optional configuration, and an optional
handler.
Details
Use withDescription and related metadata combinators to add help text. The
overloads support simple commands, configured commands, and commands with
effectful handlers.
Signature
declare const make: { <Name extends string>(name: Name): Command<Name, {}, {}, never, never>; <Name extends string, Config extends Config>(name: Name, config: Config): Command<Name, Simplify<{ [Key in string | number | symbol]: InferValue<Config[Key]> }>, {}, never, never>; <Name extends string, Config extends Config, R, E>(name: Name, config: Config, handler: (config: Simplify<{ [Key in string | number | symbol]: InferValue<Config[Key]> }>) => Effect<void, E, R>): Command<Name, Simplify<{ [Key in string | number | symbol]: InferValue<Config[Key]> }>, {}, E, Exclude<R, "effect/unstable/cli/GlobalFlag/log-level">>;}Example
(Creating commands)
import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect"import { Argument, Command, Flag } from "effect/unstable/cli"import { ChildProcessSpawner } from "effect/unstable/process"
const CliTestLayer = Layer.mergeAll( FileSystem.layerNoop({}), Path.layer, Stdio.layerTest({}), Layer.succeed(Terminal.Terminal, Terminal.make({ columns: Effect.succeed(80), rows: Effect.succeed(24), readInput: Effect.die("unused"), readLine: Effect.die("unused"), display: () => Effect.void })), Layer.succeed( ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make(() => Effect.die("unused")) ))
// Simple command with no configurationconst version = Command.make("version")
// Command with simple flagsconst greet = Command.make("greet", { name: Flag.string("name"), count: Flag.integer("count").pipe(Flag.withDefault(1))})
// Command with nested configurationconst deploy = Command.make("deploy", { environment: Flag.string("env").pipe( Flag.withDescription("Target environment") ), server: { host: Flag.string("host").pipe(Flag.withDefault("localhost")), port: Flag.integer("port").pipe(Flag.withDefault(3000)) }, files: Argument.string("files").pipe(Argument.variadic), force: Flag.boolean("force").pipe( Flag.withDescription("Force deployment"), Flag.withDefault(false) )})
// Command with handlerconst output: Array<string> = []const deployWithHandler = Command.make("deploy", { environment: Flag.string("env"), force: Flag.boolean("force").pipe(Flag.withDefault(false))}, (config) => Effect.gen(function*() { yield* Effect.sync(() => output.push(`Starting deployment to ${config.environment}`))
if (!config.force && config.environment === "production") { return yield* Effect.fail("Production deployments require --force flag") }
yield* Effect.sync(() => output.push("Deployment completed successfully")) }))
await Effect.runPromise( Command.runWith(deployWithHandler, { version: "1.0.0" })([ "--env", "staging", "--force" ]).pipe(Effect.provide(CliTestLayer)))output // => ["Starting deployment to staging", "Deployment completed successfully"]Guards
Models
Represents a CLI command with its configuration, handler, and metadata.
Details
Commands are the core building blocks of CLI applications. They define:
- The command name and description
- Configuration including flags and arguments
- Handler function for execution
- Optional subcommands for hierarchical structures
Signature
interface Command<in out Name extends string, in Input, out ContextInput = {}, out E = never, out R = never> extends Effect<ContextInput, never, CommandContext<Name>> { readonly "~effect/cli/Command": Variance<Input, E, R>; readonly alias: string | undefined; readonly annotations: Context<never>; readonly description: string | undefined; readonly examples: readonly Array<Example>; readonly name: Name; readonly shortDescription: string | undefined; readonly subcommands: readonly Array<{ readonly commands: readonly [Any, Any]; readonly group: string | undefined; }>; readonly unlisted: boolean;}Example
(Defining CLI commands)
import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect"import { Argument, Command, Flag } from "effect/unstable/cli"import { ChildProcessSpawner } from "effect/unstable/process"
const CliTestLayer = Layer.mergeAll( FileSystem.layerNoop({}), Path.layer, Stdio.layerTest({}), Layer.succeed(Terminal.Terminal, Terminal.make({ columns: Effect.succeed(80), rows: Effect.succeed(24), readInput: Effect.die("unused"), readLine: Effect.die("unused"), display: () => Effect.void })), Layer.succeed( ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make(() => Effect.die("unused")) ))
// Simple command with no configurationconst version: Command.Command<"version", {}, {}, never, never> = Command.make( "version")
// Command with flags and argumentsconst deploy: Command.Command< "deploy", { readonly env: string readonly force: boolean readonly files: ReadonlyArray<string> }, {}, never, never> = Command.make("deploy", { env: Flag.string("env"), force: Flag.boolean("force").pipe(Flag.withDefault(false)), files: Argument.string("files").pipe(Argument.variadic())})
// Command with handlerconst output: Array<string> = []const greet = Command.make("greet", { name: Flag.string("name")}, (config) => Effect.sync(() => output.push(`Hello, ${config.name}!`)).pipe(Effect.asVoid))
await Effect.runPromise( Command.runWith(greet, { version: "1.0.0" })(["--name", "Alice"]).pipe(Effect.provide(CliTestLayer)))output // => ["Hello, Alice!"]CommandContext interface
Service context for a specific command, enabling subcommands to access their parent's parsed configuration.
Details
When a subcommand handler needs access to flags or arguments from a parent command, it can yield the parent command directly to retrieve its config. This is powered by Effect's service system - each command automatically creates a service that provides its parsed input to child commands.
Signature
interface CommandContext<Name extends string> { readonly _: typeof _; readonly name: Name;}Example
(Accessing parent command context)
import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect"import { Command, Flag } from "effect/unstable/cli"import { ChildProcessSpawner } from "effect/unstable/process"
const CliTestLayer = Layer.mergeAll( FileSystem.layerNoop({}), Path.layer, Stdio.layerTest({}), Layer.succeed(Terminal.Terminal, Terminal.make({ columns: Effect.succeed(80), rows: Effect.succeed(24), readInput: Effect.die("unused"), readLine: Effect.die("unused"), display: () => Effect.void })), Layer.succeed( ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make(() => Effect.die("unused")) ))
const parent = Command.make("app").pipe( Command.withSharedFlags({ verbose: Flag.boolean("verbose").pipe(Flag.withDefault(false)), config: Flag.string("config") }))
const output: Array<string> = []const child = Command.make("deploy", { target: Flag.string("target")}, (config) => Effect.gen(function*() { // Access parent's config by yielding the parent command const parentConfig = yield* parent yield* Effect.sync(() => output.push(`Verbose: ${parentConfig.verbose}`)) yield* Effect.sync(() => output.push(`Config: ${parentConfig.config}`)) yield* Effect.sync(() => output.push(`Target: ${config.target}`)) }))
const app = parent.pipe(Command.withSubcommands([child]))
await Effect.runPromise( Command.runWith(app, { version: "1.0.0" })([ "--verbose", "--config", "prod.json", "deploy", "--target", "staging" ]).pipe(Effect.provide(CliTestLayer)))output // => ["Verbose: true", "Config: prod.json", "Target: staging"]ParsedTokens interface
Represents the parsed tokens from command-line input before validation.
Signature
interface ParsedTokens { readonly arguments: readonly Array<string>; readonly errors?: readonly Array<UnrecognizedOption | DuplicateOption | MissingOption | MissingArgument | UnexpectedArgument | InvalidValue | UnknownSubcommand | UserError>; readonly flags: Record<string, ReadonlyArray<string>>; readonly subcommand: Option<{ readonly name: string; readonly parsedInput: ParsedTokens; }>;}Other
Providing Services
Provides the handler of a command with the services produced by a layer that optionally depends on the command-line input to be created.
Signature
declare const provide: { <Input, LR, LE, LA>(layer: Layer<LA, LE, LR> | (input: Input) => Layer<LA, LE, LR>, options?: { readonly local?: boolean; }): <Name extends string, E, R, ContextInput>(self: Command<Name, Input, ContextInput, E, R>) => Command<Name, Input, ContextInput, LE | E, LR | Exclude<R, LA>>; <Name extends string, Input, E, R, ContextInput, LA, LE, LR>(self: Command<Name, Input, ContextInput, E, R>, layer: Layer<LA, LE, LR> | (input: Input) => Layer<LA, LE, LR>, options?: { readonly local?: boolean; }): Command<Name, Input, ContextInput, E | LE, LR | Exclude<R, LA>>;}Example
(Providing command services)
import { Effect, FileSystem, Layer, Path, PlatformError, Stdio, Terminal } from "effect"import { Command, Flag } from "effect/unstable/cli"import { ChildProcessSpawner } from "effect/unstable/process"
const CliTestLayer = Layer.mergeAll( FileSystem.layerNoop({}), Path.layer, Stdio.layerTest({}), Layer.succeed(Terminal.Terminal, Terminal.make({ columns: Effect.succeed(80), rows: Effect.succeed(24), readInput: Effect.die("unused"), readLine: Effect.die("unused"), display: () => Effect.void })), Layer.succeed( ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make(() => Effect.die("unused")) ))
const output: Array<string> = []const deploy = Command.make("deploy", { env: Flag.string("env")}, (config) => Effect.gen(function*() { const fs = yield* FileSystem.FileSystem yield* Effect.sync(() => output.push(`Using file system for ${config.env}`)) })).pipe( // Provide FileSystem based on the --env flag Command.provide((config) => config.env === "local" ? FileSystem.layerNoop({}) : FileSystem.layerNoop({ access: () => Effect.fail( PlatformError.badArgument({ module: "FileSystem", method: "access" }) ) }) ) )
await Effect.runPromise( Command.runWith(deploy, { version: "1.0.0" })(["--env", "local"]).pipe(Effect.provide(CliTestLayer)))output // => ["Using file system for local"]provideEffect
Provides the handler of a command with the service produced by an effect that optionally depends on the command-line input to be created.
When to use
Use to acquire a service effectfully for each command run, optionally using parsed command input.
See
- provideSync for synchronous service acquisition
- provide for providing an already-available service
- provideEffectDiscard for running an effect before the handler without providing a service
Signature
declare const provideEffect: { <I, S, Input, R2, E2>(service: Key<I, S>, effect: Effect<S, E2, R2> | (input: Input) => Effect<S, E2, R2>): <Name extends string, E, R, ContextInput>(self: Command<Name, Input, ContextInput, E, R>) => Command<Name, Input, ContextInput, E2 | E, R2 | Exclude<R, I>>; <Name extends string, Input, E, R, ContextInput, I, S, R2, E2>(self: Command<Name, Input, ContextInput, E, R>, service: Key<I, S>, effect: Effect<S, E2, R2> | (input: Input) => Effect<S, E2, R2>): Command<Name, Input, ContextInput, E | E2, R2 | Exclude<R, I>>;}provideEffectDiscard
Allows for execution of an effect, which optionally depends on command-line input to be created, prior to executing the handler of a command.
Signature
declare const provideEffectDiscard: { <_, Input, E2, R2>(effect: Effect<_, E2, R2> | (input: Input) => Effect<_, E2, R2>): <Name extends string, E, R, ContextInput>(self: Command<Name, Input, ContextInput, E, R>) => Command<Name, Input, ContextInput, E2 | E, R2 | R>; <Name extends string, Input, E, R, ContextInput, _, E2, R2>(self: Command<Name, Input, ContextInput, E, R>, effect: Effect<_, E2, R2> | (input: Input) => Effect<_, E2, R2>): Command<Name, Input, ContextInput, E | E2, R | R2>;}provideSync
Provides the handler of a command with the implementation of a service that optionally depends on the command-line input to be constructed.
When to use
Use when a command handler needs a pure service implementation, optionally derived from the parsed command input.
Signature
declare const provideSync: { <I, S, Input>(service: Key<I, S>, implementation: S | (input: Input) => S): <Name extends string, E, R, ContextInput>(self: Command<Name, Input, ContextInput, E, R>) => Command<Name, Input, ContextInput, E, Exclude<R, I>>; <Name extends string, Input, E, R, ContextInput, I, S>(self: Command<Name, Input, ContextInput, E, R>, service: Key<I, S>, implementation: S | (input: Input) => S): Command<Name, Input, ContextInput, E, Exclude<R, I>>;}Running
Runs a command using the arguments supplied by the Stdio service.
When to use
Use when command-line arguments should come from Stdio at the application
entry point.
Help documents are always rendered. By default, parse error details and
CliError.UserError failures are also rendered with the installed
CliOutput.Formatter before the error is rethrown. Set renderErrors to
false when the host application owns error rendering.
See
- runWith for running a command with an explicit argument array
Signature
declare const run: { (config: { readonly renderErrors?: boolean; readonly version: string; }): <Name extends string, Input, E, R, ContextInput>(command: Command<Name, Input, ContextInput, E, R>) => Effect<void, CliError | E, Environment | R>; <Name extends string, Input, E, R, ContextInput>(command: Command<Name, Input, ContextInput, E, R>, config: { readonly renderErrors?: boolean; readonly version: string; }): Effect<void, CliError | E, Environment | R>;}Example
(Running commands with standard input)
import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect"import { Command, Flag } from "effect/unstable/cli"import { ChildProcessSpawner } from "effect/unstable/process"
const CliTestLayer = Layer.mergeAll( FileSystem.layerNoop({}), Path.layer, Stdio.layerTest({ args: Effect.succeed(["--name", "Alice"]) }), Layer.succeed(Terminal.Terminal, Terminal.make({ columns: Effect.succeed(80), rows: Effect.succeed(24), readInput: Effect.die("unused"), readLine: Effect.die("unused"), display: () => Effect.void })), Layer.succeed( ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make(() => Effect.die("unused")) ))
const output: Array<string> = []const greetCommand = Command.make("greet", { name: Flag.string("name")}, (config) => Effect.gen(function*() { yield* Effect.sync(() => output.push(`Hello, ${config.name}!`)) }))
// Automatically gets args from the Stdio serviceconst program = Command.run(greetCommand, { version: "1.0.0"})
await Effect.runPromise(program.pipe(Effect.provide(CliTestLayer)))output // => ["Hello, Alice!"]Runs a command with explicitly provided arguments instead of using arguments from Stdio.
When to use
Use when you need to test CLI applications or programmatically execute commands with specific arguments.
Help documents are always rendered. By default, parse error details and
CliError.UserError failures are also rendered with the installed
CliOutput.Formatter before the error is rethrown. Set renderErrors to
false when the host application owns error rendering.
Signature
declare function runWith<Name extends string, Input, E, R, ContextInput>(command: Command<Name, Input, ContextInput, E, R>, config: { readonly renderErrors?: boolean; readonly version: string;}): (input: readonly Array<string>) => Effect<void, CliError | Exclude<E, QuitError>, Environment | R>Example
(Running commands with explicit arguments)
import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect"import { Command, Flag } from "effect/unstable/cli"import { ChildProcessSpawner } from "effect/unstable/process"
const CliTestLayer = Layer.mergeAll( FileSystem.layerNoop({}), Path.layer, Stdio.layerTest({}), Layer.succeed(Terminal.Terminal, Terminal.make({ columns: Effect.succeed(80), rows: Effect.succeed(24), readInput: Effect.die("unused"), readLine: Effect.die("unused"), display: () => Effect.void })), Layer.succeed( ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make(() => Effect.die("unused")) ))
const output: Array<string> = []const greet = Command.make("greet", { name: Flag.string("name"), count: Flag.integer("count").pipe(Flag.withDefault(1))}, (config) => Effect.gen(function*() { for (let i = 0; i < config.count; i++) { yield* Effect.sync(() => output.push(`Hello, ${config.name}!`)) } }))
// Test with specific argumentsconst testProgram = Effect.gen(function*() { const runCommand = Command.runWith(greet, { version: "1.0.0" })
yield* runCommand(["--name", "Alice", "--count", "2"])})
await Effect.runPromise(testProgram.pipe(Effect.provide(CliTestLayer)))output // => ["Hello, Alice!", "Hello, Alice!"]Interactively constructs command-line arguments for a command.
Details
The returned arguments include the command name and can be inspected, modified, or passed to another command runner by the caller.
Signature
declare function wizard<Name extends string, Input, E, R, ContextInput>(command: Command<Name, Input, ContextInput, E, R>, options?: { readonly prefix?: readonly Array<string>;}): Effect<Array<string>, QuitError | CliError, Environment>Example
(Constructing command arguments)
import { Console, Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect"import { Command } from "effect/unstable/cli"import { ChildProcessSpawner } from "effect/unstable/process"
const CliTestLayer = Layer.mergeAll( FileSystem.layerNoop({}), Path.layer, Stdio.layerTest({}), Layer.succeed(Terminal.Terminal, Terminal.make({ columns: Effect.succeed(80), rows: Effect.succeed(24), readInput: Effect.die("unused"), readLine: Effect.die("unused"), display: () => Effect.void })), Layer.succeed( ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make(() => Effect.die("unused")) ))
const command = Command.make("app")const silentConsole: Console.Console = Object.assign(Object.create(console), { log: () => {}})
const program = Command.wizard(command).pipe( Effect.provideService(Console.Console, silentConsole), Effect.provide(CliTestLayer))
await Effect.runPromise(program) // => ["app"]Utility Types
Environment type
Services required by CLI parsing and execution.
Details
This includes file-system and path services for arguments, terminal and stdio services for running commands, and child-process spawning for process-related CLI features.
Signature
type Environment = FileSystem.FileSystem | Path.Path | Terminal.Terminal | ChildProcessSpawner | Stdio.StdioA utility type to extract the error type from a Command.
Signature
type Error<C> = C extends Command<infer _Name, infer _Input, infer _ContextInput, infer _Error, infer _Requirements> ? _Error : neverA utility type to extract the required services type from a Command.
Signature
type Services<C> = C extends Command<infer _Name, infer _Input, infer _ContextInput, infer _Error, infer _Requirements> ? _Requirements : never