CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-listr2

Terminal task list library for creating beautiful, interactive CLI interfaces with task management, rendering options, and error handling.

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

task-configuration.mddocs/

Task Configuration

Individual task definition, execution control, and lifecycle management including skip conditions, retry logic, rollback functionality, and dynamic task behavior.

Capabilities

ListrTask Interface

Core interface for defining individual tasks with all available configuration options.

/**
 * Configuration interface for individual tasks
 * @template Ctx - Context type shared between tasks
 * @template Renderer - Renderer type for this task
 * @template FallbackRenderer - Fallback renderer type
 */
interface ListrTask<Ctx, Renderer, FallbackRenderer> {
  /** Task display title (can be dynamic) */
  title?: string | any[];
  
  /** Main task execution function */
  task: ListrTaskFn<Ctx, Renderer, FallbackRenderer>;
  
  /** Condition to determine if task should be enabled */
  enabled?: boolean | ((ctx: Ctx) => boolean | Promise<boolean>);
  
  /** Condition or message for skipping the task */
  skip?: boolean | string | ((ctx: Ctx) => boolean | string | Promise<boolean | string>);
  
  /** Retry configuration for failed tasks */
  retry?: number | ListrTaskRetry;
  
  /** Rollback function to execute if task fails */
  rollback?: ListrTaskFn<Ctx, Renderer, FallbackRenderer>;
  
  /** Whether to exit immediately if this task fails */
  exitOnError?: boolean | ((ctx: Ctx) => boolean | Promise<boolean>);
  
  /** Renderer-specific options for primary renderer */
  rendererOptions?: ListrGetRendererTaskOptions<ListrGetRendererClassFromValue<Renderer>>;
  
  /** Renderer-specific options for fallback renderer */
  fallbackRendererOptions?: ListrGetRendererTaskOptions<ListrGetRendererClassFromValue<FallbackRenderer>>;
}

Task Execution Function

The main function that defines what a task does during execution.

/**
 * Task execution function signature
 * @template Ctx - Context type
 * @template Renderer - Renderer type
 * @template FallbackRenderer - Fallback renderer type
 * @param ctx - Shared context object
 * @param task - Task wrapper for manipulation and output
 * @returns Void, promise, string output, observable, or subtask list
 */
type ListrTaskFn<Ctx, Renderer, FallbackRenderer> = (
  ctx: Ctx,
  task: TaskWrapper<Ctx, Renderer, FallbackRenderer>
) => void | ListrTaskResult<Ctx>;

/**
 * Valid return types from task execution functions
 */
type ListrTaskResult<Ctx> = 
  | string 
  | Promise<any>
  | Listr<Ctx, any, any>
  | ReadableLike
  | ObservableLike<any>;

Retry Configuration

Advanced retry configuration for handling task failures with customizable delays and attempt limits.

/**
 * Retry configuration for failed tasks
 */
interface ListrTaskRetry {
  /** Number of retry attempts */
  tries: number;
  /** Delay between retry attempts in milliseconds */
  delay?: number;
}

Usage Examples:

Basic Task Configuration

import { Listr } from "listr2";

const tasks = new Listr([
  {
    title: "Simple task",
    task: () => {
      console.log("Executing task...");
    }
  },
  {
    title: "Async task with output",
    task: async (ctx, task) => {
      task.output = "Starting async operation...";
      await new Promise(resolve => setTimeout(resolve, 2000));
      task.output = "Async operation completed";
      return "Task completed successfully";
    }
  }
]);

Conditional Task Execution

import { Listr } from "listr2";

interface BuildContext {
  environment: 'development' | 'production';
  skipTests: boolean;
}

const tasks = new Listr<BuildContext>([
  {
    title: "Install dependencies",
    task: () => {
      // Always runs
      return Promise.resolve();
    }
  },
  {
    title: "Run tests",
    enabled: (ctx) => !ctx.skipTests,
    task: () => {
      return Promise.resolve();
    }
  },
  {
    title: "Production build",
    enabled: (ctx) => ctx.environment === 'production',
    task: () => {
      return Promise.resolve();
    }
  },
  {
    title: "Development build", 
    enabled: (ctx) => ctx.environment === 'development',
    task: () => {
      return Promise.resolve();
    }
  }
]);

await tasks.run({ environment: 'production', skipTests: false });

Skip Conditions

import { Listr } from "listr2";

const tasks = new Listr([
  {
    title: "Check prerequisites",
    task: (ctx) => {
      ctx.hasPrereqs = Math.random() > 0.5;
    }
  },
  {
    title: "Main task",
    skip: (ctx) => !ctx.hasPrereqs ? "Prerequisites not met" : false,
    task: () => {
      return Promise.resolve();
    }
  },
  {
    title: "Always skipped task",
    skip: "This task is always skipped",
    task: () => {
      return Promise.resolve();
    }
  }
]);

Retry Logic

import { Listr } from "listr2";

const tasks = new Listr([
  {
    title: "Flaky network request",
    retry: 3, // Simple retry count
    task: async () => {
      if (Math.random() < 0.7) {
        throw new Error("Network request failed");
      }
      return "Request successful";
    }
  },
  {
    title: "API call with delay",
    retry: {
      tries: 5,
      delay: 1000 // 1 second delay between retries
    },
    task: async () => {
      // Simulate API call that might fail
      const response = await fetch("https://api.example.com/data");
      if (!response.ok) {
        throw new Error(`API request failed: ${response.status}`);
      }
      return response.json();
    }
  }
]);

Rollback Functionality

import { Listr } from "listr2";

interface DeployContext {
  backupCreated: boolean;
  deploymentId?: string;
}

const tasks = new Listr<DeployContext>([
  {
    title: "Create backup",
    task: (ctx) => {
      // Create backup logic
      ctx.backupCreated = true;
      return Promise.resolve();
    }
  },
  {
    title: "Deploy application",
    task: (ctx) => {
      ctx.deploymentId = "deploy-" + Date.now();
      // Simulate deployment that might fail
      if (Math.random() < 0.3) {
        throw new Error("Deployment failed");
      }
      return Promise.resolve();
    },
    rollback: (ctx, task) => {
      if (ctx.backupCreated && ctx.deploymentId) {
        task.output = `Rolling back deployment ${ctx.deploymentId}...`;
        // Rollback logic here
        return Promise.resolve();
      }
    }
  }
]);

Dynamic Task Titles

import { Listr } from "listr2";

const files = ["file1.txt", "file2.txt", "file3.txt"];

const tasks = new Listr(
  files.map((file, index) => ({
    title: `Processing ${file}`,
    task: (ctx, task) => {
      return new Promise(resolve => {
        setTimeout(() => {
          task.title = `✅ Processed ${file}`;
          resolve(undefined);
        }, 1000);
      });
    }
  }))
);

Observable and Stream Integration

import { Listr } from "listr2";
import { Observable } from "rxjs";
import { spawn } from "child_process";

const tasks = new Listr([
  {
    title: "Observable task",
    task: () => {
      return new Observable(observer => {
        let count = 0;
        const interval = setInterval(() => {
          observer.next(`Progress: ${++count}/10`);
          if (count >= 10) {
            observer.complete();
            clearInterval(interval);
          }
        }, 100);
      });
    }
  },
  {
    title: "Stream from child process",
    task: (ctx, task) => {
      const child = spawn('npm', ['install'], { cwd: process.cwd() });
      
      // Pipe stdout to task output
      child.stdout.on('data', (data) => {
        task.output = data.toString();
      });
      
      return new Promise((resolve, reject) => {
        child.on('close', (code) => {
          if (code === 0) {
            resolve(undefined);
          } else {
            reject(new Error(`Process exited with code ${code}`));
          }
        });
      });
    }
  }
]);

Error Handling Options

import { Listr } from "listr2";

const tasks = new Listr([
  {
    title: "Critical task",
    exitOnError: true, // Stop entire task list if this fails
    task: () => {
      if (Math.random() < 0.5) {
        throw new Error("Critical failure");
      }
      return Promise.resolve();
    }
  },
  {
    title: "Non-critical task",
    exitOnError: false, // Continue even if this fails
    task: () => {
      throw new Error("Non-critical failure");
    }
  },
  {
    title: "Context-dependent error handling",
    exitOnError: (ctx) => ctx.strictMode === true,
    task: () => {
      throw new Error("Conditional failure");
    }
  }
]);

await tasks.run({ strictMode: false });

Types

interface ListrTaskMessage {
  /** Error message if task failed */
  error?: string;
  /** Skip message if task was skipped */
  skip?: string;
  /** Rollback message during rollback operations */
  rollback?: string;
  /** Retry message during retry attempts */
  retry?: { count: number; message?: string };
}

interface ListrTaskPrompt {
  /** Current prompt instance */
  prompt?: any;
  /** Prompt error if prompt failed */
  error?: Error;
}

/**
 * Stream-like interface for task output
 */
interface ReadableLike {
  readable: boolean;
  read: (size?: number) => string | Buffer;
  on: (eventName: 'data' | 'error' | 'end', listener: (data: Buffer | string) => void) => unknown;
}

/**
 * Observable-like interface for task progress
 */
interface ObservableLike<T> {
  subscribe: (observer: ObserverLike<T>) => unknown;
}

interface ObserverLike<T> {
  next?: (value: T) => void;
  error?: (error: any) => void;
  complete?: () => void;
}

docs

error-handling.md

event-management.md

index.md

renderers.md

task-configuration.md

task-management.md

tile.json