Skip to content

Help

Help text is generated from your schemas — usage line, arguments, flags, subcommands, and examples all render automatically for --help, -h, help <command>, and root help.

Configuring Help

.help() sets builder-level defaults; the same fields can be overridden per call via execute(argv, { help }):

ts
import { cli, command } from '@kjanat/dreamcli';

cli('mycli')
  .command(command('deploy').action(() => {}))
  .help({
    // Render the default command's args/flags inline at the root
    inlineDefault: true,
    // Also list the default command in the Commands: table
    showDefaultInCommands: false,
    // Force the "Run '<bin> <command> --help'" footer on or off
    footer: true,
    // Pin the line width (defaults to the terminal width, falling back to 80)
    width: 100,
    // OSC 8 hyperlinks in the header. Defaults to the channel's resolved
    // support: NO_HYPERLINKS/FORCE_HYPERLINKS (and --no-hyperlinks/--hyperlinks)
    // are honored, otherwise TTY detection.
    hyperlinks: true,
    // Order of the Flags: table — 'alphabetical' (default) or 'declaration'
    flagOrder: 'declaration',
  });

Flag order

By default the Flags: table lists short-aliased flags first, then alphabetically by name. flagOrder: 'declaration' instead preserves the order you called .flag(), so you control the layout by ordering your builder calls:

ts
import { cli, command, flag } from '@kjanat/dreamcli';

cli('mycli')
  .command(
    command('serve')
      .flag('port', flag.number())
      .flag('host', flag.string())
      .action(() => {}),
  )
  // 'port' then 'host', as declared — not alphabetized to 'host', 'port'
  .help({ flagOrder: 'declaration' });

For full control, sortFlags supplies a comparator over flag long names and wins over flagOrder:

ts
import { cli, command } from '@kjanat/dreamcli';

cli('mycli')
  .command(command('serve').action(() => {}))
  // Reverse-alphabetical
  .help({ sortFlags: (a, b) => b.localeCompare(a) });

Both are also accepted per call via execute(argv, { help }) / run({ help }).

The root-help header can wrap the program name and version in OSC 8 terminal hyperlinks (pointing at the repository and release tag). Emission follows the standard hyperlink signals: NO_HYPERLINKS / --no-hyperlinks force them off, FORCE_HYPERLINKS / --hyperlinks force them on, otherwise the header links only on a TTY. The same resolved decision is exposed to handlers as out.isHyperlinkSupported, so code rendering its own out.color.link(...) output can gate on it and keep escapes out of piped or opted-out contexts.

Theming

Help output is colored by default whenever color is enabled — the same gate as handler output (out.color): stdout is a TTY, --json is off, and the environment allows color (NO_COLOR / FORCE_COLOR are honored). Piped and JSON output stays byte-clean, always.

The built-in theme follows clap/cargo conventions:

RoleApplies toDefault style
sectionTitleUsage:, Flags:, Commands:, …bold + underline
usageBinbinary/command path in the usage linebold
flagflag forms (-f, --force)cyan
commandcommand names in Commands: tablescyan
argpositional tokens (<file>)cyan
placeholdergrammar tokens (<string>, [flags])dim
defaultValue(default: …)dim
annotation[env: X], [required], (default)dim
deprecated[deprecated…]yellow
headerNameprogram name in the root headerbold
headerVersionvX.Y.Z in the root headerdim
examplePromptthe $ marker in Examples:dim

Descriptions stay unstyled for readability. Example commands are highlighted per token with the existing roles — the leading binary via usageBin (bold) and flag tokens (-x, --long) via flag (cyan), values plain. Tokenizing is quote-aware, so --scope './a b' stays one token, and the highlighting respects the color gate: with color off the command is byte-identical to its plain form.

Custom Themes

Pass a theme factory to .help(). It receives the gated palette (the same Colors instance as out.color) and returns role overrides merged over the built-in theme:

ts
import { cli, command } from '@kjanat/dreamcli';

cli('mycli')
  .command(command('deploy').action(() => {}))
  .help({
    theme: (c) => ({
      sectionTitle: c.magenta,
      flag: c.hex('#ff8800'),
      command: (input) => c.bold(c.green(input)),
    }),
  });

Two guarantees make themes safe to write:

  • The palette's formatters are identity functions when color is off, so you can style unconditionally.
  • The factory itself is never invoked when color is off — even a formatter that emits raw escape codes cannot leak them into piped output.

One constraint: roles that appear inside wrap-eligible description text (defaultValue, annotation, deprecated) should stick to foreground colors and dim. A styled span can cross a soft-wrap boundary — colors carry invisibly across the continuation indent, but underline, inverse, and background styles would visibly paint it.

JSON Help

With --json, help emits the CLI's definition document instead of text — the same machine-readable shape produced by generateSchema(), so mycli --help --json | jq '.commands[].name' just works:

sh
$ mycli --help --json          # root: { $schema, name, version, defaultCommand?, commands }
$ mycli deploy --help --json   # command: { name, description, flags, args, commands }

The root document includes the default command's full definition under defaultCommand (flags and args included), and carries a $schema pointer to the definition meta-schema for validation and editor tooling. Nothing is written to stderr — the JSON is the help.

Programmatic Rendering

formatHelp() renders a command's help as a plain string — useful for custom UIs, docs generation, or tests. Pass colors to render a themed variant:

ts
import { command, formatHelp } from '@kjanat/dreamcli';

const deploy = command('deploy').description('Deploy the app').action(() => {});

const plain = formatHelp(deploy.schema, { binName: 'mycli' });

See also Output for how out.color gating works at runtime.

Released under the MIT License.