Skip to content
// EFFECT DAYS 2026

CommandSpec unstable

The CommandSpec module projects one command path of an Effect CLI command tree into plain data for documentation tooling such as reference pages or JSON schemas. It is lossless where Completions.fromCommand is deliberately not: hidden flags and unlisted subcommands are included and marked, every effective flag says where it comes from, primitive types are not collapsed, and defaults and fallbacks are reported structurally without running handlers, prompts, Config lookups or effectful defaults.

10 exports Since v4.1.3 Source

Constructors

resolve

unstable
Since v4.1.3

Projects one command path of a command tree into a Spec.

When to use

Use when generating reference documentation or machine-readable schemas for a CLI from its Command definition, in a build step, without running the CLI.

Details

path names the commands from the root down, root name first, exactly as HelpDoc.commandPath does. Only canonical names match: a subcommand alias in the path, an unknown segment, a different root name or an empty path all yield Option.none() rather than the deepest matching ancestor.

Each flag or argument entry describes one Single parameter together with the combinators on the path from the parameter's root to it, outermost first. A combinator applied outside an orElse applies to every alternative; one applied inside a branch applies to that branch only. Combinators beneath a variadic describe each item and do not contribute to required or fallbacks.

The call is pure and synchronous. Handlers, prompts, Config lookups and effectful defaults are reported as Fallback values and never run.

See

  • Spec for the returned shape

Signature

declare function resolve(command: Any, path: readonly Array<string>, options?: ResolveOptions): Option<Spec>

Example Listing the required flags of a subcommand

import { Option } from "effect"
import { Command, CommandSpec, Flag } from "effect/cli"
const push = Command.make("push", {
dbUrl: Flag.String("db-url"),
dryRun: Flag.Boolean("dry-run").pipe(Flag.withDefault(false))
})
const cli = Command.make("app").pipe(
Command.withSharedFlags({ profile: Flag.String("profile").pipe(Flag.optional) }),
Command.withSubcommands([push])
)
const spec = Option.getOrThrow(CommandSpec.resolve(cli, ["app", "push"], { builtIns: [] }))
const surface = spec.flags.map((flag) => [flag.name, flag.scope._tag, flag.required])
surface // => [["db-url", "Local", true], ["dry-run", "Local", false], ["profile", "Inherited", false]]

Models

Argument

interface unstable
Since v4.1.3

The input surface of one positional argument. required and fallbacks follow the same branch-local rules as Flag.

Signature

interface Argument {
readonly annotations: Context<never>;
readonly description: string | undefined;
readonly fallbacks: readonly Array<Fallback>;
readonly metavar: string | undefined;
readonly name: string;
readonly required: boolean;
readonly type: Type;
readonly typeName: string;
readonly variadic: VariadicBounds | undefined;
}

Fallback

type unstable
Since v4.1.3

How a parameter obtains a value when it is absent, described without being evaluated. See Param.Fallback.

Signature

type Fallback = Param.Fallback

Flag

interface unstable
Since v4.1.3

The input surface of one flag as the user types it. Transformations applied to the parsed value (map, withSchema, filter) are transparent, and a parameter built with orElse yields one entry per alternative.

Signature

interface Flag {
readonly aliases: readonly Array<string>;
readonly annotations: Context<never>;
readonly description: string | undefined;
readonly fallbacks: readonly Array<Fallback>;
readonly hidden: boolean;
readonly metavar: string | undefined;
readonly name: string;
readonly negation: string | undefined;
readonly optionalValue: string | undefined;
readonly required: boolean;
readonly scope: Scope;
readonly type: Type;
readonly typeName: string;
readonly variadic: VariadicBounds | undefined;
}

ResolveOptions

interface unstable
Since v4.1.3

Options for resolve.

Signature

interface ResolveOptions {
readonly builtIns?: readonly Array<GlobalFlag<any>>;
}

Scope

type unstable
Since v4.1.3

Where an effective flag is declared.

Details

Local flags are declared on the resolved command itself. Inherited flags come from withSharedFlags on the ancestor at declaredBy. Global flags carry the path of the command that declared them with withGlobalFlags, or undefined for a built-in.

A nearer Local or Inherited flag hides an inherited flag of the same name. A Global flag is always listed, even when a local or inherited flag shares its name, because the parser still routes its other names and aliases to the global flag.

Signature

type Scope = {
readonly _tag: "Local";
} | {
readonly _tag: "Inherited";
readonly declaredBy: ReadonlyArray<string>;
} | {
readonly _tag: "Global";
readonly declaredBy: ReadonlyArray<string> | undefined;
readonly kind: "Action" | "Setting";
}

Spec

interface unstable
Since v4.1.3

Everything known about one command path.

Signature

interface Spec {
readonly alias: string | undefined;
readonly annotations: Context<never>;
readonly arguments: readonly Array<Argument>;
readonly description: string | undefined;
readonly examples: readonly Array<Example>;
readonly flags: readonly Array<Flag>;
readonly name: string;
readonly path: readonly Array<string>;
readonly shortDescription: string | undefined;
readonly subcommands: readonly Array<Subcommand>;
readonly unlisted: boolean;
}

Subcommand

interface unstable
Since v4.1.3

A direct subcommand of the resolved command.

Signature

interface Subcommand {
readonly alias: string | undefined;
readonly description: string | undefined;
readonly group: string | undefined;
readonly name: string;
readonly shortDescription: string | undefined;
readonly unlisted: boolean;
}

Type

type unstable
Since v4.1.3

The primitive type a parameter parses, one variant per Primitive.

Signature

type Type = {
readonly _tag: "Boolean";
} | {
readonly _tag: "String";
} | {
readonly _tag: "Int";
} | {
readonly _tag: "Finite";
} | {
readonly _tag: "Date";
} | {
readonly _tag: "Choice";
readonly values: ReadonlyArray<string>;
} | {
readonly _tag: "Path";
readonly pathType: Primitive.PathType;
} | {
readonly _tag: "Redacted";
} | {
readonly _tag: "FileText";
} | {
readonly _tag: "FileParse";
} | {
readonly _tag: "FileSchema";
} | {
readonly _tag: "KeyValuePair";
} | {
readonly _tag: "Never";
}

VariadicBounds

interface unstable
Since v4.1.3

Repeat bounds of a variadic parameter. undefined means unbounded on that side.

Signature

interface VariadicBounds {
readonly max: number | undefined;
readonly min: number | undefined;
}