npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

icore

v2.0.0

Published

Declarative command line interface and terminal presentation mechanics for Node.js

Readme

icore

npm version node types license

Small dependency-free command line interface and terminal presentation mechanics for Node.js® applications.

icore owns the path from process.argv to terminal output:

  • declarative, type-inferred option schemas;
  • multi-segment command resolution;
  • separate prepare and execute phases;
  • JSON, CSV, and text-table presentation;
  • stdout/stderr writers and reusable terminal error policy.

Application-specific API calls, configuration, domain behavior, and help text remain in the consuming application.

Requirements And Installation

icore requires Node.js >=16.9.0 and includes TypeScript declarations.

npm install icore

Import public APIs from icore. Deep imports into dist or src are not part of the package contract.

Contents

Quick Start

Create commands, put them in a registry, and give that registry to createTerminalApp():

import {
  createCommand,
  createTerminalApp
} from 'icore';

const command = createCommand();

const commands = command.registry([
  command.define({
    path: ['hello'],
    options: {
      name: {
        type: 'string',
        default: 'world'
      },
      uppercase: {
        type: 'boolean'
      }
    },
    handle({ options }) {
      const greeting = `Hello, ${options.name}!`;

      return `${options.uppercase ? greeting.toUpperCase() : greeting}\n`;
    }
  })
] as const);

const app = createTerminalApp({ commands });

async function main(args: readonly string[]): Promise<void> {
  process.exitCode = await app.run(args, undefined, {
    strict: true
  });
}

void main(process.argv.slice(2));

After compiling the application:

$ node dist/cli.js hello
Hello, world!

$ node dist/cli.js hello --name Alice --uppercase
HELLO, ALICE!

The schema determines the handler's option types. Required options and options with defaults are always present; optional options are returned as T | undefined.

strict: true rejects options placed before the command path. String output is written exactly as returned, so include \n when line output is intended.

Choose An API Level

Start with the highest-level API that fits the application:

| Need | Start with | Detailed guide | | --- | --- | --- | | Complete terminal application | createTerminalApp() | Terminal App | | Commands and option schemas | createCommand() or createCommands() | Option Schemas | | Custom prepare/execute lifecycle | commands.prepare() and commands.run() | Custom Command Flow | | Presentation without command execution | createPresentation() | Presentation And Output | | Explicit stdout/stderr writing | createOutput() | Output Writers | | Parser and resolver primitives | parseArgv(), resolveCommand(), and related exports | Primitive Mechanics |

The terminal application exposes the main lifecycle operations:

  • app.run(args, context, options?) prepares, executes, renders, and writes;
  • app.prepare(args, options?) validates input without runtime context;
  • app.runPrepared(prepared, context) executes an already prepared command;
  • app.writePreparedOutput(prepared, output) writes caller-obtained output;
  • app.reportError(error, context?) applies the configured terminal error policy and returns a process-style exit code.

Command handlers receive parsed options, option-presence metadata in provided, remaining positionals, caller-owned context, and an optional prepared payload.

Supported terminal results are strings, async string streams, presentation results, and undefined:

argv → resolve → validate/prepare → execute → render → stdout/stderr

Exact public exports live in src/index.ts and in the bundled TypeScript declarations.

Supported Argument Syntax

The supported syntax is intentionally small and predictable:

| Form | Example | Notes | | --- | --- | --- | | Long option with separate value | --name Alice | String and number options consume a value | | Long option with attached value | --name=Alice | Equivalent to the separate form | | Boolean flag | --verbose | Produces true | | Boolean negation | --no-cache | Supported for known boolean options unless syntax: 'flag' | | Short alias | -v, -n Alice | Must be declared in the schema as one ASCII letter | | Option terminator | -- | Every following token becomes positional |

Option names are exact: icore does not convert camelCase to kebab-case. Explicit boolean values such as --verbose=true, attached short values such as -nAlice, and grouped aliases such as -abc are not supported.

See CLI Argument Syntax for parsing examples, edge cases, duplicate handling, and the option terminator contract.

Error Handling

CLI parsing, validation, resolution, and definition failures are reported as IcoreError. It extends Error with:

  • a stable machine-readable code;
  • a usage or definition category;
  • required, code-specific details.

Use isIcoreError(...) instead of manually casting details. Passing a code to the guard narrows the corresponding details shape:

import {
  createTerminalApp,
  isIcoreError
} from 'icore';

const help = 'Usage: cli hello [--name value]';

const app = createTerminalApp({
  commands,
  errorPolicy: {
    renderError(error) {
      const message = error instanceof Error
        ? error.message
        : String(error);

      if (isIcoreError(error, 'UNKNOWN_COMMAND')) {
        return `${message}\n\n${help}\n`;
      }

      return `${message}\n`;
    },
    resolveExitCode(error) {
      return isIcoreError(error) && error.category === 'usage'
        ? 2
        : 1;
    }
  }
});

Inside the UNKNOWN_COMMAND branch, fields such as error.details.command and error.details.positionals are strongly typed. IcoreErrorDetailsMap is the public source of truth for every code. Direct new IcoreError(...) calls must provide a third argument matching the selected code.

Without a custom policy, terminal apps write Error.message + "\n" (or String(error) + "\n" for other thrown values) and return exit code 1. Application-specific help remains application policy.

Custom lifecycles can call app.reportError(...) to reuse the same rendering and exit-code policy. The complete prepare, execute, write, and external phase flow is shown in the Terminal App guide.

Guides

The examples index routes from regular terminal apps to lower-level mechanics. Useful starting points:

Release history is recorded in CHANGELOG.md. Directional decisions live in docs/roadmap.md.

Project Boundary

icore is a small terminal mechanics module. It owns generic behavior such as option validation, command resolution, typed handler input, presentation rendering, error contracts, and stdout/stderr delivery.

It does not own application DTO mapping, API calls, configuration loading, domain-specific validation, lifecycle resources, or help content. Keep those responsibilities in the consuming application.

Licensed under the MIT License.