effect-cli

Command.run(command, { name, version }) ├── Command (parent) │ ├── Options (named flags: --verbose, --depth 5) │ ├── Args (positional: <repo> <path>) │ └── Command.withSubcommands([...]) │ ├── Command (child — can yield* parent for its config) │ └── Command (child)

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "effect-cli" with this command: npx skills add tstelzer/skills/tstelzer-skills-effect-cli

Structure

Command.run(command, { name, version }) ├── Command (parent) │ ├── Options (named flags: --verbose, --depth 5) │ ├── Args (positional: <repo> <path>) │ └── Command.withSubcommands([...]) │ ├── Command (child — can yield* parent for its config) │ └── Command (child)

Minimal App

import { Command, Options, Args } from "@effect/cli" import { NodeContext, NodeRuntime } from "@effect/platform-node" import { Console, Effect } from "effect"

const greet = Command.make( "greet", { name: Args.text({ name: "name" }), loud: Options.boolean("loud").pipe(Options.withAlias("l")) }, ({ name, loud }) => Console.log(loud ? name.toUpperCase() + "!" : Hello, ${name}!) )

const cli = Command.run(greet, { name: "Greeter", version: "1.0.0" })

Effect.suspend(() => cli(process.argv)).pipe( Effect.provide(NodeContext.layer), NodeRuntime.runMain )

Commands

// No config const root = Command.make("app")

// With config (record of Options + Args, arbitrarily nested) const cmd = Command.make("cmd", { verbose: Options.boolean("verbose"), file: Args.text({ name: "file" }) }, ({ verbose, file }) => Console.log(file))

// Attach handler later const cmd2 = Command.make("cmd", { n: Args.integer({ name: "n" }) }).pipe( Command.withHandler(({ n }) => Console.log(n)) )

// Description const cmd3 = cmd.pipe(Command.withDescription("Does something useful"))

Subcommands

const git = Command.make("git", { verbose: Options.boolean("verbose").pipe(Options.withAlias("v")) })

const clone = Command.make("clone", { repo: Args.text({ name: "repo" }) }, ({ repo }) => Effect.gen(function* () { const { verbose } = yield* git // access parent's parsed config yield* Console.log(Cloning ${repo}, verbose=${verbose}) }) )

const add = Command.make("add", { path: Args.text({ name: "path" }) }, ({ path }) => Console.log(Adding ${path}) )

const command = git.pipe(Command.withSubcommands([clone, add]))

Key pattern: subcommand handlers yield* parentCommand to access the parent's parsed config. This works because Command extends Effect .

Providing Services

cmd.pipe(Command.provide(MyService.layer)) cmd.pipe(Command.provide((config) => MyService.layer(config.path))) // config-dependent cmd.pipe(Command.provideEffect(MyService, (_config) => Effect.succeed(impl))) cmd.pipe(Command.provideSync(MyService, impl)) cmd.pipe(Command.provideEffectDiscard((_config) => Effect.log("Starting...")))

// Transform handler cmd.pipe(Command.transformHandler((effect, config) => Effect.provideService(effect, MyService, impl) ))

Options (Named Flags)

For detailed options reference see effect-cli-options.md.

Constructor CLI syntax Type

Options.boolean("verbose")

--verbose

boolean

Options.text("name")

--name foo

string

Options.integer("depth")

--depth 5

number

Options.float("amount")

--amount 3.14

number

Options.date("when")

--when 2024-01-01

Date

Options.redacted("token")

--token abc

Redacted

Options.choice("fmt", ["json", "csv"])

--fmt json

"json" | "csv"

Options.file("config")

--config ./f.json

string

Options.directory("out")

--out ./dist

string

Options.fileText("input")

--input ./f.txt

[path, string]

Options.fileSchema("cfg", S)

--cfg ./f.json

Schema.Type<S>

Options.keyValueMap("c")

-c k=v -c k2=v2

HashMap<string,string>

Common Combinators

Options.text("name").pipe( Options.withAlias("n"), // -n shorthand Options.withDescription("Your name"), Options.optional, // Option<string> // or: Options.withDefault("world"), // string with default )

// Repetition Options.text("tag").pipe(Options.repeated) // Array<string> Options.text("tag").pipe(Options.atLeast(1)) // NonEmptyArray<string>

// Schema validation Options.text("balance").pipe(Options.withSchema(Schema.BigDecimal))

// Fallback to env var (via Effect Config) Options.integer("port").pipe(Options.withFallbackConfig(Config.integer("PORT")))

// Fallback to interactive prompt Options.text("name").pipe( Options.withFallbackPrompt(Prompt.text({ message: "Enter name:" })) )

Args (Positional Arguments)

For detailed args reference see effect-cli-args.md.

Constructor Type

Args.text({ name: "repo" })

string

Args.integer({ name: "n" })

number

Args.float({ name: "x" })

number

Args.boolean()

boolean

Args.date()

Date

Args.file()

string

Args.file({ exists: "yes" })

string (must exist)

Args.directory()

string

Args.fileText()

[path, string]

Args.fileSchema(MySchema)

Schema.Type<S>

Common Combinators

Args.text({ name: "dir" }).pipe( Args.optional, // Option<string> // or: Args.withDefault("/"), // string with default // or: Args.repeated, // Array<string> // or: Args.atLeast(1), // NonEmptyArray<string> )

Args.text({ name: "n" }).pipe(Args.withSchema(Schema.NumberFromString)) Args.text({ name: "r" }).pipe(Args.withFallbackConfig(Config.string("REPO"))) Args.text({ name: "r" }).pipe(Args.withDescription("The repository URL"))

Prompts

For detailed prompt reference see effect-cli-prompt.md.

import { Prompt } from "@effect/cli"

Prompt.text({ message: "Name:", default: "Alice" }) // string Prompt.password({ message: "Password:" }) // Redacted Prompt.integer({ message: "Age:", min: 0, max: 150 }) // number Prompt.confirm({ message: "Sure?" }) // boolean Prompt.toggle({ message: "Enable?", active: "on", inactive: "off" }) // boolean

Prompt.select({ message: "Pick env:", choices: [ { title: "Production", value: "prod" }, { title: "Staging", value: "staging" }, { title: "Dev", value: "dev" } ] }) // string

Prompt.list({ message: "Tags:", delimiter: "," }) // Array<string>

// Combine prompts Prompt.all({ name: namePrompt, age: agePrompt })

Prompt-Based Commands

const favorites = Command.prompt( "favorites", Prompt.all([ Prompt.select({ message: "Color?", choices: [...] }), Prompt.confirm({ message: "Continue?" }) ]), ([color, confirmed]) => Console.log(Color: ${color}) )

Running

const cli = Command.run(command, { name: "My App", version: "1.0.0", // summary: Span.text("Short summary"), // footer: HelpDoc.p("Footer text"), })

// cli: (args: ReadonlyArray<string>) => Effect<void, ...>

Effect.suspend(() => cli(process.argv)).pipe( Effect.provide(NodeContext.layer), NodeRuntime.runMain )

process.argv is automatically stripped of the node executable and script path.

Built-In Options (Automatic)

Every CLI app gets these for free:

Flag Effect

-h , --help

Print auto-generated help

--version

Print version

--wizard

Interactive wizard for all options/args

--completions bash|fish|zsh

Shell completion scripts

--log-level debug|info|warn|error

Set log level

Config File Support

import { ConfigFile } from "@effect/cli"

Effect.suspend(() => cli(process.argv)).pipe( Effect.provide( Layer.mergeAll( NodeContext.layer, ConfigFile.layer("myapp") // reads myapp.json, myapp.yaml, etc. ) ), NodeRuntime.runMain )

Options using withFallbackConfig will read from the config file.

Config Precedence

CLI args > withFallbackConfig (env/config file) > withFallbackPrompt (interactive) > withDefault (static)

Additional Resources

Detailed Options reference Detailed Args reference Detailed Prompt reference

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

skill-creator

No summary provided by upstream source.

Repository SourceNeeds Review
General

effect-vitest

No summary provided by upstream source.

Repository SourceNeeds Review
General

effect

No summary provided by upstream source.

Repository SourceNeeds Review