Scaffold production-ready TypeScript CLI tools with Bun. Use when creating new CLI projects, command-line utilities, or npm packages with CLI interface. Automatically sets up citty, consola, figlet banner, ESM/CJS builds, and version/help flags.
Scaffold a production-ready TypeScript CLI tool using Bun as the package manager and build tool.
Activate this skill when:
# User invokes the skill
/az-cli my-cli "A CLI tool for XYZ"
Use AskUserQuestion to collect:
my-cli@jjuidevls^[a-z][a-z0-9-]*$@ if providedCopy all files from assets/template/ to target directory:
target-dir/
├── .github/
│ └── workflows/
│ └── release.yml # Changesets CI: auto version PR + npm publish
├── .changeset/
│ ├── config.json # Changesets config (access: public, baseBranch: main)
│ └── README.md # Changeset usage guide
├── src/
│ ├── cli.ts # CLI entry point with citty
│ ├── index.ts # Library exports
│ ├── build.ts # Bun build script
│ ├── commands/
│ │ ├── index.ts
│ │ └── ls/
│ │ └── index.ts # ls command placeholder
│ └── utils/
│ ├── constants.ts # CLI_META with placeholders
│ ├── logger.ts # Consola logger wrapper
│ └── banner.ts # Figlet ASCII banner
├── package.json # With replaced placeholders
├── tsconfig.json # Base TypeScript config
├── tsconfig.cjs.json # CJS build config
├── tsconfig.esm.json # ESM build config
├── tsconfig.cli.json # CLI build config
└── tsconfig.types.json # Types generation config
In copied files, replace:
| Placeholder | Replacement | Files |
|---|---|---|
TOOL_NAME | CLI display name (Title Case) | package.json, src/utils/constants.ts, .github/workflows/release.yml |
TOOL_DESCRIPTION | User-provided description | package.json |
COMMAND_NAME | CLI command name (kebab-case) | package.json |
@scope | npm scope (or remove if none) | package.json |
bun install in target directorybun run build to verify build works./dist/cli/cli.js --version to verify CLINPM_TOKEN secret in GitHub repo → Settings → Secrets → Actionsnpm publish --access publicbun run changeset, push to main → CI handles versioning and publishingPre-configured with:
{COMMAND_NAME}: ./dist/cli/cli.js--version: Shows version from package.json (built into citty)--help: Shows help (built into citty)ls command: Lists available commands (placeholder)dist/
├── cli/
│ ├── cli.js # Executable CLI binary
│ └── package.json # { "type": "module" }
├── cjs/
│ ├── index.cjs # CommonJS module
│ └── package.json # { "type": "commonjs" }
├── esm/
│ ├── index.js # ES module
│ └── package.json # { "type": "module" }
├── types/
│ └── index.d.ts # TypeScript declarations
└── fonts/ # Figlet fonts for ASCII art
After scaffolding, to add a new command:
src/commands/<name>/index.ts:import { defineCommand } from 'citty';
export const <name>Command = defineCommand({
meta: {
name: '<name>',
description: '<description>'
},
run: async () => {
// command logic
}
});
src/commands/index.tssrc/cli.ts under subCommandsls command outputThis skill: