Skip to content

Interactive Prompts

bcli comes with 6 built-in prompts. They're themed, cancellable, and fully typed.

Text

typescript
const name = await prompt.text({
  message: 'What is your name?',
  placeholder: 'Enter your name',
  initial: 'User',
  validate: (v) => v.length < 2 ? 'Too short!' : undefined,
})

Password

typescript
const secret = await prompt.password({
  message: 'Enter your API key:',
  validate: (v) => v.length < 8 ? 'Key too short' : undefined,
})

Input is masked with * characters in real-time.

Select

typescript
const color = await prompt.select({
  message: 'Pick a color',
  options: [
    { label: 'Red',   value: 'red',   hint: '(warm)' },
    { label: 'Green', value: 'green', hint: '(nature)' },
    { label: 'Blue',  value: 'blue',  hint: '(calm)' },
  ],
  initial: 'green',
})

Navigate with arrow keys, confirm with Enter.

Multiselect

typescript
const features = await prompt.multiselect({
  message: 'Select features:',
  options: [
    { label: 'Logging',   value: 'logging',   hint: '(detailed logs)' },
    { label: 'Analytics', value: 'analytics', hint: '(track usage)' },
    { label: 'Cache',     value: 'cache',     hint: '(faster)' },
  ],
  initial: ['logging'],
})

Toggle with space, confirm with Enter.

Confirm

typescript
const proceed = await prompt.confirm({
  message: 'Continue?',
  initial: true,
})

Toggle with arrow keys, confirm with Enter.

Spinner

typescript
const spinner = prompt.spinner({ message: 'Loading...' })

// Update the message
spinner.message('Still working...')

// Finish states
spinner.succeed('Done!')
spinner.fail('Failed!')
spinner.warn('Warning!')

The spinner automatically shows/hides based on terminal capabilities.

Cancellation

Users can press Ctrl+C to cancel any prompt. The process exits cleanly.

Non-TTY Fallback

When running in pipes or CI (non-TTY), prompts fall back to simple readline input:

bash
my-cli greet < input.txt   # Works in pipes
my-cli greet | cat          # Works in pipes

Released under the MIT License.