Lightweight prompting library for terminal apps.
npm install prask
| Input | Selection | Others |
|---|---|---|
| string | multiselect | log |
| invisible | select | prompt |
| password | boolean | spinner |
| number | toggle | |
| rating |
string
This prompt can be used to ask the user for a string value.
import {string} from 'prask';
import color from 'tiny-colors';
const result = await string ({
/* REQUIRED OPTIONS */
message: 'What is your name?', // The message that the user will read
/* OPTIONAL OPTIONS */
initial: 'John Doe', // Optional default value, to allow the user to quickly press Enter for it
required: true, // Whether a non-empty value for this prompt is required or not
format: ( value, settled ) => value.length >= 2 ? colors.green ( value ) : colors.red ( value ), // Optional formatting function, the visual length of the output string must remain the same
validate: value => value.length >= 2 // Optional validation function
});
Interactions:
| Trigger | Description |
|---|---|
Esc |
Quit the prompt, which will resolve to undefined. |
Enter |
Submit the value, or the initial value if visible, checking if it's valid first. |
Tab |
Edit the initial value, if visible. |
invisible
This prompt can be used to ask the user for a password, without visually leaking even the length of the password to the console.
import {invisible} from 'prask';
const result = await invisible ({
/* REQUIRED OPTIONS */
message: 'What is your password?', // The message that the user will read
/* OPTIONAL OPTIONS */
initial: 'P@assword!', // Optional default value, to allow the user to quickly press Enter for it, better not to use it since it will be visible
required: true, // Whether a non-empty value for this prompt is required or not
validate: value => value.length >= 8 // Optional validation function
});
It can be interacted with exactly like a string prompt, the only difference is that the value is invisible.
password
This prompt can be used to ask the user for a password, without visually leaking the characters that make it up to the console.
import {password} from 'prask';
const result = await password ({
/* REQUIRED OPTIONS */
message: 'What is your password?', // The message that the user will read
/* OPTIONAL OPTIONS */
initial: 'P@assword!', // Optional default value, to allow the user to quickly press Enter for it, better not to use it since it will be visible
required: true, // Whether a non-empty value for this prompt is required or not
validate: value => value.length >= 8 // Optional validation function
});
It can be interacted with exactly like a string prompt, the only difference is that every character in the value will be replaced with an asterisk.
number
This prompt can be used to ask the user for a numeric value.
import {number} from 'prask';
import colors from 'tiny-colors';
const result = await number ({
/* REQUIRED OPTIONS */
message: 'What is your favorite even number?', // The message that the user will read
/* OPTIONAL OPTIONS */
initial: 42, // Optional default value, to allow the user to quickly press Enter for it
format: ( value, settled ) => ( value % 2 ) ? colors.red ( value ) : colors.green ( value ), // Optional formatting function, the visual length of the output string must remain the same
validate: value => ( value % 2 ) ? 'The number must be even' : true // Optional validation function
});
It can be interacted with exactly like a string prompt, the main difference is that the value of the input is also automatically validated to be a number, plus the following extra interactions are supported:
| Trigger | Description |
|---|---|
Up/Right |
Increment the current value by 1. |
Down/Left |
Decrement the current value by 1. |
multiselect
This prompt can be used to ask the user to pick from zero to many options between the provided ones.
import {multiselect} from 'prask';
const result = await multiselect ({
/* REQUIRED OPTIONS */
message: 'Which countries would you like to visit?', // The message that the user will read
options: [
{ title: 'Italy', value: 'it' },
{ title: 'Turkey', value: 'tr' },
{ title: 'United Kingdom', value: 'uk' }
],
/* OPTIONAL OPTIONS */
limit: 10, // Limit to this number the maximum number of options visible at one time
min: 1, // Require at least this number of options to be selected
max: 10, // Require at most this number of options to be selected
searchable: false, // Turn off support for filtering the list of options
validate: values => ( values.length % 2 ) // Optional validation function
});
Each option must have the following shape:
type Option<T> = {
/* REQUIRED VALUES */
title: string, // The name of the option that will be showed to the user
value: T, // The value of the option that the prompt will return you when this option is selected
/* OPTIONAL VALUES */
disabled?: boolean, // Whether this option can be toggled or not
description?: string, // The description that will be showed next to the title
heading?: boolean, // Whether this option is an unselectable heading or not
hint?: string, // The description that will be showed next to the title, only when the item is focused
selected?: boolean // Whether this option is pre-selected or not
};
Options can be provided dynamically also, with a function that returns an array of options depending on the current query. That can also be used to customize how results are searched and ranked.
Interactions:
| Trigger | Description |
|---|---|
Esc |
Quit the prompt, which will resolve to undefined. |
Enter |
Submit the selected values, checking if the selection is valid first. |
Space |
Toggle the focused option. |
Up |
Move the focus to the previous item, and potentially scroll to the previous page. |
Down |
Move the focus to the next item, and potentially scroll to the next page. |
* |
All other keys will be used to edit the search query, which will filter down the list. |
select
This prompt can be used to ask the user to pick one option between the provided ones.
import {select} from 'prask';
const result = await select ({
/* REQUIRED OPTIONS */
message: 'Which country would you like to visit first?', // The message that the user will read
options: [
{ title: 'Italy', value: 'it' },
{ title: 'Turkey', value: 'tr' },
{ title: 'United Kingdom', value: 'uk' }
],
/* OPTIONAL OPTIONS */
limit: 10, // Limit to this number the maximum number of options visible at one time
searchable: false, // Turn off support for filtering the list of options
validate: value => value !== 'Foo' // Optional validation function
});
It can be interacted with exactly like a multiselect prompt.
boolean
This prompt can be used to ask the user to pick between yes and no.
import {boolean} from 'prask';
const result = await boolean ({
/* REQUIRED OPTIONS */
message: 'Do you like this library?', // The message that the user will read
/* OPTIONAL OPTIONS */
initial: true // Select this option by default
});
It can be interacted with exactly like a select prompt, except that search is turned off for it.
toggle
This prompt can be used to ask the user to pick between yes and no, using a single line of output, rather than the three that boolean needs.
import {toggle} from 'prask';
const result = await toggle ({
/* REQUIRED OPTIONS */
message: 'Do you like this library?', // The message that the user will read
/* OPTIONAL OPTIONS */
initial: true // Select this option by default
});
Interactions:
| Trigger | Description |
|---|---|
Esc |
Quit the prompt, which will resolve to undefined. |
Enter |
Submit the selected option. |
Left/Right/Up/Down/Tab |
Select the other option. |
rating
This prompt can be used to ask the user for a numeric rating between 1 and 5.
import {rating} from 'prask';
const result = await rating ({
/* REQUIRED OPTIONS */
message: 'How would you rate this library?', // The message that the user will read
/* OPTIONAL OPTIONS */
initial: 5 // Select this option by default
});
Interactions:
| Trigger | Description |
|---|---|
Esc |
Quit the prompt, which will resolve to undefined. |
Enter |
Submit the selected rating. |
Right/Up |
Increment the selected rating. |
Left/Down |
Decrement the selected rating. |
log
This utility provides functions for logging a line of text to the console, with the same style that the built-in prompts use.
import {log} from 'prask';
log.success ( 'Success message' );
log.error ( 'Error message' );
log.warning ( 'Warning message' );
log.question ( 'Question message' );
log.info ( 'Info message' );
promptThis is a low-level general prompt, which is used internally to implement the other higher-level prompts, and which you can use to implement your own custom prompts.
```ts import {prompt} from 'prask'; import color from 'tiny-colors';
// Let's define the "rating" prompt, it returns a number between 1 and 5, which is the rating the user picked // This is the actual internal implementation for that component, so you can see exactly how it works
type Rating = 1 | 2 | 3 | 4 | 5;
type Options = { message: string, initial?: Rating };
// A prompt could always resolve to "undefined", if the user escapes from it const rating = ( options: Options ): Promise => {
// Let's define some internal constants we need for rendering
const STAR_ACTIVE = color.green ( '●' ); const STAR_INACTIVE = '○'; const STARS_DIVIDER = '─────'; const LABELS_DIVIDER = ' ';
// Let's define some internal state variables // The status variable let's us keep track of whether this prompt was escaped (-1), submitted (1), or it's still pending (0)
let {message, initial = 3} = options; let status: -1 | 0 | 1 = 0; let current = initial;
// Let's define some components // Basically each prompt re-renders its components in a loop, React-style // Each prompt must return something to render, which can be a single component or an array of components // A component is just a function that can either return a string, which will be printed to the console, or undefined, which will be ignored // The "prompt" function takes care of cleaning up the previous output and replacing it with the new one // That's basically how the entire library works, the "prompt" function tells you when a key is pressed, and you just tell it what to render next
const getStatusSymbol = ( status ): string => { if ( status < 0 ) return color.red ( '✖' ); if ( status > 0 ) return color.green ( '✔' ); return color.cyan.bold ( '?' ); };
const question = (): string => {
const question = ${getStatusSymbol ()} ${color.bold ( message )};
const result = status === 1 ? color.cyan ( String ( current ) ) : '';
return [question, result].join ( ' ' );
};
const stars = (): string => { const star1 = ( current === 1 ) ? STAR_ACTIVE : STAR_INACTIVE; const star2 = ( current === 2 ) ? STAR_ACTIVE : STAR_INACTIVE; const star3 = ( current === 3 ) ? STAR_ACTIVE : STAR_INACTIVE; const star4 = ( current === 4 ) ? STAR_ACTIVE : STAR_INACTIVE; const star5 = ( current === 5 ) ? STAR_ACTIVE : STAR_INACTIVE; const stars = [star1, star2, star3, star4, star5].join ( STARS_DIVIDER );
$ claude mcp add prask \
-- python -m otcore.mcp_server <graph>