or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

advanced-workflows.mdcancellation-handling.mdindex.mdinteractive-prompts.mdlogging-system.mdprogress-indicators.mdselection-prompts.mdsession-management.mdsettings-configuration.md
tile.json

logging-system.mddocs/

Logging System

Comprehensive logging utilities supporting both static and streaming output with different message types and custom symbols.

Capabilities

Static Logging

Standard logging functions for different message types with consistent visual styling.

/**
 * Static logging utilities for different message types
 */
interface LogSystem {
  /** Display a general message with optional custom symbol */
  message: (message?: string, options?: LogMessageOptions) => void;
  /** Display an info message with blue info symbol */
  info: (message: string) => void;
  /** Display a success message with green checkmark symbol */
  success: (message: string) => void;
  /** Display a step message with green step symbol */
  step: (message: string) => void;
  /** Display a warning message with yellow warning symbol */
  warn: (message: string) => void;
  /** Alias for warn() */
  warning: (message: string) => void;
  /** Display an error message with red error symbol */
  error: (message: string) => void;
}

interface LogMessageOptions {
  /** Custom symbol to display before the message */
  symbol?: string;
}

Usage Examples:

import { log } from "@clack/prompts";
import color from "picocolors"; // Optional for custom symbols

// Basic logging
log.info("Starting application setup");
log.success("Database connection established");
log.step("Installing dependencies");
log.warn("Using deprecated configuration format");
log.error("Failed to connect to remote server");

// General message logging
log.message("Processing user data");

// Custom symbol logging
log.message("Custom operation completed", {
  symbol: color.cyan("β˜…")
});

// Multi-line messages
log.info("Configuration loaded successfully\nUsing development environment");

log.error(`Build failed with errors:
- Missing required dependency
- Invalid configuration file
- Permission denied`);

Streaming Logging

Asynchronous logging functions for streaming iterables and real-time output.

/**
 * Streaming logging utilities for iterables and async iterables
 */
interface StreamSystem {
  /** Stream a general message with optional custom symbol */
  message: (iterable: Iterable<string> | AsyncIterable<string>, options?: LogMessageOptions) => Promise<void>;
  /** Stream an info message with blue info symbol */
  info: (iterable: Iterable<string> | AsyncIterable<string>) => Promise<void>;
  /** Stream a success message with green checkmark symbol */
  success: (iterable: Iterable<string> | AsyncIterable<string>) => Promise<void>;
  /** Stream a step message with green step symbol */
  step: (iterable: Iterable<string> | AsyncIterable<string>) => Promise<void>;
  /** Stream a warning message with yellow warning symbol */
  warn: (iterable: Iterable<string> | AsyncIterable<string>) => Promise<void>;
  /** Alias for warn() */
  warning: (iterable: Iterable<string> | AsyncIterable<string>) => Promise<void>;
  /** Stream an error message with red error symbol */
  error: (iterable: Iterable<string> | AsyncIterable<string>) => Promise<void>;
}

Usage Examples:

import { stream } from "@clack/prompts";
import color from "picocolors";

// Streaming from generators
await stream.info((function* () {
  yield "Connecting to API";
  yield "...";
  yield " connected!";
})());

// Streaming from async generators
async function* buildProgress() {
  yield "Building components";
  await new Promise(resolve => setTimeout(resolve, 1000));
  yield "\nOptimizing assets";
  await new Promise(resolve => setTimeout(resolve, 500));
  yield "\nBuild complete!";
}

await stream.success(buildProgress());

// Streaming arrays
const steps = ["Step 1", "Step 2", "Step 3"];
await stream.step(steps);

// Custom symbol streaming
await stream.message((function* () {
  yield "Deploying to production";
  yield "...";
  yield " deployed!";
})(), { symbol: color.magenta("πŸš€") });

// Real-time log streaming
async function* processLogs() {
  const logs = [
    "Starting process",
    "Loading configuration",
    "Connecting to database",
    "Processing data",
    "Generating report",
    "Process complete"
  ];
  
  for (const log of logs) {
    yield log + "\n";
    await new Promise(resolve => setTimeout(resolve, 500));
  }
}

await stream.info(processLogs());

Advanced Streaming Patterns

import { stream } from "@clack/prompts";

// Streaming from fetch response
async function streamApiResponse(url: string) {
  const response = await fetch(url);
  const reader = response.body?.getReader();
  
  if (!reader) return;
  
  async function* streamResponse() {
    try {
      while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        yield new TextDecoder().decode(value);
      }
    } finally {
      reader.releaseLock();
    }
  }
  
  await stream.info(streamResponse());
}

// Streaming command output
import { spawn } from "child_process";

async function streamCommandOutput(command: string, args: string[]) {
  const child = spawn(command, args);
  
  async function* streamStdout() {
    for await (const chunk of child.stdout) {
      yield chunk.toString();
    }
  }
  
  async function* streamStderr() {
    for await (const chunk of child.stderr) {
      yield chunk.toString();
    }
  }
  
  // Stream both stdout and stderr
  await Promise.all([
    stream.info(streamStdout()),
    stream.error(streamStderr())
  ]);
}

// Streaming with progress updates
async function* downloadProgress(totalSize: number) {
  let downloaded = 0;
  
  while (downloaded < totalSize) {
    downloaded += Math.random() * 1000000; // Simulate download
    const percent = Math.min(Math.round((downloaded / totalSize) * 100), 100);
    yield `Downloading... ${percent}%\n`;
    
    if (downloaded >= totalSize) {
      yield "Download complete!";
      break;
    }
    
    await new Promise(resolve => setTimeout(resolve, 100));
  }
}

await stream.success(downloadProgress(10000000));

Message Formatting

Line Wrapping

The logging system automatically handles line wrapping based on terminal width:

import { log } from "@clack/prompts";

// Long messages wrap appropriately
log.info("This is a very long message that will automatically wrap to the next line when it exceeds the terminal width, maintaining proper indentation and visual consistency throughout the entire message.");

Multi-line Messages

import { log } from "@clack/prompts";

// Explicit line breaks
log.success(`Operation completed successfully!

Results:
- 15 files processed
- 3 errors fixed
- 0 warnings remaining`);

// Streaming multi-line content
await stream.info((function* () {
  yield "Multi-line content:\n";
  yield "Line 1\n";
  yield "Line 2\n";
  yield "Line 3";
})());

Symbol Customization

import { log, stream } from "@clack/prompts";
import color from "picocolors";

// Custom symbols for different operations
const symbols = {
  download: color.blue("⬇"),
  upload: color.green("⬆"),
  process: color.yellow("βš™"),
  complete: color.green("βœ“"),
  failed: color.red("βœ—")
};

log.message("Downloading package", { symbol: symbols.download });
log.message("Processing files", { symbol: symbols.process });
log.message("Upload complete", { symbol: symbols.complete });

// Custom streaming symbols
await stream.message((function* () {
  yield "Syncing data";
  yield "...";
  yield " synchronized!";
})(), { symbol: color.cyan("⟲") });

Integration Patterns

With Spinners

import { spinner, log } from "@clack/prompts";

const s = spinner();
s.start("Processing");

try {
  // Work here
  s.stop("Processing complete");
  log.success("All operations completed successfully");
} catch (error) {
  s.stop("Processing failed", 1);
  log.error(`Operation failed: ${error.message}`);
}

With Prompt Sessions

import { intro, outro, log, text, isCancel, cancel } from "@clack/prompts";

intro("Configuration Setup");

log.info("Welcome to the configuration wizard");

const config = await text({
  message: "Enter configuration value:",
});

if (isCancel(config)) {
  cancel("Setup cancelled");
  process.exit(0);
}

log.step("Validating configuration");
log.success("Configuration validated successfully");

outro("Setup complete!");

Error Reporting

import { log } from "@clack/prompts";

try {
  // Risky operation
  throw new Error("Something went wrong");
} catch (error) {
  log.error(`Operation failed: ${error.message}`);
  
  if (error.stack) {
    log.message(`Stack trace:\n${error.stack}`, {
      symbol: " " // Indent without symbol
    });
  }
  
  log.info("Please check your configuration and try again");
}

Best Practices

  1. Use appropriate log levels - info for general information, success for completions, warn for non-fatal issues, error for failures
  2. Provide context - Include relevant details in log messages to help users understand what's happening
  3. Use streaming for real-time updates - Stream long-running operations or real-time data
  4. Consistent symbol usage - Use custom symbols consistently throughout your application
  5. Handle line breaks properly - Use \n explicitly for multi-line content
  6. Combine with other components - Integrate logging with spinners, prompts, and session management for cohesive UX