
Company News
Socket Named Top Sales Organization by RepVue
Socket won two 2026 Reppy Awards from RepVue, ranking in the top 5% of all sales orgs. AE Alexandra Lister shares what it's like to grow a sales career here.
Elegant CLI Builder
util.parseArgs)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 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.
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.
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 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.
| Type | Description | Example |
|---|---|---|
positional | Unnamed positional args | cli <name> |
string | Named string options | --name value |
boolean | Boolean flags, supports --no- negation | --verbose |
enum | Constrained to options array | --level=info|warn|error |
| Option | Description |
|---|---|
description | Help text shown in usage output |
required | Whether the argument is required |
default | Default value when not provided |
alias | Short aliases (e.g., ["f"]). Not for positional |
valueHint | Display hint in help (e.g., "host" renders --name=<host>) |
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 args support --no- prefix. The negative variant appears in help when default: true or negativeDescription is set.
Kebab-case args can be accessed as camelCase: args["output-dir"] and args.outputDir both work.
--help / -h and --version / -v are handled automatically. Disabled if your command defines args with the same names or aliases.
| Function | Description |
|---|---|
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 |
corepack enablepnpm installpnpm devMade with 💛 Published under MIT License.
FAQs
Elegant CLI Builder
The npm package citty receives a total of 18,914,575 weekly downloads. As such, citty popularity was classified as popular.
We found that citty demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

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.

Company News
Socket won two 2026 Reppy Awards from RepVue, ranking in the top 5% of all sales orgs. AE Alexandra Lister shares what it's like to grow a sales career here.

Security News
NIST will stop enriching most CVEs under a new risk-based model, narrowing the NVD's scope as vulnerability submissions continue to surge.

Company News
/Security News
Socket is an initial recipient of OpenAI's Cybersecurity Grant Program, which commits $10M in API credits to defenders securing open source software.