Skip to content

defineCommand()

defineCommand() creates a CLI command. The returned object must be the file’s default export.

import { defineCommand } from "@rune-cli/rune";
export default defineCommand({
description: "Create a new project",
options: [{ name: "force", type: "boolean", short: "f" }],
args: [{ name: "name", type: "string", required: true }],
run({ options, args }) {
// ...
},
});
  • Type: string
  • Optional

A one-line summary shown in --help output.

  • Type: CommandArgField[]
  • Optional

Positional arguments declared in the order they appear on the command line. Required arguments must come before optional ones.

Each entry is a primitive field, an enum field, or a schema field. A field must use exactly one of type with a primitive type, type: "enum" with values, or schema — never a mix.

  • Type: string
  • Required

Identifier used as the key in ctx.args.

  • Type: "string" | "number" | "boolean"
  • Required

The type Rune parses the raw token into.

  • Type: true
  • Optional

When set, the argument must be provided. Omit to keep the argument optional.

  • Type: Matches type
  • Optional

Value used when the user omits the argument. Primitive defaults are shown in --help.

  • Type: string
  • Optional

Help text shown in --help output.

  • Type: string
  • Required

Identifier used as the key in ctx.args.

  • Type: "enum"
  • Required
  • Type: readonly (string | number)[]
  • Required

Allowed values. The raw CLI token is matched against each entry using strict string comparison (String(value) === rawToken), so values: [1, 2] accepts "1" or "2" but not "007" or "1.0". String values must match /^[A-Za-z0-9_.-]+$/ (letters, digits, _, ., -) — values that contain spaces or other special characters are rejected at definition time. Empty strings, NaN, Infinity, and duplicates (after string conversion) are rejected as well.

  • Type: true
  • Optional

When set, the field must be provided by the user. Omit to keep the field optional.

  • Type: One of values
  • Optional

Value used when the user omits the field. Must be listed in values.

  • Type: string
  • Optional

Help text shown in --help output. The allowed values are rendered alongside the name as <a|b|c>.

  • Type: string
  • Required

Identifier used as the key in ctx.args.

  • Type: StandardSchemaV1
  • Required

A Standard Schema object (e.g. Zod, Valibot) for validation and transformation. Required/optional semantics are derived from the schema.

  • Type: string
  • Optional

Display-only type hint rendered as <typeLabel> in --help output (e.g. "uuid", "number"). Has no effect on validation or type inference. Use this when the schema’s runtime value shape is not otherwise communicated to the reader.

  • Type: string
  • Optional

Display-only default-value label rendered as (default: defaultLabel) in --help output. Has no effect on required/optional handling, which is still derived from the schema itself. Keep this in sync with the schema’s actual default if one is set.

  • Type: string
  • Optional

Help text shown in --help output.

  • Type: CommandOptionField[]
  • Optional

Options declared as --name flags.

Each entry is a primitive field, an enum field, or a schema field, with the same base properties as args plus the following additional properties. Primitive defaults are shown in --help, except for boolean options. Primitive boolean options always default to false, even when required and default are omitted. When a primitive boolean option sets default: true, a --no-<name> flag is automatically generated so users can override the default. See Negatable boolean options for details.

The option name "help" is reserved by the framework and cannot be used. When json: true or jsonl: true is set, the name "json" is also reserved because Rune manages JSON-related runtime behavior.

  • Type: Single ASCII letter
  • Optional

Single-character shorthand (e.g. "f" for --force -> -f). Must be unique across all options. The short name "h" is reserved for the built-in --help flag and cannot be used.

  • Type: string
  • Optional (scalar options only)

Environment variable name used as a fallback when the option is not provided on the command line.

options: [{ name: "port", type: "number", env: "PORT", default: 3000 }];

Resolution order is CLI > env > default. For example, --port 4000 wins over PORT=5000; if --port is omitted, PORT=5000 is parsed as the option value; if neither is present, default is used. env does not affect type inference or required/optional typing.

Env values are parsed through the same validation path as CLI values. Invalid env values fail the command instead of falling back to defaults. Empty strings are treated as provided values, not as unset.

Primitive boolean options accept only "true" and "false" from env. Schema flag: true options also accept only "true" and "false" from env, then pass the boolean value to the schema. multiple: true options cannot use env.

The env name must match /^[A-Za-z_][A-Za-z0-9_]*$/. When shown in default help, env metadata is rendered with defaults when present, for example (default: 3000, env: PORT) or (env: PORT).

  • Type: true
  • Optional (options only)

When set, the option may be provided more than once. Primitive "string" and "number" options, as well as enum options, are parsed into arrays in declaration order:

options: [
{ name: "tag", type: "string", multiple: true, default: [] },
{ name: "level", type: "number", multiple: true },
];

Here ctx.options.tag is string[], while ctx.options.level is number[] | undefined unless the option is required or has an array default. Enum options follow the same rule, with each item restricted to values.

For schema-backed options, Rune passes the collected raw string values to the schema as an array, so use an array-shaped schema:

options: [{ name: "tag", schema: z.array(z.string()).default([]), multiple: true }];

Primitive boolean options and schema flag: true options cannot use multiple: true.

  • Type: true
  • Optional (schema fields only)

When set, the option is parsed as a boolean flag with no value. The schema receives true when the flag is present, undefined when absent.

  • Type: readonly string[]
  • Optional

Alternative names for this command. Each alias is an additional path segment that routes to this command. Aliases must follow kebab-case rules (lowercase letters, digits, and internal hyphens). The root command cannot have aliases.

  • Type: readonly string[]
  • Optional

Usage examples shown in the Examples: section of --help output. Each entry is a string representing a full command invocation.

  • Type: true
  • Optional

When set, the framework accepts a built-in --json flag. In JSON mode, the return value of run() becomes structured JSON output on stdout, output.log() calls are suppressed, and JSON error payloads are written to stderr. Omit to keep JSON mode disabled.

Commands with json: true receive a framework-managed options.json: boolean value in run(). It is true whenever JSON mode is active, including automatic activation under AI agents.

  • Type: true
  • Optional

When set, the command stdout contract is JSON Lines (NDJSON). The run() function must return an Iterable or AsyncIterable; Rune serializes each yielded record as one compact JSON line. output.log() is suppressed and output.error() continues to write to stderr. If the downstream pipe closes early, Rune treats the broken pipe as a normal early stop instead of printing an error.

jsonl: true cannot be combined with json: true. Rune does not add a --jsonl flag.

  • Type: (data: CommandHelpData) => string
  • Optional

Custom help renderer for this command. When provided, Rune calls this function instead of the global or default renderer for this command’s --help output.

The data argument is the structured CommandHelpData for the matched command.

import { defineCommand, renderDefaultHelp } from "@rune-cli/rune";
export default defineCommand({
description: "Deploy to production",
help(data) {
return `Deploy Command\n\n${renderDefaultHelp(data)}`;
},
async run() {
// ...
},
});

If help() throws, Rune falls back to the default help renderer and writes a warning to stderr.

  • Type: (ctx: CommandContext) => void | Promise<void> (default), (ctx: CommandContext) => TCommandData | Promise<TCommandData> (when json is true), or (ctx: CommandContext) => Iterable<TRecord> | AsyncIterable<TRecord> | Promise<Iterable<TRecord> | AsyncIterable<TRecord>> (when jsonl is true)
  • Required

The function executed when the command is invoked. When json is true, the return value becomes part of the command’s API, is serialized as JSON output when the user passes --json, and is preserved in runCommand().output.document. When jsonl is true, yielded records are serialized as JSON Lines and preserved in runCommand().output.records.

The run function receives a CommandContext object with the following properties:

  • Type: object

Parsed positional argument values, keyed by field name.

  • Type: object

Parsed option values, keyed by field name.

For commands with json: true, this object also includes the framework-managed json boolean. It reflects the effective JSON mode for the invocation. Use rawArgs if you need to distinguish an explicit --json flag from automatic JSON activation.

  • Type: object

Project-defined runtime values returned by defineConfig({ locals }). When no locals factory is configured, this is an empty object.

  • Type: string

Working directory the CLI was invoked from.

  • Type: readonly string[]

Unparsed argv tokens before Rune splits them into args and options. Useful for forwarding to child processes.

  • Type: CommandOutput

Framework output API. Use output.log() for stdout and output.error() for stderr.

  • Type: CommandStdin

Framework input API for reading stdin. stdin.isTTY tells whether stdin is a TTY, and stdin.isPiped is true when piped input is available. Reading is explicit: call await stdin.text() for UTF-8 text or await stdin.bytes() for bytes.

export default defineCommand({
async run({ stdin, output }) {
const input = stdin.isPiped ? await stdin.text() : "";
output.log(input.trim());
},
});

stdin can be consumed only once. Calling text() and then bytes(), or calling either method twice, fails with kind: "rune/stdin-consumed".

Fields with hyphenated names (e.g. dry-run) are accessible by both the original name and its camelCase equivalent (dryRun) on the ctx.args and ctx.options objects. This is enforced at the type level.

When a primitive boolean option has default: true, Rune automatically generates a --no-<name> flag that sets the value to false.

export default defineCommand({
options: [{ name: "color", type: "boolean", default: true }],
run({ options }) {
console.log(options.color); // true by default, false with --no-color
},
});

The --help output shows both forms:

Options:
--color, --no-color
-h, --help Show help

--<name> and --no-<name> cannot be used together — doing so produces an error. Defining a separate option whose name matches the generated negation (e.g. an option named no-color alongside a negatable color option) is also rejected at definition time.

Other primitive defaults are shown directly in help output.

This feature only applies to primitive boolean options with an explicit default: true. Schema-backed fields are not affected because their default values cannot be inspected at definition time.