Examples
This guide provides practical examples of using Tab with different CLI frameworks and common scenarios you might encounter when building CLI tools.
Basic CLI with Core API
Section titled “Basic CLI with Core API”A simple CLI tool with basic autocompletions:
#!/usr/bin/env nodeimport { RootCommand } from '@bombsh/tab';
const t = new RootCommand();
// Add global optionst.option('--config', 'Use specified config file', function(complete) { complete('vite.config.ts', 'Vite config file'); complete('vite.config.js', 'Vite config file');}, 'c');
t.option('--mode', 'Set env mode', function(complete) { complete('development', 'Development mode'); complete('production', 'Production mode');}, 'm');
// Add root command argumentt.argument('project', function(complete) { complete('my-app', 'My application'); complete('my-lib', 'My library');});
// Add commands with completionsconst devCmd = t.command('dev', 'Start development server');devCmd.option('--port', 'Port number', function(complete) { complete('3000', 'Development port'); complete('8080', 'Production port');}, 'p');
devCmd.option('--host', 'Host address', function(complete) { complete('localhost', 'Local development'); complete('0.0.0.0', 'All interfaces');}, 'h');
devCmd.option('--verbose', 'Enable verbose logging', 'v');
// Add build commandconst buildCmd = t.command('build', 'Build for production');buildCmd.option('--mode', 'Build mode', function(complete) { complete('development', 'Development build'); complete('production', 'Production build');});
buildCmd.option('--out-dir', 'Output directory', function(complete) { complete('dist/', 'Distribution directory'); complete('build/', 'Build directory');});
// Add command with argumentst.command('copy', 'Copy files') .argument('source', function(complete) { complete('src/', 'Source directory'); complete('dist/', 'Distribution directory'); }) .argument('destination', function(complete) { complete('build/', 'Build output'); complete('release/', 'Release directory'); });
// Handle completion requestsif (process.argv[2] === 'complete') { const shell = process.argv[3]; if (shell && ['zsh', 'bash', 'fish', 'powershell'].includes(shell)) { t.setup('my-cli', process.execPath, shell); } else { // Parse completion arguments (everything after --) const separatorIndex = process.argv.indexOf('--'); const completionArgs = separatorIndex !== -1 ? process.argv.slice(separatorIndex + 1) : []; t.parse(completionArgs); }} else { // Regular CLI usage console.log('My CLI Tool'); console.log('Use "complete" command for shell completion');}
CAC Framework Example
Section titled “CAC Framework Example”A more complex CLI using CAC with Tab integration:
#!/usr/bin/env nodeimport cac from 'cac';import tab from '@bombsh/tab/cac';
const cli = cac('my-cli');
// Define commandscli.command('dev', 'Start development server') .option('--port <port>', 'Specify port', { default: 3000 }) .option('--host <host>', 'Specify host', { default: 'localhost' }) .option('--config <file>', 'Config file') .action((options) => { console.log('Starting dev server...', options); });
cli.command('build', 'Build for production') .option('--mode <mode>', 'Build mode', { default: 'production' }) .option('--out-dir <dir>', 'Output directory') .action((options) => { console.log('Building...', options); });
cli.command('deploy', 'Deploy application') .option('--env <environment>', 'Deployment environment') .option('--region <region>', 'Deployment region') .action((options) => { console.log('Deploying...', options); });
// Initialize Tab completionconst completion = tab(cli);
// Configure custom completionsconst devCommandCompletion = completion.commands.get('dev');if (devCommandCompletion) { const portOptionCompletion = devCommandCompletion.options.get('--port'); if (portOptionCompletion) { portOptionCompletion.handler = async () => { return [ { value: '3000', description: 'Development port' }, { value: '8080', description: 'Production port' }, ]; }; }
const configOptionCompletion = devCommandCompletion.options.get('--config'); if (configOptionCompletion) { configOptionCompletion.handler = async () => { return [ { value: 'vite.config.ts', description: 'Vite config file' }, { value: 'vite.config.js', description: 'Vite config file' }, ]; }; }}
const buildCommandCompletion = completion.commands.get('build');if (buildCommandCompletion) { const modeOptionCompletion = buildCommandCompletion.options.get('--mode'); if (modeOptionCompletion) { modeOptionCompletion.handler = async () => { return [ { value: 'development', description: 'Development build' }, { value: 'production', description: 'Production build' }, ]; }; }}
cli.parse();
Citty Framework Example
Section titled “Citty Framework Example”A CLI using Citty with Tab integration:
#!/usr/bin/env nodeimport { defineCommand, createMain } from 'citty';import tab from '@bombsh/tab/citty';
const main = defineCommand({ meta: { name: 'my-cli', description: 'My CLI tool', },});
const devCommand = defineCommand({ meta: { name: 'dev', description: 'Start development server', }, args: { port: { type: 'string', description: 'Specify port' }, host: { type: 'string', description: 'Specify host' }, config: { type: 'string', description: 'Config file' }, },});
const buildCommand = defineCommand({ meta: { name: 'build', description: 'Build for production', }, args: { mode: { type: 'string', description: 'Build mode' }, outDir: { type: 'string', description: 'Output directory' }, },});
const deployCommand = defineCommand({ meta: { name: 'deploy', description: 'Deploy application', }, args: { env: { type: 'string', description: 'Deployment environment' }, region: { type: 'string', description: 'Deployment region' }, },});
main.subCommands = { dev: devCommand, build: buildCommand, deploy: deployCommand,};
const completion = await tab(main);
// Configure completionsconst devCommandCompletion = completion.commands.get('dev');if (devCommandCompletion) { const portOptionCompletion = devCommandCompletion.options.get('--port'); if (portOptionCompletion) { portOptionCompletion.handler = async () => { return [ { value: '3000', description: 'Development port' }, { value: '8080', description: 'Production port' }, ]; }; }}
const cli = createMain(main);cli();
Commander.js Example
Section titled “Commander.js Example”A CLI using Commander.js with Tab integration:
#!/usr/bin/env nodeimport { Command } from 'commander';import tab from '@bombsh/tab/commander';
const program = new Command();
program .name('my-cli') .description('My CLI tool') .version('1.0.0');
program .command('dev') .description('Start development server') .option('-p, --port <port>', 'Specify port') .option('-h, --host <host>', 'Specify host') .option('-c, --config <file>', 'Config file') .action((options) => { console.log('Starting dev server...', options); });
program .command('build') .description('Build for production') .option('-m, --mode <mode>', 'Build mode') .option('-o, --out-dir <dir>', 'Output directory') .action((options) => { console.log('Building...', options); });
program .command('deploy') .description('Deploy application') .option('-e, --env <environment>', 'Deployment environment') .option('-r, --region <region>', 'Deployment region') .action((options) => { console.log('Deploying...', options); });
const completion = tab(program);
// Configure completionsconst devCommandCompletion = completion.commands.get('dev');if (devCommandCompletion) { const portOptionCompletion = devCommandCompletion.options.get('--port'); if (portOptionCompletion) { portOptionCompletion.handler = async () => { return [ { value: '3000', description: 'Development port' }, { value: '8080', description: 'Production port' }, ]; }; }}
program.parse();
Advanced Examples
Section titled “Advanced Examples”Dynamic File Completions
Section titled “Dynamic File Completions”Load completions from the file system:
import { readdir } from 'fs/promises';import { RootCommand } from '@bombsh/tab';
const t = new RootCommand();
t.command('build', 'Build project') .option('--config', 'Config file', async function(complete) { try { const files = await readdir('.'); const configFiles = files.filter(f => f.includes('config') && (f.endsWith('.js') || f.endsWith('.ts')) ); configFiles.forEach(file => complete(file, `Config file: ${file}`)); } catch (error) { // Fallback completions complete('vite.config.ts', 'Vite config file'); complete('vite.config.js', 'Vite config file'); } });
Context-Aware Completions
Section titled “Context-Aware Completions”Provide different completions based on context:
const t = new RootCommand();
t.command('deploy', 'Deploy application') .option('--env', 'Environment', function(complete) { // Check if user is typing a specific environment if (this.toComplete?.startsWith('prod')) { complete('production', 'Production environment'); return; }
complete('development', 'Development environment'); complete('staging', 'Staging environment'); complete('production', 'Production environment'); }) .option('--region', 'Region', function(complete) { // Provide region completions based on environment const env = this.command?.options?.get('--env')?.value;
if (env === 'production') { complete('us-east-1', 'US East (N. Virginia)'); complete('us-west-2', 'US West (Oregon)'); complete('eu-west-1', 'Europe (Ireland)'); } else { complete('us-east-1', 'US East (N. Virginia)'); complete('eu-west-1', 'Europe (Ireland)'); } });
Package.json Script Completions
Section titled “Package.json Script Completions”Dynamically load completions from package.json:
import { readFile } from 'fs/promises';import { RootCommand } from '@bombsh/tab';
const t = new RootCommand();
t.command('run', 'Run scripts') .argument('script', async function(complete) { try { const packageJson = JSON.parse(await readFile('package.json', 'utf8')); const scripts = Object.keys(packageJson.scripts || {}); scripts.forEach(script => complete(script, `Run ${script} script`)); } catch (error) { // Fallback completions complete('dev', 'Start development server'); complete('build', 'Build for production'); complete('test', 'Run tests'); } });
Workspace Completions
Section titled “Workspace Completions”Handle monorepo workspace completions:
import { readFile, readdir } from 'fs/promises';import { RootCommand } from '@bombsh/tab';
const t = new RootCommand();
t.command('workspace', 'Workspace commands') .option('--filter', 'Filter workspaces', async function(complete) { try { const packageJson = JSON.parse(await readFile('package.json', 'utf8')); const workspaces = packageJson.workspaces || [];
// Get workspace names const workspaceNames = []; for (const workspace of workspaces) { if (workspace.includes('*')) { // Handle glob patterns const dir = workspace.replace('/*', ''); const items = await readdir(dir); workspaceNames.push(...items); } else { workspaceNames.push(workspace); } }
workspaceNames.forEach(name => complete(name, `Workspace: ${name}`)); } catch (error) { // Fallback completions complete('packages/*', 'All packages'); complete('apps/*', 'All applications'); } });
Testing Completions
Section titled “Testing Completions”Test your completions manually:
# Test command completionsmy-cli complete -- "dev"
# Test option completionsmy-cli complete -- "dev --port"
# Test argument completionsmy-cli complete -- "copy src/"
# Generate shell completion scriptmy-cli complete zsh
Next Steps
Section titled “Next Steps”- Learn about Framework Adapters for easier integration
- Check out Best Practices for effective autocompletions
- Explore the API Reference for advanced usage