Documentation Index
Fetch the complete documentation index at: https://mintlify.com/effect-TS/effect-smol/llms.txt
Use this file to discover all available pages before exploring further.
The CLI module is marked as unstable, meaning its APIs may change in minor version releases. Use caution when upgrading Effect versions.
Overview
The effect/unstable/cli module provides powerful tools for building type-safe command-line applications with Effect. It offers composable primitives for defining commands, flags, arguments, and prompts with built-in parsing, validation, and help generation.
Installation
Key Modules
Command
The core building block for CLI applications. Commands define the structure, configuration, and behavior of CLI operations.
import { Console } from "effect"
import { Command, Flag, Argument } from "effect/unstable/cli"
// Simple command
const version = Command.make("version")
// Command with flags and arguments
const deploy = Command.make("deploy", {
env: Flag.string("env"),
force: Flag.boolean("force"),
files: Argument.string("files").pipe(Argument.variadic())
})
// Command with handler
const greet = Command.make(
"greet",
{
name: Flag.string("name")
},
(config) => Console.log(`Hello, ${config.name}!`)
)
Key Functions:
Command.make(name, config?, handler?) - Create a new command
Command.withSubcommands(parent, subcommands) - Add subcommands for hierarchical CLIs
Command.run(command, args) - Execute a command with arguments
Command.provide(command, layer) - Provide services to command handler
Flag
Define command-line flags (options) with various types and behaviors.
import { Flag } from "effect/unstable/cli"
// Boolean flag: --verbose or -v
const verbose = Flag.boolean("verbose").pipe(
Flag.withAlias("v")
)
// String flag with default: --config=path/to/file
const config = Flag.string("config").pipe(
Flag.withDefault("./config.json")
)
// Number flag: --port=3000
const port = Flag.number("port")
// Optional flag
const optional = Flag.string("optional").pipe(
Flag.optional
)
// Flag with validation
const validated = Flag.number("threads").pipe(
Flag.withDefault(4),
Flag.mapEffect((n) =>
n > 0 && n <= 16
? Effect.succeed(n)
: Effect.fail("Threads must be between 1 and 16")
)
)
Flag Types:
Flag.boolean(name) - Boolean flag
Flag.string(name) - String flag
Flag.number(name) - Numeric flag
Flag.integer(name) - Integer flag
Flag.date(name) - Date flag
Flag.choice(name, choices) - Enum flag
Flag Modifiers:
Flag.withAlias(alias) - Add short alias (e.g., -v for —verbose)
Flag.withDefault(value) - Provide default value
Flag.optional - Make flag optional
Flag.repeated - Allow multiple occurrences
Flag.withDescription(desc) - Add help description
Argument
Define positional command-line arguments.
import { Argument } from "effect/unstable/cli"
// Single string argument
const filename = Argument.string("filename")
// Multiple arguments (variadic)
const files = Argument.string("files").pipe(
Argument.variadic()
)
// Optional argument
const output = Argument.string("output").pipe(
Argument.optional
)
// Argument with validation
const port = Argument.integer("port").pipe(
Argument.mapEffect((p) =>
p >= 1024 && p <= 65535
? Effect.succeed(p)
: Effect.fail("Port must be between 1024 and 65535")
)
)
Argument Types:
Argument.string(name) - String argument
Argument.number(name) - Numeric argument
Argument.integer(name) - Integer argument
Argument.boolean(name) - Boolean argument
Argument.date(name) - Date argument
Argument Modifiers:
Argument.variadic() - Accept multiple values
Argument.optional - Make argument optional
Argument.withDefault(value) - Provide default value
Argument.withDescription(desc) - Add help description
GlobalFlag
Define flags that apply to all commands in a CLI application.
import { Command, GlobalFlag } from "effect/unstable/cli"
// Global verbose flag available to all commands
const verbose = GlobalFlag.boolean("verbose").pipe(
GlobalFlag.withAlias("v"),
GlobalFlag.withDescription("Enable verbose output")
)
const cli = Command.make("mycli")
.pipe(Command.withGlobalFlags({ verbose }))
Prompt
Interactive user prompts for CLI applications.
import { Effect } from "effect"
import { Prompt } from "effect/unstable/cli"
// Text input prompt
const getName = Effect.gen(function*() {
const name = yield* Prompt.text({
message: "What is your name?",
default: "User"
})
return name
})
// Password prompt (hidden input)
const getPassword = Prompt.password({
message: "Enter password:",
validate: (pwd) => pwd.length >= 8 || "Password must be at least 8 characters"
})
// Confirmation prompt
const confirm = Prompt.confirm({
message: "Are you sure?",
default: false
})
// Select from list
const selectOption = Prompt.select({
message: "Choose an option:",
choices: [
{ title: "Option 1", value: "opt1" },
{ title: "Option 2", value: "opt2" },
{ title: "Option 3", value: "opt3" }
]
})
// Multi-select
const multiSelect = Prompt.multiSelect({
message: "Select features:",
choices: [
{ title: "Feature A", value: "a" },
{ title: "Feature B", value: "b" },
{ title: "Feature C", value: "c" }
]
})
HelpDoc
Automatically generate help documentation for commands.
import { Command, HelpDoc } from "effect/unstable/cli"
// Help is automatically generated from command structure
const command = Command.make(
"deploy",
{
env: Flag.string("env").pipe(
Flag.withDescription("Deployment environment")
),
force: Flag.boolean("force").pipe(
Flag.withDescription("Force deployment")
)
}
).pipe(
Command.withDescription("Deploy the application")
)
// Users can run: mycli deploy --help
// to see generated documentation
CliError
Type-safe error handling for CLI operations.
import { Effect } from "effect"
import { CliError } from "effect/unstable/cli"
// Handle CLI parsing errors
const handleError = (error: CliError.CliError) => {
switch (error._tag) {
case "ValidationError":
return Effect.logError(`Validation failed: ${error.message}`)
case "MissingValue":
return Effect.logError(`Missing required value: ${error.name}`)
case "InvalidArgument":
return Effect.logError(`Invalid argument: ${error.message}`)
default:
return Effect.logError(`CLI error: ${error.message}`)
}
}
Complete Example
Here’s a complete CLI application with subcommands:
import { Console, Effect } from "effect"
import { Command, Flag, Argument } from "effect/unstable/cli"
// Define subcommands
const init = Command.make(
"init",
{
name: Argument.string("name"),
typescript: Flag.boolean("typescript").pipe(
Flag.withAlias("ts"),
Flag.withDefault(false)
)
},
({ name, typescript }) =>
Console.log(`Initializing project "${name}" with TypeScript: ${typescript}`)
).pipe(
Command.withDescription("Initialize a new project")
)
const build = Command.make(
"build",
{
watch: Flag.boolean("watch").pipe(
Flag.withAlias("w"),
Flag.withDefault(false)
),
outDir: Flag.string("outDir").pipe(
Flag.withDefault("./dist")
)
},
({ watch, outDir }) =>
Console.log(`Building to ${outDir}${watch ? " (watch mode)" : ""}`)
).pipe(
Command.withDescription("Build the project")
)
const test = Command.make(
"test",
{
coverage: Flag.boolean("coverage").pipe(
Flag.withDefault(false)
),
files: Argument.string("files").pipe(
Argument.variadic(),
Argument.optional
)
},
({ coverage, files }) =>
Console.log(`Running tests${coverage ? " with coverage" : ""}${files ? ` for: ${files.join(", ")}` : ""}`)
).pipe(
Command.withDescription("Run tests")
)
// Create main CLI with subcommands
const cli = Command.make("mycli").pipe(
Command.withSubcommands([init, build, test]),
Command.withDescription("My CLI application")
)
// Run the CLI
const program = Command.run(cli, process.argv.slice(2))
// Execute
Effect.runPromise(program)
Command-Line Completions
The CLI module supports shell completions for bash, zsh, and fish:
import { Command } from "effect/unstable/cli"
// Add completion command to your CLI
const cli = Command.make("mycli")
.pipe(
Command.withSubcommands([init, build, test]),
Command.withCompletions() // Adds 'completions' subcommand
)
// Users can then run:
// mycli completions bash > /etc/bash_completion.d/mycli
// mycli completions zsh > ~/.zsh/completions/_mycli
// mycli completions fish > ~/.config/fish/completions/mycli.fish
Best Practices
- Type Safety - Leverage TypeScript’s type inference for command configs
- Descriptions - Always add descriptions to commands, flags, and arguments
- Validation - Use
mapEffect to validate inputs with Effect
- Defaults - Provide sensible defaults for optional flags
- Subcommands - Organize complex CLIs with subcommands
- Error Handling - Handle CLI errors gracefully with proper messages
- Prompts - Use interactive prompts for better UX when appropriate
- AI - AI and LLM integration
- Process - Child process management
- Cluster - Distributed computing