CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-stricli--core

Build complex CLIs with type safety and no dependencies

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

@stricli/core

@stricli/core is a comprehensive TypeScript library for building type-safe, complex command-line interface (CLI) applications with zero runtime dependencies. It provides a complete framework including command builders, parameter parsing, routing, automatic help text generation, shell completion support, and extensive configuration options. The library leverages TypeScript's type system to provide full compile-time type safety for CLI parameters and commands.

Package: @stricli/core | Type: npm | Language: TypeScript

Installation

npm install @stricli/core

Core Imports

import {
  buildApplication,
  buildCommand,
  buildRouteMap,
  run,
  type CommandContext,
  type Application
} from "@stricli/core";

For CommonJS:

const {
  buildApplication,
  buildCommand,
  buildRouteMap,
  run
} = require("@stricli/core");

Quick Start

import { buildApplication, buildCommand, run } from "@stricli/core";

const app = buildApplication(
  buildCommand({
    func: async function(flags, name) {
      this.process.stdout.write(`Hello, ${name || flags.default}!\n`);
    },
    parameters: {
      flags: {
        default: { kind: "parsed", parse: String, brief: "Default name", default: "World" }
      },
      positional: {
        kind: "tuple",
        parameters: [{ brief: "Name", parse: String, optional: true }]
      }
    },
    docs: { brief: "Greet someone" }
  }),
  { name: "greet" }
);

await run(app, process.argv.slice(2), { process });

Common Patterns Cheat Sheet

Basic Command

buildCommand({
  func: async function(flags) { /* ... */ },
  parameters: { flags: { /* ... */ } },
  docs: { brief: "Description" }
})

Route Map (Multiple Commands)

buildApplication(
  buildRouteMap({
    routes: { cmd1: command1, cmd2: command2 },
    docs: { brief: "CLI description" }
  }),
  { name: "myapp" }
)

Flag Types

// Boolean
verbose: { kind: "boolean", brief: "Enable verbose", default: false }

// Counter (repeatable -vvv)
verbosity: { kind: "counter", brief: "Verbosity level" }

// Enum (predefined values)
env: { kind: "enum", values: ["dev", "prod"], brief: "Environment", default: "dev" }

// Parsed (custom type)
port: { kind: "parsed", parse: numberParser, brief: "Port", default: "3000" }

// Variadic (multiple values)
files: { kind: "parsed", parse: String, variadic: true, brief: "Files" }

// Optional
timeout: { kind: "parsed", parse: numberParser, brief: "Timeout", optional: true }

Positional Arguments

// Tuple (fixed args)
positional: {
  kind: "tuple",
  parameters: [
    { brief: "Source", parse: String },
    { brief: "Dest", parse: String, optional: true }
  ]
}

// Array (variadic args)
positional: {
  kind: "array",
  parameter: { brief: "Files", parse: String },
  minimum: 1
}

Aliases

parameters: {
  flags: { verbose: { /* ... */ }, output: { /* ... */ } },
  aliases: { v: "verbose", o: "output" }  // -v, -o
}

Decision Guide

When to Use Each Flag Type

  • boolean: On/off toggle (--verbose, --force)
  • counter: Repeated flag for levels (-v, -vv, -vvv)
  • enum: Fixed set of string values (--env dev|staging|prod)
  • parsed: Any type needing transformation (numbers, URLs, dates, custom objects)

When to Use enum vs parsed with buildChoiceParser

  • Use enum: String literal unions, simple validation
  • Use parsed: Need custom error messages, complex validation, or non-string types

When to Use tuple vs array for Positional Args

  • tuple: Fixed number of named arguments (copy source dest)
  • array: Variable number of similar arguments (process file1 file2 file3...)

Architecture

@stricli/core is built around several key components:

  • Application Layer: buildApplication and run functions for creating and executing CLI applications
  • Routing System: buildCommand and buildRouteMap for defining commands and nested command structures
  • Parameter System: Type-safe flag and positional parameter definitions with automatic parsing and validation
  • Documentation Engine: Automatic help text generation with customizable formatting and localization
  • Context System: Generic context threading for passing custom data through command execution
  • Error Handling: Comprehensive error classes with detailed validation and "did you mean?" suggestions
  • Configuration: Extensive configuration for scanner behavior, documentation style, and completion proposals

Core API Reference

Application & Execution

Core functionality for creating and running CLI applications. Supports both single-command and multi-command (route map) applications with full configuration.

/**
 * Builds a CLI application from a command or route map with configuration
 */
function buildApplication<CONTEXT extends CommandContext>(
  root: Command<CONTEXT>,
  appConfig: PartialApplicationConfiguration
): Application<CONTEXT>;
function buildApplication<CONTEXT extends CommandContext>(
  root: RouteMap<CONTEXT>,
  config: PartialApplicationConfiguration
): Application<CONTEXT>;

/**
 * Runs a CLI application with given inputs and context
 */
async function run<CONTEXT extends CommandContext>(
  app: Application<CONTEXT>,
  inputs: readonly string[],
  context: StricliDynamicCommandContext<CONTEXT>
): Promise<void>;

interface Application<CONTEXT extends CommandContext> {
  root: Command<CONTEXT> | RouteMap<CONTEXT>;
  config: ApplicationConfiguration;
  defaultText: ApplicationText;
}

Full Documentation

Commands & Routing

System for defining individual commands and organizing them into nested route maps. Supports both immediate and lazy-loaded command functions with full type inference.

/**
 * Builds a command with parameters, action, and documentation
 */
function buildCommand<
  const FLAGS extends Readonly<Partial<Record<keyof FLAGS, unknown>>> = NonNullable<unknown>,
  const ARGS extends BaseArgs = [],
  const CONTEXT extends CommandContext = CommandContext
>(builderArgs: CommandBuilderArguments<FLAGS, ARGS, CONTEXT>): Command<CONTEXT>;

/**
 * Builds a route map for organizing multiple commands
 */
function buildRouteMap<
  R extends string,
  CONTEXT extends CommandContext = CommandContext
>({
  routes,
  defaultCommand,
  docs,
  aliases
}: RouteMapBuilderArguments<R, CONTEXT>): RouteMap<CONTEXT>;

interface Command<CONTEXT extends CommandContext> {
  kind: typeof CommandSymbol;
  loader: CommandFunctionLoader<BaseFlags, BaseArgs, CONTEXT>;
  parameters: CommandParameters;
  usesFlag: (flagName: string) => boolean;
  brief: string;
  formatUsageLine: (args: UsageFormattingArguments) => string;
  formatHelp: (args: HelpFormattingArguments) => string;
}

interface RouteMap<CONTEXT extends CommandContext> {
  kind: typeof RouteMapSymbol;
  getRoutingTargetForInput: (input: string) => Command<CONTEXT> | RouteMap<CONTEXT> | undefined;
  getDefaultCommand: () => Command<CONTEXT> | undefined;
  getOtherAliasesForInput: (input: string, caseStyle: ScannerCaseStyle) => Readonly<Record<DisplayCaseStyle, readonly string[]>>;
  getAllEntries: () => readonly RouteMapEntry<CONTEXT>[];
  brief: string;
  formatUsageLine: (args: UsageFormattingArguments) => string;
  formatHelp: (args: HelpFormattingArguments) => string;
}

type CommandFunction<FLAGS extends BaseFlags, ARGS extends BaseArgs, CONTEXT extends CommandContext> =
  (this: CONTEXT, flags: FLAGS, ...args: ARGS) => void | Error | Promise<void | Error>;

Full Documentation

Parameter Parsers

Built-in parsers for common types and utilities for building custom parsers. Supports synchronous and asynchronous parsing.

/**
 * Generic input parser type
 */
type InputParser<T, CONTEXT extends CommandContext = CommandContext> =
  (this: CONTEXT, input: string) => T | Promise<T>;

/**
 * Parse boolean values (strict: "true" or "false")
 */
const booleanParser: (input: string) => boolean;

/**
 * Parse boolean values (loose: "yes", "no", "on", "off", "1", "0", etc.)
 */
const looseBooleanParser: (input: string) => boolean;

/**
 * Parse numeric values
 */
const numberParser: (input: string) => number;

/**
 * Build a parser that validates against a set of choices
 */
function buildChoiceParser<T extends string>(choices: readonly T[]): InputParser<T>;

Full Documentation

Configuration

interface PartialApplicationConfiguration {
  name: string
  versionInfo?: VersionInfo
  scanner?: Partial<ScannerConfiguration>
  documentation?: Partial<DocumentationConfiguration>
  completion?: Partial<CompletionConfiguration>
  localization?: Partial<LocalizationConfiguration>
  determineExitCode?: (exc: unknown) => number
}

Scanner Options:

  • caseStyle: "original" | "allow-kebab-for-camel" (default: "original")
  • allowArgumentEscapeSequence: boolean (default: false) - Enable -- separator

Documentation Options:

  • caseStyle: "original" | "convert-camel-to-kebab"
  • useAliasInUsageLine: boolean (default: false)
  • disableAnsiColor: boolean (default: false)

Full Documentation

Custom Context

interface CommandContext {
  process: { stdout: Writable; stderr: Writable }
}

// Extend for custom context
interface MyContext extends CommandContext {
  database: Database
  logger: Logger
}

await run(app, inputs, {
  process,
  async forCommand(info) {
    return { process, database: await connectDB(), logger: createLogger() }
  }
})

Full Documentation

Exit Codes

const ExitCode = {
  Success: 0,
  CommandRunError: 1,
  InternalError: -1,
  CommandLoadError: -2,
  ContextLoadError: -3,
  InvalidArgument: -4,
  UnknownCommand: -5
}

Full Documentation

Error Handling

Comprehensive error classes for parameter parsing and validation with detailed error messages and "did you mean?" suggestions.

/**
 * Base class for all argument scanner errors
 */
abstract class ArgumentScannerError extends Error {}

class FlagNotFoundError extends ArgumentScannerError {
  readonly input: string;
  readonly corrections: readonly string[];
  readonly aliasName?: string;
}

class AliasNotFoundError extends ArgumentScannerError {
  readonly input: string;
}

class ArgumentParseError extends ArgumentScannerError {
  readonly externalFlagNameOrPlaceholder: string;
  readonly input: string;
  readonly exception: unknown;
}

class EnumValidationError extends ArgumentScannerError {
  readonly externalFlagName: string;
  readonly input: string;
  readonly values: readonly string[];
}

class UnsatisfiedFlagError extends ArgumentScannerError {
  readonly externalFlagName: string;
  readonly nextFlagName?: string;
}

class UnsatisfiedPositionalError extends ArgumentScannerError {
  readonly placeholder: string;
  readonly limit?: [minimum: number, count: number];
}

class UnexpectedPositionalError extends ArgumentScannerError {
  readonly expectedCount: number;
  readonly input: string;
}

class UnexpectedFlagError extends ArgumentScannerError {
  readonly externalFlagName: string;
  readonly previousInput: string;
  readonly input: string;
}

class InvalidNegatedFlagSyntaxError extends ArgumentScannerError {
  readonly externalFlagName: string;
  readonly valueText: string;
}

/**
 * Utility for formatting scanner error messages
 */
function formatMessageForArgumentScannerError(
  error: ArgumentScannerError,
  formatter: Partial<ArgumentScannerErrorFormatter>
): string;

Full Documentation

Documentation & Completions

Automatic help text generation for all commands with customizable formatting and localization support. Includes shell completion proposals.

/**
 * Generates help text for all commands in an application
 */
function generateHelpTextForAllCommands(
  app: Application<CommandContext>,
  locale?: string
): readonly DocumentedCommand[];

type DocumentedCommand = readonly [route: string, helpText: string];

/**
 * Proposes completions for partial input (aliased as proposeCompletions)
 */
async function proposeCompletionsForApplication<CONTEXT extends CommandContext>(
  app: Application<CONTEXT>,
  rawInputs: readonly string[],
  context: StricliDynamicCommandContext<CONTEXT>
): Promise<readonly InputCompletion[]>;

type InputCompletion = ArgumentCompletion | RoutingTargetCompletion;

interface RoutingTargetCompletion {
  kind: "routing-target:command" | "routing-target:route-map";
  completion: string;
  brief: string;
}

Full Documentation

Complete Example

import {
  buildApplication,
  buildRouteMap,
  buildCommand,
  run,
  numberParser,
  type CommandContext
} from "@stricli/core";

// Custom context
interface AppContext extends CommandContext {
  config: { apiUrl: string };
}

// Commands
const deployCmd = buildCommand({
  func: async function(this: AppContext, flags, service) {
    this.process.stdout.write(
      `Deploying ${service} to ${flags.env} (${this.config.apiUrl})\n`
    );
    if (flags.force) this.process.stdout.write("Force mode enabled\n");
  },
  parameters: {
    flags: {
      env: { kind: "enum", values: ["dev", "staging", "prod"], brief: "Environment", default: "dev" },
      force: { kind: "boolean", brief: "Force deployment", default: false },
      timeout: { kind: "parsed", parse: numberParser, brief: "Timeout (seconds)", optional: true }
    },
    positional: {
      kind: "tuple",
      parameters: [{ brief: "Service name", parse: String }]
    },
    aliases: { e: "env", f: "force", t: "timeout" }
  },
  docs: { brief: "Deploy a service" }
});

const statusCmd = buildCommand({
  func: async function() {
    this.process.stdout.write("All systems operational\n");
  },
  parameters: {},
  docs: { brief: "Check status" }
});

// Application
const app = buildApplication(
  buildRouteMap({
    routes: { deploy: deployCmd, status: statusCmd },
    docs: { brief: "Deployment CLI" }
  }),
  {
    name: "myapp",
    versionInfo: { currentVersion: "1.0.0" },
    scanner: { caseStyle: "allow-kebab-for-camel", allowArgumentEscapeSequence: true }
  }
);

// Run with custom context
await run(app, process.argv.slice(2), {
  process,
  locale: "en",
  async forCommand() {
    return { process, config: { apiUrl: "https://api.example.com" } };
  }
});

Detailed Documentation

Text and Localization

Comprehensive localization system with customizable text for all user-facing strings including help text, error messages, and documentation.

interface ApplicationText extends ApplicationErrorFormatting {
  keywords: DocumentationKeywords;
  headers: DocumentationHeaders;
  briefs: DocumentationBriefs;
  currentVersionIsNotLatest: (args: {
    currentVersion: string;
    latestVersion: string;
    upgradeCommand?: string;
    ansiColor: boolean;
  }) => string;
}

/**
 * Default English text implementation
 */
const text_en: ApplicationText;

interface DocumentationKeywords {
  default: string;
  separator: string;
}

interface DocumentationHeaders {
  usage: string;
  aliases: string;
  commands: string;
  flags: string;
  arguments: string;
}

interface DocumentationBriefs {
  help: string;
  helpAll: string;
  version: string;
  argumentEscapeSequence: string;
}

Full Documentation

Exit Codes

Standard exit codes returned by Stricli applications for different execution outcomes.

/**
 * Enumeration of all possible exit codes
 */
const ExitCode: {
  readonly UnknownCommand: -5;
  readonly InvalidArgument: -4;
  readonly ContextLoadError: -3;
  readonly CommandLoadError: -2;
  readonly InternalError: -1;
  readonly Success: 0;
  readonly CommandRunError: 1;
};

/**
 * Environment variable names used by Stricli
 */
type EnvironmentVariableName =
  | "STRICLI_SKIP_VERSION_CHECK"
  | "STRICLI_NO_COLOR";

Full Documentation

Types

Core Type Definitions

interface StricliProcess {
  stdout: Writable;
  stderr: Writable;
  env?: Readonly<Partial<Record<string, string>>>;
  exitCode?: number | string | null;
}

interface Writable {
  write: (str: string) => void;
  getColorDepth?: (env?: Readonly<Partial<Record<string, string>>>) => number;
}

interface CommandInfo {
  prefix: readonly string[];
}

type StricliCommandContextBuilder<CONTEXT extends CommandContext> =
  (info: CommandInfo) => CONTEXT | Promise<CONTEXT>;

interface CommandModule<
  FLAGS extends BaseFlags,
  ARGS extends BaseArgs,
  CONTEXT extends CommandContext
> {
  default: CommandFunction<FLAGS, ARGS, CONTEXT>;
}

type CommandFunctionLoader<
  FLAGS extends BaseFlags,
  ARGS extends BaseArgs,
  CONTEXT extends CommandContext
> = () => Promise<CommandModule<FLAGS, ARGS, CONTEXT> | CommandFunction<FLAGS, ARGS, CONTEXT>>;

Environment Variables

  • STRICLI_SKIP_VERSION_CHECK=1 - Skip automatic version checking
  • STRICLI_NO_COLOR=1 - Disable ANSI color output
Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/@stricli/core@1.2.x
Publish Source
CLI
Badge
tessl/npm-stricli--core badge