···11-# Important! Never install from registry even when new version is available
22-prefer-workspace-packages=true
33-link-workspace-packages=true
44-save-workspace-protocol=false
55-auto-install-peers=false
···11# @clack/core
2233+## 1.4.0
44+55+### Minor Changes
66+77+- [#543](https://github.com/bombshell-dev/clack/pull/543) [`83428ac`](https://github.com/bombshell-dev/clack/commit/83428ac6d8bc5eda87615cc7b1f14e0c8b16e1b6) Thanks [@florian-lefebvre](https://github.com/florian-lefebvre)! - Adds support for Standard Schema validation
88+99+ Prompts accept an optional `validate()` function to validate user input. While a function provides more flexibility and customization over your validation, it can be a bit verbose. To help solve this, there are libraries that provide schema-based validation to make shorthand and type-strict validation substantially easier.
1010+1111+ Libraries following the [Standard Schema specification](https://github.com/standard-schema/standard-schema) are now natively supported. For example, using [Arktype](https://arktype.io/):
1212+1313+ ```diff
1414+ import { text } from '@clack/prompts';
1515+ import { type } from 'arktype';
1616+1717+ const name = await text({
1818+ message: 'Enter your email',
1919+ + validate: type('string.email').describe('Invalid email'),
2020+ });
2121+ ```
2222+2323+### Patch Changes
2424+2525+- [#534](https://github.com/bombshell-dev/clack/pull/534) [`3dcb31a`](https://github.com/bombshell-dev/clack/commit/3dcb31a7d63827d95a5a52ac630cbd48e3a68364) Thanks [@MattStypa](https://github.com/MattStypa)! - Fixed spaces and uppercase characters in multiline prompt
2626+2727+## 1.3.1
2828+2929+### Patch Changes
3030+3131+- 54be8d7: Fix line wrapping and overflow computation in group multi-select and other list-like prompts.
3232+3333+## 1.3.0
3434+3535+### Minor Changes
3636+3737+- 78fd3ae: Remove unused `debug` option key.
3838+- ea5702e: fix: add engines field expressing node >=20.12 requirement
3939+- 814ab9a: Add new multiline prompt for multi-line text input.
4040+341## 1.2.0
442543### Minor Changes
···11+// https://standardschema.dev/schema
22+33+/** The Standard Schema interface. */
44+export interface StandardSchemaV1<Input = unknown, Output = Input> {
55+ /** The Standard Schema properties. */
66+ readonly '~standard': StandardSchemaV1.Props<Input, Output>;
77+}
88+99+export declare namespace StandardSchemaV1 {
1010+ /** The Standard Schema properties interface. */
1111+ export interface Props<Input = unknown, Output = Input> {
1212+ /** The version number of the standard. */
1313+ readonly version: 1;
1414+ /** The vendor name of the schema library. */
1515+ readonly vendor: string;
1616+ /** Validates unknown input values. */
1717+ readonly validate: (
1818+ value: unknown,
1919+ options?: StandardSchemaV1.Options | undefined
2020+ ) => Result<Output> | Promise<Result<Output>>;
2121+ /** Inferred types associated with the schema. */
2222+ readonly types?: Types<Input, Output> | undefined;
2323+ }
2424+2525+ /** The result interface of the validate function. */
2626+ export type Result<Output> = SuccessResult<Output> | FailureResult;
2727+2828+ /** The result interface if validation succeeds. */
2929+ export interface SuccessResult<Output> {
3030+ /** The typed output value. */
3131+ readonly value: Output;
3232+ /** A falsy value for `issues` indicates success. */
3333+ readonly issues?: undefined;
3434+ }
3535+3636+ export interface Options {
3737+ /** Explicit support for additional vendor-specific parameters, if needed. */
3838+ readonly libraryOptions?: Record<string, unknown> | undefined;
3939+ }
4040+4141+ /** The result interface if validation fails. */
4242+ export interface FailureResult {
4343+ /** The issues of failed validation. */
4444+ readonly issues: ReadonlyArray<Issue>;
4545+ }
4646+4747+ /** The issue interface of the failure output. */
4848+ export interface Issue {
4949+ /** The error message of the issue. */
5050+ readonly message: string;
5151+ /** The path of the issue, if any. */
5252+ readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;
5353+ }
5454+5555+ /** The path segment interface of the issue. */
5656+ export interface PathSegment {
5757+ /** The key representing a path segment. */
5858+ readonly key: PropertyKey;
5959+ }
6060+6161+ /** The Standard Schema types interface. */
6262+ export interface Types<Input = unknown, Output = Input> {
6363+ /** The input type of the schema. */
6464+ readonly input: Input;
6565+ /** The output type of the schema. */
6666+ readonly output: Output;
6767+ }
6868+6969+ /** Infers the input type of a Standard Schema. */
7070+ export type InferInput<Schema extends StandardSchemaV1> = NonNullable<
7171+ Schema['~standard']['types']
7272+ >['input'];
7373+7474+ /** Infers the output type of a Standard Schema. */
7575+ export type InferOutput<Schema extends StandardSchemaV1> = NonNullable<
7676+ Schema['~standard']['types']
7777+ >['output'];
7878+}
+61
packages/core/src/utils/validation.ts
···11+import type { StandardSchemaV1 } from './standard-schema.js';
22+33+/**
44+ * A function or [Standard Schema](https://github.com/standard-schema/standard-schema)
55+ * that validates user input. If a custom function is given, you should return a
66+ * `string` or `Error` to show as a validation error, or `undefined` to accept the result.
77+ *
88+ * @example Using arktype
99+ * ```ts
1010+ * import { text } from '@clack/prompts';
1111+ * import { type } from 'arktype';
1212+ *
1313+ * const name = await text({
1414+ * message: 'Enter your name (letters only)',
1515+ * validate: type('string.alpha').describe('Name can only contain letters'),
1616+ * });
1717+ * ```
1818+ *
1919+ * @example Custom validator
2020+ * ```ts
2121+ * import { text } from '@clack/prompts';
2222+ *
2323+ * const age = await text({
2424+ * message: 'Enter your age:',
2525+ * validate(value) {
2626+ * if (!value) return 'Please enter a value';
2727+ * const num = parseInt(value);
2828+ * if (isNaN(num)) return 'Please enter a valid number';
2929+ * if (num < 0 || num > 120) return 'Age must be between 0 and 120';
3030+ * return undefined;
3131+ * },
3232+ * });
3333+ * ```
3434+ */
3535+export type Validate<TValue> =
3636+ | ((value: TValue | undefined) => string | Error | undefined)
3737+ | StandardSchemaV1<TValue | undefined, unknown>;
3838+3939+/**
4040+ * Runs the `validate()` option and normalizes the result
4141+ * @param validate - The validate option
4242+ * @param value - The user input
4343+ * @returns the validation result
4444+ */
4545+export function runValidation<TValue>(
4646+ validate: Validate<TValue>,
4747+ value: TValue | undefined
4848+): string | Error | undefined {
4949+ if ('~standard' in validate) {
5050+ const result = validate['~standard'].validate(value);
5151+ // https://standardschema.dev/schema#how-to-only-allow-synchronous-validation
5252+ // TODO: https://github.com/bombshell-dev/clack/issues/92
5353+ if (result instanceof Promise) {
5454+ throw new TypeError(
5555+ 'Schema validation must be synchronous. Update `validate()` and remove any asynchronous logic.'
5656+ );
5757+ }
5858+ return result.issues?.at(0)?.message;
5959+ }
6060+ return validate(value);
6161+}
···11# @clack/prompts
2233+## 1.5.0
44+55+### Minor Changes
66+77+- [#543](https://github.com/bombshell-dev/clack/pull/543) [`83428ac`](https://github.com/bombshell-dev/clack/commit/83428ac6d8bc5eda87615cc7b1f14e0c8b16e1b6) Thanks [@florian-lefebvre](https://github.com/florian-lefebvre)! - Adds support for Standard Schema validation
88+99+ Prompts accept an optional `validate()` function to validate user input. While a function provides more flexibility and customization over your validation, it can be a bit verbose. To help solve this, there are libraries that provide schema-based validation to make shorthand and type-strict validation substantially easier.
1010+1111+ Libraries following the [Standard Schema specification](https://github.com/standard-schema/standard-schema) are now natively supported. For example, using [Arktype](https://arktype.io/):
1212+1313+ ```diff
1414+ import { text } from '@clack/prompts';
1515+ import { type } from 'arktype';
1616+1717+ const name = await text({
1818+ message: 'Enter your email',
1919+ + validate: type('string.email').describe('Invalid email'),
2020+ });
2121+ ```
2222+2323+### Patch Changes
2424+2525+- [#542](https://github.com/bombshell-dev/clack/pull/542) [`adb6af9`](https://github.com/bombshell-dev/clack/commit/adb6af9f5fb39408934323a7415beb46b63ecd9a) Thanks [@ghostdevv](https://github.com/ghostdevv)! - docs: add jsdoc for `box`, `group`, and `group-multi-select`
2626+2727+- [#534](https://github.com/bombshell-dev/clack/pull/534) [`3dcb31a`](https://github.com/bombshell-dev/clack/commit/3dcb31a7d63827d95a5a52ac630cbd48e3a68364) Thanks [@MattStypa](https://github.com/MattStypa)! - Fixed spaces and uppercase characters in multiline prompt
2828+2929+- [#540](https://github.com/bombshell-dev/clack/pull/540) [`3170ed9`](https://github.com/bombshell-dev/clack/commit/3170ed94dc2a6ed7973228d46c664fb7461969ad) Thanks [@ghostdevv](https://github.com/ghostdevv)! - docs: add jsdoc for `autocomplete`, `confirm`, and `path` prompts
3030+3131+- Updated dependencies [[`83428ac`](https://github.com/bombshell-dev/clack/commit/83428ac6d8bc5eda87615cc7b1f14e0c8b16e1b6), [`3dcb31a`](https://github.com/bombshell-dev/clack/commit/3dcb31a7d63827d95a5a52ac630cbd48e3a68364)]:
3232+ - @clack/core@1.4.0
3333+3434+## 1.4.0
3535+3636+### Minor Changes
3737+3838+- 284677e: Support scrolling and `maxItems` option for `groupMultiselect`, and removes indent when `withGuide` is set to `false`
3939+4040+### Patch Changes
4141+4242+- aab46a2: docs: add jsdoc for `text`, `password`, and `multiline` prompts
4343+- 54be8d7: Fix line wrapping and overflow computation in group multi-select and other list-like prompts.
4444+- Updated dependencies [54be8d7]
4545+ - @clack/core@1.3.1
4646+4747+## 1.3.0
4848+4949+### Minor Changes
5050+5151+- ea5702e: fix: add engines field expressing node >=20.12 requirement
5252+- 814ab9a: Add new multiline prompt for multi-line text input.
5353+5454+### Patch Changes
5555+5656+- 5b897a7: Fix mixed type-only and runtime exports from @clack/core.
5757+- Updated dependencies [78fd3ae]
5858+- Updated dependencies [ea5702e]
5959+- Updated dependencies [814ab9a]
6060+ - @clack/core@1.3.0
6161+362## 1.2.0
463564### Minor Changes
+26-5
packages/prompts/README.md
···65656666### Password
67676868-The password component behaves like `text`, but masks the input as the user types.
6868+The password prompt behaves like the [`text`](#text) prompt, but masks the input as the user types.
69697070```js
7171import { password } from '@clack/prompts';
···81818282### Confirm
83838484-The confirm component accepts a yes or no answer. The result is a boolean value of `true` or `false`.
8484+The `confirm` prompt accepts a yes or no choice, returning a boolean value corresponding to the user's selection.
85858686```js
8787import { confirm } from '@clack/prompts';
···125125126126### Autocomplete
127127128128-The autocomplete component lets a user filter a list by typing, then choose one option from the matching results. By default, matching uses each option's `label`, `hint`, and `value`. The result is the selected option's `value`.
128128+The `autocomplete` prompt combines text input with a searchable list of options. It's perfect for when you have a large list of options and want to help users find what they're looking for quickly.
129129130130```js
131131import { autocomplete } from '@clack/prompts';
···142142});
143143```
144144145145+### Autocomplete Multi-Select
146146+147147+The `autocompleteMultiselect` prompt combines the search functionality of [autocomplete](#autocomplete) with the ability to select multiple options.
148148+149149+```js
150150+import { autocomplete } from '@clack/prompts';
151151+152152+const framework = await autocomplete({
153153+ message: 'Search for a framework',
154154+ options: [
155155+ { value: 'next', label: 'Next.js', hint: 'React framework' },
156156+ { value: 'astro', label: 'Astro', hint: 'Content-focused' },
157157+ { value: 'svelte', label: 'SvelteKit', hint: 'Compile-time framework' },
158158+ { value: 'remix', label: 'Remix', hint: 'Full stack framework' },
159159+ { value: 'nuxt', label: 'Nuxt', hint: 'Vue framework' },
160160+ ],
161161+ placeholder: 'Type to search...',
162162+ maxItems: 5,
163163+});
164164+```
165165+145166### Select Key
146167147168The `selectKey` component lets a user choose an option by pressing its single-character string `value` key directly.
···201222202223### Multi-Line Text
203224204204-The multi-line text component accepts multiple lines of text input. By default, pressing `Enter` twice submits the input.
225225+The multi-line prompt accepts multiple lines of text input. By default, pressing `Enter` twice submits the input.
205226206227```js
207228import { multiline } from '@clack/prompts';
···226247227248### Path
228249229229-The path component offers filesystem path suggestions and returns the selected path as a string. When `directory: true` is set, only directories can be selected.
250250+The `path` prompt extends [`autocomplete`](#autocomplete) to provide file and directory suggestions.
230251231252```js
232253import { path } from '@clack/prompts';
···11import { styleText } from 'node:util';
22+import type { Validate } from '@clack/core';
23import { AutocompletePrompt, settings } from '@clack/core';
34import {
45 type CommonOptions,
···4142 return results;
4243}
43444545+/**
4646+ * Options for the {@link autocomplete} prompt.
4747+ */
4448interface AutocompleteSharedOptions<Value> extends CommonOptions {
4549 /**
4646- * The message to display to the user.
5050+ * The message or question shown to the user above the input.
4751 */
4852 message: string;
5353+4954 /**
5050- * Available options for the autocomplete prompt.
5555+ * The options to present, or a function that returns the options to present
5656+ * allowing for custom search/filtering.
5757+ *
5858+ * @see https://bomb.sh/docs/clack/packages/prompts/#dynamic-options-getter
5159 */
5260 options: Option<Value>[] | ((this: AutocompletePrompt<Option<Value>>) => Option<Value>[]);
6161+5362 /**
5454- * Maximum number of items to display at once.
6363+ * The maximum number of items/options to display in the autocomplete list at once.
5564 */
5665 maxItems?: number;
6666+5767 /**
5858- * Placeholder text to display when no input is provided.
6868+ * Placeholder text displayed when the search field is empty. When set, pressing
6969+ * tab copies the placeholder into the input.
5970 */
6071 placeholder?: string;
7272+6173 /**
6262- * Validates the value
7474+ * A function or a [Standard Schema](https://github.com/standard-schema/standard-schema)
7575+ * that validates user input. If a custom function is given, you should return a `string` or `Error`
7676+ * to show as a validation error, or `undefined` to accept the result.
6377 */
6464- validate?: (value: Value | Value[] | undefined) => string | Error | undefined;
7878+ validate?: Validate<Value | Value[]>;
7979+6580 /**
6666- * Custom filter function to match options against search input.
6767- * If not provided, a default filter that matches label, hint, and value is used.
8181+ * Custom filter function to match options against the search input.
6882 */
6983 filter?: (search: string, option: Option<Value>) => boolean;
7084}
71857286export interface AutocompleteOptions<Value> extends AutocompleteSharedOptions<Value> {
7387 /**
7474- * The initial selected value.
8888+ * The initially selected option from the list.
7589 */
7690 initialValue?: Value;
9191+7792 /**
7878- * The initial user input
9393+ * The starting value shown in the users input box.
7994 */
8095 initialUserInput?: string;
8196}
82979898+/**
9999+ * The `autocomplete` prompt combines a text input with a searchable list of options.
100100+ * It's perfect for when you have a large list of options and want to help users
101101+ * find what they're looking for quickly.
102102+ *
103103+ * @see https://bomb.sh/docs/clack/packages/prompts/#autocomplete
104104+ *
105105+ * @example
106106+ * ```ts
107107+ * import { autocomplete } from '@clack/prompts';
108108+ *
109109+ * const framework = await autocomplete({
110110+ * message: 'Search for a framework',
111111+ * options: [
112112+ * { value: 'next', label: 'Next.js', hint: 'React framework' },
113113+ * { value: 'astro', label: 'Astro', hint: 'Content-focused' },
114114+ * { value: 'svelte', label: 'SvelteKit', hint: 'Compile-time framework' },
115115+ * { value: 'remix', label: 'Remix', hint: 'Full stack framework' },
116116+ * { value: 'nuxt', label: 'Nuxt', hint: 'Vue framework' },
117117+ * ],
118118+ * placeholder: 'Type to search...',
119119+ * maxItems: 5,
120120+ * });
121121+ * ```
122122+ */
83123export const autocomplete = <Value>(opts: AutocompleteOptions<Value>) => {
84124 const prompt = new AutocompletePrompt({
85125 options: opts.options,
···223263 return prompt.prompt() as Promise<Value | symbol>;
224264};
225265226226-// Type definition for the autocompleteMultiselect component
266266+/**
267267+ * Options for the {@link autocompleteMultiselect} prompt
268268+ */
227269export interface AutocompleteMultiSelectOptions<Value> extends AutocompleteSharedOptions<Value> {
228270 /**
229229- * The initial selected values
271271+ * The initially selected option(s) from the list.
230272 */
231273 initialValues?: Value[];
274274+232275 /**
233233- * If true, at least one option must be selected
276276+ * When `true` at least one option must be selected.
277277+ * @default false
234278 */
235279 required?: boolean;
236280}
237281238282/**
239239- * Integrated autocomplete multiselect - combines type-ahead filtering with multiselect in one UI
283283+ * The `autocompleteMultiselect` prompt combines the search functionality of autocomplete
284284+ * with the ability to select multiple options.
285285+ *
286286+ * @see https://bomb.sh/docs/clack/packages/prompts/#autocomplete-multiselect
287287+ *
288288+ * @example
289289+ * ```ts
290290+ * import { autocompleteMultiselect } from '@clack/prompts';
291291+ *
292292+ * const frameworks = await autocompleteMultiselect({
293293+ * message: 'Select frameworks',
294294+ * options: [
295295+ * { value: 'next', label: 'Next.js', hint: 'React framework' },
296296+ * { value: 'astro', label: 'Astro', hint: 'Content-focused' },
297297+ * { value: 'svelte', label: 'SvelteKit', hint: 'Compile-time framework' },
298298+ * { value: 'remix', label: 'Remix', hint: 'Full stack framework' },
299299+ * { value: 'nuxt', label: 'Nuxt', hint: 'Vue framework' },
300300+ * ],
301301+ * placeholder: 'Type to search...',
302302+ * maxItems: 5,
303303+ * });
304304+ * ```
240305 */
241306export const autocompleteMultiselect = <Value>(opts: AutocompleteMultiSelectOptions<Value>) => {
242307 const formatOption = (
+61
packages/prompts/src/box.ts
···1616 S_CORNER_TOP_RIGHT,
1717} from './common.js';
18181919+/**
2020+ * Alignment for content or titles within the box.
2121+ */
1922export type BoxAlignment = 'left' | 'center' | 'right';
20232124type BoxSymbols = [topLeft: string, topRight: string, bottomLeft: string, bottomRight: string];
···2831];
2932const squareSymbols: BoxSymbols = [S_BAR_START, S_BAR_START_RIGHT, S_BAR_END, S_BAR_END_RIGHT];
30333434+/**
3535+ * Options for the {@link box} prompt.
3636+ */
3137export interface BoxOptions extends CommonOptions {
3838+ /**
3939+ * Alignment of the content (`'left'`, `'center'`, or `'right'`).
4040+ * @default 'left'
4141+ */
3242 contentAlign?: BoxAlignment;
4343+4444+ /**
4545+ * Alignment of the title (`'left'`, `'center'`, or `'right'`).
4646+ * @default 'left'
4747+ */
3348 titleAlign?: BoxAlignment;
4949+5050+ /**
5151+ * The width of the box, either `'auto'` to fit the content or a number for a fixed width.
5252+ * @default 'auto'
5353+ */
3454 width?: number | 'auto';
5555+5656+ /**
5757+ * Padding around the title.
5858+ * @default 1
5959+ */
3560 titlePadding?: number;
6161+6262+ /**
6363+ * Padding around the content.
6464+ * @default 2
6565+ */
3666 contentPadding?: number;
6767+6868+ /**
6969+ * Use rounded corners when `true`, square corners when `false`.
7070+ * @default true
7171+ */
3772 rounded?: boolean;
7373+7474+ /**
7575+ * Custom function to style the border characters.
7676+ */
3877 formatBorder?: (text: string) => string;
3978}
4079···59986099const defaultFormatBorder = (text: string) => text;
61100101101+/**
102102+ * Renders a customizable box around text content. It's similar to {@link note} but offers
103103+ * more styling options.
104104+ *
105105+ * @see https://bomb.sh/docs/clack/packages/prompts/#box
106106+ *
107107+ * @param message - The content to display inside the box.
108108+ * @param title - The title to display in the top border of the box.
109109+ * @param opts - Optional configuration for the box styling and behavior.
110110+ *
111111+ * @example
112112+ * ```ts
113113+ * import { box } from '@clack/prompts';
114114+ *
115115+ * box('This is the content of the box', 'Box Title', {
116116+ * contentAlign: 'center',
117117+ * titleAlign: 'center',
118118+ * width: 'auto',
119119+ * rounded: true,
120120+ * });
121121+ * ```
122122+ */
62123export const box = (message = '', title = '', opts?: BoxOptions) => {
63124 const output: Writable = opts?.output ?? process.stdout;
64125 const columns = getColumns(output);
+42
packages/prompts/src/confirm.ts
···99 symbol,
1010} from './common.js';
11111212+/**
1313+ * Options for the {@link confirm} prompt.
1414+ */
1215export interface ConfirmOptions extends CommonOptions {
1616+ /**
1717+ * The message or question shown to the user above the input.
1818+ */
1319 message: string;
2020+2121+ /**
2222+ * The label to use for the active (true) option.
2323+ * @default 'Yes'
2424+ */
1425 active?: string;
2626+2727+ /**
2828+ * The label to use for the inactive (false) option.
2929+ * @default 'No'
3030+ */
1531 inactive?: string;
3232+3333+ /**
3434+ * The initial selected value (true or false).
3535+ * @default true
3636+ */
1637 initialValue?: boolean;
3838+3939+ /**
4040+ * Whether to render the options vertically instead of horizontally.
4141+ * @default false
4242+ */
1743 vertical?: boolean;
1844}
4545+4646+/**
4747+ * The `confirm` prompt accepts a yes or no choice, returning a boolean value
4848+ * corresponding to the user's selection.
4949+ *
5050+ * @see https://bomb.sh/docs/clack/packages/prompts/#confirmation
5151+ *
5252+ * @example
5353+ * ```ts
5454+ * import { confirm } from '@clack/prompts';
5555+ *
5656+ * const shouldProceed = await confirm({
5757+ * message: 'Do you want to continue?',
5858+ * });
5959+ * ```
6060+ */
1961export const confirm = (opts: ConfirmOptions) => {
2062 const active = opts.active ?? 'Yes';
2163 const inactive = opts.inactive ?? 'No';
+11-5
packages/prompts/src/date.ts
···11import { styleText } from 'node:util';
22-import type { DateFormat, State } from '@clack/core';
33-import { DatePrompt, settings } from '@clack/core';
22+import type { DateFormat, State, Validate } from '@clack/core';
33+import { DatePrompt, runValidation, settings } from '@clack/core';
44import { type CommonOptions, S_BAR, S_BAR_END, symbol } from './common.js';
5566export type { DateFormat };
···1313 initialValue?: Date;
1414 minDate?: Date;
1515 maxDate?: Date;
1616- validate?: (value: Date | undefined) => string | Error | undefined;
1616+1717+ /**
1818+ * A function or a [Standard Schema](https://github.com/standard-schema/standard-schema)
1919+ * that validates user input. If a custom function is given, you should return a `string` or `Error`
2020+ * to show as a validation error, or `undefined` to accept the result.
2121+ */
2222+ validate?: Validate<Date>;
1723}
18241925export const date = (opts: DateOptions) => {
···2329 validate(value: Date | undefined) {
2430 if (value === undefined) {
2531 if (opts.defaultValue !== undefined) return undefined;
2626- if (validate) return validate(value);
3232+ if (validate) return runValidation(validate, value);
2733 return settings.date.messages.required;
2834 }
2935 const iso = (d: Date) => d.toISOString().slice(0, 10);
···3339 if (opts.maxDate && iso(value) > iso(opts.maxDate)) {
3440 return settings.date.messages.beforeMax(opts.maxDate);
3541 }
3636- if (validate) return validate(value);
4242+ if (validate) return runValidation(validate, value);
3743 return undefined;
3844 },
3945 render() {
···44 [P in keyof T]: T[P];
55} & {};
6677+/**
88+ * The return type of a {@link PromptGroup}.
99+ * Resolves all prompt results, excluding the cancel symbol.
1010+ */
711export type PromptGroupAwaitedReturn<T> = {
812 [P in keyof T]: Exclude<Awaited<T[P]>, symbol>;
913};
10141515+/**
1616+ * Options for the {@link group} utility.
1717+ */
1118export interface PromptGroupOptions<T> {
1219 /**
1313- * Control how the group can be canceled
1414- * if one of the prompts is canceled.
2020+ * Called when any one of the prompts is canceled.
1521 */
1622 onCancel?: (opts: { results: Prettify<Partial<PromptGroupAwaitedReturn<T>>> }) => void;
1723}
18242525+/**
2626+ * A group of prompts to be displayed sequentially, with each prompt receiving
2727+ * the results of all previous prompts in the group.
2828+ */
1929export type PromptGroup<T> = {
2030 [P in keyof T]: (opts: {
2131 results: Prettify<Partial<PromptGroupAwaitedReturn<Omit<T, P>>>>;
···2333};
24342535/**
2626- * Define a group of prompts to be displayed
2727- * and return a results of objects within the group
3636+ * The `group` utility provides a consistent way to combine a series of prompts,
3737+ * combining each answer into one object. Each prompt receives the results of
3838+ * all previously completed prompts, and are displayed sequentially.
3939+ *
4040+ * @see https://bomb.sh/docs/clack/packages/prompts/#group
4141+ *
4242+ * @example
4343+ * ```ts
4444+ * import { group, text, password } from '@clack/prompts';
4545+ *
4646+ * const account = await group({
4747+ * email: () => text({
4848+ * message: 'What is your email address?',
4949+ * }),
5050+ * username: ({ results }) => text({
5151+ * message: 'What is your username?',
5252+ * placeholder: results.email?.replace(/@.+$/, '').toLowerCase() ?? '',
5353+ * }),
5454+ * password: () => password({
5555+ * message: 'Define your password',
5656+ * }),
5757+ * });
5858+ * ```
2859 */
2960export const group = async <T>(
3061 prompts: PromptGroup<T>,
+2-1
packages/prompts/src/index.ts
···11-export { type ClackSettings, isCancel, settings, updateSettings } from '@clack/core';
11+export type { ClackSettings } from '@clack/core';
22+export { isCancel, settings, updateSettings } from '@clack/core';
2334export * from './autocomplete.js';
45export * from './box.js';
···33import { S_BAR, S_BAR_END, symbol } from './common.js';
44import type { TextOptions } from './text.js';
5566+/**
77+ * Options for the {@link multiline} prompt
88+ */
69export interface MultiLineOptions extends TextOptions {
1010+ /**
1111+ * When enabled it shows a `[ submit ]` button that can be focused with tab.
1212+ * By default, pressing `Enter` twice submits the input
1313+ *
1414+ * @default false
1515+ */
716 showSubmit?: boolean;
817}
9181919+/**
2020+ * The multi-line prompt accepts multiple lines of text input.
2121+ * By default, pressing `Enter` twice submits the input.
2222+ *
2323+ * @see https://bomb.sh/docs/clack/packages/prompts/#multi-line-text
2424+ *
2525+ * @example
2626+ * ```ts
2727+ * import { multiline } from '@clack/prompts';
2828+ *
2929+ * const bio = await multiline({
3030+ * message: 'Enter your bio',
3131+ * placeholder: 'Tell us about yourself...',
3232+ * showSubmit: true,
3333+ * });
3434+ * ```
3535+ */
1036export const multiline = (opts: MultiLineOptions) => {
1137 return new MultiLinePrompt({
1238 validate: opts.validate,
···4167 case 'submit': {
4268 const submitPrefix = `${styleText('gray', S_BAR)} `;
4369 const lines = hasGuide
4444- ? wrapTextWithPrefix(opts.output, value, submitPrefix, undefined, (str) =>
7070+ ? wrapTextWithPrefix(opts.output, value, submitPrefix, undefined, undefined, (str) =>
4571 styleText('dim', str)
4672 )
4773 : value
···5278 case 'cancel': {
5379 const cancelPrefix = `${styleText('gray', S_BAR)} `;
5480 const lines = hasGuide
5555- ? wrapTextWithPrefix(opts.output, value, cancelPrefix, undefined, (str) =>
8181+ ? wrapTextWithPrefix(opts.output, value, cancelPrefix, undefined, undefined, (str) =>
5682 styleText(['strikethrough', 'dim'], str)
5783 )
5884 : value
+39-1
packages/prompts/src/password.ts
···11import { styleText } from 'node:util';
22+import type { Validate } from '@clack/core';
23import { PasswordPrompt, settings } from '@clack/core';
34import { type CommonOptions, S_BAR, S_BAR_END, S_PASSWORD_MASK, symbol } from './common.js';
4566+/**
77+ * Options for the {@link password} prompt
88+ */
59export interface PasswordOptions extends CommonOptions {
1010+ /**
1111+ * The prompt message or question shown to the user above the input.
1212+ */
613 message: string;
1414+1515+ /**
1616+ * Character to use for masking input.
1717+ * @default ▪/•
1818+ */
719 mask?: string;
88- validate?: (value: string | undefined) => string | Error | undefined;
2020+2121+ /**
2222+ * A function or a [Standard Schema](https://github.com/standard-schema/standard-schema)
2323+ * that validates user input. If a custom function is given, you should return a `string` or `Error`
2424+ * to show as a validation error, or `undefined` to accept the result.
2525+ */
2626+ validate?: Validate<string>;
2727+2828+ /**
2929+ * When enabled it causes the input to be cleared if/when validation fails.
3030+ * @default false
3131+ */
932 clearOnError?: boolean;
1033}
3434+3535+/**
3636+ * The password prompt behaves like the {@link text} prompt, but the input is masked.
3737+ *
3838+ * @see https://bomb.sh/docs/clack/packages/prompts/#password-input
3939+ *
4040+ * @example
4141+ * ```ts
4242+ * import { password } from '@clack/prompts';
4343+ *
4444+ * const result = await password({
4545+ * message: 'Enter your password',
4646+ * });
4747+ * ```
4848+ */
1149export const password = (opts: PasswordOptions) => {
1250 return new PasswordPrompt({
1351 validate: opts.validate,
+50-3
packages/prompts/src/path.ts
···11import { existsSync, lstatSync, readdirSync } from 'node:fs';
22import { dirname, join } from 'node:path';
33+import type { Validate } from '@clack/core';
44+import { runValidation } from '@clack/core';
35import { autocomplete } from './autocomplete.js';
46import type { CommonOptions } from './common.js';
5788+/**
99+ * Options for the {@link path} prompt.
1010+ */
611export interface PathOptions extends CommonOptions {
1212+ /**
1313+ * The message or question shown to the user above the input.
1414+ */
1515+ message: string;
1616+1717+ /**
1818+ * The starting directory for path suggestions (defaults to current working directory).
1919+ */
720 root?: string;
2121+2222+ /**
2323+ * When `true` only **directories** appear in suggestions while you navigate.
2424+ */
825 directory?: boolean;
2626+2727+ /**
2828+ * The starting path shown when the prompt first renders, which users can edit
2929+ * before submitting. If not provided it will fall back to the given `root`,
3030+ * or the current working directory.
3131+ *
3232+ * In `directory` mode, if the initial value points to a directory that exists,
3333+ * pressing enter will submit the input instead of jumping to the first child.
3434+ */
935 initialValue?: string;
1010- message: string;
1111- validate?: (value: string | undefined) => string | Error | undefined;
3636+3737+ /**
3838+ * A function or a [Standard Schema](https://github.com/standard-schema/standard-schema)
3939+ * that validates user input. If a custom function is given, you should return a `string` or `Error`
4040+ * to show as a validation error, or `undefined` to accept the result.
4141+ */
4242+ validate?: Validate<string>;
1243}
13444545+/**
4646+ * The `path` prompt extends `autocomplete` to provide file and directory suggestions.
4747+ *
4848+ * @see https://bomb.sh/docs/clack/packages/prompts/#path-selection
4949+ *
5050+ * @example
5151+ * ```ts
5252+ * import { path } from '@clack/prompts';
5353+ *
5454+ * const result = await path({
5555+ * message: 'Select a file:',
5656+ * root: process.cwd(),
5757+ * directory: false,
5858+ * });
5959+ * ```
6060+ */
1461export const path = (opts: PathOptions) => {
1562 const validate = opts.validate;
1663···2774 return 'Please select a path';
2875 }
2976 if (validate) {
3030- return validate(value);
7777+ return runValidation(validate, value);
3178 }
3279 return undefined;
3380 },
+46-1
packages/prompts/src/text.ts
···11import { styleText } from 'node:util';
22+import type { Validate } from '@clack/core';
23import { settings, TextPrompt } from '@clack/core';
34import { type CommonOptions, S_BAR, S_BAR_END, symbol } from './common.js';
4566+/**
77+ * Options for the {@link text} prompt
88+ */
59export interface TextOptions extends CommonOptions {
1010+ /**
1111+ * The prompt message or question shown to the user above the input.
1212+ */
613 message: string;
1414+1515+ /**
1616+ * A visual hint shown when the field has no content.
1717+ */
718 placeholder?: string;
1919+2020+ /**
2121+ * A fallback value returned when the user provides nothing (empty input).
2222+ */
823 defaultValue?: string;
2424+2525+ /**
2626+ * The starting value shown when the prompt first renders.
2727+ * Users can edit this value before submitting.
2828+ */
929 initialValue?: string;
1010- validate?: (value: string | undefined) => string | Error | undefined;
3030+3131+ /**
3232+ * A function or a [Standard Schema](https://github.com/standard-schema/standard-schema)
3333+ * that validates user input. If a custom function is given, you should return a `string` or `Error`
3434+ * to show as a validation error, or `undefined` to accept the result.
3535+ */
3636+ validate?: Validate<string>;
1137}
12383939+/**
4040+ * The text prompt accepts a single line of text.
4141+ *
4242+ * @see https://bomb.sh/docs/clack/packages/prompts/#text-input
4343+ *
4444+ * @example
4545+ * ```ts
4646+ * import { text } from '@clack/prompts';
4747+ *
4848+ * const name = await text({
4949+ * message: 'What is your name?',
5050+ * placeholder: 'John Doe',
5151+ * validate: (value) => {
5252+ * if (!value || value.length < 2) return 'Name must be at least 2 characters';
5353+ * return undefined;
5454+ * },
5555+ * });
5656+ * ```
5757+ */
1358export const text = (opts: TextOptions) => {
1459 return new TextPrompt({
1560 validate: opts.validate,
···142142 'Item 4',
143143 'Item 5',
144144 'Item 6',
145145- 'Item 7',
146146- 'Item 8',
147145 styleText('dim', '...'),
148146 ]);
149147 });
···171169 const result = limitOptions(options);
172170 expect(result).toEqual([
173171 styleText('dim', '...'),
174174- 'Item 2',
175175- 'Item 3',
176172 'Item 4',
177173 'A long item that will take up a lot of space (line 0)',
178174 'A long item that will take up a lot of space (line 1)',
···208204 const result = limitOptions(options);
209205 expect(result).toEqual([
210206 styleText('dim', '...'),
211211- 'Item 4',
212212- 'Item 5',
213207 'Item 6',
214208 'Item 7',
215209 'A long item that will take up a lot of space (line 0)',