New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details
Socket
Book a DemoSign in
Socket

citty

Package Overview
Dependencies
Maintainers
1
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

citty

Elegant CLI Builder

latest
Source
npmnpm
Version
0.2.2
Version published
Weekly downloads
21M
5.2%
Maintainers
1
Weekly downloads
 
Created
Source

🌆 citty

npm version npm downloads bundle size

Elegant CLI Builder

Usage

npx nypm add -D citty
import { defineCommand, runMain } from "citty";

const main = defineCommand({
  meta: {
    name: "hello",
    version: "1.0.0",
    description: "My Awesome CLI App",
  },
  args: {
    name: {
      type: "positional",
      description: "Your name",
      required: true,
    },
    friendly: {
      type: "boolean",
      description: "Use friendly greeting",
    },
  },
  setup({ args }) {
    console.log(`now setup ${args.command}`);
  },
  cleanup({ args }) {
    console.log(`now cleanup ${args.command}`);
  },
  run({ args }) {
    console.log(`${args.friendly ? "Hi" : "Greetings"} ${args.name}!`);
  },
});

runMain(main);
node index.mjs john
# Greetings john!

Sub Commands

Sub commands can be nested recursively. Use lazy imports for large CLIs to avoid loading all commands at once.

import { defineCommand, runMain } from "citty";

const sub = defineCommand({
  meta: { name: "sub", description: "Sub command" },
  args: {
    name: { type: "positional", description: "Your name", required: true },
  },
  run({ args }) {
    console.log(`Hello ${args.name}!`);
  },
});

const main = defineCommand({
  meta: { name: "hello", version: "1.0.0", description: "My Awesome CLI App" },
  subCommands: { sub },
});

runMain(main);

Subcommands support meta.alias (e.g., ["i", "add"]) and meta.hidden: true to hide from help output.

Lazy Commands

For large CLIs, lazy load sub commands so only the executed command is imported:

const main = defineCommand({
  meta: { name: "hello", version: "1.0.0", description: "My Awesome CLI App" },
  subCommands: {
    sub: () => import("./sub.mjs").then((m) => m.default),
  },
});

meta, args, and subCommands all accept Resolvable<T> values — a value, Promise, function, or async function — enabling lazy and dynamic resolution.

Hooks

Commands support setup and cleanup functions called before and after run(). Only the executed command's hooks run. cleanup always runs, even if run() throws.

const main = defineCommand({
  meta: { name: "hello", version: "1.0.0", description: "My Awesome CLI App" },
  setup() {
    console.log("Setting up...");
  },
  cleanup() {
    console.log("Cleaning up...");
  },
  run() {
    console.log("Hello World!");
  },
});

Plugins

Plugins extend commands with reusable setup and cleanup hooks:

import { defineCommand, defineCittyPlugin, runMain } from "citty";

const logger = defineCittyPlugin({
  name: "logger",
  setup({ args }) {
    console.log("Logger setup, args:", args);
  },
  cleanup() {
    console.log("Logger cleanup");
  },
});

const main = defineCommand({
  meta: { name: "hello", description: "My CLI App" },
  plugins: [logger],
  run() {
    console.log("Hello!");
  },
});

runMain(main);

Plugin setup hooks run before the command's setup (in order), cleanup hooks run after (in reverse). Plugins can be async or factory functions.

Arguments

Argument Types

TypeDescriptionExample
positionalUnnamed positional argscli <name>
stringNamed string options--name value
booleanBoolean flags, supports --no- negation--verbose
enumConstrained to options array--level=info|warn|error

Common Options

OptionDescription
descriptionHelp text shown in usage output
requiredWhether the argument is required
defaultDefault value when not provided
aliasShort aliases (e.g., ["f"]). Not for positional
valueHintDisplay hint in help (e.g., "host" renders --name=<host>)

Example

const main = defineCommand({
  args: {
    name: { type: "positional", description: "Your name", required: true },
    friendly: { type: "boolean", description: "Use friendly greeting", alias: ["f"] },
    greeting: { type: "string", description: "Custom greeting", default: "Hello" },
    level: {
      type: "enum",
      description: "Log level",
      options: ["debug", "info", "warn", "error"],
      default: "info",
    },
  },
  run({ args }) {
    console.log(`${args.greeting} ${args.name}! (level: ${args.level})`);
  },
});

Boolean Negation

Boolean args support --no- prefix. The negative variant appears in help when default: true or negativeDescription is set.

Case-Agnostic Access

Kebab-case args can be accessed as camelCase: args["output-dir"] and args.outputDir both work.

Built-in Flags

--help / -h and --version / -v are handled automatically. Disabled if your command defines args with the same names or aliases.

API

FunctionDescription
defineCommand(def)Type helper for defining commands
runMain(cmd, opts?)Run a command with usage support and graceful error handling
createMain(cmd)Create a wrapper that calls runMain when invoked
runCommand(cmd, opts)Parse args and run command/sub-commands; access result from return value
parseArgs(rawArgs, argsDef)Parse input arguments and apply defaults
renderUsage(cmd, parent?)Render command usage to a string
showUsage(cmd, parent?)Render usage and print to console
defineCittyPlugin(def)Type helper for defining plugins

Development

  • Clone this repository
  • Install latest LTS version of Node.js
  • Enable Corepack using corepack enable
  • Install dependencies using pnpm install
  • Run interactive tests using pnpm dev

License

Made with 💛 Published under MIT License.

FAQs

Package last updated on 01 Apr 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts