@listr2/prompt-adapter-inquirer
@listr2/prompt-adapter-inquirer connects modern @inquirer/prompts functions to a running Listr2 task. Listr2 owns the terminal renderer, so calling an Inquirer prompt directly can fight with its spinner and line updates; this adapter redirects prompt output through the task, reports prompt state to the renderer, and supplies an AbortSignal for cancellation. It is not a prompt library by itself. You still install @inquirer/prompts, import input, select, confirm, or another prompt function, and invoke it through task.prompt(ListrInquirerPromptAdapter).run(...).
Install this only as the narrow bridge between Listr2 11 and @inquirer/prompts; it is the recommended adapter for that exact stack. For a CLI without Listr2, call @inquirer/prompts directly and avoid the extra package and peer-version coupling.
Use it if
- Your CLI already uses Listr2 11 and needs interactive questions inside tasks without corrupting the live renderer
- You want the actively maintained @inquirer/prompts functions while keeping prompt state visible to Listr2
- You need a running prompt to be cancelled when a task is skipped or when your code calls the adapter's cancel method
- You use TypeScript and want the chosen Inquirer prompt function to determine the config and return types
- Your CLI does not use Listr2: @inquirer/prompts works directly and this adapter adds no value outside a Listr task
- You cannot run Node 22.13 or newer: version 4.2.5 declares that minimum engine and the changelog says version 4 dropped Node 20 support
- You use a different Listr2 release: the package pins the listr2 peer dependency to exactly 11.0.0 rather than accepting a range
- Your command runs unattended in CI or with piped input: interactive prompts can hang or render poorly, and the docs say non-TTY renderers may repeat prompt output
- You intend to prompt from concurrent tasks: Listr2's prompt documentation warns that prompts clash, overwrite console output, and apply keyboard input to both
Setup reality
Install three pieces together: listr2@11.0.0, @listr2/prompt-adapter-inquirer@4.2.5, and a compatible @inquirer/prompts release. The adapter declares @inquirer/prompts >=3 <9 as an optional peer, so npm can install the adapter without the actual prompt functions; the missing package only becomes obvious when your import fails. It also pins listr2 to exactly 11.0.0, which makes routine independent upgrades capable of producing peer-dependency warnings. Node must be at least 22.13.0. The published entry point is ESM even for its require export, so treat this as modern module-oriented CLI code and test any CommonJS packaging setup. Inside a task, create the adapter with task.prompt(ListrInquirerPromptAdapter), then call run with an imported prompt function as the first argument and its options as the second. Do not call input() or select() directly because their stdout writes bypass Listr2's renderer. Keep the enclosing task async and await the answer. Set concurrent: false for any list that can prompt; simultaneous questions compete for the same keyboard and terminal lines. Cancellation rejects the Inquirer cancellable promise rather than returning a sentinel, so catch it only if cancellation is an expected branch. In CI, cron, Docker builds, and redirected stdin, provide flags or environment variables that bypass the question entirely. Prompt output is routed into Listr2's stream, but non-TTY renderers may print repeated updates and still look noisy.
Patterns
Ask for text inside a Listr taskprompt-for-input
import { input } from '@inquirer/prompts';
import { ListrInquirerPromptAdapter } from '@listr2/prompt-adapter-inquirer';
import { Listr } from 'listr2';
const tasks = new Listr([{
title: 'Configure project',
task: async (ctx, task) => {
ctx.name = await task.prompt(ListrInquirerPromptAdapter).run(input, {
message: 'Project name?',
});
},
}], { concurrent: false });
const ctx = await tasks.run();Pass the imported prompt function to run; do not invoke input() directly while the Listr renderer owns stdout.
Confirm a destructive actionconfirm-action
import { confirm } from '@inquirer/prompts';
const approved = await task.prompt(ListrInquirerPromptAdapter).run(confirm, {
message: 'Delete generated files?',
default: false,
});
if (!approved) task.skip('Kept generated files');Use a false default for destructive choices. Skipping the task also cancels an active prompt through the adapter.
Choose one item from a listselect-one
import { select } from '@inquirer/prompts';
const environment = await task.prompt(ListrInquirerPromptAdapter).run(select, {
message: 'Deploy where?',
choices: [
{ name: 'Development', value: 'dev' },
{ name: 'Staging', value: 'staging' },
{ name: 'Production', value: 'prod' },
],
});The returned value is the choice value, not its display name.
Choose several itemsselect-many
import { checkbox } from '@inquirer/prompts';
const features = await task.prompt(ListrInquirerPromptAdapter).run(checkbox, {
message: 'Enable features',
choices: [
{ name: 'Linting', value: 'lint', checked: true },
{ name: 'Tests', value: 'test', checked: true },
{ name: 'Docker', value: 'docker' },
],
required: true,
});required prevents an empty submission; the answer is an array of choice values.
Read a masked secretread-password
import { password } from '@inquirer/prompts';
const token = await task.prompt(ListrInquirerPromptAdapter).run(password, {
message: 'API token',
mask: '*',
validate: (value) => value.length > 10 || 'Token is too short',
});Masking only hides terminal echo. Do not put the returned secret in task titles, output, debug logs, or Listr context that you later serialize.
Validate and transform textvalidate-input
import { input } from '@inquirer/prompts';
const slug = await task.prompt(ListrInquirerPromptAdapter).run(input, {
message: 'Package slug',
validate: (value) => /^[a-z0-9-]+$/.test(value) || 'Use lowercase letters, numbers, and hyphens',
transformer: (value) => value.trim().toLowerCase(),
});A transformer changes what is displayed while typing; normalize the final returned value yourself if storage must be canonical.
Ask for a numeric valueprompt-for-number
import { number } from '@inquirer/prompts';
const port = await task.prompt(ListrInquirerPromptAdapter).run(number, {
message: 'Port',
default: 3000,
min: 1,
max: 65535,
required: true,
});Use required when undefined is not valid; otherwise an empty answer can produce no number.
Search a long list of choicessearch-choices
import { search } from '@inquirer/prompts';
const project = await task.prompt(ListrInquirerPromptAdapter).run(search, {
message: 'Choose a project',
source: async (term = '') => projects
.filter((item) => item.name.toLowerCase().includes(term.toLowerCase()))
.map((item) => ({ name: item.name, value: item.id })),
});Debounce network-backed source functions yourself so every keystroke does not become an API request.
Cancel a prompt after a deadlinecancel-prompt
import { input } from '@inquirer/prompts';
const prompt = task.prompt(ListrInquirerPromptAdapter);
const timer = setTimeout(() => prompt.cancel(), 30_000);
try {
ctx.value = await prompt.run(input, { message: 'Value?' });
} finally {
clearTimeout(timer);
}cancel() aborts the Inquirer promise, so expect run() to reject. Always clear your timer after completion.
Avoid prompting in non-interactive runsbypass-in-ci
import { input } from '@inquirer/prompts';
const supplied = process.env.PROJECT_NAME;
if (supplied) {
ctx.name = supplied;
} else if (!process.stdin.isTTY) {
throw new Error('PROJECT_NAME is required without a TTY');
} else {
ctx.name = await task.prompt(ListrInquirerPromptAdapter).run(input, {
message: 'Project name?',
});
}The docs warn that prompts are meant for TTY terminals; fail clearly or accept flags and environment variables in automation.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| @inquirer/prompts | npm | Use it directly when your CLI does not have a Listr2 renderer competing for stdout |
| @listr2/prompt-adapter-enquirer | npm | Use it only for an existing Enquirer-based Listr2 command that cannot migrate yet |
| prompts | npm | Use it for a small standalone prompt flow without Listr2 task integration |