CliOutput unstable
Formats and presents CLI help, errors, and version information.
The Formatter service turns help documents, errors, and version
information into strings. The Presenter service decides where the built-in
help, version, and invalid-invocation output of Command.runWith is written,
and can be replaced to suppress or redirect it.
Constructors
defaultFormatter
unstableCreates a default formatter with configurable options.
Details
colors: force ANSI colors on or off. Defaults to terminal detection.inheritedFlags: how subcommand help renders flags inherited from ancestor commands."full"(default) lists them underINHERITED FLAGS,"summary"replaces the section with a pointer to the parent command's help, and"none"omits it.globalFlags: the same choice forGLOBAL FLAGS, with the summary pointing at the help of the nearest command that declares global flags (the root command for built-in flags). Commands that declare global flags always list them in full.
Gotchas
Per-flag filtering in a custom formatter matches names, not flag identities. Renaming a flag without updating the filter silently stops it from matching.
Example (Hiding one inherited flag)
Filter only inheritedFlags to keep --profile visible on its declaring
command while hiding it on descendants. Other inherited flags still render.
Signature
declare function defaultFormatter(options?: { readonly colors?: boolean; readonly globalFlags?: "none" | "full" | "summary"; readonly inheritedFlags?: "none" | "full" | "summary";}): FormatterExample Creating default formatters
import { CliError, CliOutput } from "effect/cli"
// Create a formatter without colors for tests or CI environmentsconst noColorFormatter = CliOutput.defaultFormatter({ colors: false })
// Create a formatter with colors forced onconst colorFormatter = CliOutput.defaultFormatter({ colors: true })
// Auto-detect colors based on terminal support (default behavior)const autoFormatter = CliOutput.defaultFormatter()
// Point subcommand help at the declaring scope for global flagsconst compactFormatter = CliOutput.defaultFormatter({ globalFlags: "summary" })
const error = new CliError.InvalidValue({ option: "foo", value: "bar", expected: "baz", kind: "flag"})
noColorFormatter.formatError(error).includes("Invalid value") // => truecolorFormatter.formatVersion("my-tool", "1.2.3").includes("my-tool") // => trueautoFormatter.formatVersion("my-tool", "1.2.3").includes("1.2.3") // => truecompactFormatter.formatVersion("my-tool", "1.2.3").includes("my-tool") // => trueExample Hiding one inherited flag
import { Context, Effect, Option } from "effect"import { CliOutput } from "effect/cli"
const base = CliOutput.defaultFormatter({ colors: false })const formatter: CliOutput.Formatter = { ...base, formatHelpDoc: (doc) => base.formatHelpDoc({ ...doc, inheritedFlags: doc.inheritedFlags?.filter((flag) => flag.name !== "profile") })}
const program = Effect.gen(function*() { const output = yield* CliOutput.Formatter return output.formatHelpDoc({ description: "Deploy the application", usage: "app deploy", annotations: Context.empty(), flags: [], inheritedFlags: ["profile", "region"].map((name) => ({ name, aliases: [], type: "string", description: Option.none(), required: false })) })}).pipe(Effect.provide(CliOutput.layer(formatter)))
const help = await Effect.runPromise(program)help.includes("--profile") // => falsehelp.includes("--region") // => trueDefaults
defaultPresenter
unstableDefault presenter that renders events with the current Formatter and
writes them with Console.
Details
Help and version output goes to Console.log. Invalid invocations write
help to Console.log and errors to Console.error. When no Formatter is
installed, the default formatter colors output when TerminalCapabilities
says it can.
See
- Presenter for replacing the default output
Signature
declare const defaultPresenter: PresenterLayers
Creates a Layer that provides a custom Formatter implementation.
Signature
declare function layer(formatter: Formatter): Layer<never>Example Providing a custom formatter
import { Effect } from "effect"import { CliOutput } from "effect/cli"
// Create a custom formatter without colorsconst noColorFormatter = CliOutput.defaultFormatter({ colors: false })const NoColorLayer = CliOutput.layer(noColorFormatter)
// Create a program that uses the custom formatterconst program = Effect.gen(function*() { const formatter = yield* CliOutput.Formatter return formatter.formatVersion("my-cli", "1.0.0")}).pipe( Effect.provide(NoColorLayer))
await Effect.runPromise(program) // => "my-cli v1.0.0"layerPresenter
unstableCreates a Layer that provides a custom Presenter implementation.
See
- Presenter for the events a presenter receives
Signature
declare function layerPresenter(presenter: Presenter): Layer<never>Measuring
displayWidth
unstableMeasures the number of terminal cells a string occupies.
Details
Uses the same measurement as built-in help and table rendering. ANSI styling
is stripped before measuring graphemes: zero-width graphemes count as 0,
emoji presentation sequences and fullwidth characters as 2, and other
graphemes as 1.
Signature
declare function displayWidth(text: string): numberExample Measuring terminal-cell widths
import { CliOutput } from "effect/cli"
CliOutput.displayWidth("file") // => 4CliOutput.displayWidth("ใใกใคใซ") // => 8CliOutput.displayWidth("รฉ") // => 1CliOutput.displayWidth("1๏ธโฃ") // => 2CliOutput.displayWidth("\u001B[1mbold\u001B[0m") // => 4Models
Defines the service interface for formatting CLI output including help, errors, and version info. This allows customization of output formatting, including color support.
Signature
interface Formatter { readonly formatCliError: (error: CliError) => string; readonly formatError: (error: CliError) => string; readonly formatErrors: (errors: readonly Array<CliError>) => string; readonly formatHelpDoc: (doc: HelpDoc) => string; readonly formatVersion: (name: string, version: string) => string;}Example Customizing CLI output formatting
import { Effect } from "effect"import { CliOutput } from "effect/cli"
// Create a custom formatter implementationconst customFormatter: CliOutput.Formatter = { formatHelpDoc: (doc) => `Custom Help: ${doc.usage}`, formatCliError: (error) => `Error: ${error.message}`, formatError: (error) => `[ERROR] ${error.message}`, formatVersion: (name, version) => `${name} (${version})`, formatErrors: (errors) => errors.map((error) => error.message).join("\\n")}
// Use the custom formatter in a programconst program = Effect.gen(function*() { const formatter = yield* CliOutput.Formatter return formatter.formatVersion("myapp", "1.0.0")}).pipe( Effect.provide(CliOutput.layer(customFormatter)))
await Effect.runPromise(program) // => "myapp (1.0.0)"Presents the built-in help, version, and invalid-invocation output of
Command.runWith.
When to use
Use to suppress, redirect, or replace that output, for example to keep stdout clean for machine-readable results.
Details
Events carry the help document and errors rather than rendered text, so a
presenter can still render them with the Formatter service. Presenting an
event does not change the result of the run: implicit help and invalid
invocations still fail with CliError.ShowHelp.
Gotchas
--completions scripts, wizard mode output, and handler failures rendered
through CliError.userMessage do not go through the presenter.
See
- defaultPresenter for the default console output
Signature
interface Presenter { readonly present: (event: Event) => Effect<void>;}Example Writing invalid-invocation output to stderr
import { Console, Effect } from "effect"import { CliOutput } from "effect/cli"
const StderrPresenter = CliOutput.layerPresenter({ present: (event) => event._tag === "InvalidInvocation" ? Effect.gen(function*() { const formatter = yield* CliOutput.Formatter yield* Console.error(formatter.formatHelpDoc(event.helpDoc)) yield* Console.error(formatter.formatErrors(event.errors)) }) : CliOutput.defaultPresenter.present(event)})Other
Services
Service reference for the CLI output formatter. Provides a default implementation that can be overridden for custom formatting or testing.
Signature
declare const Formatter: Reference<Formatter>Example Accessing the output formatter
import { Effect } from "effect"import { CliOutput } from "effect/cli"
// Access the formatter serviceconst program = Effect.gen(function*() { const formatter = yield* CliOutput.Formatter
// Format version information return formatter.formatVersion("my-cli", "2.1.0")})
// Run with default formatterawait Effect.runPromise(program.pipe( Effect.provide(CliOutput.layer(CliOutput.defaultFormatter({ colors: false }))))) // => "my-cli v2.1.0"Service reference for the CLI output presenter. Defaults to
defaultPresenter.
See
- layerPresenter for providing a custom presenter
Signature
declare const Presenter: Reference<Presenter>