Interactive command-line prompts for user input, confirmations, selections, and data collection using the enquirer library with type-safe interfaces.
Simple yes/no confirmation prompts for user decisions.
/**
* Display a yes/no confirmation prompt
* @param message - Question to ask the user
* @param initial - Default value (true for yes, false for no)
* @returns Promise resolving to user's boolean choice
*/
confirm(message: string, initial?: boolean): Promise<boolean>;Confirmation Examples:
import { prompt } from "gluegun";
// Basic confirmation
const shouldContinue = await prompt.confirm("Do you want to continue?");
if (shouldContinue) {
console.log("Continuing...");
} else {
console.log("Cancelled.");
return;
}
// Confirmation with default
const shouldOverwrite = await prompt.confirm(
"File exists. Overwrite?",
false // Default to 'no'
);
// Destructive action confirmation
const shouldDelete = await prompt.confirm(
"⚠️ This will permanently delete all data. Are you sure?",
false
);
if (shouldDelete) {
// Perform deletion
}Advanced prompting system supporting multiple question types and complex user input scenarios.
/**
* Ask one or more interactive questions
* @param questions - Single question or array of question objects
* @returns Promise resolving to answers object
*/
ask<T = GluegunAskResponse>(
questions: PromptOptions | PromptOptions[]
): Promise<T>;
interface GluegunAskResponse {
[key: string]: string;
}
interface PromptOptions {
type?: 'input' | 'select' | 'multiselect' | 'confirm' | 'password' | 'invisible' | 'list' | 'scale' | 'date' | 'snippet' | 'sort' | 'survey' | 'quiz' | 'toggle';
name: string;
message: string;
initial?: any;
choices?: string[] | { name: string; message?: string; value?: any; hint?: string }[];
validate?: (value: any) => boolean | string;
format?: (value: any) => any;
result?: (value: any) => any;
skip?: ((state: any) => boolean) | boolean;
enabled?: (answers: any) => boolean;
when?: (answers: any) => boolean;
stdin?: NodeJS.ReadStream;
stdout?: NodeJS.WriteStream;
}Interactive Question Examples:
import { prompt } from "gluegun";
// Single text input
const answer = await prompt.ask({
type: 'input',
name: 'projectName',
message: 'What is your project name?',
initial: 'my-project'
});
console.log(`Creating project: ${answer.projectName}`);
// Multiple questions
const config = await prompt.ask([
{
type: 'input',
name: 'name',
message: 'Project name:',
validate: (value) => value.length > 0 || 'Project name is required'
},
{
type: 'select',
name: 'framework',
message: 'Choose a framework:',
choices: ['React', 'Vue', 'Angular', 'Svelte']
},
{
type: 'confirm',
name: 'typescript',
message: 'Use TypeScript?',
initial: true
},
{
type: 'multiselect',
name: 'features',
message: 'Select additional features:',
choices: ['ESLint', 'Prettier', 'Jest', 'Storybook', 'PWA']
}
]);
console.log('Configuration:', config);
// { name: 'my-app', framework: 'React', typescript: true, features: ['ESLint', 'Jest'] }Comprehensive question types for different input scenarios.
Input Types:
// Text input
const name = await prompt.ask({
type: 'input',
name: 'username',
message: 'Enter username:',
validate: (value) => value.match(/^[a-zA-Z0-9_]+$/) || 'Invalid username format'
});
// Password input (hidden)
const password = await prompt.ask({
type: 'password',
name: 'password',
message: 'Enter password:'
});
// List input (comma-separated)
const tags = await prompt.ask({
type: 'list',
name: 'tags',
message: 'Enter tags (comma-separated):'
});Selection Types:
// Single selection
const choice = await prompt.ask({
type: 'select',
name: 'deployment',
message: 'Choose deployment target:',
choices: [
{ name: 'production', message: 'Production (AWS)' },
{ name: 'staging', message: 'Staging (Heroku)' },
{ name: 'local', message: 'Local Docker' }
]
});
// Multiple selection
const plugins = await prompt.ask({
type: 'multiselect',
name: 'plugins',
message: 'Select plugins to install:',
choices: [
{ name: 'auth', message: 'Authentication' },
{ name: 'db', message: 'Database ORM' },
{ name: 'cache', message: 'Redis Cache' },
{ name: 'queue', message: 'Job Queue' }
]
});
// Toggle selection
const settings = await prompt.ask({
type: 'toggle',
name: 'notifications',
message: 'Enable notifications?',
enabled: 'Yes',
disabled: 'No'
});Advanced Types:
// Scale/rating
const rating = await prompt.ask({
type: 'scale',
name: 'satisfaction',
message: 'Rate your satisfaction:',
scale: [
{ name: '1', message: 'Terrible' },
{ name: '2', message: 'Bad' },
{ name: '3', message: 'Okay' },
{ name: '4', message: 'Good' },
{ name: '5', message: 'Excellent' }
]
});
// Date input
const deadline = await prompt.ask({
type: 'date',
name: 'deadline',
message: 'Enter project deadline:'
});
// Survey/form
const survey = await prompt.ask([
{
type: 'survey',
name: 'experience',
message: 'Rate your experience with:',
scale: [
{ name: '1', message: 'Beginner' },
{ name: '2', message: 'Intermediate' },
{ name: '3', message: 'Advanced' }
],
choices: [
{ name: 'javascript', message: 'JavaScript' },
{ name: 'typescript', message: 'TypeScript' },
{ name: 'react', message: 'React' }
]
}
]);Create visual separators in prompt lists for better organization.
/**
* Create a visual separator for prompt choices
* @returns Separator object for use in choice arrays
*/
separator(): string;Separator Examples:
import { prompt } from "gluegun";
const action = await prompt.ask({
type: 'select',
name: 'action',
message: 'What would you like to do?',
choices: [
'Create new project',
'Generate component',
prompt.separator(), // Visual separator
'Run tests',
'Build project',
prompt.separator(),
'Deploy to staging',
'Deploy to production'
]
});Implement complex prompt flows with conditional questions and validation.
// Conditional questions based on previous answers
const answers = await prompt.ask([
{
type: 'select',
name: 'projectType',
message: 'Project type:',
choices: ['web', 'mobile', 'desktop']
},
{
type: 'select',
name: 'framework',
message: 'Framework:',
choices: (answers) => {
if (answers.projectType === 'web') {
return ['React', 'Vue', 'Angular'];
} else if (answers.projectType === 'mobile') {
return ['React Native', 'Flutter', 'Ionic'];
} else {
return ['Electron', 'Tauri', 'Qt'];
}
}
},
{
type: 'confirm',
name: 'useTypeScript',
message: 'Use TypeScript?',
when: (answers) => answers.projectType === 'web'
}
]);
// Advanced validation
const config = await prompt.ask({
type: 'input',
name: 'port',
message: 'Server port:',
initial: '3000',
validate: (value) => {
const port = parseInt(value);
if (isNaN(port)) return 'Port must be a number';
if (port < 1 || port > 65535) return 'Port must be between 1 and 65535';
if (port < 1024) return 'Port must be 1024 or higher (non-privileged)';
return true;
},
format: (value) => parseInt(value)
});Gluegun provides backward compatibility with older prompt libraries by automatically transforming legacy prompt types to work with the modern enquirer library.
When using the ask() method, Gluegun automatically transforms legacy prompt types to their modern equivalents:
// Legacy type transformations (automatic)
{
'rawlist' → 'select', // Raw list becomes select
'list' → 'select', // List becomes select
'expand' → 'autocomplete', // Expand becomes autocomplete
'checkbox' → 'multiselect', // Checkbox becomes multiselect
'radio' → 'select', // Radio becomes select
'question' → 'input' // Question becomes input
}Backward Compatibility Examples:
// These legacy formats are automatically transformed
const answers = await prompt.ask([
{
type: 'list', // Automatically becomes 'select'
name: 'framework',
message: 'Choose framework:',
choices: ['React', 'Vue', 'Angular']
},
{
type: 'checkbox', // Automatically becomes 'multiselect'
name: 'features',
message: 'Select features:',
choices: ['TypeScript', 'ESLint', 'Jest']
},
{
type: 'question', // Automatically becomes 'input'
name: 'projectName',
message: 'Project name:'
}
]);Gluegun supports dynamic question generation using functions:
// Questions can be functions that return question objects
const dynamicQuestions = [
() => ({
type: 'input',
name: 'name',
message: 'What is your name?'
}),
(answers) => ({
type: 'input',
name: 'greeting',
message: `Hello ${answers.name}! What's your favorite greeting?`,
initial: `Hi there, ${answers.name}!`
})
];
const answers = await prompt.ask(dynamicQuestions);If you're migrating from older versions of Gluegun or other prompt libraries:
From Inquirer.js:
// Old inquirer.js style (still works)
const answers = await prompt.ask([
{
type: 'list',
name: 'action',
message: 'What do you want to do?',
choices: ['Create', 'Update', 'Delete']
}
]);
// Modern enquirer style (recommended)
const answers = await prompt.ask([
{
type: 'select', // Modern equivalent
name: 'action',
message: 'What do you want to do?',
choices: ['Create', 'Update', 'Delete']
}
]);Performance Benefits:
interface GluegunPrompt {
/**
* Display yes/no confirmation prompt
* @param message - Question message
* @param initial - Default value
* @returns User's boolean choice
*/
confirm(message: string, initial?: boolean): Promise<boolean>;
/**
* Ask interactive questions
* @param questions - Question or array of questions
* @returns Answers object
*/
ask<T>(questions: PromptOptions | PromptOptions[]): Promise<T>;
/**
* Create visual separator
* @returns Separator for choice lists
*/
separator(): string;
}
interface GluegunAskResponse {
[key: string]: string;
}
type GluegunEnquirer = typeof import('enquirer');Comprehensive Usage Example:
// CLI command for project setup with comprehensive prompting
export = {
name: "create",
description: "Create a new project with interactive setup",
run: async (toolbox) => {
const { prompt, print, filesystem, strings } = toolbox;
print.info("🚀 Let's create your new project!");
print.newline();
// Multi-step project configuration
const config = await prompt.ask([
{
type: 'input',
name: 'name',
message: 'Project name:',
validate: (value) => {
if (!value || value.trim().length === 0) {
return 'Project name is required';
}
if (!/^[a-zA-Z0-9-_\s]+$/.test(value)) {
return 'Project name can only contain letters, numbers, spaces, dashes, and underscores';
}
return true;
},
format: (value) => strings.kebabCase(value.trim())
},
{
type: 'input',
name: 'description',
message: 'Project description:',
initial: 'A new project'
},
{
type: 'select',
name: 'template',
message: 'Choose project template:',
choices: [
{ name: 'basic', message: '📦 Basic - Simple starter template' },
{ name: 'full', message: '🎯 Full - Complete with all features' },
prompt.separator(),
{ name: 'custom', message: '⚙️ Custom - Choose individual features' }
]
},
{
type: 'multiselect',
name: 'features',
message: 'Select features to include:',
when: (answers) => answers.template === 'custom',
choices: [
{ name: 'typescript', message: 'TypeScript support' },
{ name: 'eslint', message: 'ESLint linting' },
{ name: 'prettier', message: 'Prettier formatting' },
{ name: 'jest', message: 'Jest testing' },
{ name: 'husky', message: 'Git hooks with Husky' },
{ name: 'ci', message: 'GitHub Actions CI/CD' }
]
},
{
type: 'confirm',
name: 'initGit',
message: 'Initialize Git repository?',
initial: true
},
{
type: 'confirm',
name: 'installDeps',
message: 'Install dependencies now?',
initial: true
}
]);
// Confirmation step
print.newline();
print.info("Project configuration:");
print.table([
['Name', config.name],
['Description', config.description],
['Template', config.template],
['Features', config.features ? config.features.join(', ') : 'Default'],
['Git', config.initGit ? 'Yes' : 'No'],
['Install deps', config.installDeps ? 'Yes' : 'No']
]);
const proceed = await prompt.confirm('\nProceed with project creation?', true);
if (!proceed) {
print.info('Project creation cancelled.');
return;
}
// Create project based on configuration...
print.success(`🎉 Project "${config.name}" created successfully!`);
}
};