Skip to content

Prompt adapters ​

This API is available since Optique 1.2.0.

The @optique/prompt package provides the shared parser wrapper used by interactive prompt integrations. Most applications should use @optique/inquirer or @optique/clack directly. Reach for this package when you want to connect Optique to another prompt library.

The adapter controls only prompt execution. @optique/prompt handles the parser behavior: CLI values take priority, source bindings such as bindEnv() and bindConfig() can satisfy values before prompting, usage is marked optional, completion and suggestion behavior is preserved, and the returned parser is always async.

Wrapper order determines source-binding priority. With the source binding inside the prompt wrapper, the fallback priority is:

  1. CLI argument
  2. Source binding such as environment variables or config files
  3. Prompt adapter
deno add jsr:@optique/prompt
npm add @optique/prompt
pnpm add @optique/prompt
yarn add @optique/prompt
bun add @optique/prompt

When to use this package ​

Use @optique/prompt when you are publishing or maintaining a prompt integration package. A normal application should usually depend on a concrete integration:

  • @optique/clack for Clack prompts
  • @optique/inquirer for Inquirer.js prompts

The shared wrapper exists so each integration does not need to reimplement the same parser semantics. Your integration supplies a config type and an execute() function; @optique/prompt supplies the prompt(parser, config, options?) wrapper.

Basic usage ​

Create an adapter with createPromptAdapter(), then use the returned prompt() wrapper around any parser:

import { 
option
} from "@optique/core/primitives";
import {
string
} from "@optique/core/valueparser";
import {
createPromptAdapter
} from "@optique/prompt";
interface DemoPromptConfig { readonly
message
: string;
readonly
value
: string;
} const
prompt
=
createPromptAdapter
<DemoPromptConfig>({
async
execute
<
TValue
>(
config
: DemoPromptConfig) {
// A real adapter would call a prompt library here. return {
success
: true,
value
:
config
.
value
as
TValue
};
}, }); const
name
=
prompt
(
option
("--name",
string
()), {
message
: "Name:",
value
: "Alice",
});

If --name Alice is provided on the command line, the adapter is not called. If the CLI value is absent, the adapter runs during parser completion.

The generated wrapper is a fluent async parser, so it still supports modifier methods such as map():

import { 
option
} from "@optique/core/primitives";
import {
string
} from "@optique/core/valueparser";
import {
createPromptAdapter
} from "@optique/prompt";
interface PromptConfig { readonly
value
: string;
} const
prompt
=
createPromptAdapter
<PromptConfig>({
async
execute
<
TValue
>(
config
: PromptConfig) {
return {
success
: true,
value
:
config
.
value
as
TValue
};
}, }); const
upperName
=
prompt
(
option
("--name",
string
()), {
value
: "Alice",
}).
map
((
value
) =>
value
.
toUpperCase
());
upperName
.
mode
;
// `upperName` is a fluent async parser

Writing an adapter ​

An adapter usually has three layers:

  • Config types: Public types that match the prompt library's terminology.
  • Execution mapping: Code that calls the prompt library and translates its result into Optique's ValueParserResult<TValue> shape.
  • Wrapper export: The prompt() function returned by createPromptAdapter().

The config type can be as narrow or broad as your prompt library requires. A small string-only adapter might look like this:

import { 
message
} from "@optique/core/message";
import {
createPromptAdapter
} from "@optique/prompt";
interface TextConfig { readonly
type
: "text";
readonly
message
: string;
readonly
default
?: string;
readonly
promptText
: (
message
: string) =>
Promise
<string | null>;
} export const
prompt
=
createPromptAdapter
<TextConfig>({
async
execute
<
TValue
>(
config
: TextConfig) {
const
value
= await
config
.
promptText
(
config
.
message
);
if (
value
== null) {
return {
success
: false,
error
:
message
`Prompt cancelled.` };
} return {
success
: true,
value
:
value
as
TValue
};
}, });

Concrete integrations can keep their own naming conventions. For example, @optique/inquirer uses Inquirer.js-style input and checkbox names, while @optique/clack uses Clack-style text and multiselect names.

Adapter contract ​

createPromptAdapter(adapter) accepts a small object:

execute(config, context)
Runs the prompt library and returns a ValueParserResult<TValue>. Return { success: true, value } for a prompted value, or { success: false, error } for a prompt-level failure such as cancellation. context.attempt identifies this execution, starting from 1. After shared validation rejects a value, context.previousValidationMessage contains that message on the next execution. An adapter should display it before asking again. context.signal, when present, should also be forwarded to prompt-library APIs that support cancellation.
getDefaultValue(config)
(optional) Returns a config default for documentation fragments. If it is omitted, object configs with a default property use that value.

Prompt failures and thrown errors ​

Use a failed ValueParserResult for expected prompt outcomes that should be reported as parse failures:

import { 
message
} from "@optique/core/message";
import {
createPromptAdapter
} from "@optique/prompt";
interface PromptConfig { readonly
cancelled
: boolean;
} const
prompt
=
createPromptAdapter
<PromptConfig>({
async
execute
<
TValue
>(
config
: PromptConfig) {
if (
config
.
cancelled
) {
return {
success
: false,
error
:
message
`Prompt cancelled.` };
} return {
success
: true,
value
: "value" as
TValue
};
}, });

Let unexpected prompt-library errors throw. The generated parser does not turn thrown exceptions into parse failures; they propagate to the caller.

Generated parser behavior ​

The generated prompt(parser, config, options?) wrapper preserves the inner parser's shape while changing how missing values are completed.

CLI values skip prompting ​

The inner parser is tried first. If it consumes CLI tokens, its completed value is used and the adapter is not called:

import { 
parseAsync
} from "@optique/core/parser";
import {
option
} from "@optique/core/primitives";
import {
string
} from "@optique/core/valueparser";
import {
createPromptAdapter
} from "@optique/prompt";
const
calls
: string[] = [];
interface PromptConfig { readonly
value
: string;
} const
prompt
=
createPromptAdapter
<PromptConfig>({
async
execute
<
TValue
>(
config
: PromptConfig) {
calls
.
push
(
config
.
value
);
return {
success
: true,
value
:
config
.
value
as
TValue
};
}, }); const
parser
=
prompt
(
option
("--name",
string
()), {
value
: "Prompted" });
const
result
= await
parseAsync
(
parser
, ["--name", "Alice"]);
// result.value === "Alice" // calls.length === 0

Source bindings can skip prompting ​

When the wrapped parser is also bound to another source, that source is checked before prompting. This lets concrete prompt integrations compose with bindEnv() and bindConfig():

import { 
parseAsync
} from "@optique/core/parser";
import {
option
} from "@optique/core/primitives";
import {
string
} from "@optique/core/valueparser";
import {
bindEnv
,
createEnvContext
} from "@optique/env";
import {
createPromptAdapter
} from "@optique/prompt";
const
envContext
=
createEnvContext
({
prefix
: "MYAPP_",
source
: (
key
) => ({
MYAPP_NAME
: "EnvName" })[
key
],
}); const
annotations
=
envContext
.
getAnnotations
();
interface PromptConfig { readonly
value
: string;
} const
prompt
=
createPromptAdapter
<PromptConfig>({
async
execute
<
TValue
>(
config
: PromptConfig) {
return {
success
: true,
value
:
config
.
value
as
TValue
};
}, }); const
parser
=
prompt
(
bindEnv
(
option
("--name",
string
()), {
context
:
envContext
,
key
: "NAME",
parser
:
string
(),
}), {
value
: "PromptName" },
); if (!(
annotations
instanceof
Promise
)) {
const
result
= await
parseAsync
(
parser
, [], {
annotations
});
// result.value === "EnvName" }

This gives the priority:

CLI argument > Environment variable > Prompt adapter

Runtime conditions can skip prompting ​

Add when and otherwise to a prompt config when the fallback depends on a runtime capability. when can return a Boolean or a promise of one. When it returns false, the adapter is not called and otherwise becomes the parsed value:

import { 
parseAsync
} from "@optique/core/parser";
import {
option
} from "@optique/core/primitives";
import {
string
} from "@optique/core/valueparser";
import {
createPromptAdapter
} from "@optique/prompt";
interface PromptConfig { readonly
value
: string;
} const
prompt
=
createPromptAdapter
<PromptConfig>({
async
execute
<
TValue
>(
config
: PromptConfig) {
return {
success
: true,
value
:
config
.
value
as
TValue
};
}, }); const
parser
=
prompt
(
option
("--token",
string
()), {
value
: "prompted token",
when
:
canPromptSecurely
,
otherwise
: "",
}); const
result
= await
parseAsync
(
parser
, []);

The condition is evaluated only when an actual parse reaches the prompt fallback. CLI values, source bindings, help, version output, completion probes, and shell suggestions do not run it. Each fallback evaluates its condition at most once per parse. A thrown or rejected condition error propagates to the caller; it is not treated as prompt cancellation or a parse failure.

otherwise is a static value with the parser's result type. It is returned as-is, without running the inner parser's validation or normalization, and it is not used as a documented default.

Shared validation and retries ​

Pass a validator in the wrapper's third argument when a prompted value needs a recoverable check. It receives the typed value returned by the adapter. Return undefined to accept the value, or a Message to reject it and run another adapter execution:

import { 
message
} from "@optique/core/message";
import {
option
} from "@optique/core/primitives";
import {
string
} from "@optique/core/valueparser";
import {
createPromptAdapter
,
type PromptExecutionContext, } from "@optique/prompt"; interface PromptConfig { readonly
message
: string;
} const
prompt
=
createPromptAdapter
<PromptConfig>({
async
execute
<
TValue
>(
_config
: PromptConfig,
context
: PromptExecutionContext,
) { const
value
= await
askPackageManager
(
context
.
attempt
);
return {
success
: true,
value
:
value
as
TValue
};
}, }); const
packageManager
=
prompt
(
option
("--package-manager",
string
()),
{
message
: "Package manager:" },
{
maxAttempts
: 3,
async
validate
(
value
) {
return await
commandExists
(
value
)
?
undefined
:
message
`${
value
} is not available.`;
}, }, );

Each execute() call is one attempt inside a single prompt completion. A validator may be synchronous or asynchronous. Adapter-native validation stays inside an attempt, while shared validation runs only after the adapter returns a successful value. Cancellation or another failed ValueParserResult ends the completion without another retry. Exceptions from the adapter or validator propagate unchanged.

maxAttempts must be a positive integer and defaults to no limit. Exhausting the limit returns the last validation message as a parse failure. A derived prompt configuration resolves only once, and every attempt receives that same resolved config. Rejected values are not cached or published as dependency values; only the terminal result reaches the existing completion cache.

An optional AbortSignal stops an active adapter execution, validator, or derived configuration resolver and propagates its reason. CLI values, source-bound values, a false runtime condition, help, suggestions, and completion probes do not consult the signal. The signal does not add general cancellation to parsing or dependency scheduling. If it aborts while a resolver is pending, parsing rejects with its reason right away instead of waiting for the resolver to settle. The resolver receives the same signal in its context and should pass it on to cancellable work such as network requests; otherwise that work keeps running in the background after the prompt has been abandoned.

IMPORTANT

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

Missing values run the adapter ​

If the inner parser does not consume CLI tokens and no source binding supplies a value, the adapter runs during completion:

import { 
parseAsync
} from "@optique/core/parser";
import {
option
} from "@optique/core/primitives";
import {
string
} from "@optique/core/valueparser";
import {
createPromptAdapter
} from "@optique/prompt";
interface PromptConfig { readonly
value
: string;
} const
prompt
=
createPromptAdapter
<PromptConfig>({
async
execute
<
TValue
>(
config
: PromptConfig) {
return {
success
: true,
value
:
config
.
value
as
TValue
};
}, }); const
parser
=
prompt
(
option
("--name",
string
()), {
value
: "Bob" });
const
result
= await
parseAsync
(
parser
, []);
// result.value === "Bob"

Prompt-only values ​

When a value should only come from a prompt, wrap fail<T>():

import { 
fail
} from "@optique/core/primitives";
import {
createPromptAdapter
} from "@optique/prompt";
interface PromptConfig { readonly
value
: string;
} const
prompt
=
createPromptAdapter
<PromptConfig>({
async
execute
<
TValue
>(
config
: PromptConfig) {
return {
success
: true,
value
:
config
.
value
as
TValue
};
}, }); const
secret
=
prompt
(
fail
<string>(), {
value
: "from prompt" });

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

Optional and repeated values ​

The wrapper works with parser modifiers such as optional() and multiple():

import { 
multiple
,
optional
} from "@optique/core/modifiers";
import {
option
} from "@optique/core/primitives";
import {
string
} from "@optique/core/valueparser";
import {
createPromptAdapter
} from "@optique/prompt";
interface PromptConfig { readonly
value
: unknown;
} const
prompt
=
createPromptAdapter
<PromptConfig>({
async
execute
<
TValue
>(
config
: PromptConfig) {
return {
success
: true,
value
:
config
.
value
as
TValue
};
}, }); const
description
=
prompt
(
optional
(
option
("--description",
string
())), {
value
: "prompted description",
}); const
tags
=
prompt
(
multiple
(
option
("--tag",
string
())), {
value
: ["typescript", "deno"],
});

For repeated values, your prompt config type should return the same value shape as the wrapped parser, such as readonly string[] for multiple(option("--tag", string())).

Defaults and documentation ​

getDefaultValue(config) affects documentation fragments, not parse fallback behavior. It lets an integration pass a prompt-level default to the wrapped parser so generated help can show it consistently.

If getDefaultValue is omitted, @optique/prompt reads a default property from object-shaped configs:

import { 
option
} from "@optique/core/primitives";
import {
string
} from "@optique/core/valueparser";
import {
createPromptAdapter
} from "@optique/prompt";
interface ConfigWithDefault { readonly
message
: string;
readonly
default
?: string;
} const
prompt
=
createPromptAdapter
<ConfigWithDefault>({
async
execute
<
TValue
>(
config
: ConfigWithDefault) {
return {
success
: true,
value
: (
config
.
default
?? "") as
TValue
};
}, }); const
name
=
prompt
(
option
("--name",
string
()), {
message
: "Name:",
default
: "Alice",
});

Use getDefaultValue when your prompt library uses another property name, such as Clack's initialValue:

import { 
createPromptAdapter
} from "@optique/prompt";
interface ConfigWithInitialValue { readonly
message
: string;
readonly
initialValue
?: string;
} const
prompt
=
createPromptAdapter
<ConfigWithInitialValue>({
async
execute
<
TValue
>(
config
: ConfigWithInitialValue) {
return {
success
: true,
value
: (
config
.
initialValue
?? "") as
TValue
};
},
getDefaultValue
(
config
: ConfigWithInitialValue) {
return
config
.
initialValue
;
}, });

NOTE

withDefault() inside a prompt wrapper does not replace the prompt fallback. Missing CLI values still run the adapter. Put prompt defaults in your prompt config and expose them with getDefaultValue() when you want them reflected in help text.

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 whatever your adapter returns.

This is intentional: combinators like map() can transform the value domain in ways that are not valid CLI input. An adapter can validate a library-native answer before returning { success: true, value }; callers can then use the shared validate option for recoverable checks on the returned typed value. Neither path feeds a prompted value through the wrapped parser.

For example, a number prompt adapter should parse and validate the prompt's string result before returning a number:

import { 
message
} from "@optique/core/message";
import {
createPromptAdapter
} from "@optique/prompt";
interface NumberConfig { readonly
message
: string;
readonly
promptText
: (
message
: string) =>
Promise
<string>;
} const
promptNumber
=
createPromptAdapter
<NumberConfig>({
async
execute
<
TValue
>(
config
: NumberConfig) {
const
text
= await
config
.
promptText
(
config
.
message
);
const
value
=
Number
(
text
);
if (!
Number
.
isFinite
(
value
)) {
return {
success
: false,
error
:
message
`Enter a number.` };
} return {
success
: true,
value
:
value
as
TValue
};
}, });

Suggestions and usage ​

The generated parser delegates shell-completion suggestions to the wrapped parser. Prompt-only values do not add new shell-completion suggestions.

Usage is also based on the wrapped parser, but @optique/prompt wraps the usage in an optional term when needed. This prevents help text from implying that a missing CLI value is always an error, because the prompt can supply the value interactively.

The wrapper preserves parser metadata used by dependency-aware completions and suggest*() flows. Concrete integrations normally do not need to handle this metadata themselves.

Dependency sources ​

This behavior is available since Optique 1.3.0.

When prompt() wraps a dependency source, the prompted value registers in the dependency runtime, so parsers derived from that source observe the value the user actually selected. A derived parser behaves identically whether its dependency value came from the command line, a source binding, or an interactive prompt.

To make the value available before derived parsers re-evaluate, dependency-source prompts run serially in dependency order before dependency replay. Declaration order breaks ties between independent prompts, while non-source prompts keep running after the other fields complete. As a consequence, a dependency-source prompt may be displayed before a non-source prompt declared earlier in the same object. Structural precedence applies per field: a prompted field whose own value already came from the command line or a source binding does not prompt, while another prompted field sharing the same source still does, and its answer registers last.

Each source-prompt occurrence runs at most once per parse operation, and never during help, shell suggestion, or probe phases. Reusing one prompt parser at several paths creates a separate occurrence at each path. When the user cancels a source prompt, the parse fails immediately and later prompts do not run. A source prompt transformed with map() registers its pre-transform value, and a source prompt nested in a child construct (such as a concat() child tuple) still completes before sibling consumers. A source prompt wrapped in optional() follows optional()'s suppression: an unmatched field resolves to undefined without prompting, just as for a non-source prompt. Note the direction of map(): prompt(...).map(...) registers the pre-transform prompt answer, while prompt() around an already-transformed source cannot recover the pre-transform value and registers nothing. When distinct prompts wrap the same source in duplicate merge() fields, each prompt runs in its own child and the later field's value wins, as with other duplicate fields. Likewise, when several prompted fields share one dependency source, every prompt runs and the last occurrence's value registers, matching how repeated command-line source occurrences overwrite earlier ones.

Under runWith() with two-pass source contexts, each source-prompt occurrence runs at most once per run. During the phase-two seed pass it runs only when another parser's command-line input demands the source value; otherwise it defers to the final pass, and phase-two contexts see the field as deferred.

Inside a conditional(), a prompted source discriminator and the prompted sources of the selected branch both run before sibling derived parsers re-evaluate, even when nothing on the command line selects a branch: the discriminator's answer resolves the branch once, and the same selection is reused when the conditional completes. A prompted discriminator that does not wrap a dependency source cannot take part in this early resolution, so the branch it selects prompts only during the conditional's own completion. Wrap the discriminator's value parser with dependency() when sibling consumers need sources from the selected branch before dependency replay.

One pre-existing limitation carries over: a prompt used directly as an or()/longestMatch() branch never executes when no command-line input matches, because the parse fails before any branch is chosen. Prompts inside a branch still run once other input commits that branch.

Concrete integrations built on createPromptAdapter() get this behavior automatically.

Derived prompt configurations ​

This API is available since Optique 1.3.0.

derivePromptConfig() derives a prompt configuration from one or more dependency source values, so a later prompt can adapt its question to earlier answers. The resolver may return the configuration synchronously or asynchronously, and it runs immediately before the adapter executes, inside the same effectful completion as the prompt itself:

const 
prompt
=
createPromptAdapter
<SelectConfig>({
async
execute
<
TValue
>(
config
: SelectConfig) {
return {
success
: true,
value
: await
promptSelect
(
config
) as
TValue
};
}, }); const
framework
=
dependency
(
choice
(["fresh", "hono"] as
const
));
const
packageManager
=
choice
(["deno", "npm", "pnpm"] as
const
);
const
parser
=
object
({
framework
:
prompt
(
option
("--framework",
framework
), {
message
: "Web framework:",
choices
: ["fresh", "hono"],
}),
packageManager
:
prompt
(
option
("--package-manager",
packageManager
),
derivePromptConfig
(
framework
, (
value
) => ({
message
: "Package manager:",
choices
:
value
=== "fresh" ? ["deno"] : ["npm", "pnpm"],
})), ), });

This example changes the interactive choices, 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 named sources must publish their values before the resolver runs, so the scheduler orders prompts by the dependency graph: in the example above, the framework prompt always runs before the package manager prompt, even if the fields were declared in the opposite order. Declaration order still breaks ties between independent prompts. A configuration may also read several sources at once by passing a tuple; the resolver then receives the values as a tuple in the same order.

The resolver does not care where a dependency value came from: the command line, a source binding, a withDefault() fallback, and an interactive prompt all publish real values. When a source has published nothing, the configuration's own declared default applies instead:

  • With defaultValue (or defaultValues for a tuple), the resolver receives the lazily evaluated fallback, and its context reports usedDefault: true (or the matching usedDefaults position) so the resolver can distinguish an actual answer from a fallback.
  • Without a declared default, an unpublished dependency fails the prompt with a diagnostic naming the missing source; the resolver and the adapter never run.

Failures follow the same rules as a cancelled prompt. A resolver that throws or rejects fails the prompt, and when the prompt is also a dependency source, the failure propagates to its consumers with the full dependency chain in the diagnostic. A failed upstream source likewise fails the prompt before the resolver runs. Mutually dependent configurations are rejected with a circular dependency error.

The optional third argument to derivePromptConfig() accepts the same when/otherwise pair as static configurations, evaluated before the resolver, so a skipped prompt performs no configuration work. Note that upstream sources may already have prompted by then: the condition skips this prompt's own question, not the dependency resolution that scheduled before it.

Because probes, help, and suggestions never run resolvers, generated documentation cannot reflect a derived configuration. getDocFragments falls back to the wrapped parser's static metadata, and the adapter's getDefaultValue() is never called with a derived configuration.

Under runWith() with two-pass source contexts, a prompt with a derived configuration whose wrapped parser is not a dependency source defers during the phase-two seed pass and resolves in the final pass, after every source has published. If phase-two contexts need its value, make the wrapped parser a source with dependency().

Derived configurations also work inside a conditional() whose branch is selected only during completion. Only the selected branch's actual dependencies determine which sources and effects run, which provider each consumer reads, and whether the active graph contains a cycle. A prompt needed only by an unselected or rejected speculative branch does not run.

Values published inside a completion-selected branch serve that branch's derived value parsers and derived prompt configurations without leaking to consumers outside it. An absent optional() occurrence, an unselected nested alternative, or a fill-only default does not hide a later value that was actually published. See Evaluation order and failures for the full branch and repeated-source rules.

Configurations without dependencies ​

This API is available since Optique 1.4.0.

derivePromptConfig() also accepts a resolver alone. This defers the whole configuration to the moment the prompt is about to be shown, which suits choices that have to be fetched, such as objects in a storage bucket or branches on a remote server:

const 
prompt
=
createPromptAdapter
<SelectConfig>({
async
execute
<
TValue
>(
config
: SelectConfig) {
return {
success
: true,
value
: await
promptSelect
(
config
) as
TValue
};
}, }); const
key
=
prompt
(
option
("--key",
string
()),
derivePromptConfig
(async ({
signal
}) => ({
message
: "Object to download:",
choices
: await
listObjects
({
signal
}),
})), );

The resolver follows the same rules as one with dependencies. It does not run when the command line or a source binding supplies the value, or during help, suggestions, and completion probes. It runs at most once per completion, and validation retries reuse the configuration it returned. A when/otherwise pair goes in the optional second argument and is checked before the resolver, so a skipped prompt fetches nothing.

Setting a select-style configuration by returning a plain object literal relies on the prompt() wrapper's expected configuration type. When you assign the derived configuration to a variable before passing it on, that context is gone and a field such as type: "select" widens to string. Mark the returned object with satisfies and the adapter's configuration type, or annotate the resolver's return type, to keep it narrow.

Testing adapters ​

You can test concrete integrations without a TTY by putting an injectable prompt function into your config, or by adding an explicit testing escape hatch such as prompter.

The core behavior to test is:

  • CLI values skip prompt execution.
  • Missing CLI values call execute().
  • Source bindings such as bindEnv() skip prompt execution.
  • Runtime conditions run only at the real prompt fallback.
  • A false runtime condition returns otherwise without calling execute().
  • Shared validation retries with increasing attempt numbers and the preceding message.
  • Retry exhaustion, cancellation, abort, and thrown errors remain distinct terminal outcomes.
  • Prompt failures are returned as parse failures.
  • Prompt fields run serially in dependency order, with parser order breaking ties between independent prompts.

A minimal test adapter can record calls:

import { 
message
} from "@optique/core/message";
import {
option
} from "@optique/core/primitives";
import {
parseAsync
} from "@optique/core/parser";
import {
string
} from "@optique/core/valueparser";
import {
createPromptAdapter
} from "@optique/prompt";
interface
TestConfig
<
TValue
> {
readonly
value
:
TValue
;
readonly
reject
?: boolean;
} const
calls
:
TestConfig
<unknown>[] = [];
const
prompt
=
createPromptAdapter
<
TestConfig
<unknown>>({
async
execute
<
TValue
>(
config
:
TestConfig
<unknown>) {
calls
.
push
(
config
);
if (
config
.
reject
=== true) {
return {
success
: false,
error
:
message
`Prompt rejected.` };
} return {
success
: true,
value
:
config
.
value
as
TValue
};
}, }); const
parser
=
prompt
(
option
("--name",
string
()), {
value
: "Prompted" });
await
parseAsync
(
parser
, ["--name", "Alice"]);
// calls.length === 0

API reference ​

createPromptAdapter(adapter) ​

Creates a prompt(parser, config, options?) wrapper for one prompt library.

Parameters
adapter: A PromptAdapter<TConfig> that executes prompts for your library.
Returns
A function that wraps any parser and always returns a FluentParser<"async", TValue, TState>. Its config accepts the adapter's fields together with PromptCondition<TValue>, or a DerivedPromptConfig whose resolver returns the adapter's config type. The optional third argument accepts PromptOptions<TValue>.
Throws
RangeError when options.maxAttempts is not a positive integer.

PromptCondition<TValue> ​

Shared runtime condition fields accepted by every generated prompt wrapper. Provide both fields or neither:

when
A function returning boolean or Promise<boolean>. The prompt runs when the result is true.
otherwise
The typed value returned when when resolves to false.

PromptAdapter<TConfig> ​

Adapter object accepted by createPromptAdapter().

execute(config, context)
Executes the library-specific prompt and returns a Promise<ValueParserResult<TValue>>. Each call represents one attempt. The context is a PromptExecutionContext.
getDefaultValue(config)
Optional function that returns a prompt-level default for documentation fragments. Never called with a derived configuration.

PromptValidator<TValue> ​

Available since Optique 1.3.0.

Validates the typed value returned by an adapter. It returns undefined to accept the value, or a Message to reject it and request another attempt. A promise of either result is also accepted.

PromptOptions<TValue> ​

Available since Optique 1.3.0.

validate
Optional PromptValidator<TValue> applied after each successful adapter execution.
maxAttempts
Optional positive integer limiting adapter executions within one completion. Omit it for unlimited retries.
signal
Optional AbortSignal for the interactive fallback. Its reason propagates when it stops an active adapter execution, validator, or derived configuration resolver. Resolvers also receive it in their context. Stopping a pending resolver is available since Optique 1.4.0.

PromptExecutionContext ​

Available since Optique 1.3.0.

attempt
One-based number of the current adapter execution.
previousValidationMessage
Message returned by shared validation after the preceding execution. It is absent on the first attempt.
signal
Signal supplied through PromptOptions, when present.

derivePromptConfig(source, resolver, options?) ​

Available since Optique 1.3.0.

Creates a DerivedPromptConfig that resolves the adapter configuration from dependency source values during the real completion phase.

Parameters

source: A dependency source created with dependency(), or a non-empty tuple of such sources.

resolver: Receives the source value (or the tuple of values) and a context object, and returns the adapter configuration synchronously or as a promise. The single-source context has usedDefault: boolean; the tuple context has a positional usedDefaults tuple. A flag is true only when the value came from this configuration's own declared default, not from source-level fallbacks such as withDefault(). Both contexts also carry the optional signal from PromptOptions (since Optique 1.4.0).

options: Optional defaultValue (or defaultValues for a tuple) thunk evaluated lazily for unpublished sources, plus the same when/otherwise pair as static configurations.

Returns

An opaque DerivedPromptConfig accepted by every prompt() wrapper generated by createPromptAdapter().

Throws

TypeError when source is empty or contains a value that is not a dependency source.

derivePromptConfig(resolver, options?) ​

Available since Optique 1.4.0.

Creates a DerivedPromptConfig that resolves the adapter configuration during the real completion phase without reading any dependency source.

Parameters

resolver: Receives a context object with the optional signal from PromptOptions, and returns the adapter configuration synchronously or as a promise.

options: The same optional when/otherwise pair as static configurations.

Returns

An opaque DerivedPromptConfig accepted by every prompt() wrapper generated by createPromptAdapter().

isDerivedPromptConfig(config) ​

Available since Optique 1.3.0.

Returns whether a prompt configuration was created by derivePromptConfig(). Adapters that inspect config objects (for example, in a custom getDefaultValue()) can use it to skip the derived marker, although @optique/prompt already never passes the marker to adapter callbacks.

Implementation checklist ​

When adding a concrete prompt integration, make sure it:

  • Exports a library-specific prompt() created with createPromptAdapter().
  • Uses prompt type names that match the underlying library.
  • Returns failed ValueParserResult values for expected outcomes such as cancellation.
  • Throws only for unexpected prompt-library failures.
  • Validates and converts library-native values before returning success.
  • Accepts shared PromptOptions in its public wrapper and passes the options to the generated prompt function.
  • Displays previousValidationMessage before another attempt and forwards the signal where the prompt library supports it.
  • Exposes prompt-level defaults through getDefaultValue() if the library does not use a default config property.
  • Accepts derivePromptConfig() results in its public prompt() signature, typed so the resolver returns the integration's own config union.
  • Provides a TTY-free testing path.