@bunli/plugin-completions
Shell completion generation plugin for Bunli
@bunli/plugin-completions
The completions plugin automatically generates shell completion scripts for your Bunli CLI applications. It supports Bash, Zsh, and Fish shells with full support for commands, options, flags, and enum value completions.
Installation
bun add @bunli/plugin-completionsFeatures
- 🐚 Multi-shell support: Generate completions for Bash, Zsh, and Fish
- 🎯 Type-aware: Leverages command metadata from Bunli's type generation
- 📋 Enum completions: Automatically completes enum option values
- ⚡ Zero configuration: Works out of the box with your existing commands
- 🔧 Flexible output: Save to file or output to stdout
- 📝 Shell-specific syntax: Generates idiomatic completions for each shell
- 🚀 Auto-generated: No manual completion script maintenance required
Basic Usage
import { createCLI } from '@bunli/core'
import { completionsPlugin } from '@bunli/plugin-completions'
const cli = await createCLI({
name: 'my-cli',
version: '1.0.0',
plugins: [completionsPlugin()] as const
})
await cli.load({
deploy: () => import('./commands/deploy'),
build: () => import('./commands/build')
})
await cli.run()Or in your bunli.config.ts:
import { defineConfig } from '@bunli/core'
import { completionsPlugin } from '@bunli/plugin-completions'
export default defineConfig({
name: 'my-cli',
plugins: [completionsPlugin()]
})Generating Completions
The plugin adds a completions command to your CLI:
# Output to stdout with installation instructions
my-cli completions --shell bash
my-cli completions --shell zsh
my-cli completions --shell fish
# Save to file
my-cli completions --shell bash --output completions.sh
my-cli completions -s zsh -o _my-cli
my-cli completions -s fish -o my-cli.fishCommand Options
Required Options
--shell, -s <shell>: Target shell for completion generation- Values:
bash,zsh,fish
- Values:
Optional Options
--output, -o <path>: Output file path (default: stdout)
Installation Methods
Bash Completions
Option 1 - Current session:
source <(my-cli completions --shell bash)Option 2 - Save to completion directory:
# Linux
my-cli completions --shell bash > /etc/bash_completion.d/my-cli
# macOS with Homebrew
my-cli completions --shell bash > $(brew --prefix)/etc/bash_completion.d/my-cliOption 3 - Add to ~/.bashrc:
echo 'source <(my-cli completions --shell bash)' >> ~/.bashrc
source ~/.bashrcZsh Completions
Option 1 - Current session:
source <(my-cli completions --shell zsh)Option 2 - User completion directory:
mkdir -p ~/.zsh/completions
my-cli completions --shell zsh > ~/.zsh/completions/_my-cli
# Add to ~/.zshrc if not already present:
echo 'fpath=(~/.zsh/completions $fpath)' >> ~/.zshrc
echo 'autoload -U compinit && compinit' >> ~/.zshrc
source ~/.zshrcOption 3 - System-wide:
sudo my-cli completions --shell zsh > /usr/local/share/zsh/site-functions/_my-cli
# Restart your shellFish Completions
User completions:
my-cli completions --shell fish > ~/.config/fish/completions/my-cli.fish
# Completions will be available in new fish sessionsSystem-wide:
sudo my-cli completions --shell fish > /usr/share/fish/vendor_completions.d/my-cli.fishHow It Works
The plugin leverages Bunli's metadata generation system to create accurate completions:
- Metadata Extraction: Reads the
.bunli/commands.gen.tsfile generated by Bunli - Command Structure: Parses command names, descriptions, options, and flags
- Type Information: Uses Zod schema metadata to extract enum values and validation rules
- Shell-Specific Generation: Generates appropriate completion syntax for each shell
Examples
Enum Value Completions
Given a command with enum options:
import { defineCommand, option } from '@bunli/core'
import { z } from 'zod'
export default defineCommand({
name: 'deploy',
description: 'Deploy application',
options: {
environment: option(
z.enum(['development', 'staging', 'production']),
{
short: 'e',
description: 'Target environment'
}
),
region: option(
z.enum(['us-east-1', 'us-west-2', 'eu-west-1']),
{
short: 'r',
description: 'AWS region'
}
)
},
handler: async ({ flags }) => {
console.log(`Deploying to ${flags.environment} in ${flags.region}`)
}
})The completions plugin will generate:
Bash:
# Provides enum completions for --environment
if [[ "$prev" == "--environment" || "$prev" == "-e" ]]; then
COMPREPLY=($(compgen -W "development staging production" -- "$cur"))
return
fi
# Provides enum completions for --region
if [[ "$prev" == "--region" || "$prev" == "-r" ]]; then
COMPREPLY=($(compgen -W "us-east-1 us-west-2 eu-west-1" -- "$cur"))
return
fiZsh:
"(-e --environment)"{-e,--environment}[Target environment]:(development staging production)
"(-r --region)"{-r,--region}[AWS region]:(us-east-1 us-west-2 eu-west-1)Fish:
complete -c my-cli -n '__fish_seen_subcommand_from deploy' \
-l environment -s e -d 'Target environment' -r -a 'development staging production'
complete -c my-cli -n '__fish_seen_subcommand_from deploy' \
-l region -s r -d 'AWS region' -r -a 'us-east-1 us-west-2 eu-west-1'Multiple Commands
// commands/build.ts
export default defineCommand({
name: 'build',
description: 'Build the project',
options: {
mode: option(z.enum(['development', 'production']), {
short: 'm',
description: 'Build mode'
})
},
handler: async ({ flags }) => {
console.log(`Building in ${flags.mode} mode`)
}
})
// commands/test.ts
export default defineCommand({
name: 'test',
description: 'Run tests',
options: {
coverage: option(z.boolean().optional(), {
short: 'c',
description: 'Generate coverage report'
})
},
handler: async ({ flags }) => {
console.log('Running tests...')
}
})Completions will include both commands:
# Bash
my-cli <TAB>
# Shows: build test --help --version
my-cli build --mode <TAB>
# Shows: development productionBoolean Flags
Boolean flags are automatically detected and don't require values:
export default defineCommand({
name: 'serve',
options: {
watch: option(z.boolean().optional(), {
short: 'w',
description: 'Watch for changes'
}),
open: option(z.boolean().optional(), {
short: 'o',
description: 'Open in browser'
})
},
handler: async ({ flags }) => {
// ...
}
})my-cli serve --watch --open # No values required
my-cli serve -w -o # Short flags work tooPlugin Options
interface CompletionsPluginOptions {
// Currently no configuration options
// Reserved for future features like:
// - Custom completion handlers
// - Additional shell support
// - Completion behavior customization
}Best Practices
1. Use Enum Types for Fixed Values
// ✅ Good - generates completions
environment: option(z.enum(['dev', 'staging', 'prod']), {
description: 'Target environment'
})
// ❌ Less useful - no completions
environment: option(z.string(), {
description: 'Target environment'
})2. Provide Descriptions
// ✅ Good - shows helpful descriptions
preset: option(z.enum(['minimal', 'standard', 'full']), {
description: 'Configuration preset'
})
// ❌ Less helpful - no context
preset: option(z.enum(['minimal', 'standard', 'full']))3. Use Short Flags
// ✅ Good - both long and short forms complete
force: option(z.boolean().optional(), {
short: 'f',
description: 'Force operation'
})4. Generate After Command Changes
Always regenerate completions when you add or modify commands:
# Make changes to commands
# ...
# Regenerate type metadata
bun run generate
# Update installed completions
my-cli completions --shell bash > ~/.bash_completion.d/my-cliDistribution
Including Completions in Your Package
You can include pre-generated completions in your package:
{
"name": "my-cli",
"files": [
"dist",
"completions"
],
"scripts": {
"completions": "bun run cli.js completions",
"postinstall": "node scripts/install-completions.js"
}
}Create a postinstall script to prompt users:
// scripts/install-completions.js
import { execSync } from 'child_process'
import { existsSync, writeFileSync, mkdirSync } from 'fs'
import { homedir } from 'os'
import { join } from 'path'
const shell = process.env.SHELL?.includes('zsh') ? 'zsh'
: process.env.SHELL?.includes('fish') ? 'fish'
: 'bash'
console.log(`\n🐚 Shell completions are available for ${shell}`)
console.log(`Run: my-cli completions --shell ${shell}\n`)Documentation for Users
Include installation instructions in your README:
## Shell Completions
Enable tab completion for your shell:
### Bash
```bash
my-cli completions --shell bash > ~/.bash_completion.d/my-cli
source ~/.bashrc
```
### Zsh
```bash
my-cli completions --shell zsh > ~/.zsh/completions/_my-cli
# Add to ~/.zshrc:
fpath=(~/.zsh/completions $fpath)
autoload -U compinit && compinit
```
### Fish
```bash
my-cli completions --shell fish > ~/.config/fish/completions/my-cli.fish
```Limitations
- No subcommand completions: Currently only supports top-level commands
- No dynamic completions: Completions are static based on command metadata
- No positional argument completions: Only flags and options are completed
- No custom completion functions: Cannot define custom completion logic
Troubleshooting
Completions Not Working
-
Check shell type: Verify you generated completions for the correct shell
echo $SHELL -
Verify installation: Make sure the completion file is in the correct location
# Bash ls ~/.bash_completion.d/ # Zsh ls ~/.zsh/completions/ # Fish ls ~/.config/fish/completions/ -
Reload shell: Source your shell config or restart your shell
# Bash source ~/.bashrc # Zsh source ~/.zshrc # Fish - restart fish
Enum Values Not Completing
- Regenerate metadata: Run
bun run generateto update command metadata - Check schema: Verify you're using
z.enum()notz.string() - Test output: Check the generated completion script includes enum values
Wrong Command Name
The plugin uses the CLI name from createCLI() or bunli.config.ts. Update it:
const cli = await createCLI({
name: 'correct-cli-name', // This name is used in completions
// ...
})