Skillshub unjs-citty
ALWAYS use when writing code importing \"citty\". Consult for debugging, best practices, or modifying citty.
git clone https://github.com/ComeOnOliver/skillshub
T=$(mktemp -d) && git clone --depth=1 https://github.com/ComeOnOliver/skillshub "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/harlan-zw/skilld/unjs-citty" ~/.claude/skills/comeonoliver-skillshub-unjs-citty && rm -rf "$T"
skills/harlan-zw/skilld/unjs-citty/SKILL.mdunjs/citty citty
cittyVersion: 0.2.1 (yesterday) Tags: latest: 0.2.1 (yesterday)
References: package.json • README • GitHub Issues • Releases
Search
Use
npx -y skilld search instead of grepping .skilld/ directories — hybrid semantic + keyword search across all indexed docs, issues, and releases.
npx -y skilld search "query" -p citty npx -y skilld search "issues:error handling" -p citty npx -y skilld search "releases:deprecated" -p citty
Filters:
docs:, issues:, releases: prefix narrows by source type.
API Changes
⚠️ ESM-only — v0.2.0 ships ESM only,
require('citty') no longer works source
⚠️
internally — v0.2.0 replaced custom parser with Node.js native node:util.parseArgs
util.parseArgs, edge cases around arg parsing may differ from v0.1.x source
⚠️ Optional args type
— v0.2.0 improved type inference: args without T | undefined
required: true or default now correctly type as T | undefined instead of T source
⚠️
negation conditionally printed — v0.2.0 only shows --no-
--no-<flag> in usage when negativeDescription is set; previously always shown source
✨
type: "enum" — new arg type in v0.2.0, requires options: string[] array. Typed as union of options values source
args: { color: { type: "enum", options: ["red", "blue", "green"] as const, description: "Pick a color", }, } // args.color typed as "red" | "blue" | "green" | undefined
✨
meta.hidden — v0.2.0, hides a subcommand from usage/help output source
✨
negativeDescription — v0.2.0, on boolean args, sets description for the --no-<flag> variant in usage source
✨
cleanup hook — v0.1.4, runs after run() completes (mirror of setup) source
✨
createMain(cmd) — v0.1.4, returns a reusable (opts?) => Promise<void> wrapper around runMain source
✨
--version flag — v0.1.4, auto-handled when meta.version is set source
✨
runMain({ showUsage }) — v0.1.5, accepts custom showUsage function to override default help rendering source
⚠️
--no- propagation fix — v0.2.1, --no-<flag> now correctly negates aliases too (was broken in v0.2.0) source
Best Practices
✅ Use
setup and cleanup hooks for lifecycle management — undocumented in README but fully supported; cleanup runs in finally block so it executes even on errors source
defineCommand({ args: { db: { type: "string", default: "mydb" } }, async setup({ args }) { await connectDb(args.db) }, async cleanup() { await disconnectDb() }, async run({ args }) { /* db is connected */ }, })
✅ Use
enum type with options for constrained values — validates input and shows allowed values in usage/error messages (v0.2.0+) source
args: { format: { type: "enum", options: ["json", "yaml", "toml"], default: "json", description: "Output format", }, }
✅ Use
meta.hidden: true to hide subcommands from usage output — keeps internal/debug commands accessible but invisible (v0.2.0+) source
subCommands: { debug: () => defineCommand({ meta: { name: "debug", hidden: true }, run() {} }), }
✅ Make
subCommands values lazy via arrow functions — citty resolves them with resolveValue(), enabling code-splitting and faster startup source
subCommands: { deploy: () => import("./commands/deploy").then(m => m.default), build: () => import("./commands/build").then(m => m.default), }
✅ Use
negativeDescription on boolean args that default to true — citty auto-generates --no-* flags with separate help text (v0.2.0+) source
args: { color: { type: "boolean", default: true, description: "Colorize output", negativeDescription: "Disable colored output", }, }
✅ Pass custom
showUsage to runMain for branded help screens — citty calls your function instead of the built-in one for --help and error display source
runMain(cmd, { showUsage: async (cmd, parent) => { console.log(await renderUsage(cmd, parent)) console.log("\nDocs: https://example.com/docs") }, })
✅ Arg names auto-alias between camelCase and kebab-case — defining
outputDir auto-creates --output-dir and vice versa; don't add redundant aliases source
✅
--version only works as the sole argument — citty checks rawArgs.length === 1 && rawArgs[0] === "--version", so --version --verbose won't trigger it; set meta.version on the root command source
✅ Use
runCommand over runMain for programmatic invocation — runMain calls process.exit(1) on errors and handles --help/--version; runCommand returns { result } and lets errors propagate source
const { result } = await runCommand(cmd, { rawArgs: ["build", "--prod"] })
✅ Avoid positional args on commands with subcommands — if a positional value matches a subcommand name, citty routes to the subcommand instead of using it as the arg value source