Terminal task list library for creating beautiful, interactive CLI interfaces with task management, rendering options, and error handling.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Core task management functionality providing the main Listr class for orchestrating task execution, context sharing, concurrent processing, and lifecycle management.
The main class for creating and managing task lists with full type safety and extensive configuration options.
/**
* Main class for creating and managing task lists
* @template Ctx - Context type shared between tasks
* @template Renderer - Primary renderer type
* @template FallbackRenderer - Fallback renderer type
*/
class Listr<
Ctx = ListrContext,
Renderer extends ListrRendererValue = ListrPrimaryRendererValue,
FallbackRenderer extends ListrRendererValue = ListrSecondaryRendererValue
> {
/** Array of tasks to execute */
public tasks: Task<Ctx, ListrGetRendererClassFromValue<Renderer>, ListrGetRendererClassFromValue<FallbackRenderer>>[];
/** Collected errors from task execution */
public errors: ListrError<Ctx>[];
/** Shared context object between tasks */
public ctx: Ctx;
/** Event manager instance */
public events: ListrEventManager;
/** Current execution path */
public path: string[];
/** Selected renderer class */
public rendererClass: ListrRendererFactory;
/** Options for the current renderer */
public rendererClassOptions: ListrGetRendererOptions<ListrGetRendererClassFromValue<Renderer> | ListrGetRendererClassFromValue<FallbackRenderer>>;
/** Current renderer selection state */
public rendererSelection: ListrRendererSelection;
/**
* Create a new task list
* @param task - Single task or array of tasks to execute
* @param options - Configuration options for the task list
* @param parentTask - Parent task if this is a subtask list
*/
constructor(
task: ListrTask<Ctx, ListrGetRendererClassFromValue<Renderer>, ListrGetRendererClassFromValue<FallbackRenderer>>
| ListrTask<Ctx, ListrGetRendererClassFromValue<Renderer>, ListrGetRendererClassFromValue<FallbackRenderer>>[],
options?: ListrBaseClassOptions<Ctx, Renderer, FallbackRenderer>,
parentTask?: Task<any, ListrGetRendererClassFromValue<Renderer>, ListrGetRendererClassFromValue<FallbackRenderer>>
);
/**
* Check if this is the root task list
* @returns true if this is the root task list
*/
isRoot(): boolean;
/**
* Check if this is a subtask of another task list
* @returns true if this is a subtask
*/
isSubtask(): boolean;
/**
* Add tasks to the current task list
* @param tasks - Single task or array of tasks to add
*/
add(tasks: ListrTask<Ctx, ListrGetRendererClassFromValue<Renderer>> | ListrTask<Ctx, ListrGetRendererClassFromValue<Renderer>>[]): void;
/**
* Execute the task list
* @param context - Initial context to pass to tasks
* @returns Promise resolving to the final context
*/
run(context?: Ctx): Promise<Ctx>;
}User-facing API wrapper for task manipulation during execution, providing methods for output, state control, and subtask creation.
/**
* Task wrapper providing user-facing API for task manipulation
* @template Ctx - Context type
* @template Renderer - Renderer type
* @template FallbackRenderer - Fallback renderer type
*/
class TaskWrapper<Ctx, Renderer, FallbackRenderer> {
/** Get or set the task title */
title: string | any[];
/** Get or set the task output */
output: string | any[];
/**
* Create a new subtask list
* @param task - Task or array of tasks for the subtask list
* @param options - Options for the subtask list
* @returns New Listr instance for subtasks
*/
newListr(
task: ListrTask<Ctx, Renderer> | ListrTask<Ctx, Renderer>[],
options?: ListrBaseClassOptions<Ctx, any, any>
): Listr<Ctx, any, any>;
/**
* Report an error for the current task
* @param error - Error to report
* @param type - Type of error for categorization
*/
report(error: Error, type?: ListrErrorTypes): void;
/**
* Skip the current task with an optional message
* @param message - Optional skip message
*/
skip(message?: string): void;
/**
* Check whether this task is currently in a retry state
* @returns Retry state information or { count: 0 } if not retrying
*/
isRetrying(): Task['retry'];
/**
* Create a new prompt for getting user input through the prompt adapter
* @param adapter - Prompt adapter constructor
* @returns Prompt adapter instance
*/
prompt<T extends ListrPromptAdapter = ListrPromptAdapter>(
adapter: new (task: Task<Ctx, Renderer, FallbackRenderer>, wrapper: TaskWrapper<Ctx, Renderer, FallbackRenderer>) => T
): T;
/**
* Get stdout stream for process output capture
* @param type - Output type for the stream
* @returns Stdout stream
*/
stdout(type?: ListrTaskEventType.OUTPUT | ListrTaskEventType.PROMPT): NodeJS.WritableStream;
}Internal task representation with state management and lifecycle control.
/**
* Internal task representation with state management
* @template Ctx - Context type
* @template Renderer - Renderer type
* @template FallbackRenderer - Fallback renderer type
*/
class Task<Ctx, Renderer, FallbackRenderer> {
/** Unique task identifier */
public id: string;
/** Current task state */
public state: ListrTaskState;
/** Task title */
public title?: string;
/** Task output */
public output?: string;
/** Child/subtasks array */
public subtasks: Task<Ctx, Renderer, FallbackRenderer>[];
/** Retry configuration */
public retry?: ListrTaskRetry;
/** Message channel for task communication */
public message: ListrTaskMessage;
/** Current prompt instance */
public prompt: ListrTaskPrompt;
/** Associated Listr instance */
public listr: Listr<Ctx, any, any>;
/**
* Check if task should be enabled
* @param ctx - Current context
* @returns Promise resolving to boolean indicating if task is enabled
*/
check(ctx: Ctx): Promise<boolean>;
/**
* Check if task is in pending state
* @returns true if task is pending
*/
isPending(): boolean;
/**
* Check if task is in running state
* @returns true if task is running
*/
isRunning(): boolean;
/**
* Check if task has completed successfully
* @returns true if task has completed
*/
isCompleted(): boolean;
/**
* Check if task has failed
* @returns true if task has failed
*/
isFailed(): boolean;
/**
* Check if task was skipped
* @returns true if task was skipped
*/
isSkipped(): boolean;
/**
* Check if task is in retry state
* @returns true if task is retrying
*/
isRetrying(): boolean;
/**
* Check if task is rolling back
* @returns true if task is rolling back
*/
isRollingBack(): boolean;
/**
* Check if task has been rolled back
* @returns true if task has been rolled back
*/
isRolledBack(): boolean;
/**
* Pause task execution for specified time
* @param time - Time to pause in milliseconds
* @returns Promise that resolves when pause is complete
*/
pause(time: number): Promise<void>;
}Usage Examples:
import { Listr } from "listr2";
interface MyContext {
files: string[];
results: any[];
}
// Basic task list with typed context
const tasks = new Listr<MyContext>([
{
title: "Scanning files",
task: (ctx, task) => {
// Simulate file scanning
return new Promise(resolve => {
setTimeout(() => {
ctx.files = ["file1.js", "file2.js", "file3.js"];
task.output = `Found ${ctx.files.length} files`;
resolve(undefined);
}, 1000);
});
}
},
{
title: "Processing files",
task: (ctx, task) => {
// Create subtasks for each file
return task.newListr(
ctx.files.map(file => ({
title: `Processing ${file}`,
task: async (subCtx) => {
// Simulate processing
await new Promise(resolve => setTimeout(resolve, 500));
subCtx.results = subCtx.results || [];
subCtx.results.push(`processed-${file}`);
}
})),
{ concurrent: true }
);
}
}
]);
// Run with initial context
const result = await tasks.run({ files: [], results: [] });
console.log("Processing complete:", result.results);import { Listr } from "listr2";
// Sequential execution (default)
const sequential = new Listr([
{ title: "Task 1", task: () => Promise.resolve() },
{ title: "Task 2", task: () => Promise.resolve() },
{ title: "Task 3", task: () => Promise.resolve() }
]);
// Concurrent execution
const concurrent = new Listr([
{ title: "Task 1", task: () => Promise.resolve() },
{ title: "Task 2", task: () => Promise.resolve() },
{ title: "Task 3", task: () => Promise.resolve() }
], { concurrent: true });
// Limited concurrency
const limitedConcurrent = new Listr([
{ title: "Task 1", task: () => Promise.resolve() },
{ title: "Task 2", task: () => Promise.resolve() },
{ title: "Task 3", task: () => Promise.resolve() },
{ title: "Task 4", task: () => Promise.resolve() }
], { concurrent: 2 }); // Max 2 tasks running simultaneouslyinterface ListrBaseClassOptions<Ctx, Renderer, FallbackRenderer> {
/** Initial context to pass to tasks */
ctx?: Ctx;
/** Enable concurrent execution or set concurrency limit */
concurrent?: boolean | number;
/** Exit immediately when a task fails */
exitOnError?: boolean;
/** Exit after rollback operations complete */
exitAfterRollback?: boolean;
/** Error collection mode */
collectErrors?: false | 'minimal' | 'full';
/** Register signal listeners for graceful shutdown */
registerSignalListeners?: boolean;
/** Primary renderer to use */
renderer?: Renderer;
/** Fallback renderer when primary is unavailable */
fallbackRenderer?: FallbackRenderer;
/** Options for the primary renderer */
rendererOptions?: ListrGetRendererOptions<ListrGetRendererClassFromValue<Renderer>>;
/** Options for the fallback renderer */
fallbackRendererOptions?: ListrGetRendererOptions<ListrGetRendererClassFromValue<FallbackRenderer>>;
/** Condition to determine when to use fallback renderer */
fallbackRendererCondition?: boolean | (() => boolean);
/** Condition to determine when to use silent renderer */
silentRendererCondition?: boolean | (() => boolean);
/** Force TTY mode even when not detected */
forceTTY?: boolean;
/** Force Unicode support even when not detected */
forceUnicode?: boolean;
}
type ListrContext = Record<PropertyKey, any>;
type ListrPrimaryRendererValue = 'default';
type ListrSecondaryRendererValue = 'simple';
enum ListrRendererSelection {
PRIMARY = 'PRIMARY',
SECONDARY = 'SECONDARY',
SILENT = 'SILENT'
}