Terminal task list library for creating beautiful, interactive CLI interfaces with task management, rendering options, and error handling.
npx @tessl/cli install tessl/npm-listr2@8.3.0Listr2 is a modern terminal task list library for Node.js that creates beautiful, interactive CLI interfaces. It provides multiple built-in renderers (default, verbose, silent, simple, test) with customizable themes and colors, supports nested task lists with concurrent and sequential execution modes, and includes comprehensive error handling and retry mechanisms with built-in progress indicators and spinners.
npm install listr2import { Listr } from "listr2";For CommonJS:
const { Listr } = require("listr2");Additional imports:
import {
Listr,
ListrTaskState,
ListrError,
ListrEventManager,
ListrTaskEventManager,
EventManager,
DefaultRenderer,
SimpleRenderer,
VerboseRenderer,
PRESET_TIMER,
PRESET_TIMESTAMP
} from "listr2";import { Listr } from "listr2";
const tasks = new Listr([
{
title: "Installing dependencies",
task: () => {
// Simulate async work
return new Promise(resolve => setTimeout(resolve, 2000));
}
},
{
title: "Building project",
task: (ctx, task) => {
// Access task wrapper for output
task.output = "Compiling TypeScript...";
return new Promise(resolve => setTimeout(resolve, 3000));
}
},
{
title: "Running tests",
task: () => {
return new Promise(resolve => setTimeout(resolve, 1000));
}
}
]);
// Run the task list
try {
await tasks.run();
console.log("All tasks completed successfully!");
} catch (error) {
console.error("Tasks failed:", error);
}Listr2 is built around several key components:
Main Listr class for creating and executing task lists with context sharing, concurrent execution, and error handling.
class Listr<Ctx = ListrContext, Renderer = ListrPrimaryRendererValue, FallbackRenderer = ListrSecondaryRendererValue> {
constructor(
task: ListrTask<Ctx, Renderer, FallbackRenderer> | ListrTask<Ctx, Renderer, FallbackRenderer>[],
options?: ListrBaseClassOptions<Ctx, Renderer, FallbackRenderer>,
parentTask?: Task<any, Renderer, FallbackRenderer>
);
add(tasks: ListrTask<Ctx, Renderer>[] | ListrTask<Ctx, Renderer>): void;
run(context?: Ctx): Promise<Ctx>;
isRoot(): boolean;
isSubtask(): boolean;
}Individual task definition, execution control, and lifecycle management including skip conditions, retry logic, and rollback functionality.
interface ListrTask<Ctx, Renderer, FallbackRenderer> {
title?: string | any[];
task: ListrTaskFn<Ctx, Renderer, FallbackRenderer>;
enabled?: boolean | ((ctx: Ctx) => boolean | Promise<boolean>);
skip?: boolean | string | ((ctx: Ctx) => boolean | string | Promise<boolean | string>);
retry?: number | { tries: number; delay?: number };
rollback?: ListrTaskFn<Ctx, Renderer, FallbackRenderer>;
exitOnError?: boolean | ((ctx: Ctx) => boolean | Promise<boolean>);
}
type ListrTaskFn<Ctx, Renderer, FallbackRenderer> = (
ctx: Ctx,
task: TaskWrapper<Ctx, Renderer, FallbackRenderer>
) => void | ListrTaskResult<Ctx>;Multiple built-in renderers for different environments and use cases, with customizable options and TTY detection.
type ListrRendererValue = 'default' | 'simple' | 'verbose' | 'test' | 'silent' | ListrRendererFactory;
interface ListrBaseClassOptions<Ctx, Renderer, FallbackRenderer> {
renderer?: Renderer;
fallbackRenderer?: FallbackRenderer;
rendererOptions?: ListrGetRendererOptions<ListrGetRendererClassFromValue<Renderer>>;
fallbackRendererOptions?: ListrGetRendererOptions<ListrGetRendererClassFromValue<FallbackRenderer>>;
fallbackRendererCondition?: boolean | (() => boolean);
silentRendererCondition?: boolean | (() => boolean);
}Observable-based event system for monitoring task state changes, progress updates, and lifecycle events.
class ListrEventManager extends EventManager {
// Inherits all EventManager functionality
}
class ListrTaskEventManager extends EventManager {
// Inherits all EventManager functionality
}
enum ListrTaskEventType {
TITLE = 'TITLE',
STATE = 'STATE',
ENABLED = 'ENABLED',
SUBTASK = 'SUBTASK',
PROMPT = 'PROMPT',
OUTPUT = 'OUTPUT',
MESSAGE = 'MESSAGE',
CLOSED = 'CLOSED'
}Comprehensive error handling system with task state management, error collection modes, and rollback capabilities.
class ListrError<Ctx> extends Error {
public path: string[];
public ctx: Ctx;
constructor(error: Error, type: ListrErrorTypes, task: Task);
}
enum ListrTaskState {
WAITING = 'WAITING',
STARTED = 'STARTED',
COMPLETED = 'COMPLETED',
FAILED = 'FAILED',
SKIPPED = 'SKIPPED',
ROLLING_BACK = 'ROLLING_BACK',
ROLLED_BACK = 'ROLLED_BACK',
RETRY = 'RETRY',
PAUSED = 'PAUSED',
PROMPT = 'PROMPT',
PROMPT_COMPLETED = 'PROMPT_COMPLETED',
PROMPT_FAILED = 'PROMPT_FAILED'
}
enum ListrErrorTypes {
WILL_RETRY = 'WILL_RETRY',
WILL_ROLLBACK = 'WILL_ROLLBACK',
HAS_FAILED_TO_ROLLBACK = 'HAS_FAILED_TO_ROLLBACK',
HAS_FAILED = 'HAS_FAILED',
HAS_FAILED_WITHOUT_ERROR = 'HAS_FAILED_WITHOUT_ERROR'
}Built-in formatting presets for timestamps and timers, plus utility functions for environment detection and UI formatting.
// Timer preset for displaying elapsed time
const PRESET_TIMER: PresetTimer;
type PresetTimer = LoggerFieldFn<[number]>;
interface RendererPresetTimer {
/** Show duration for the tasks */
timer?: PresetTimer;
}
// Timestamp preset for displaying current time
const PRESET_TIMESTAMP: PresetTimestamp;
type PresetTimestamp = LoggerFieldFn;
interface RendererPresetTimestamp {
/** Show timestamp for each event that has been logged */
timestamp?: PresetTimestamp;
}
interface LoggerFieldFn<Args extends any[] = any[]> {
/** The value of the given field */
field: ((...args: Args) => string) | string;
/** Condition to display the given field */
condition?: ((...args: Args) => boolean) | boolean;
/** Formatting/coloring of the field */
format?: (...args: Args) => LoggerFormat;
/** Args to pass to other functions whenever this field is triggered */
args?: Args;
}
type LoggerFormat = (message?: string) => string;
// Utility functions
function getRenderer<Renderer extends ListrRendererValue, FallbackRenderer extends ListrRendererValue>(options: {
renderer: Renderer;
rendererOptions: ListrGetRendererOptions<Renderer>;
fallbackRenderer: FallbackRenderer;
fallbackRendererOptions: ListrGetRendererOptions<FallbackRenderer>;
fallbackRendererCondition?: boolean | (() => boolean);
silentRendererCondition?: boolean | (() => boolean);
}): SupportedRenderer<ListrRendererFactory>;
function getRendererClass(renderer: ListrRendererValue): ListrRendererFactory;type ListrContext = Record<PropertyKey, any>;
type ListrTaskResult<Ctx> =
| string
| Promise<any>
| Listr<Ctx, any, any>
| ReadableLike
| ObservableLike<any>;
interface ObservableLike<T> {
subscribe: (observer: ObserverLike<T>) => unknown;
}
interface ReadableLike {
readable: boolean;
read: (size?: number) => string | Buffer;
on: (eventName: 'data' | 'error' | 'end', listener: (data: Buffer | string) => void) => unknown;
}
enum ListrEnvironmentVariables {
FORCE_UNICODE = 'LISTR_FORCE_UNICODE',
FORCE_TTY = 'LISTR_FORCE_TTY',
DISABLE_COLOR = 'NO_COLOR',
FORCE_COLOR = 'FORCE_COLOR'
}
// Helper types for renderer-related generics
type ListrGetRendererClassFromValue<T extends ListrRendererValue> =
T extends ListrRendererFactory ? T :
T extends 'default' ? typeof DefaultRenderer :
T extends 'simple' ? typeof SimpleRenderer :
T extends 'verbose' ? typeof VerboseRenderer :
T extends 'test' ? typeof TestRenderer :
T extends 'silent' ? typeof SilentRenderer :
never;
type ListrGetRendererOptions<T extends ListrRendererFactory> =
T extends { rendererOptions: infer R } ? R : Record<PropertyKey, any>;
type ListrGetRendererTaskOptions<T extends ListrRendererFactory> =
T extends { rendererTaskOptions: infer R } ? R : Record<PropertyKey, any>;
type ListrRendererFactory = new (
tasks: Task[],
options: Record<PropertyKey, any>,
events?: ListrEventManager
) => ListrRenderer;
interface SupportedRenderer<Renderer extends ListrRendererFactory> {
renderer: Renderer;
options?: ListrGetRendererOptions<Renderer>;
selection: ListrRendererSelection;
}
enum ListrRendererSelection {
PRIMARY = 'PRIMARY',
SECONDARY = 'SECONDARY',
SILENT = 'SILENT'
}
// Renderer class declarations
declare class ListrRenderer {
static rendererOptions: Record<PropertyKey, any>;
static rendererTaskOptions: Record<PropertyKey, any>;
static nonTTY: boolean;
constructor(tasks: Task[], options: Record<PropertyKey, any>, events?: ListrEventManager);
render(): void | Promise<void>;
end(err?: Error): void;
}
declare class DefaultRenderer extends ListrRenderer {
static nonTTY: false;
}
declare class SimpleRenderer extends ListrRenderer {
static nonTTY: true;
}
declare class VerboseRenderer extends ListrRenderer {
static nonTTY: true;
}
declare class TestRenderer extends ListrRenderer {}
declare class SilentRenderer extends ListrRenderer {}
declare abstract class ListrPromptAdapter {
constructor(task: Task<any, any, any>, wrapper: TaskWrapper<any, any, any>);
protected reportStarted(): void;
protected reportFailed(): void;
protected reportCompleted(): void;
protected restoreState(): void;
public abstract run<T = any>(...args: any[]): T | Promise<T>;
}
// Core task classes
declare class Task<Ctx, Renderer, FallbackRenderer> {
public id: string;
public state: ListrTaskState;
public title?: string;
public output?: string;
public subtasks: Task<Ctx, Renderer, FallbackRenderer>[];
public retry?: ListrTaskRetry;
public message: ListrTaskMessage;
public prompt: ListrTaskPrompt;
public listr: Listr<Ctx, any, any>;
check(ctx: Ctx): Promise<boolean>;
isPending(): boolean;
isRunning(): boolean;
isCompleted(): boolean;
isFailed(): boolean;
isSkipped(): boolean;
isRetrying(): boolean;
isRollingBack(): boolean;
isRolledBack(): boolean;
pause(time: number): Promise<void>;
}
declare class TaskWrapper<Ctx, Renderer, FallbackRenderer> {
title: string | any[];
output: string | any[];
newListr(task: ListrTask<Ctx, Renderer> | ListrTask<Ctx, Renderer>[], options?: ListrBaseClassOptions<Ctx, any, any>): Listr<Ctx, any, any>;
report(error: Error, type?: ListrErrorTypes): void;
skip(message?: string): void;
isRetrying(): Task['retry'];
prompt<T extends ListrPromptAdapter = ListrPromptAdapter>(
adapter: new (task: Task<Ctx, Renderer, FallbackRenderer>, wrapper: TaskWrapper<Ctx, Renderer, FallbackRenderer>) => T
): T;
stdout(type?: ListrTaskEventType.OUTPUT | ListrTaskEventType.PROMPT): NodeJS.WritableStream;
}