citty

A zero-dependency, type-safe CLI framework for Node.js built on native util.parseArgs, with nested subcommands, lifecycle hooks, and a plugin system.

Framework
npm
v0.2.2
1,313stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
60/100Good
Development Activity60
Maintenance40
Community48
Maturity52
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
76/100Good
Architecture82
Code Quality88
Innovation78
Learning Curve55

citty is a lightweight framework for building command-line interfaces in Node.js. It wraps Node’s built-in util.parseArgs instead of shipping its own argument parser, keeping the package dependency-free while still supporting typed positional, string, boolean, and enum arguments with aliases, defaults, and negation (—no-flag). Commands are defined declaratively via defineCommand, and nested subcommands can be resolved eagerly or lazily (via dynamic import) so large multi-command CLIs only load the code path that’s actually invoked.

Beyond argument parsing, citty provides a small application lifecycle: each command supports setup and cleanup hooks that run around run(), auto-generated —help/—version handling, and a plugin system (defineCittyPlugin) that composes reusable setup/cleanup behavior across commands. It’s maintained by the UnJS team and used as the CLI foundation for projects like Nuxt and Nitro tooling.

What You Get

  • Declarative defineCommand API with full TypeScript inference for positional, string, boolean, and enum arguments
  • Nested subcommands with eager or lazy (dynamic import) resolution for large CLIs
  • Auto-generated —help and —version output, with usage text rendered directly from your argument definitions
  • setup/cleanup lifecycle hooks that run around every command, including on error
  • A composable plugin system (defineCittyPlugin) for sharing setup/cleanup behavior across commands
  • Case-agnostic argument access — kebab-case and camelCase flag spellings both resolve to the same value

Common Use Cases

  • Building a multi-command developer CLI (build/dev/lint-style subcommands) with per-command argument definitions
  • Wrapping an existing Node script with typed flags, positional arguments, and auto-generated —help output
  • Structuring a large CLI where each subcommand is code-split and lazily imported to keep startup fast
  • Adding cross-cutting setup/teardown logic (e.g. telemetry, config loading) via a shared citty plugin

Under The Hood

Architecture The library is organized into small single-responsibility modules — command.ts for command resolution/execution, args.ts plus _parser.ts for argument parsing as a thin wrapper over node:util’s parseArgs, usage.ts for help-text rendering, plugin.ts for the plugin system, main.ts for the runMain/createMain entrypoints, and types.ts for the full type-level modeling of ArgsDef-to-ParsedArgs inference. Command execution flows through runCommand, which resolves plugins and the command’s own setup hook, recurses through resolveSubCommand/_findSubCommand to walk nested subCommands (supporting lazy Resolvable values and meta.alias lookups), invokes run(), and always executes cleanup and plugin-cleanup hooks even when run() throws, aggregating multiple cleanup errors into a single Error with a cause array. It’s a clean layered design (parsing to command resolution to execution to usage rendering) with no circular dependencies, though runCommand and resolveSubCommand duplicate similar sub-command-walking logic that could drift if one is changed without the other.

Tech Stack The package has zero runtime dependencies — its only import is Node’s built-in util.parseArgs — and uses scule, a small casing-conversion utility, only for camelCase/kebabCase/snakeCase transforms of argument names. Type-checking runs through TypeScript’s native-preview compiler (tsgo) as a dedicated test:types script, the package is ESM-only with sideEffects: false and a single export pointing at a bundled dist build produced by obuild. Linting and formatting go through oxlint/oxfmt rather than ESLint/Prettier, tests run under Vitest with v8 coverage, and CI runs lint, build, and coverage on every push before uploading to Codecov.

Code Quality Test coverage is broad — dedicated test files exercise the parser, args resolution, main entrypoint, plugin system, and usage rendering separately, mocking process.exit and console methods to assert on —help/—version/error-handling paths with table-driven tests for flag variants. Error handling is explicit and typed via a dedicated CLIError class carrying an error code (unknown command, no command specified, conflicting default/run, argument errors) rather than throwing bare errors, and cleanup-hook failures are collected and re-thrown distinctly from run-time errors instead of being silently swallowed. TypeScript strict mode is enabled with additional safety flags like noUncheckedIndexedAccess, and CI enforces lint plus type-check plus coverage on every push.

API Design citty’s core idea is deferring nearly all argument-parsing behavior to Node’s built-in parseArgs and layering a typed, declarative command/subcommand model on top of it, rather than shipping a bespoke parser the way most CLI libraries in this space do — this keeps the package genuinely dependency-free while Node’s own parser handles low-level tokenization. The declarative defineCommand API, combined with argument types that are fully inferred end-to-end from the args definition, gives strong editor autocomplete with very little boilerplate — a minimal defineCommand with just a run function is already a valid, runnable CLI. Getting started requires almost no ceremony, though the type-level machinery behind that inference is dense enough that debugging a type error in a large argument definition is non-trivial.

Used by 6 apps in this directory

TypeScript
95%
Apache 2.0

Enclosed

Security

2,072

Send end-to-end encrypted notes and files where the server never sees your content — true zero-knowledge sharing.

View details
57
Repo Health
88
Technical
71
Dependency
Built with
TypeScript95%
Updated 4 days ago
TypeScript
90%
Apache 2.0

HyperFrames

AI Development · AI Design Tools

44,430

Turn plain HTML and CSS into deterministic, pixel-perfect MP4 videos — authored by humans or AI agents, rendered by headless Chrome and FFmpeg.

View details
82
Repo Health
83
Technical
65
Dependency
Built with
TypeScript90%
Updated today
TypeScript
97%
MIT

likec4

Developer Tools · Devops

5,634

Define your software architecture as code and get always up-to-date, interactive C4 diagrams generated automatically from a DSL.

View details
89
Repo Health
83
Technical
77
Dependency
Built with
TypeScript97%
Updated yesterday
TypeScript
99%
Apache 2.0

Mastra Code

AI Code Assistants

27,743

"A coding agent that never compacts" — a terminal-based AI coding agent built on the Mastra framework, with Observational Memory instead of context compaction, multi-model support, and OAuth login for Claude Max or ChatGPT Plus.

View details
88
Repo Health
73
Technical
65
Dependency
Built with
TypeScript99%
Updated today
TypeScript
91%
MIT

open-pencil

AI Design Tools · Design Tools

8,197

An open-source design editor that reads native Figma files, ships a built-in AI assistant with 100+ design tools, and offers real-time serverless collaboration — all without giving up your files.

View details
82
Repo Health
78
Technical
69
Dependency
Built with
TypeScript91%
Updated yesterday
TypeScript
94%
AGPL 3.0

Papra

Bookmarks Archiving

5,296

Self-hosted document archiving with email ingestion, OCR full-text search, and pluggable storage — store once, find anything.

View details
86
Repo Health
82
Technical
69
Dependency
Built with
TypeScript94%
Updated yesterday

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search