icore
v2.0.0
Published
Declarative command line interface and terminal presentation mechanics for Node.js
Maintainers
Readme
icore
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 icoreImport 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/stderrExact 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
usageordefinitioncategory; - 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:
- Terminal App — regular application composition, format resolution, and reusable error reporting;
- Option Schemas — strings, booleans, numbers, choices, defaults, aliases, and inferred types;
- Practical CLI Patterns — help/version shortcuts, shared options, and compatibility aliases;
- Command Resolution — registries, explicit resolution, and command-name guards;
- Two-Phase Primitives — preparation, payloads, execution, and provided-option metadata;
- Presentation Primitives — text, record, table, CSV, JSON, and direct renderers.
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.
