Skip to main content
Bunli uses a configuration file to define CLI metadata, build settings, plugins, and more. The config is validated at runtime using Zod schemas for type safety.

Configuration File

Create a bunli.config.ts file in your project root:
import { defineConfig } from '@bunli/core'

export default defineConfig({
  name: 'my-cli',
  version: '1.0.0',
  description: 'A powerful CLI tool',
  
  commands: {
    entry: './cli.ts',
    directory: './commands'
  },
  
  build: {
    entry: 'cli.ts',
    outdir: 'dist',
    targets: ['native'],
    minify: true
  },
  
  plugins: []
})
Bunli also supports bunli.config.js and bunli.config.mjs for JavaScript projects.

defineConfig

The defineConfig function provides type safety and IntelliSense:
import { defineConfig } from '@bunli/core'
import type { BunliConfigInput } from '@bunli/core'

const config: BunliConfigInput = {
  // TypeScript will validate this
}

export default defineConfig(config)

Configuration Options

Basic Metadata

name
string
required
CLI name shown in help output and package.json.
name: 'my-cli'
version
string
required
CLI version (usually from package.json).
version: '1.0.0'
description
string
Brief description of your CLI.
description: 'A modern CLI tool for developers'

Commands Configuration

commands
object
Commands discovery and generation settings.
commands.entry
string
Entry point file for your CLI.
entry: './cli.ts'
commands.directory
string
Directory containing command files.
directory: './commands'
commands.generateReport
boolean
Generate a commands report during type generation.
generateReport: true

Build Configuration

build
object
Build and compilation settings.
build.entry
string | string[]
Entry point(s) for building.
entry: 'cli.ts'
// or multiple entries
entry: ['cli.ts', 'server.ts']
build.outdir
string
Output directory for built files.
outdir: 'dist'
build.targets
string[]
Build targets (e.g., 'native', 'bun-linux-x64', 'bun-darwin-arm64').
targets: ['native']  // Current platform
targets: ['bun-linux-x64', 'bun-darwin-arm64']  // Cross-platform
build.compress
boolean
default:"false"
Compress output binaries.
compress: true
build.minify
boolean
default:"false"
Minify JavaScript output.
minify: true
build.external
string[]
External dependencies to exclude from bundle.
external: ['@bunli/core', 'zod']
build.sourcemap
boolean
default:"true"
Generate source maps.
sourcemap: false

Development Configuration

dev
object
Development mode settings.
dev.watch
boolean
default:"true"
Enable file watching.
watch: true
dev.inspect
boolean
default:"false"
Enable debugging.
inspect: true
dev.port
number
Port for dev server (if applicable).
port: 3000

Test Configuration

test
object
Test execution settings.
test.pattern
string | string[]
default:"['**/*.test.ts', '**/*.spec.ts']"
Test file patterns.
pattern: ['**/*.test.ts', '**/*.spec.ts']
test.coverage
boolean
default:"false"
Enable coverage reporting.
coverage: true
test.watch
boolean
default:"false"
Run tests in watch mode.
watch: true

Workspace Configuration

workspace
object
Monorepo and workspace settings.
workspace.packages
string[]
Workspace package patterns.
packages: ['packages/*', 'apps/*']
workspace.versionStrategy
'fixed' | 'independent'
default:"'fixed'"
Versioning strategy for workspace packages.
versionStrategy: 'independent'
workspace.shared
unknown
Shared workspace configuration.

Release Configuration

release
object
Publishing and release settings.
release.npm
boolean
default:"true"
Publish to npm.
npm: true
release.github
boolean
default:"false"
Create GitHub releases.
github: true
release.tagFormat
string
default:"'v{{version}}'"
Git tag format.
tagFormat: 'v{{version}}'
release.conventionalCommits
boolean
default:"true"
Use conventional commits for changelog.
conventionalCommits: true
release.binary
object
Binary publishing configuration (per-platform packages).
release.binary.packageNameFormat
string
default:"'{{name}}-{{platform}}'"
Format for platform-specific package names.
packageNameFormat: '{{name}}-{{platform}}'
release.binary.shimPath
string
default:"'bin/run.mjs'"
Path to platform shim.
shimPath: 'bin/run.mjs'

Plugin Configuration

plugins
PluginConfig[]
default:"[]"
Array of plugins to load.
import { defineConfig } from '@bunli/core'
import metricsPlugin from './plugins/metrics'

export default defineConfig({
  plugins: [
    metricsPlugin({ enabled: true })
  ]
})
See Plugins for details.

TUI Configuration

tui
object
Terminal UI rendering settings.
tui.renderer
object
Renderer configuration.
tui.renderer.bufferMode
'alternate' | 'standard'
Terminal buffer mode.
  • alternate: Full-screen alternate buffer (like vim)
  • standard: Render in main buffer (scrollback-friendly)
tui: {
  renderer: {
    bufferMode: 'alternate'
  }
}

Help Configuration

help
object
Custom help rendering.
help.renderer
HelpRenderer
Custom help renderer function.
import type { HelpRenderer } from '@bunli/core'

const customHelp: HelpRenderer = (context) => {
  console.log(`${context.cliName} v${context.version}`)
  // Custom help formatting
}

export default defineConfig({
  help: {
    renderer: customHelp
  }
})

Loading Configuration

Bunli automatically loads configuration from your project root:
import { loadConfig, loadConfigResult } from '@bunli/core'

// Load config (throws on error)
const config = await loadConfig()

// Load config with Result type (better-result pattern)
const result = await loadConfigResult()
if (result.isOk()) {
  const config = result.value
} else {
  const error = result.error
  // Handle ConfigNotFoundError or ConfigLoadError
}

Config Loading Order

Bunli searches for config files in this order:
  1. bunli.config.ts
  2. bunli.config.js
  3. bunli.config.mjs

Real-World Examples

import { defineConfig } from '@bunli/core'

export default defineConfig({
  name: 'my-cli',
  version: '1.0.0',
  description: 'A simple CLI tool',
  
  commands: {
    entry: './cli.ts'
  },
  
  build: {
    entry: 'cli.ts',
    outdir: 'dist',
    targets: ['native']
  }
})

Type-Safe Configuration

Bunli validates your configuration at runtime:
import { bunliConfigSchema } from '@bunli/core'
import { z } from 'zod'

// Get the TypeScript type
type BunliConfig = z.infer<typeof bunliConfigSchema>

// Validate a config object
const result = bunliConfigSchema.safeParse(myConfig)
if (!result.success) {
  console.error(result.error)
}

Overriding Configuration

You can override config when creating a CLI:
import { createCLI } from '@bunli/core'

const cli = await createCLI({
  name: 'my-cli',
  version: '1.0.0',
  // Overrides bunli.config.ts
})

Best Practices

1

Use TypeScript for config

TypeScript provides IntelliSense and type checking.
// bunli.config.ts (recommended)
export default defineConfig({ /* ... */ })
2

Keep config minimal

Only configure what you need. Bunli provides sensible defaults.
3

Use environment variables

Don’t hardcode secrets in config files.
export default defineConfig({
  plugins: [
    apiPlugin({ apiKey: process.env.API_KEY })
  ]
})
4

Validate plugin options

Use Zod schemas to validate plugin configuration.

See Also

Build docs developers (and LLMs) love