Skip to main content

Create Your First CLI

The fastest way to get started with Bunli is using create-bunli, the official scaffolding tool.
1

Create a new project

Run the following command to create a new CLI project:
bunx create-bunli my-cli
You’ll be prompted to choose a template:
✔ Select a template
❯ basic     - Minimal CLI with one command
  advanced  - CLI with multiple commands and features  
  monorepo  - Multi-package Turborepo setup
Choose basic for this quickstart. You can explore other templates later.
The tool will create a new directory with your project and install dependencies automatically.
2

Navigate to your project

Change into your new project directory:
cd my-cli
Your project structure looks like this:
my-cli/
├── src/
│   ├── index.ts              # CLI entry point
│   └── commands/
│       └── hello.ts          # Example command
├── package.json              # Project metadata
├── tsconfig.json             # TypeScript config
├── bunli.config.ts           # Bunli configuration
└── README.md                 # Documentation
3

Run in development mode

Start your CLI in development mode with hot reload:
bun run dev hello --name "World"
You should see:
Hello, World.
Development mode automatically reloads when you change files. No need to restart!
4

Try different options

The example command includes options you can experiment with:
# Try the excited flag
bun run dev hello --name "Bunli" --excited
Output:
Hello, Bunli!
# Use short flags
bun run dev hello -n "Developer" -e
Output:
Hello, Developer!

Understanding Your First Command

Let’s examine the command that was generated in src/commands/hello.ts:
src/commands/hello.ts
import { defineCommand, option } from '@bunli/core'
import { z } from 'zod'

const helloCommand = defineCommand({
  name: 'hello',
  description: 'Say hello to someone',
  options: {
    name: option(
      z.string().default('World'),
      { 
        description: 'Name to greet',
        short: 'n'
      }
    ),
    excited: option(
      z.boolean().default(false),
      {
        description: 'Add excitement!',
        short: 'e'
      }
    )
  },
  handler: async ({ flags, colors }) => {
    const greeting = `Hello, ${flags.name}`
    const message = flags.excited ? `${greeting}!` : `${greeting}.`
    
    console.log(colors.green(message))
  }
})

export default helloCommand

Key Concepts

  • defineCommand: Creates a type-safe command definition
  • option(): Defines command options with Zod validation
  • Zod schemas: Provide runtime validation and type inference
  • Short flags: Single-letter aliases for options (e.g., -n for --name)
  • Handler context: Access to flags, colors, spinner, and more

Build Your CLI

Compile your CLI to a standalone binary:
1

Run the build command

bun run build
Expected output:
Building CLI...
✓ Compiled successfully
✓ Generated binary: ./dist/index.js
2

Run the built CLI

Execute your compiled CLI:
./dist/index.js hello --name "Production"
Or if you’ve set up the bin field in package.json:
node ./dist/index.js hello -n "Production" -e

Test Your CLI

Bunli includes testing utilities out of the box. Run tests with:
bun test
The basic template doesn’t include tests by default, but you can add them using @bunli/test. See the Testing Guide for details.

Available Templates

Beyond the basic template, create-bunli offers other starting points:

Basic

Perfect for: Simple CLIs with a single commandIncludes:
  • One example command
  • TypeScript configuration
  • Basic build setup
bunx create-bunli my-cli --template basic

Advanced

Perfect for: Complex CLIs with multiple commandsIncludes:
  • Multiple commands
  • Configuration management
  • File validation system
  • Development server example
bunx create-bunli my-cli --template advanced

Monorepo

Perfect for: Large projects with multiple packagesIncludes:
  • Turborepo configuration
  • Multiple packages setup
  • Shared dependencies
  • Parallel builds
bunx create-bunli my-cli --template monorepo

Common Commands

Here are the essential commands you’ll use regularly:
CommandDescription
bun run devStart development mode with hot reload
bun run buildCompile CLI to production binary
bun testRun tests with Bun’s test runner
bun run typecheckCheck TypeScript types without building
bunli generateGenerate TypeScript definitions from commands

Using create-bunli Options

create-bunli supports several options for customization:
# Create without installing dependencies
bunx create-bunli my-cli --no-install

# Create in a custom directory
bunx create-bunli my-cli --dir ~/projects/my-cli

# Use a specific template
bunx create-bunli my-cli --template advanced

# Create in the current directory
bunx create-bunli .

# Use an external GitHub template
bunx create-bunli my-cli --template username/repo
You can use any GitHub repository as a template by providing the username/repo format.

Customizing Your CLI

Edit src/index.ts to customize your CLI metadata:
src/index.ts
import { createCLI } from '@bunli/core'
import helloCommand from './commands/hello.js'

const cli = await createCLI({
  name: 'my-awesome-cli',
  version: '1.0.0',
  description: 'My awesome CLI tool'
})

cli.command(helloCommand)

await cli.run()

Next Steps

Now that you have a working CLI, dive deeper:

Build from Scratch

Learn how to build a CLI from scratch without templates

Core Concepts

Understand commands, options, and handlers in depth

Add Plugins

Extend your CLI with the powerful plugin system

Type Generation

Enable autocomplete and type-safe wrappers

Build docs developers (and LLMs) love