Crust logoCrust

Flags

Define named flags with types, aliases, defaults, multiple values, and inheritance.

Flags are named options passed with -- (long form) or - (short form). They're defined as a record via .flags().

my-cli --port 3000 --verbose -o dist
#      ^^^^^^^^^^^  ^^^^^^^^^  ^^^^^^
#      flag         flag       short

Defining Flags

Flags are defined with the .flags() method:

const cmd = new Crust("serve")
  .flags({
    port: {
      type: "number",
      description: "Port to listen on",
      default: 3000,
      short: "p",
    },
    verbose: {
      type: "boolean",
      description: "Enable verbose logging",
      short: "v",
    },
  })
  .run(({ flags }) => {
    console.log(`Port: ${flags.port}`);       // number (has default)
    console.log(`Verbose: ${flags.verbose}`); // boolean | undefined
  });

Flag Properties

Each flag is a FlagDef object with properties like type, description, default, required, short, aliases, multiple, and inherit. See the FlagDef reference for the full property table.

Types

Crust supports three flag type literals:

.flags({
  output: { type: "string" },   // --output value (string)
  port: { type: "number" },      // --port 3000 (coerced to number)
  verbose: { type: "boolean" },  // --verbose (boolean toggle)
})
  • "string" — Requires a value: --output dist
  • "number" — Requires a value, coerced to number: --port 3000. Throws a PARSE error if the value isn't a valid number
  • "boolean" — Toggle flag, no value needed: --verbose

Boolean Negation

Boolean flags support --no- prefix negation:

my-cli --no-minify  # flags.minify === false

This works automatically for any boolean flag. When a boolean flag is repeated, the last occurrence wins:

my-cli --verbose --no-verbose   # flags.verbose === false
my-cli --no-verbose --verbose   # flags.verbose === true

Use the canonical flag name for negation (--no-verbose), not aliases like --no-v or --no-loud.

Boolean flags are toggles and do not accept explicit value assignment. Forms like --verbose=true or --verbose=false are parse errors.

Short & Aliases

Provide a single-character short form with the short property, and additional aliases with the aliases property:

.flags({
  output: { type: "string", short: "o" },                    // -o dist
  verbose: { type: "boolean", short: "v" },                   // -v
  target: { type: "string", short: "t", aliases: ["T"] },    // -t or -T
})

short accepts a single character for the -x short form. aliases accepts an array of additional aliases (single-char or long).

Alias collisions are caught at compile time and runtime. If an alias conflicts with another flag name or another alias, you'll get an error.

The no- prefix is reserved for boolean negation. Do not define flag names, short forms, or aliases that start with no-.

Required Flags

Mark a flag as required to error when it's missing:

.flags({
  config: { type: "string", required: true },
})
$ my-cli
# Error: Missing required flag "--config"

Default Values

Provide a default to use when the flag isn't passed:

.flags({
  port: { type: "number", default: 3000 },
  format: { type: "string", default: "json" },
  minify: { type: "boolean", default: true },
})

When a default is set, the inferred type is guaranteed (no | undefined).

Multiple Values

Set multiple: true to collect repeated flag values into an array:

.flags({
  target: {
    type: "string",
    multiple: true,
    description: "Target platform(s)",
    short: "t",
  },
})
my-cli --target linux-x64 --target darwin-arm64
# flags.target === ["linux-x64", "darwin-arm64"]

Boolean flags with multiple: true collect each occurrence (including --no- negations) into the array:

my-cli --verbose --no-verbose --verbose
# flags.verbose === [true, false, true]

You can also provide a default array:

.flags({
  include: {
    type: "string",
    multiple: true,
    default: ["src/**"],
  },
})

Flag Inheritance

Set inherit: true on a flag to make it available to subcommands:

const app = new Crust("my-cli")
  .flags({
    verbose: { type: "boolean", short: "v", inherit: true },
    debug: { type: "boolean" }, // not inherited
  })
  .command("deploy", (cmd) =>
    cmd
      .flags({ env: { type: "string", required: true } })
      .run(({ flags }) => {
        flags.verbose; // boolean | undefined (inherited from parent)
        flags.env;     // string (local)
        // flags.debug — not available (not inherited)
      })
  );

Inherited flags are:

  • Parsed by the subcommand's parser alongside local flags
  • Typed correctly in the subcommand's handler via EffectiveFlags<Inherited, Local>
  • Shown in the subcommand's help output

Local flags override inherited flags with the same key. Only flags with inherit: true are propagated — all other parent flags are excluded.

See the .sub() factory for how to use flag inheritance with the file-splitting pattern.

Unknown Flags

Crust uses strict mode for flag parsing. Unknown flags immediately produce a PARSE error:

$ my-cli --unknown-flag
# Error: Unknown flag "--unknown-flag"

Type Inference

Crust infers flag types from your definitions — required and default guarantee the value, otherwise it's T | undefined. Flags with multiple: true resolve to arrays. See the full inference rules for all cases.

On this page