Built for Bun
The Minimal CLI Framework for Bun
Type-safe, fast, and delightfully simple. Build production-ready CLIs with zero configuration and full TypeScript support.
greet.ts
import { defineCommand, option } from '@bunli/core'
import { z } from 'zod'
export default defineCommand({
name: 'greet',
description: 'Greet someone',
options: {
name: option(
z.string().min(1),
{ description: 'Name to greet', short: 'n' }
),
excited: option(
z.coerce.boolean().default(false),
{ description: 'Add excitement', short: 'e' }
)
},
handler: async ({ flags }) => {
const greeting = `Hello, ${flags.name}${flags.excited ? '!' : '.'}`
console.log(greeting)
}
})
Terminal
$ greet --name World --excited Hello, World!
✨ Full TypeScript support with automatic type inference
Everything You Need
Simple, powerful, and ready for production
Blazing Fast
Powered by Bun's native speed
Type-Safe
Full TypeScript autocompletion
Zero Config
Works out of the box, sensible defaults
Minimal API
Learn once, use everywhere
Testing Built-in
First-class testing utilities included
Easy Deploy
Compile to single executable
See the Difference
Less boilerplate, more productivity
bunli-cli.ts
// Clean and type-safe
import { defineCommand, option } from '@bunli/core'
import { z } from 'zod'
export default defineCommand({
name: 'serve',
description: 'Start the server',
options: {
port: option(
z.coerce.number().default(5000),
{ description: 'Port to bind', short: 'p' }
),
verbose: option(
z.coerce.boolean().default(false),
{ description: 'Run with verbose logging', short: 'v' }
)
},
handler: ({ flags }) => {
// flags.port is number
// flags.verbose is boolean
console.log(`Server on ${flags.port}`)
}
})
80%
Less boilerplate
100%
Type safe
10x
Faster development
Powerful Features, Simple API
Everything you need to build production-ready CLIs
Schema Validation
import { defineCommand, option } from '@bunli/core'
import { z } from 'zod'
export default defineCommand({
name: 'deploy',
description: 'Deploy to environment',
options: {
env: option(
z.enum(['dev', 'staging', 'prod']),
{ description: 'Target environment' }
),
port: option(
z.coerce.number().min(1000).max(9999),
{ description: 'Port number (1000-9999)' }
),
force: option(
z.coerce.boolean().default(false),
{ description: 'Force deployment', short: 'f' }
)
},
handler: ({ flags }) => {
// TypeScript knows:
// flags.env is 'dev' | 'staging' | 'prod'
// flags.port is number (1000-9999)
// flags.force is boolean
console.log(`Deploying to ${flags.env}:${flags.port}`)
}
})