Clipanion
A type-safe, dependency-free CLI framework for building nested, typed command-line applications in TypeScript.
Repository Health
Technical Analysis
Clipanion is a TypeScript CLI framework for declaring commands as classes, wiring them into nested paths, and validating their options with strong static and runtime typing — all with zero runtime dependencies. Commands extend a base Command class, expose one or more paths under which they’re reachable, and declare their options as typed class properties (Option.String(), Option.Boolean(), Option.Rest(), etc.) that Clipanion parses, coerces, and validates before execute() ever runs.
Under the hood, command definitions are compiled into a state machine (rather than parsed imperatively token by token), which is what lets Clipanion support advanced parsing behavior — option batching, negation, transparent argument proxying, and automatic detection of the closest matching command for helpful error messages — while still generating polished usage and help pages out of the box. It optionally integrates with Typanion for cascading schema validation, and it’s the CLI engine behind Yarn Berry, so its parsing model is exercised at scale in production.
What You Get
- A
Commandbase class with typedOption.String()/Boolean()/Array()/Counter()/Rest()/Proxy()helpers for declaring CLI arguments as class fields - Nested command routing via static
pathsarrays, so a single binary can expose many subcommands (e.g.yarn workspaces list) cleanly - A compiled state-machine parser (not a naive token walker) supporting negated flags, batched short options, and transparent argument proxying to subprocesses
- Auto-generated, good-looking help pages and a built-in help/version command with no extra setup
- Optional Typanion integration for cascading schema validation on top of parsed options
Common Use Cases
- Building a multi-command developer CLI (package manager, build tool, scaffolding tool) with strongly-typed subcommands
- Adding a typed command layer to an existing Node.js tool without pulling in a heavy dependency tree
- Replacing an ad-hoc
process.argvparser with validated, self-documenting commands that generate their own help text - Proxying unrecognized arguments through to an underlying process (e.g. wrapping another CLI) without a
--separator
Under The Hood
Architecture
Clipanion compiles Command class definitions into a state machine (sources/core.ts, ~1,100 lines) via CliBuilder, rather than parsing arguments imperatively token by token. The advanced-facing Cli class (sources/advanced/Cli.ts) walks tokens through that compiled machine to select the matching command, resolves its typed options through the CommandOption abstraction (sources/advanced/options/{String,Boolean,Array,Counter,Rest,Proxy}.ts), instantiates the Command subclass, runs any Typanion schema validation, and calls execute(). Built-in commands (help, version) register through the same pipeline as user commands (sources/advanced/builtins/). The separation is clean — core.ts is purely the token/state-machine engine, advanced/ layers the user-facing class-based API on top — though Cli.ts itself is a large (~800-line) file handling help formatting, error recovery, and stream capture together, which is the main spot that would ripple if the core state-machine format changed.
Tech Stack
Written almost entirely in TypeScript (98.5% of the codebase) with zero runtime dependencies of its own — the only dependency is typanion, a peer/sibling project by the same author, dynamically imported only when a command declares a validation schema. Built with Rollup (rollup.config.js, @rollup/plugin-typescript, @rollup/plugin-node-resolve) into dual CJS/ESM bundles, with separate Node and browser platform entry points swapped via package.json’s browser field. The repo uses Yarn Berry workspaces/PnP and ships its own docs site as a workspace package.
Code Quality
Tests live under tests/specs/ and are organized by abstraction level — core.test.ts exercises the state machine directly, advanced.test.ts covers the Command/Option developer-facing API, bundling.test.ts checks the packaged build output, and e2e.test.ts runs full CLI processes end to end — giving broad coverage across layers rather than only unit-level checks. TypeScript’s type system is central to the library’s value proposition, with extensive conditional and mapped types used to infer option shapes from class declarations. ESLint is configured with --max-warnings 0 enforced in the lint script. Errors are explicit and typed via a dedicated errors.ts module rather than swallowed.
What Makes It Unique
The compiled state-machine dispatch (build once from command definitions, then walk tokens through it) is a comparatively unusual design next to typical imperative arg-parsers, and it’s what enables features like negatable/batchable short options and transparent argument proxying to a subprocess without a -- separator. Options are declared as typed class-field initializers (Option.String(), Option.Boolean()), giving a fully-typed, low-boilerplate developer experience for defining a command’s inputs, and the library generates polished help pages automatically from the same definitions used for parsing.
Used by 2 apps in this directory
AFFiNE
Productivity · Project Management · Note Taking
Write, draw, and plan in one infinite canvas — the open-source alternative to Notion and Miro that keeps your data yours.
TinaCMS
CMS
An open-source, Git-backed headless CMS that gives editors a live visual editing UI over Markdown, MDX, JSON, and YAML content while developers keep everything in version control.