clack-prompts

star 0

Build beautiful CLI prompts with @clack/prompts and @clack/core. Local source at ./_workstream/benchmark/clack (two packages: core + prompts). Use when building interactive CLI flows, terminal prompts, spinners, progress bars, or custom prompt components. Triggers on: clack, CLI prompt, terminal UI, interactive input, spinner, progress bar, select prompt, multiselect, autocomplete prompt, confirm prompt, text input prompt, task log, CLI wizard.

olavocarvalho By olavocarvalho schedule Updated 2/8/2026

name: clack-prompts description: >- Build beautiful CLI prompts with @clack/prompts and @clack/core. Local source at ./_workstream/benchmark/clack (two packages: core + prompts). Use when building interactive CLI flows, terminal prompts, spinners, progress bars, or custom prompt components. Triggers on: clack, CLI prompt, terminal UI, interactive input, spinner, progress bar, select prompt, multiselect, autocomplete prompt, confirm prompt, text input prompt, task log, CLI wizard.

Clack Prompts

Local source: ./_workstream/benchmark/clack/

Two packages:

  • @clack/core (packages/core/) — Low-level headless prompt primitives with state machine
  • @clack/prompts (packages/prompts/) — High-level styled prompt components (primary API)

Import from @clack/prompts for all standard use. Use @clack/core only when building custom prompt components.

Essential Pattern: Cancel Handling

Every prompt returns Value | symbol. Always check for cancellation:

import { isCancel, text, cancel } from '@clack/prompts';

const name = await text({ message: 'Project name?' });
if (isCancel(name)) {
  cancel('Operation cancelled.');
  process.exit(0);
}
// name is now typed as string

Session Framing

import { intro, outro, cancel } from '@clack/prompts';

intro('create-my-app');
// ... prompts ...
outro('Done! Your project is ready.');

Prompt Functions

text — Text input

const name = await text({
  message: 'What is your project name?',
  placeholder: 'my-project',
  defaultValue: 'my-app',
  initialValue: '',
  validate: (value) => {
    if (!value) return 'Name is required';
    if (value.length < 3) return 'Name must be at least 3 characters';
  },
});

password — Masked input

const secret = await password({
  message: 'Enter your API key:',
  validate: (value) => { if (!value) return 'Required'; },
});

confirm — Yes/No

const shouldContinue = await confirm({
  message: 'Continue?',
  active: 'Yes',     // default: 'Yes'
  inactive: 'No',    // default: 'No'
  initialValue: true,
});

select — Single selection

const framework = await select({
  message: 'Pick a framework:',
  options: [
    { value: 'next', label: 'Next.js', hint: 'recommended' },
    { value: 'svelte', label: 'SvelteKit' },
    { value: 'astro', label: 'Astro', disabled: true },
  ],
  initialValue: 'next',
  maxItems: 5, // viewport limit
});

multiselect — Multiple selection

const features = await multiselect({
  message: 'Select features:',
  options: [
    { value: 'ts', label: 'TypeScript', hint: 'recommended' },
    { value: 'eslint', label: 'ESLint' },
    { value: 'prettier', label: 'Prettier' },
  ],
  initialValues: ['ts'],
  required: true, // default: true
  cursorAt: 'ts',
});

autocomplete — Type-ahead single select

const pkg = await autocomplete({
  message: 'Search packages:',
  options: packages.map(p => ({ value: p.name, label: p.name, hint: p.version })),
  placeholder: 'Type to search...',
  maxItems: 8,
  filter: (search, option) => option.label.toLowerCase().includes(search.toLowerCase()),
});

autocompleteMultiselect — Type-ahead multi select

const deps = await autocompleteMultiselect({
  message: 'Select dependencies:',
  options: allPackages,
  placeholder: 'Search...',
  required: true,
  initialValues: ['react'],
});

groupMultiselect — Grouped multi select

const tools = await groupMultiselect({
  message: 'Select tools:',
  options: {
    'Linting': [
      { value: 'eslint', label: 'ESLint' },
      { value: 'biome', label: 'Biome' },
    ],
    'Testing': [
      { value: 'vitest', label: 'Vitest' },
      { value: 'playwright', label: 'Playwright' },
    ],
  },
  selectableGroups: true,  // default: true
  groupSpacing: 0,
  required: true,
});

UI Components

spinner — Loading indicator

import { spinner } from '@clack/prompts';

const s = spinner();
s.start('Installing dependencies');
await install();
s.message('Building project');  // update message
await build();
s.stop('Installation complete');
// Also: s.cancel('Cancelled'), s.error('Failed'), s.clear()

Timer mode: spinner({ indicator: 'timer' }) shows elapsed time.

progress — Progress bar

import { progress } from '@clack/prompts';

const p = progress({ max: 100, size: 40, style: 'heavy' });
p.start('Processing files');
for (let i = 0; i < 100; i++) {
  await processFile(i);
  p.advance(1, `File ${i + 1}/100`);
}
p.stop('Done');

tasks — Sequential task runner

import { tasks } from '@clack/prompts';

await tasks([
  { title: 'Installing', task: async (message) => { await install(); return 'Installed'; } },
  { title: 'Building', task: async () => { await build(); return 'Built'; } },
  { title: 'Optional', task: async () => {}, enabled: false },
]);

taskLog — Log that clears on success

import { taskLog } from '@clack/prompts';

const tl = taskLog({ title: 'Running tests', limit: 20 });
tl.message('test: auth.test.ts passed');
tl.message('test: api.test.ts passed');
// Groups:
const g = tl.group('Unit Tests');
g.message('Running...');
g.success('All passed');
// End:
tl.success('All tests passed');  // clears log
// tl.error('3 tests failed');   // keeps log visible

log — Structured logging

import { log } from '@clack/prompts';

log.info('Info message');
log.success('Success message');
log.step('Step completed');
log.warn('Warning message');
log.error('Error message');
log.message('Custom', { symbol: '→' });

stream — Streaming output (async iterables)

import { stream } from '@clack/prompts';

await stream.message(asyncIterable);
await stream.info(asyncIterable);
await stream.success(asyncIterable);

note — Bordered note box

import { note } from '@clack/prompts';

note('npm run dev\nhttp://localhost:3000', 'Next steps');

box — Bordered box with alignment

import { box } from '@clack/prompts';

box('Content here', 'Title', {
  width: 'auto',
  contentAlign: 'center',
  titleAlign: 'left',
  rounded: true,
  contentPadding: 2,
});

group — Prompt sequences with cancel handling

import { group, isCancel } from '@clack/prompts';

const result = await group({
  name: () => text({ message: 'Name?' }),
  framework: ({ results }) =>
    select({
      message: `Framework for ${results.name}?`,
      options: [{ value: 'next', label: 'Next.js' }],
    }),
  confirm: ({ results }) =>
    confirm({ message: `Create ${results.name} with ${results.framework}?` }),
}, {
  onCancel: () => {
    cancel('Setup cancelled.');
    process.exit(0);
  },
});
// result is typed: { name: string, framework: string, confirm: boolean }

CommonOptions (shared by all prompts)

All prompt functions accept these optional fields:

interface CommonOptions {
  input?: Readable;    // default: process.stdin
  output?: Writable;   // default: process.stdout
  signal?: AbortSignal;
  withGuide?: boolean; // show guide bars (│)
}

Option Type

Used by select, multiselect, autocomplete, groupMultiselect:

type Option<Value> = {
  value: Value;
  label?: string;    // defaults to String(value) for primitives
  hint?: string;
  disabled?: boolean;
};

Global Settings

import { updateSettings } from '@clack/prompts';

updateSettings({ withGuide: true });  // enable guide bars globally

Building Custom Prompts

For custom prompt components beyond the standard set, see references/custom-prompts.md.

The local codebase includes a complex example at _workstream/benchmark/skills/src/prompts/search-multiselect.ts — a searchable multiselect with locked sections, built without @clack/core using raw readline/keypress.

API Reference

For complete type signatures and all options, see references/api.md.

Source Navigation

  • Prompts source: _workstream/benchmark/clack/packages/prompts/src/
  • Core source: _workstream/benchmark/clack/packages/core/src/
  • Core base class: packages/core/src/prompts/prompt.ts (state machine, render loop, keypress)
  • Symbols/styling: packages/prompts/src/common.ts
  • Viewport limiting: packages/prompts/src/limit-options.ts
Install via CLI
npx skills add https://github.com/olavocarvalho/engrain --skill clack-prompts
Repository Details
star Stars 0
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator
olavocarvalho
olavocarvalho Explore all skills →