Overview
Theeffect/unstable/cli modules provide tools for building robust command-line applications with:
- Type-safe arguments and flags
- Automatic help text generation
- Subcommand composition
- Built-in validation and error messages
Quick start
import { NodeRuntime, NodeServices } from "@effect/platform-node"
import { Effect } from "effect"
import { Argument, Command, Flag } from "effect/unstable/cli"
const greet = Command.make(
"greet",
{
name: Argument.string("name"),
loud: Flag.boolean("loud")
},
Effect.fnUntraced(function*({ name, loud }) {
const greeting = `Hello, ${name}!`
yield* Effect.log(loud ? greeting.toUpperCase() : greeting)
})
).pipe(
Command.withDescription("Greet someone")
)
greet.pipe(
Command.run({ version: "1.0.0" }),
Effect.provide(NodeServices.layer),
NodeRuntime.runMain
)
Defining flags
Boolean flags
const verbose = Flag.boolean("verbose").pipe(
Flag.withAlias("v"),
Flag.withDescription("Enable verbose output")
)
String flags
const output = Flag.string("output").pipe(
Flag.withAlias("o"),
Flag.withDescription("Output file path"),
Flag.withDefault("./output.txt")
)
Choice flags
const logLevel = Flag.choice("log-level", ["debug", "info", "warn", "error"]).pipe(
Flag.withDescription("Set the log level"),
Flag.withDefault("info")
)
Integer flags
const port = Flag.integer("port").pipe(
Flag.withAlias("p"),
Flag.withDescription("Port to listen on"),
Flag.withDefault(3000)
)
Defining arguments
const filename = Argument.string("filename").pipe(
Argument.withDescription("File to process")
)
const count = Argument.integer("count").pipe(
Argument.withDescription("Number of items")
)
Creating commands
Simple command
const deploy = Command.make(
"deploy",
{
environment: Argument.string("environment"),
dryRun: Flag.boolean("dry-run")
},
Effect.fnUntraced(function*({ environment, dryRun }) {
yield* Effect.log(`Deploying to ${environment}...`)
if (dryRun) {
yield* Effect.log("Dry run mode - no changes made")
}
})
).pipe(
Command.withDescription("Deploy the application")
)
Command with examples
const build = Command.make(
"build",
{ watch: Flag.boolean("watch") },
buildHandler
).pipe(
Command.withDescription("Build the project"),
Command.withExamples([
{
command: "build --watch",
description: "Build and watch for changes"
}
])
)
Subcommands
const tasks = Command.make("tasks", {
workspace: Flag.string("workspace").pipe(
Flag.withDefault("personal")
)
}).pipe(
Command.withDescription("Manage tasks")
)
const create = Command.make(
"create",
{
title: Argument.string("title"),
priority: Flag.choice("priority", ["low", "normal", "high"]).pipe(
Flag.withDefault("normal")
)
},
Effect.fnUntraced(function*({ title, priority }) {
const root = yield* tasks
yield* Effect.log(`Creating task "${title}" in ${root.workspace}`)
})
)
const list = Command.make(
"list",
{},
Effect.fnUntraced(function*() {
const root = yield* tasks
yield* Effect.log(`Listing tasks in ${root.workspace}`)
})
)
// Compose into final command
const app = tasks.pipe(
Command.withSubcommands([create, list])
)
Accessing parent command options
Subcommands can access parent command input:const subcommand = Command.make(
"subcommand",
{ flag: Flag.boolean("flag") },
Effect.fnUntraced(function*({ flag }) {
// Access parent command
const parent = yield* parentCommand
yield* Effect.log(`Parent option: ${parent.workspace}`)
yield* Effect.log(`Subcommand flag: ${flag}`)
})
)
Running commands
import { NodeRuntime, NodeServices } from "@effect/platform-node"
command.pipe(
Command.run({
version: "1.0.0",
name: "myapp"
}),
Effect.provide(NodeServices.layer),
NodeRuntime.runMain
)
Complete example
import { NodeRuntime, NodeServices } from "@effect/platform-node"
import { Console, Effect } from "effect"
import { Argument, Command, Flag } from "effect/unstable/cli"
const workspace = Flag.string("workspace").pipe(
Flag.withAlias("w"),
Flag.withDescription("Workspace to operate on"),
Flag.withDefault("personal")
)
const tasks = Command.make("tasks", {
workspace,
verbose: Flag.boolean("verbose").pipe(
Flag.withAlias("v"),
Flag.withDescription("Print diagnostic output")
)
}).pipe(
Command.withDescription("Track and manage tasks")
)
const create = Command.make(
"create",
{
title: Argument.string("title").pipe(
Argument.withDescription("Task title")
),
priority: Flag.choice("priority", ["low", "normal", "high"]).pipe(
Flag.withDescription("Priority for the new task"),
Flag.withDefault("normal")
)
},
Effect.fnUntraced(function*({ title, priority }) {
const root = yield* tasks
if (root.verbose) {
yield* Console.log(`workspace=${root.workspace} action=create`)
}
yield* Console.log(`Created "${title}" in ${root.workspace} with ${priority} priority`)
})
).pipe(
Command.withDescription("Create a task"),
Command.withExamples([
{
command: "tasks create \"Ship 4.0\" --priority high",
description: "Create a high-priority task"
}
])
)
const list = Command.make(
"list",
{
status: Flag.choice("status", ["open", "done", "all"]).pipe(
Flag.withDescription("Filter tasks by status"),
Flag.withDefault("open")
)
},
Effect.fnUntraced(function*({ status }) {
const root = yield* tasks
if (root.verbose) {
yield* Console.log(`workspace=${root.workspace} action=list`)
}
yield* Console.log(`Listing ${status} tasks in ${root.workspace}`)
})
).pipe(
Command.withDescription("List tasks"),
Command.withAlias("ls")
)
tasks.pipe(
Command.withSubcommands([create, list]),
Command.run({ version: "1.0.0" }),
Effect.provide(NodeServices.layer),
NodeRuntime.runMain
)
See also
- Process - Run child processes from CLI apps