CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-zx

A tool for writing better scripts by bridging JavaScript and shell commands with cross-platform wrappers around child_process

Overview
Eval results
Files

user-interaction.mddocs/

User Interaction

Interactive utilities for user input, output formatting, visual feedback, and data processing in command-line scripts.

Capabilities

Output Functions

Display formatted output and information to users.

/**
 * Print formatted output to console
 * @param args - Values to print (or template literal with interpolations)
 */
function echo(...args: any[]): void;
function echo(pieces: TemplateStringsArray, ...args: any[]): void;

Usage Examples:

import { echo, $ } from "zx";

// Simple output
echo('Hello World');
echo('Deployment status:', 'success');

// Template literal format
const status = 'completed';
const time = new Date().toISOString();
echo`Deployment ${status} at ${time}`;

// With ProcessOutput
const result = await $`git branch --show-current`;
echo`Current branch: ${result}`;

// Multiple values
echo('Server:', 'production', 'Port:', 8080);

User Input

Get interactive input from users with optional choices and completion.

/**
 * Prompt user for input with optional choices and completion
 * @param query - Question to ask the user
 * @param options - Configuration options
 * @returns Promise resolving to user's input
 */
function question(
  query?: string,
  options?: {
    /** Array of suggested choices for tab completion */
    choices?: string[];
    /** Input stream (default: process.stdin) */
    input?: NodeJS.ReadStream;
    /** Output stream (default: process.stdout) */
    output?: NodeJS.WriteStream;
  }
): Promise<string>;

Usage Examples:

import { question, echo } from "zx";

// Simple question
const name = await question('What is your name? ');
echo`Hello, ${name}!`;

// With choices (tab completion)
const environment = await question('Deploy to which environment? ', {
  choices: ['development', 'staging', 'production']
});

// Confirmation prompt
const proceed = await question('Continue with deployment? (y/n) ');
if (proceed.toLowerCase() !== 'y') {
  echo('Deployment cancelled');
  process.exit(0);
}

// Custom streams
import fs from 'fs';
const input = fs.createReadStream('./input.txt');
const response = await question('Question: ', { input });

Standard Input Processing

Read and process data from standard input streams.

/**
 * Read all data from standard input
 * @param stream - Input stream (default: process.stdin)
 * @returns Promise resolving to input content as string
 */
function stdin(stream?: Readable): Promise<string>;

Usage Examples:

import { stdin, echo, $ } from "zx";

// Read from standard input
echo('Please enter your data (Ctrl+D to finish):');
const input = await stdin();
echo`You entered: ${input}`;

// Process piped input
const data = await stdin();
const lines = data.trim().split('\n');
echo`Received ${lines.length} lines`;

// Use with custom stream
import fs from 'fs';
const fileStream = fs.createReadStream('./data.txt');
const fileContent = await stdin(fileStream);
echo`File content: ${fileContent}`;

// Combine with shell commands
const logData = await $`tail -f /var/log/system.log`.pipe(process.stdout);

Visual Feedback

Provide visual feedback during long-running operations.

/**
 * Show a spinner during async operation execution
 * @param title - Spinner text (optional)
 * @param callback - Async function to execute
 * @returns Promise resolving to callback result
 */
function spinner<T>(title: string, callback: () => T): Promise<T>;
function spinner<T>(callback: () => T): Promise<T>;

Usage Examples:

import { spinner, $, sleep } from "zx";

// Simple spinner
const result = await spinner('Deploying application...', async () => {
  await $`docker build -t myapp .`;
  await $`docker push myapp`;
  return 'success';
});

// Spinner without title
await spinner(async () => {
  await sleep('2s');
  await $`npm install`;
});

// Complex operations
const deployment = await spinner('Setting up infrastructure...', async () => {
  // Multiple operations
  await $`terraform init`;
  await $`terraform plan -out=plan.out`;
  await $`terraform apply plan.out`;
  
  return {
    status: 'deployed',
    timestamp: new Date().toISOString()
  };
});

echo`Deployment completed: ${deployment.status}`;

// Conditional spinner (disabled in CI)
const buildResult = await spinner('Building project...', async () => {
  return await $`npm run build`;
});
// Spinner automatically disabled if process.env.CI is set

Argument Processing

Parse and process command-line arguments.

/**
 * Parse command-line arguments with options
 * @param args - Arguments array (default: process.argv.slice(2))
 * @param opts - Parsing options
 * @param defs - Default values
 * @returns Parsed arguments object
 */
function parseArgv(
  args?: string[],
  opts?: ArgvOpts,
  defs?: Record<string, any>
): minimist.ParsedArgs;

/**
 * Update the global argv object with new arguments
 * @param args - New arguments to parse
 * @param opts - Parsing options
 */
function updateArgv(args?: string[], opts?: ArgvOpts): void;

/**
 * Global parsed arguments object
 */
declare const argv: minimist.ParsedArgs;

interface ArgvOpts extends minimist.Opts {
  /** Convert kebab-case to camelCase */
  camelCase?: boolean;
  /** Parse string 'true'/'false' to boolean */
  parseBoolean?: boolean;
}

Usage Examples:

import { parseArgv, argv, echo } from "zx";

// Using global argv
echo`Script called with:`, argv;
console.log('Environment:', argv.env || 'development');
console.log('Verbose mode:', argv.verbose || false);

// Custom argument parsing
const args = parseArgv(['--port', '3000', '--host', 'localhost', '--verbose'], {
  default: { port: 8080 },
  boolean: ['verbose'],
  string: ['host']
});
console.log(args); // { port: 3000, host: 'localhost', verbose: true, _: [] }

// With camelCase conversion
const camelArgs = parseArgv(['--api-key', 'secret', '--max-retries', '3'], {
  camelCase: true,
  parseBoolean: true
});
console.log(camelArgs.apiKey);     // 'secret'
console.log(camelArgs.maxRetries); // 3

// Update global argv
updateArgv(['--env', 'production'], { camelCase: true });
echo`Environment: ${argv.env}`;

// Default values
const config = parseArgv(process.argv.slice(2), {
  default: {
    port: 8080,
    host: '0.0.0.0',
    env: 'development'
  },
  boolean: ['verbose', 'debug'],
  string: ['host', 'env'],
  number: ['port']
});

Sleep and Timing

Asynchronous delays and timing utilities.

/**
 * Asynchronous sleep/delay
 * @param duration - Sleep duration (string like '2s', '500ms' or number in ms)
 * @returns Promise that resolves after the specified duration
 */
function sleep(duration: Duration): Promise<void>;

type Duration = string | number;

Usage Examples:

import { sleep, echo, $ } from "zx";

// String duration formats
await sleep('2s');      // 2 seconds
await sleep('500ms');   // 500 milliseconds
await sleep('1m');      // 1 minute
await sleep('1.5s');    // 1.5 seconds

// Numeric duration (milliseconds)
await sleep(1000);      // 1 second

// In scripts
echo('Starting deployment...');
await sleep('1s');

await $`docker build -t myapp .`;
echo('Build complete, waiting before push...');
await sleep('2s');

await $`docker push myapp`;
echo('Deployment complete!');

// Retry with delays
for (let i = 0; i < 3; i++) {
  try {
    await $`curl -f https://api.example.com/health`;
    echo('Service is healthy');
    break;
  } catch (error) {
    echo(`Health check failed (attempt ${i + 1}), retrying...`);
    await sleep('5s');
  }
}

Types

/**
 * Duration specification as string or milliseconds
 */
type Duration = string | number;

/**
 * Parsed command-line arguments
 */
interface ParsedArgs {
  /** Non-option arguments */
  _: string[];
  /** Named arguments */
  [key: string]: any;
}

/**
 * minimist parsing options extended with zx-specific options
 */
interface ArgvOpts extends minimist.Opts {
  /** Convert kebab-case to camelCase */
  camelCase?: boolean;
  /** Parse 'true'/'false' strings to booleans */
  parseBoolean?: boolean;
  /** Default values for arguments */
  default?: Record<string, any>;
  /** Keys that should be treated as booleans */
  boolean?: string | string[];
  /** Keys that should be treated as strings */
  string?: string | string[];
  /** Keys that should be treated as numbers */
  number?: string | string[];
  /** Argument aliases */
  alias?: Record<string, string | string[]>;
}

/**
 * Node.js readable stream interface
 */
interface Readable extends NodeJS.ReadableStream {
  setEncoding(encoding: BufferEncoding): this;
  [Symbol.asyncIterator](): AsyncIterableIterator<any>;
}

Install with Tessl CLI

npx tessl i tessl/npm-zx

docs

cli.md

file-system.md

index.md

network-operations.md

process-management.md

shell-execution.md

user-interaction.md

utilities.md

tile.json