Prompt
Interactive prompts — text, password, number, confirm, select, multiselect and more.
@ozaco/cli/prompt@ozaco/cli/prompt is the default implementation of the Prompt protocol: a family of interactive questions that render over a raw-input Terminal session and resolve to a typed value. It exposes one plugin, DefaultPrompt, whose actions cover free-text entry (text, password, number, path) and choosing from options (confirm, select, multiselect, autocomplete).
Every prompt shares a common frame: a message line, optional description, a live-editing body, and — on submit or cancel — a committed summary line. Validators run on submit and return a message to reject; Ctrl-C cancels with a CoreErrors.Cancelled failure. List prompts paginate, skip disabled choices during navigation, and show per-choice hints.
When the terminal is not interactive (piped input, CI), each prompt takes a non-interactive fallback instead of blocking: text-like prompts return their initial (or empty/zero), confirm returns its initial, and choice prompts return the initial/first choice — or fail with CoreErrors.NotInteractive when none is available. Requires Terminal and Palette to be installed. Option types live in PromptDef in @ozaco/cli/core.
Implementation
DefaultPrompt
The prompt implementation providing all eight prompt actions.
const DefaultPrompt: PromptDefInstall it alongside Terminal and Palette, then call Prompt.actions.*. Each action opens its own terminal session, so prompts can be issued one after another.
import { install } from '@ozaco/std/plugin'
import { Prompt } from '@ozaco/cli/core'
import { DefaultPrompt } from '@ozaco/cli/prompt'
yield* install(DefaultPrompt)
const name = yield* Prompt.actions.text({ message: 'Your name?' })Free-text prompts
Single-line editors that resolve to a string or number.
text
Ask for a line of text, with an optional initial value, placeholder and validator.
text(options: PromptDef.TextOptions): Future<string>initial pre-fills the input, placeholder shows dim guide text while empty, and validate(value) runs on Enter — returning a message keeps the prompt open with that error. Falls back to initial ?? "" when non-interactive.
const email = yield* Prompt.actions.text({
message: 'Email',
placeholder: 'you@example.com',
validate: v => (v.includes('@') ? undefined : 'Enter a valid email'),
})password
Ask for a secret, echoing a mask character instead of the typed text.
password(options: PromptDef.PasswordOptions): Future<string>The typed value is hidden behind mask (default •); an empty mask hides input entirely. validate runs on Enter. Falls back to an empty string when non-interactive.
const token = yield* Prompt.actions.password({
message: 'API token',
validate: v => (v.length >= 20 ? undefined : 'Too short'),
})number
Ask for a number, restricting keystrokes and enforcing range/integer rules.
number(options: PromptDef.NumberOptions): Future<number>Only digits (and -, plus . when float is set) are accepted. On Enter the value is parsed and checked against min/max, integer-ness (unless float), then validate. Falls back to initial ?? 0 when non-interactive.
const port = yield* Prompt.actions.number({
message: 'Port',
initial: 3000,
min: 1,
max: 65535,
})path
Ask for a filesystem path with Tab completion and a live directory listing.
path(options: PromptDef.PathOptions): Future<string>Resolves relative input against root (default process.cwd()). Tab completes to the longest common prefix of matching entries (appending / for a single directory). Matching entries are listed live; set directory to offer only directories. Falls back to initial ?? "" when non-interactive.
const dir = yield* Prompt.actions.path({
message: 'Output directory',
directory: true,
})Choice prompts
Pick one or more values from a list of `PromptDef.Choice`s.
confirm
Ask a yes/no question; returns a boolean.
confirm(options: PromptDef.ConfirmOptions): Future<boolean>Starts at initial (default false), shown as (y/N) or (Y/n). Arrow/Tab/Space toggle the choice; pressing y or n submits immediately. Falls back to initial when non-interactive.
const ok = yield* Prompt.actions.confirm({
message: 'Overwrite existing file?',
initial: false,
})select
Pick one value from a list; returns the chosen `value`.
select<T>(options: PromptDef.SelectOptions<T>): Future<T>Up/Down move the highlight (skipping disabled choices), Enter selects. initial may be an index or a value. The list paginates at 10 rows and shows each choice’s hint. Falls back to the initial choice, or fails with NotInteractive if none is available.
const env = yield* Prompt.actions.select({
message: 'Environment',
choices: [
{ value: 'dev', hint: 'local' },
{ value: 'prod', label: 'Production' },
],
})multiselect
Pick several values with checkboxes; returns the chosen values.
multiselect<T>(options: PromptDef.MultiSelectOptions<T>): Future<T[]>Space toggles the highlighted checkbox, Enter submits. initial pre-selects indices or values; required, min and max are enforced on submit with an inline error. Falls back to the resolved initial selection when non-interactive.
const features = yield* Prompt.actions.multiselect({
message: 'Enable features',
min: 1,
choices: [
{ value: 'ssr' },
{ value: 'pwa' },
{ value: 'i18n' },
],
})autocomplete
Filter a list as you type, then pick a match; returns the chosen `value`.
autocomplete<T>(options: PromptDef.AutocompleteOptions<T>): Future<T>Typing filters the choices (default: case-insensitive substring on the label); Up/Down cycle through matches (wrapping), Enter selects. Provide a custom suggest(input, choices) to control matching. Matches paginate at 8 rows. Falls back to the first choice, or fails with NotInteractive if there are none.
const pkg = yield* Prompt.actions.autocomplete({
message: 'Package',
placeholder: 'type to search',
choices: packages.map(name => ({ value: name })),
})