Skip to content

Inquirer.js prompts ​

This API is available since Optique 1.0.0.

The @optique/inquirer package wraps any Optique parser with an interactive Inquirer.js prompt. When the user provides a value via CLI, that value is used directly. When the argument is absent, an interactive prompt is shown instead of failing.

This package is built on the shared @optique/prompt adapter foundation. If you want to connect another prompt library, see prompt adapters.

The fallback priority is:

  1. CLI argument
  2. Inquirer.js prompt

Because Inquirer.js prompts are inherently asynchronous, the returned parser always has mode: "async".

deno add jsr:@optique/inquirer
npm add @optique/inquirer
pnpm add @optique/inquirer
yarn add @optique/inquirer
bun add @optique/inquirer

Basic usage ​

Wrap any parser with prompt() and provide a prompt configuration object:

import { 
object
} from "@optique/core/constructs";
import {
option
} from "@optique/core/primitives";
import {
integer
,
string
} from "@optique/core/valueparser";
import {
prompt
} from "@optique/inquirer";
import {
run
} from "@optique/run";
const
parser
=
object
({
name
:
prompt
(
option
("--name",
string
()), {
type
: "input",
message
: "Enter your name:",
}),
port
:
prompt
(
option
("--port",
integer
()), {
type
: "number",
message
: "Enter the port number:",
default
: 3000,
}), }); await
run
(
parser
);

When --name and --port are provided on the command line, the prompts are skipped. When they are absent, the user sees Inquirer.js prompts.

Prompt types ​

input—free-text string ​

Prompts the user for an arbitrary string value:

import { 
option
} from "@optique/core/primitives";
import {
string
} from "@optique/core/valueparser";
import {
prompt
} from "@optique/inquirer";
const
name
=
prompt
(
option
("--name",
string
()), {
type
: "input",
message
: "Enter your name:",
default
: "World",
validate
: (
value
) =>
value
.
length
> 0 || "Name cannot be empty.",
});

input properties

message
(required) The question to display.
default
Pre-filled text shown in the input field.
validate
Function called when the user submits. Return true to accept or a string error message to reject and re-prompt.

confirm—Boolean yes/no ​

Prompts the user with a yes/no question:

import { 
flag
} from "@optique/core/primitives";
import {
prompt
} from "@optique/inquirer";
const
verbose
=
prompt
(
flag
("--verbose"), {
type
: "confirm",
message
: "Enable verbose output?",
default
: false,
});

confirm properties

message
(required) The question to display.
default
Default answer when the user presses Enter without typing.

number—numeric input ​

Prompts the user for a number:

import { 
option
} from "@optique/core/primitives";
import {
integer
} from "@optique/core/valueparser";
import {
prompt
} from "@optique/inquirer";
const
port
=
prompt
(
option
("--port",
integer
()), {
type
: "number",
message
: "Enter the port:",
default
: 8080,
min
: 1,
max
: 65535,
});

number properties

message
(required) The question to display.
default
Default number shown to the user.
min, max
Accepted value range.
step
Granularity of valid values. Use "any" for arbitrary decimals.

NOTE

If the user submits the prompt without entering a number (leaving it blank), the result is a parse failure rather than undefined.

password—masked input ​

Prompts for a secret value without displaying the characters:

import { 
option
} from "@optique/core/primitives";
import {
string
} from "@optique/core/valueparser";
import {
prompt
} from "@optique/inquirer";
const
apiKey
=
prompt
(
option
("--api-key",
string
()), {
type
: "password",
message
: "Enter your API key:",
mask
: true,
});

password properties

message
(required) The question to display.
mask
When true, show * for each keystroke. When false or omitted, input is completely hidden.
validate
Same as input.

editor—multi-line text ​

Opens the user's $VISUAL or $EDITOR for multi-line input:

import { 
option
} from "@optique/core/primitives";
import {
string
} from "@optique/core/valueparser";
import {
prompt
} from "@optique/inquirer";
const
message
=
prompt
(
option
("--message",
string
()), {
type
: "editor",
message
: "Write your commit message:",
default
: "",
validate
: (
value
) =>
value
.
trim
().
length
> 0 || "Message cannot be empty.",
});

editor properties

message
(required) The question to display.
default
Content pre-filled in the editor buffer.
validate
Same as input.

select—arrow-key single-select ​

Shows a scrollable list where the user selects one option using arrow keys:

import { 
option
} from "@optique/core/primitives";
import {
string
} from "@optique/core/valueparser";
import {
prompt
} from "@optique/inquirer";
const
env
=
prompt
(
option
("--env",
string
()), {
type
: "select",
message
: "Choose the deployment environment:",
choices
: ["development", "staging", "production"],
default
: "development",
});

Choices can also be objects with display names and descriptions:

import { 
option
} from "@optique/core/primitives";
import {
string
} from "@optique/core/valueparser";
import {
prompt
,
Separator
} from "@optique/inquirer";
const
color
=
prompt
(
option
("--color",
string
()), {
type
: "select",
message
: "Choose a color:",
choices
: [
{
value
: "red",
name
: "Red",
description
: "A warm primary color" },
{
value
: "green",
name
: "Green",
description
: "A cool secondary color" },
new
Separator
("──────────"),
{
value
: "custom",
name
: "Custom…",
disabled
: "Coming soon" },
], });

select properties

message
(required) The question to display.
choices
(required) Array of strings, Choice objects, or Separator instances.
default
Initially highlighted choice value.

rawlist—numbered list ​

Shows a numbered list and prompts the user to type a number:

import { 
option
} from "@optique/core/primitives";
import {
string
} from "@optique/core/valueparser";
import {
prompt
} from "@optique/inquirer";
const
format
=
prompt
(
option
("--format",
string
()), {
type
: "rawlist",
message
: "Choose the output format:",
choices
: ["json", "yaml", "toml"],
});

rawlist properties

message
(required) The question to display.
choices
(required) Array of strings or Choice objects.
default
Pre-selected choice value.

expand—keyboard shortcut single-select ​

Prompts the user to press a single key to select an option:

import { 
option
} from "@optique/core/primitives";
import {
string
} from "@optique/core/valueparser";
import {
prompt
} from "@optique/inquirer";
const
action
=
prompt
(
option
("--action",
string
()), {
type
: "expand",
message
: "What do you want to do?",
choices
: [
{
value
: "overwrite",
name
: "Overwrite",
key
: "o" },
{
value
: "skip",
name
: "Skip",
key
: "s" },
{
value
: "abort",
name
: "Abort",
key
: "a" },
], });

expand properties

message
(required) The question to display.
choices
(required) Array of ExpandChoice objects, each with a single lowercase alphanumeric key.
default
Default choice key.

checkbox—multi-select ​

Shows a scrollable list where the user toggles multiple options with Space:

import { 
option
} from "@optique/core/primitives";
import {
string
} from "@optique/core/valueparser";
import {
multiple
} from "@optique/core/modifiers";
import {
prompt
} from "@optique/inquirer";
const
tags
=
prompt
(
multiple
(
option
("--tag",
string
())), {
type
: "checkbox",
message
: "Select tags:",
choices
: ["typescript", "deno", "node", "bun"],
});

The inner parser must produce readonly string[], so use multiple() around an option or argument parser.

checkbox properties

message
(required) The question to display.
choices
(required) Array of strings, Choice objects, or Separator instances.

Prompt-only values ​

When a value should only come from a prompt (no CLI flag at all), pair prompt() with fail<T>():

import { 
object
} from "@optique/core/constructs";
import {
fail
} from "@optique/core/primitives";
import {
prompt
} from "@optique/inquirer";
const
parser
=
object
({
name
:
prompt
(
fail
<string>(), {
type
: "input",
message
: "Enter your name:",
}),
confirm
:
prompt
(
fail
<boolean>(), {
type
: "confirm",
message
: "Are you sure?",
default
: false,
}), });

fail() always fails the CLI parse, so the prompt runs unconditionally.

Optional prompts ​

Wrap the inner parser with optional() to allow the user to skip the prompt via CLI while still showing a prompt when the flag is absent. This is equivalent to any other prompt() usage—optional() is handled transparently:

import { 
option
} from "@optique/core/primitives";
import {
optional
} from "@optique/core/modifiers";
import {
string
} from "@optique/core/valueparser";
import {
prompt
} from "@optique/inquirer";
const
description
=
prompt
(
optional
(
option
("--description",
string
())), {
type
: "input",
message
: "Enter a description (or press Enter to skip):",
});

NOTE

In this case, if the user just presses Enter at the prompt, the returned value is an empty string "", not undefined. To get undefined when the user leaves the field blank, use validate to reject empty input or handle the empty string in your application.

Composing with other integrations ​

prompt() composes naturally with bindEnv() and bindConfig(). The innermost wrapper is evaluated first, so nesting order determines fallback priority. This works the same inside object(), tuple(), merge(), and concat(), including dependency-aware suggest*() flows.

For example, to fall back to an environment variable before prompting:

import { 
object
} from "@optique/core/constructs";
import {
option
} from "@optique/core/primitives";
import {
string
} from "@optique/core/valueparser";
import {
bindEnv
,
createEnvContext
} from "@optique/env";
import {
prompt
} from "@optique/inquirer";
import {
run
} from "@optique/run";
const
envContext
=
createEnvContext
({
prefix
: "MYAPP_" });
const
parser
=
object
({
apiKey
:
prompt
(
bindEnv
(
option
("--api-key",
string
()), {
context
:
envContext
,
key
: "API_KEY",
parser
:
string
(),
}), {
type
: "password",
message
: "Enter your API key:",
mask
: true,
}, ), }); await
run
(
parser
, {
contexts
: [
envContext
] });

This gives the priority:

CLI argument > Environment variable > Inquirer.js prompt

Conditional prompt skipping ​

Use when with a matching otherwise value when a prompt depends on a runtime capability. This example asks about GitHub CLI integration only when the gh executable is available:

import { 
execFile
} from "node:child_process";
import {
promisify
} from "node:util";
import {
fail
} from "@optique/core/primitives";
import {
prompt
} from "@optique/inquirer";
const
execFileAsync
=
promisify
(
execFile
);
async function
commandExists
(
command
: string):
Promise
<boolean> {
try { await
execFileAsync
(
command
, ["--version"]);
return true; } catch { return false; } } const
useGitHubCli
=
prompt
(
fail
<boolean>(), {
type
: "confirm",
message
: "Use GitHub CLI?",
default
: true,
when
: () =>
commandExists
("gh"),
otherwise
: false,
});

The condition runs only when an actual parse reaches this fallback. CLI values and configured sources take priority without running it, and Optique also skips it while generating help, version output, or shell completion. When when returns false, otherwise is returned without opening an Inquirer.js prompt. If the condition throws or rejects, the error propagates to the caller.

Dependency-derived configurations ​

This API is available since Optique 1.3.0.

derivePromptConfig(), re-exported from @optique/prompt, derives a prompt configuration from dependency source values, so a later question can adapt its choices to earlier answers:

import { 
object
} from "@optique/core/constructs";
import {
dependency
} from "@optique/core/dependency";
import {
option
} from "@optique/core/primitives";
import {
choice
} from "@optique/core/valueparser";
import {
derivePromptConfig
,
prompt
} from "@optique/inquirer";
const
framework
=
dependency
(
choice
(["fresh", "hono"] as
const
));
const
packageManager
=
choice
(["deno", "npm", "pnpm"] as
const
);
const
parser
=
object
({
framework
:
prompt
(
option
("--framework",
framework
), {
type
: "select",
message
: "Web framework:",
choices
: ["fresh", "hono"],
}),
packageManager
:
prompt
(
option
("--package-manager",
packageManager
),
derivePromptConfig
(
framework
, (
value
) => ({
type
: "select",
message
: "Package manager:",
choices
:
value
=== "fresh" ? ["deno"] : ["npm", "pnpm"],
})), ), });

This changes the choices shown by the prompt, not the values accepted from the command line: --package-manager deno is still valid with --framework hono. If the framework should also constrain CLI input, derive the wrapped value parser from framework. Wrap that derived parser with dependency() only when its result must serve another dependency consumer.

The resolver may be synchronous or asynchronous and runs right before the prompt opens, after the named sources have published their values—whether they came from the command line, a binding, or another prompt. See the @optique/prompt documentation for declared defaults, failure behavior, and the runtime condition form.

Choices loaded at prompt time ​

This API is available since Optique 1.4.0.

derivePromptConfig() also accepts a resolver without any dependency source. Use it when the choices have to be fetched, for example from a remote service. The resolver runs only when the prompt is about to open, so --key on the command line, --help, and shell completion never trigger the request:

import { 
option
} from "@optique/core/primitives";
import {
string
} from "@optique/core/valueparser";
import {
derivePromptConfig
,
prompt
} from "@optique/inquirer";
const
key
=
prompt
(
option
("--key",
string
()),
derivePromptConfig
(async ({
signal
}) => ({
type
: "select",
message
: "Object to download:",
choices
: await
listObjects
({
signal
}),
})), );

The resolver receives the signal passed in the shared options, if any; forward it to the request so that an abort also cancels the fetch. When you store the derived configuration in a variable before passing it to prompt(), add satisfies SelectConfig to the returned object so that type: "select" does not widen to string.

Shared validation, retries, and aborts ​

This API is available since Optique 1.3.0.

Pass shared prompt options as the third argument to prompt() when a prompted value needs a recoverable check. The validator may be synchronous or asynchronous. Return undefined to accept the value, or a structured Message to show an explanation and open the prompt again:

import { 
message
} from "@optique/core/message";
import {
option
} from "@optique/core/primitives";
import {
string
} from "@optique/core/valueparser";
import {
prompt
} from "@optique/inquirer";
const
environment
=
prompt
(
option
("--environment",
string
()), {
type
: "select",
message
: "Choose the deployment environment:",
choices
: ["development", "staging", "production"],
}, {
maxAttempts
: 3,
async
validate
(
value
) {
return await
canDeployTo
(
value
)
?
undefined
:
message
`Cannot deploy to ${
value
}.`;
}, });

This shared validator runs after Inquirer.js returns a value and works with every prompt type. The input, password, and editor configs also retain their native validate callbacks. Native validation keeps the same Inquirer.js prompt open, so rejected native submissions do not advance the shared attempt count.

Before retrying a built-in Inquirer.js prompt, Optique writes the previous shared validation message to stderr. A custom prompter receives that message in its attempt context and is responsible for displaying it. maxAttempts must be a positive integer; when the limit is reached, the last validation message becomes the parse failure. Omitting the limit allows retries until the answer passes or the prompt ends.

Pass an AbortSignal as signal to stop an active prompt, validator, or derived configuration resolver. The parse rejects with the signal's reason. User cancellation, such as Ctrl+C, instead produces a Prompt cancelled. parse failure. Unexpected errors from Inquirer.js, a custom prompter, or a shared validator propagate unchanged.

Testing ​

All prompt configuration types accept an optional prompter property for testing. It receives the shared attempt context and runs instead of an interactive Inquirer.js prompt:

import { 
parseAsync
} from "@optique/core/parser";
import {
message
} from "@optique/core/message";
import {
option
} from "@optique/core/primitives";
import {
string
} from "@optique/core/valueparser";
import {
prompt
} from "@optique/inquirer";
const
answers
= ["", "Alice"];
const
parser
=
prompt
(
option
("--name",
string
()), {
type
: "input",
message
: "Enter your name:",
prompter
: ({
attempt
}) =>
Promise
.
resolve
(
answers
[
attempt
- 1] ?? "Alice"),
}, {
validate
: (
value
) =>
value
=== "" ?
message
`Enter a name.` :
undefined
,
}); const
result
= await
parseAsync
(
parser
, []);
// result.value === "Alice"

API reference ​

prompt(parser, config, options?) ​

Wraps a parser with an Inquirer.js prompt fallback.

Parameters
  • parser: The inner parser. CLI tokens consumed by this parser suppress the prompt.
  • config: A PromptConfig<T> object specifying the prompt type and its options, or a configuration derived from dependency sources with derivePromptConfig() (re-exported from @optique/prompt since 1.3.0). A derived configuration's resolver may return any RuntimePromptConfig member; see the @optique/prompt documentation for the resolver contract.
  • options: Shared validation, retry-limit, and abort options. See PromptOptions<T>.
Returns
A new parser with mode: "async" and Inquirer.js prompt fallback. The usage is wrapped in an optional term since the prompt handles the missing-value case.
Throws
RangeError when options.maxAttempts is not a positive integer.

PromptConfig<T> ​

A conditional type that maps a parser's value type T to the appropriate prompt configuration union. Every variant also accepts either both when and otherwise, or neither. when may be synchronous or asynchronous, and otherwise must match T.

Value typeAccepted config type
booleanConfirmConfig
numberNumberPromptConfig
stringInputConfig | PasswordConfig | EditorConfig | SelectConfig | RawlistConfig | ExpandConfig
readonly string[]CheckboxConfig

Optional variants (boolean | undefined, string | undefined, etc.) map to the same config types as their non-optional counterparts.

RuntimePromptConfig ​

Available since Optique 1.3.0.

The union of every prompt configuration this package can execute, regardless of the parser value type. A derivePromptConfig() resolver returns a member of this union; the per-value-type narrowing of PromptConfig<T> applies only to static configurations. The resolver must therefore return a prompt kind whose result has the wrapped parser's value type. For example, do not return a number configuration for a parser that produces a string. See Prompt and inner parser independence.

PromptValidator<T> ​

Available since Optique 1.3.0.

Re-exported from @optique/prompt. It validates a returned prompt value and returns undefined to accept it or a Message to retry. See the @optique/prompt API reference.

PromptOptions<T> ​

Available since Optique 1.3.0.

Re-exported from @optique/prompt. It supplies the shared validate, maxAttempts, and signal options. See the @optique/prompt API reference.

PromptExecutionContext ​

Available since Optique 1.3.0.

Re-exported from @optique/prompt. A custom prompter receives the one-based attempt number, previous validation message, and optional abort signal through this context. See the @optique/prompt API reference.

Choice ​

An object with value, optional name, description, short, and disabled fields. Used in select, rawlist, and checkbox prompts.

ExpandChoice ​

Like Choice but requires a key field (single lowercase alphanumeric character). Used in expand prompts.

Separator ​

Re-exported from Inquirer.js. Use new Separator(text?) to add visual dividers in select and checkbox choice lists.

Prompt and inner parser independence ​

The CLI path and the prompt path are independent value sources. When a value comes from the CLI, the inner parser's full constraint pipeline (value parsing, choice() domain checks, integer({ min, max }), etc.) is applied. When a value comes from a prompt, it is used as-is—the inner parser's constraints are not re-applied.

This design is intentional: combinators like map() can transform the value domain, making the prompted value incompatible with the inner parser's input path. Treating the two paths independently avoids false rejections and keeps the architecture sound.

Use the shared validate option in the third argument to prompt() for recoverable checks on any prompted value. The input, password, and editor configs also provide native validate callbacks for feedback within one Inquirer.js prompt.

Matching constraints between CLI and prompt ​

When the inner parser carries constraints, you should mirror them in the prompt config.

number prompt with integer() semantics

Use step: 1 to restrict the prompt to integers, and min/max to match the inner parser's range.

import { 
option
} from "@optique/core/primitives";
import {
integer
} from "@optique/core/valueparser";
import {
prompt
} from "@optique/inquirer";
const
port
=
prompt
(
option
("--port",
integer
({
min
: 1024,
max
: 65535 })), {
type
: "number",
message
: "Enter the port:",
min
: 1024,
max
: 65535,
step
: 1,
});
input prompt with string({ pattern }) semantics

Use validate to enforce the same pattern.

import { 
option
} from "@optique/core/primitives";
import {
string
} from "@optique/core/valueparser";
import {
prompt
} from "@optique/inquirer";
const
id
=
prompt
(
option
("--id",
string
({
pattern
: /^[A-Z]{3}-\d+$/ })), {
type
: "input",
message
: "Enter the ID:",
validate
: (
value
) =>
/^[A-Z]{3}-\d+$/.
test
(
value
) || "Must match AAA-123 format.",
});
select/rawlist/expand with choice() values

Keep the prompt choices array consistent with the inner parser's choice() domain. Ensuring this consistency is the caller's responsibility.

import { 
option
} from "@optique/core/primitives";
import {
choice
} from "@optique/core/valueparser";
import {
prompt
} from "@optique/inquirer";
const
env
=
prompt
(
option
("--env",
choice
(["dev", "staging", "prod"])), {
type
: "select",
message
: "Choose environment:",
choices
: ["dev", "staging", "prod"], // must match choice() values
});
checkbox with multiple() cardinality

Use shared validation to mirror cardinality constraints from multiple(..., { min, max }):

import { 
message
} from "@optique/core/message";
import {
option
} from "@optique/core/primitives";
import {
multiple
} from "@optique/core/modifiers";
import {
string
} from "@optique/core/valueparser";
import {
prompt
} from "@optique/inquirer";
const
tags
=
prompt
(
multiple
(
option
("--tag",
string
()), {
min
: 2 }),
{
type
: "checkbox",
message
: "Select at least two tags:",
choices
: ["typescript", "deno", "node", "bun"],
}, {
validate
: (
values
) =>
values
.
length
>= 2 ?
undefined
:
message
`Select at least two tags.`,
}, );

IMPORTANT

Shared validation applies only to prompted values. It does not re-run the wrapped parser's value parser, modifiers, or mappings.

Limitations ​

  • Always async — prompt() always returns an async parser because Inquirer.js prompts are inherently asynchronous. This means any object() or other combinator containing a prompt() parser also becomes async.
  • No shell completion — Interactive prompts do not contribute to shell tab-completion suggestions. Only the wrapped inner parser's suggestions are used.
  • Per-occurrence caching — A reached prompt() occurrence completes once per parse. Shared validation may call Inquirer.js or a custom prompter several times inside that completion, but only the terminal result is cached. Reusing one prompt parser at several positions creates a separate occurrence at each position.
  • TTY required: Inquirer.js requires an interactive terminal (TTY). In non-interactive environments (CI pipelines, piped input), prompts will error. Use the prompter override for non-interactive testing.

TIP

See the cookbook for a complete example combining interactive prompts with environment variables and configuration files.