Effortlessly build beautiful command-line apps - an opinionated, pre-styled wrapper around @clack/core
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Comprehensive logging utilities supporting both static and streaming output with different message types and custom symbols.
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`);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());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));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.");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";
})());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("⟲") });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}`);
}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!");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");
}info for general information, success for completions, warn for non-fatal issues, error for failures\n explicitly for multi-line content