Interactive prompts

The prompts package is a complete question toolkit: eight prompt types that render as live, repainting lines at a terminal — and fall back to plain line input automatically when stdin isn’t a TTY, so the same command works in a pipe, a script, or CI without a non-interactive flag.

In a command, context.prompts() returns an instance pre-wired to the command’s streams, arena allocator, and theme:

// Pre-wired to the command's streams, allocator, and theme.
const p = context.prompts();

const name = try p.text(.{
    .message = "Project name:", .default = "my-project",
});
const features = try p.multiSelect(.{
    .message = "Features:",
    .choices = &.{ "typescript", "eslint", "prettier" },
    .defaults = &.{ true, true, false },
});
const pw = try p.password(.{
    .message = "Token:",
});

The eight types

PromptAsksReturns
textfree-form input, optional default and live preview hintthe entered string
confirmyes/no with a default (Y/n hint, single keypress)bool
selectone choice from a list — ↑/↓ to move, Enter to pickthe chosen index
multiSelectseveral choices — Space toggles, per-item defaultsthe chosen indices
passwordmasked input, never echoedthe entered string
searchtype-to-filter over a long list, then pickthe chosen index
numbernumeric input with optional min/max, validation inlinethe parsed number
editoropens $EDITOR on a seeded temp filethe edited text

Terminal behavior, handled

  • Wrapping & resize — long messages word-wrap at the terminal width (grapheme- and ANSI-aware), and every prompt re-lays-out live on window resize.
  • Unicode-correct editing — backspace deletes one visual character; wide characters and combining sequences count correctly.
  • Answers persist — a completed prompt emits its styled question-and-answer as a static line into scrollback, so the transcript reads cleanly afterward.
  • Interrupts are yours — pass interrupt_keys and the prompt returns error.Interrupted when one is pressed; the caller decides whether that means cancel, back, or help.

Off a TTY

Every prompt degrades to a line-based equivalent with the same return type and value — there is no second code path to write:

at a terminalpiped / CI
text / passwordlive editing, masked for passwordreads a line
confirmsingle keypressreads y/n from a line
select / searcharrow-key highlighted listnumbered list, reads a number
multiSelectSpace-toggle listprints the list, reads selections
editoropens $EDITORreads remaining stdin

Prompts or widgets?

Prompts are one-shot questions: they block, return a value, and move on — right for a wizard, a scaffold, a few sequential questions. For a persistent, laid-out, keyboard-driven surface — a live form, a dashboard — build a full-screen App from the interactive widgets instead: see prompts vs widgets.

Standalone use

Like every zcli package, prompts works without the framework — construct a Prompts value with your own writer, reader, allocator, and theme context, and every prompt type behaves identically. The package’s examples/ directory has a runnable example per prompt type.