Language Page

kcli for JavaScript

This page is assembled from markdown in ktools-javascript. GitHub links point to the workspace repo and the relevant source files or directories.

5 markdown sections
QM-Code/ktools-javascript primary repo
HTML static output

Karma CLI Parsing SDK (JavaScript)

ktools-javascript/kcli/README.md

kcli is the JavaScript implementation of the ktools CLI parsing SDK. It is used by ktrace, and is designed around two common CLI shapes:

  • top-level options such as --verbose and --output
  • inline roots such as --trace-*, --config-*, and --build-*

The library gives you two explicit entrypoints:

  • parseOrExit(argc, argv) for normal executable startup
  • parseOrThrow(argc, argv) when the caller wants to intercept CliError

Documentation

Quick Start

"use strict";

const kcli = require("./src/kcli");

function handleVerbose(context) {
    void context;
}

function handleProfile(context, value) {
    void context;
    void value;
}

const parser = new kcli.Parser();
const build = new kcli.InlineParser("--build");

build.setHandler("-profile", handleProfile, "Set build profile.");

parser.addInlineParser(build);
parser.addAlias("-v", "--verbose");
parser.setHandler("--verbose", handleVerbose, "Enable verbose logging.");

parser.parseOrExit(process.argv.length, process.argv);

Behavior Highlights

  • The full command line is validated before any registered handler runs.
  • parseOrExit() preserves the caller's argv, reports invalid CLI input to stderr, and exits with code 2.
  • parseOrThrow() preserves the caller's argv and throws CliError.
  • Bare inline roots such as --build print inline help when no root value is provided.
  • setHandler(..., handler, ...) treats a two-argument handler as a required-value option.
  • setOptionalValueHandler(...) registers an optional-value option.
  • Required values may consume a first token that begins with -.
  • Literal -- is rejected as an unknown option; it is not treated as an option terminator.

For the full parsing rules, see docs/behavior.md.

Build SDK

kbuild --build-latest

SDK output:

  • build/latest/sdk/src/kcli
  • build/latest/sdk/share/kbuild-javascript-sdk.json

Build And Run Demos

# Builds the SDK plus demos listed in .kbuild.json build.defaults.demos.
kbuild --build-latest

# Explicit demo-only run (uses build.demos when no args are provided).
kbuild --build-demos

Demo directories:

  • Bootstrap load/use check: demo/bootstrap/
  • SDK demos: demo/sdk/
  • Executable demos: demo/exe/{core,omega}

Useful demo commands:

./demo/bootstrap/build/latest/bootstrap
./demo/exe/core/build/latest/test
./demo/exe/core/build/latest/test --alpha
./demo/exe/core/build/latest/test --alpha-message "hello"
./demo/exe/core/build/latest/test --output stdout
./demo/exe/omega/build/latest/test --beta-workers 8
./demo/exe/omega/build/latest/test --newgamma-tag "prod"
./demo/exe/omega/build/latest/test --build

Repository Layout

  • Public API: src/kcli/
  • Source-level behavior coverage: tests/test_kcli_js.js
  • Demo CLI coverage: demo/tests/
  • Integration demos: demo/

Build And Test

kbuild --build-latest
kbuild --build-demos

Direct source-level tests:

node --test tests/*.js demo/tests/*.js

kcli JavaScript Overview

ktools-javascript/kcli/docs/index.md

kcli is the JavaScript implementation of the ktools CLI parsing SDK.

It is designed around two common CLI shapes:

  • top-level options such as --verbose
  • inline roots such as --trace-*, --config-*, and --build-*

The implementation keeps the same core behavior model as the C++ repo while using JavaScript calling conventions.

Public Entry Points

  • parseOrExit(argc, argv) for normal executable startup
  • parseOrThrow(argc, argv) for callers that want to intercept CliError

Quick Start

"use strict";

const kcli = require("../src/kcli");

const parser = new kcli.Parser();
const build = new kcli.InlineParser("--build");

build.setHandler("-profile", (context, value) => {
    void context;
    console.log(`profile=${value}`);
}, "Set build profile.");

parser.addInlineParser(build);
parser.addAlias("-v", "--verbose");
parser.setHandler("--verbose", () => {
    console.log("verbose enabled");
}, "Enable verbose logging.");

parser.parseOrExit(process.argv.length, process.argv);

What To Read Next

Demo Programs

The demo wrappers built by kbuild are the quickest way to validate behavior:

kbuild --build-latest
./demo/exe/core/build/latest/test --alpha-message "hello"
./demo/exe/omega/build/latest/test --beta-workers 8

kcli JavaScript API

ktools-javascript/kcli/docs/api.md

kcli exposes four public exports:

  • Parser
  • InlineParser
  • HandlerContext
  • CliError

Import

From this repo:

const kcli = require("../src/kcli");

Parser

Top-level parser for ordinary CLI options and positional handling.

Constructor

const parser = new kcli.Parser();

Methods

addAlias(alias, target, presetTokens = [])

Registers a single-dash alias that rewrites to a double-dash target.

parser.addAlias("-v", "--verbose");
parser.addAlias("-p", "--profile", ["release"]);

setHandler(option, handler, description)

Registers a flag or required-value handler.

  • If handler accepts one argument, it is treated as a flag handler.
  • If handler accepts two or more arguments, it is treated as a required-value handler.
parser.setHandler("--verbose", (context) => {
    console.log(context.option);
}, "Enable verbose logging.");

parser.setHandler("--output", (context, value) => {
    console.log(value);
}, "Set output target.");

setOptionalValueHandler(option, handler, description)

Registers an option whose value may be omitted.

parser.setOptionalValueHandler("--color", (context, value) => {
    console.log(value || "auto");
}, "Enable color output.");

setPositionalHandler(handler)

Registers a handler that receives all unconsumed positional tokens after validation.

parser.setPositionalHandler((context) => {
    console.log(context.value_tokens);
});

addInlineParser(parser)

Adds an inline-root parser such as --trace-* or --build-*.

const build = new kcli.InlineParser("--build");
parser.addInlineParser(build);

parseOrThrow(argc, argv)

Validates the full command line and runs handlers. Throws CliError on CLI failures.

parseOrExit(argc, argv)

Same behavior as parseOrThrow, but reports the CLI error to stderr and exits with code 2.

InlineParser

Inline parser for a namespaced root such as --trace or --build.

Constructor

const trace = new kcli.InlineParser("--trace");

The constructor accepts either "trace" or "--trace".

Methods

setRoot(root)

Overrides the parser root after construction.

trace.setRoot("--newtrace");

setRootValueHandler(handler, valuePlaceholder?, description?)

Registers a handler for the bare root form, for example --trace '*.*'.

trace.setRootValueHandler((context, value) => {
    console.log(value);
}, "<selectors>", "Trace selected channels.");

If help metadata is provided, both valuePlaceholder and description are required.

setHandler(option, handler, description)

Registers an inline flag or required-value handler.

trace.setHandler("-namespaces", () => {
}, "Show initialized namespaces.");

trace.setHandler("-output", (context, value) => {
}, "Set trace output target.");

Options may use either the short inline form or the fully qualified form:

  • "-namespaces"
  • "--trace-namespaces"

setOptionalValueHandler(option, handler, description)

Registers an inline option whose value may be omitted.

trace.setOptionalValueHandler("-enable", (context, value) => {
}, "Enable tracing.");

HandlerContext

Context object passed to handlers.

Properties

  • root: inline root without the leading --, or "" for top-level options
  • option: the matched option token, such as "--verbose" or "--trace-files"
  • command: normalized command name without leading dashes
  • value_tokens: collected value tokens before joining

CliError

Error thrown for CLI validation or handler failures.

Methods

option()

Returns the option token that caused the failure when available.

try {
    parser.parseOrThrow(argv.length, argv);
} catch (error) {
    if (error instanceof kcli.CliError) {
        console.error(error.option(), error.message);
    }
}

Behavior Notes

  • The full CLI is validated before any registered handler runs.
  • Required values may consume a first token that begins with -.
  • Bare inline roots print inline help when no root value is provided.
  • Alias rewrites do not mutate the caller's argv.
  • Literal -- is rejected as an unknown option.

Parsing Behavior

ktools-javascript/kcli/docs/behavior.md

This page collects the parsing rules that matter in practice for the JavaScript implementation.

Parse Lifecycle

kcli processes the command line in three stages:

  1. Read the caller's argv into an internal token list.
  2. Validate and schedule handler invocations in a single pass.
  3. Execute scheduled handlers only after the full command line validates.

This means:

  • handlers do not run on partially-valid command lines
  • unknown options fail the parse before any handler side effects occur
  • the caller's argv is never rewritten or compacted

Option Naming Rules

Top-level handlers:

  • accepted forms: "name" or "--name"
  • effective option token at runtime: --name

Inline roots:

  • accepted forms: "build" or "--build"
  • effective bare root token at runtime: --build

Inline handlers:

  • accepted forms: "-flag" or "--build-flag"
  • effective option token at runtime: --build-flag

Aliases:

  • alias form must be single-dash, such as -v
  • target form must be double-dash, such as --verbose

Inline Root Behavior

Bare inline roots behave specially.

--build

  • prints a help listing for the --build-* handlers

--build release

  • invokes the root value handler if one is registered
  • fails if no root value handler is registered

If a root value handler is registered with a placeholder and description, the bare-root help view includes a row such as:

--build <selector>  Select build targets.

Value Consumption Rules

kcli supports three public registration styles:

  • flag handlers consume no trailing value tokens
  • required-value handlers consume at least one value token
  • optional-value handlers consume values only when the next token looks like a value

Additional details:

  • once value collection starts, kcli keeps consuming subsequent non-option tokens for that handler
  • explicit empty tokens are preserved
  • joined handler values are produced by joining value_tokens with spaces

Examples:

--name "Joe"            -> value_tokens = ["Joe"]
--name "Joe" "Smith"    -> value_tokens = ["Joe", "Smith"]
--name ""               -> value_tokens = [""]
--profile -debug        -> value_tokens = ["-debug"]

Alias Behavior

Aliases are only expanded when a token is parsed as an option.

Examples:

parser.addAlias("-v", "--verbose");
parser.addAlias("-c", "--config-load", ["user-file"]);

Rules:

  • consumed value tokens are not alias-expanded
  • preset tokens are prepended to effective value_tokens
  • preset tokens can satisfy required-value handlers
  • aliases with preset tokens cannot target flag handlers

Positionals

The positional handler receives all remaining non-option tokens in a single invocation.

Important details:

  • explicit empty positional tokens are preserved
  • positionals are dispatched only after option parsing succeeds

Failure Behavior

Unknown option-like tokens fail the parse.

Notable cases:

  • unknown top-level option: --bogus
  • unknown inline option: --build-unknown
  • literal --

kcli does not treat -- as an option terminator. It is reported as an unknown option.

Error Surface

parseOrExit()

  • prints [error] [cli] ... to stderr
  • colors error red and cli blue on terminals
  • exits with code 2

parseOrThrow()

  • throws CliError
  • preserves the human-facing error message
  • surfaces handler exceptions as CliError

Behavior Coverage

The JavaScript behavior is covered by:

  • tests/test_kcli_js.js
  • demo/tests/test_core_cli.js
  • demo/tests/test_omega_cli.js

Examples

ktools-javascript/kcli/docs/examples.md

This page shows a few common kcli patterns in JavaScript.

For complete runnable examples, also see:

  • demo/sdk/alpha.js
  • demo/sdk/beta.js
  • demo/sdk/gamma.js
  • demo/exe/core/main.js
  • demo/exe/omega/main.js

Minimal Executable

const kcli = require("../src/kcli");

const parser = new kcli.Parser();

parser.addAlias("-v", "--verbose");
parser.setHandler("--verbose", () => {
    console.log("verbose enabled");
}, "Enable verbose logging.");

parser.parseOrExit(process.argv.length, process.argv);

Inline Root With Subcommands-Like Options

const parser = new kcli.Parser();
const build = new kcli.InlineParser("--build");

build.setHandler("-profile", (context, value) => {
    void context;
    console.log(value);
}, "Set build profile.");

build.setHandler("-clean", (context) => {
    void context;
}, "Enable clean build.");

parser.addInlineParser(build);
parser.parseOrExit(process.argv.length, process.argv);

This enables:

--build
--build-profile release
--build-clean

Bare Root Value Handler

const config = new kcli.InlineParser("--config");

config.setRootValueHandler((context, value) => {
    void context;
    console.log(value);
}, "<assignment>", "Store a config assignment.");

This enables:

--config
--config user=alice

Behavior:

  • --config prints inline help
  • --config user=alice invokes the root value handler

Alias Preset Tokens

const parser = new kcli.Parser();

parser.addAlias("-c", "--config-load", ["user-file"]);
parser.setHandler("--config-load", (context, value) => {
    console.log(context.option, value, context.value_tokens);
}, "Load config.");

This makes:

-c settings.json

behave like:

--config-load user-file settings.json

Optional Values

parser.setOptionalValueHandler("--color", (context, value) => {
    void context;
    console.log(value || "auto");
}, "Set or auto-detect color output.");

This enables both:

--color
--color always

Positionals

parser.setPositionalHandler((context) => {
    for (const token of context.value_tokens) {
        console.log(token);
    }
});

Custom Error Handling

If you want your own formatting or exit policy, use parseOrThrow():

try {
    parser.parseOrThrow(process.argv.length, process.argv);
} catch (error) {
    if (error instanceof kcli.CliError) {
        console.error(`custom cli error: ${error.message}`);
        process.exitCode = 2;
    } else {
        throw error;
    }
}