command()

Define a command with typed options, positionals, and a handler function.

Import from maltty.

import { command } from 'maltty'
import { z } from 'zod'

const deploy = command({
  description: 'Deploy the application',
  options: z.object({
    env: z.enum(['staging', 'production']).describe('Target environment'),
    dryRun: z.boolean().default(false).describe('Preview without applying'),
  }),
  async handler(ctx) {
    ctx.log.info(`Deploying to ${ctx.args.env}`)
  },
})

CommandDef

FieldTypeDefaultDescription
descriptionResolvable<string>--Human-readable description (static or function)
optionsArgsDef--Option (flag) definitions -- Zod schema or yargs-native format
positionalsArgsDef--Positional argument definitions
handler(ctx: Context) => Promise<void>--Command handler function
middlewareMiddleware[]--Command-scoped middleware
commandsCommandMap--Nested subcommands
hiddenResolvable<boolean>--When true, hidden from help output
deprecatedResolvable<string | boolean>--Marks the command as deprecated
namestring--Explicit command name (overrides autoload filename)
aliasesreadonly string[]--Alternative names

Yargs-native arg format

As an alternative to Zod, commands accept a yargs-native format for options and positionals. Both produce the same typed ctx.args.

const deploy = command({
  description: 'Deploy the application',
  options: {
    env: {
      type: 'string',
      description: 'Target environment',
      required: true,
      choices: ['staging', 'production'],
    },
    dryRun: { type: 'boolean', description: 'Preview without applying', default: false },
  },
  async handler(ctx) {
    ctx.log.info(`Deploying to ${ctx.args.env}`)
  },
})

YargsArgDef

FieldTypeDescription
type'string' | 'number' | 'boolean' | 'array'Argument type
descriptionstringHelp text
requiredbooleanWhether the arg is required
defaultunknownDefault value
aliasstring | string[]Short aliases
choicesreadonly string[]Allowed values
hiddenResolvable<boolean>Omit from --help output (flag still works)
deprecatedResolvable<string | boolean>Show deprecation notice in help and on use
groupstringGroup heading in help output (e.g. 'Auth Options:')

Hidden and deprecated

Both accept a static value or a function (Resolvable<T>), resolved once at registration time.

// Hidden from --help but still callable
const debug = command({
  description: 'Internal debugging tools',
  hidden: true,
  handler: async (ctx) => {
    /* ... */
  },
})

// Conditionally hidden
const experimental = command({
  description: 'Experimental feature',
  hidden: () => process.env['NODE_ENV'] === 'production',
  handler: async (ctx) => {
    /* ... */
  },
})

// Deprecated with message
const oldDeploy = command({
  description: 'Deploy (legacy)',
  deprecated: 'Use "deploy-v2" instead',
  handler: async (ctx) => {
    /* ... */
  },
})

Subcommands

Commands can contain nested subcommands:

const generate = command({
  description: 'Code generation utilities',
  commands: {
    types: generateTypes,
    schema: generateSchema,
  },
})

autoload()

Dynamically discover commands from a directory at runtime.

import { autoload } from 'maltty'

cli({
  name: 'my-app',
  version: '1.0.0',
  commands: {
    generate: autoload({ dir: './commands/generate' }),
  },
})

References