Overview
The prompt module provides interactive CLI prompts with support for text input, passwords, confirmations, single/multi-select, and more. All prompts support fallback values for non-interactive environments.
Imports
import {
text ,
password ,
confirm ,
select ,
multiselect ,
group ,
spinner ,
intro ,
outro ,
note ,
log ,
cancel ,
isCancel ,
CANCEL
} from '@bunli/runtime/prompt'
text
Prompt for text input with optional validation and schema support.
async function text < T = string >(
message : string ,
options ?: PromptOptions
) : Promise < T >
Parameters
The prompt message to display
Default value if user presses Enter
validate
(input: string) => boolean | string
Validation function. Return true for valid, or error message string
Schema for validation and type coercion (e.g., Zod schema)
Prompt mode. inline for simple environments, interactive for full features
Value to use in non-interactive environments (CI, non-TTY)
Example
import { text } from '@bunli/runtime/prompt'
import { z } from 'zod'
// Simple text input
const name = await text ( 'What is your name?' )
// With default value
const repo = await text ( 'Repository name?' , {
default: 'my-project'
})
// With validation
const email = await text ( 'Email address?' , {
validate : ( input ) => {
if ( ! input . includes ( '@' )) return 'Invalid email'
return true
}
})
// With schema validation
const port = await text < number >( 'Port number?' , {
schema: z . coerce . number (). int (). min ( 1 ). max ( 65535 )
})
// Non-interactive with fallback
const apiKey = await text ( 'API key?' , {
fallbackValue: process . env . API_KEY
})
password
Prompt for password input (hidden or toggleable).
async function password < T = string >(
message : string ,
options ?: PromptOptions
) : Promise < T >
Parameters
The prompt message to display
Same as text, but input is masked
Example
import { password } from '@bunli/runtime/prompt'
const secret = await password ( 'Enter password:' )
// With validation
const strongPassword = await password ( 'Create password:' , {
validate : ( input ) => {
if ( input . length < 8 ) return 'Password must be at least 8 characters'
return true
}
})
Press Ctrl+R to toggle password visibility during input
confirm
Prompt for yes/no confirmation.
async function confirm (
message : string ,
options ?: ConfirmOptions
) : Promise < boolean >
Parameters
Default value (true = Yes, false = No)
Fallback for non-interactive
Example
import { confirm } from '@bunli/runtime/prompt'
const shouldContinue = await confirm ( 'Continue?' , {
default: true
})
if ( shouldContinue ) {
// proceed
}
select
Prompt to select a single option from a list.
async function select < T = string >(
message : string ,
options : SelectOptions < T >
) : Promise < T >
Parameters
options
SelectOption<T>[]
required
Array of options to choose from Show SelectOption properties
Value to return when selected
Whether option is disabled
Fallback for non-interactive
Example
import { select } from '@bunli/runtime/prompt'
const framework = await select ( 'Choose a framework:' , {
options: [
{ label: 'React' , value: 'react' , hint: 'Popular UI library' },
{ label: 'Vue' , value: 'vue' , hint: 'Progressive framework' },
{ label: 'Svelte' , value: 'svelte' , hint: 'Compiler-based' },
{ label: 'Angular' , value: 'angular' , disabled: true }
],
default: 'react'
})
console . log ( `Selected: ${ framework } ` )
Use Up/Down arrows or 1-9 number keys for quick selection
multiselect
Prompt to select multiple options from a list.
async function multiselect < T = string >(
message : string ,
options : MultiSelectOptions < T >
) : Promise < T []>
Parameters
options
MultiSelectOptions<T>
required
options
SelectOption<T>[]
required
Array of options (same structure as select)
Initially selected values
Minimum number of selections required
Maximum number of selections allowed
Fallback for non-interactive
Example
import { multiselect } from '@bunli/runtime/prompt'
const features = await multiselect ( 'Select features:' , {
options: [
{ label: 'TypeScript' , value: 'typescript' },
{ label: 'ESLint' , value: 'eslint' },
{ label: 'Prettier' , value: 'prettier' },
{ label: 'Tests' , value: 'tests' }
],
min: 1 ,
initialValues: [ 'typescript' ]
})
console . log ( `Selected: ${ features . join ( ', ' ) } ` )
Press Space to toggle selection, Enter to submit
group
Run multiple prompts in sequence and collect results.
async function group < T extends Record < string , () => Promise < unknown >>>(
steps : T
) : Promise <{ [ K in keyof T ] : Awaited < ReturnType < T [ K ]>> }>
Example
import { group , text , select , confirm } from '@bunli/runtime/prompt'
const result = await group ({
name : () => text ( 'Project name?' ),
framework : () => select ( 'Framework?' , {
options: [
{ label: 'React' , value: 'react' },
{ label: 'Vue' , value: 'vue' }
]
}),
install : () => confirm ( 'Install dependencies?' )
})
// result is typed as:
// {
// name: string
// framework: string
// install: boolean
// }
spinner
Create an animated spinner for long-running operations.
function spinner ( options ?: SpinnerOptions | string ) : Spinner
Parameters
String shorthand for initial text, or options object Show SpinnerOptions properties
animation
'line' | 'dots' | 'braille'
default: "'braille'"
Animation style
Animation interval in milliseconds
Methods
Start the spinner with optional text
Stop the spinner with optional final text
Stop with success indicator
Stop with error indicator
Stop with warning indicator
Update spinner text while running
Example
import { spinner } from '@bunli/runtime/prompt'
const s = spinner ( 'Installing dependencies...' )
s . start ()
try {
await installDeps ()
s . succeed ( 'Dependencies installed!' )
} catch ( error ) {
s . fail ( 'Installation failed' )
}
// With timer
const s2 = spinner ({
text: 'Building project...' ,
showTimer: true ,
animation: 'braille'
})
s2 . start ()
await build ()
s2 . succeed ()
Output Functions
intro
Display an intro message.
function intro ( message : string ) : void
outro
Display an outro message.
function outro ( message : string ) : void
note
Display a note box with optional title.
function note ( message : string , title ?: string ) : void
log
Structured logging functions.
const log = {
info ( message : string ) : void
success ( message : string ) : void
warn ( message : string ) : void
error ( message : string ) : void
}
cancel
Display a cancellation message.
function cancel ( message ?: string ) : void
Example
import { intro , outro , note , log } from '@bunli/runtime/prompt'
intro ( 'Project Setup' )
log . info ( 'Starting installation...' )
log . success ( 'Installation complete' )
note ( `
Project: my-app
Framework: React
TypeScript: Yes
` , 'Configuration' )
outro ( 'Setup complete!' )
Cancellation
All prompts can be cancelled with Esc or Ctrl+C , returning the CANCEL symbol.
isCancel
Check if a value is the cancel symbol.
function isCancel ( value : unknown ) : value is Cancel
Example
import { text , isCancel } from '@bunli/runtime/prompt'
const name = await text ( 'Your name?' )
if ( isCancel ( name )) {
console . log ( 'Cancelled!' )
process . exit ( 0 )
}
console . log ( `Hello, ${ name } !` )
promptOrExit
Automatically exit if prompt is cancelled.
import { text , promptOrExit } from '@bunli/runtime/prompt'
const name = promptOrExit (
await text ( 'Your name?' ),
'Setup cancelled'
)
// name is guaranteed to be string, not Cancel
Best Practices
Non-Interactive Environments Always provide fallbackValue for CI/CD pipelines and non-TTY environments
Validation Use schema for complex validation and type coercion (Zod, Valibot, etc.)
User Experience
Use clear, concise messages
Provide sensible default values
Add hints for complex options
Group related prompts together
Error Handling
Check for cancellation with isCancel()
Provide clear validation error messages
Use spinners for long operations