···11+---
22+title: Core API
33+description: Complete API reference for the Tab core package
44+---
55+66+This page provides the complete API reference for the Tab core package, including all classes, methods, types, and interfaces.
77+88+## Classes
99+1010+### Completion
1111+1212+The main class for managing command and option completions.
1313+1414+```ts
1515+import { Completion } from '@bombsh/tab';
1616+1717+const completion = new Completion();
1818+```
1919+2020+#### Methods
2121+2222+##### addCommand
2323+2424+Adds a completion handler for a command.
2525+2626+**Parameters:**
2727+- `name` (string): The command name
2828+- `description` (string): Command description
2929+- `args` (boolean[]): Array indicating which arguments are required (false) or optional (true)
3030+- `handler` (Handler): Function that returns completion suggestions
3131+- `parent` (string, optional): Parent command name for nested commands
3232+3333+**Returns:** string - The command key
3434+3535+**Example:**
3636+```ts
3737+// Command with required argument: "vite <entry>"
3838+completion.addCommand('dev', 'Start development server', [false], async () => {
3939+ return [
4040+ { value: 'src/main.ts', description: 'Main entry point' },
4141+ { value: 'src/index.ts', description: 'Index entry point' },
4242+ ];
4343+});
4444+4545+// Command with optional argument: "vite [entry]"
4646+completion.addCommand('serve', 'Start the server', [true], async () => {
4747+ return [
4848+ { value: 'dist', description: 'Distribution directory' },
4949+ ];
5050+});
5151+5252+// Nested command: "vite dev build"
5353+completion.addCommand('build', 'Build project', [], async () => {
5454+ return [
5555+ { value: 'build', description: 'Build command' },
5656+ ];
5757+}, 'dev');
5858+```
5959+6060+##### addOption
6161+6262+Adds a completion handler for an option of a specific command.
6363+6464+**Parameters:**
6565+- `command` (string): The command name
6666+- `option` (string): The option name (e.g., '--port')
6767+- `description` (string): Option description
6868+- `handler` (Handler): Function that returns completion suggestions
6969+- `alias` (string, optional): Short flag alias (e.g., 'p' for '--port')
7070+7171+**Returns:** string - The option name
7272+7373+**Example:**
7474+```ts
7575+completion.addOption('dev', '--port', 'Port number', async () => {
7676+ return [
7777+ { value: '3000', description: 'Development port' },
7878+ { value: '8080', description: 'Production port' },
7979+ ];
8080+}, 'p');
8181+```
8282+8383+##### parse
8484+8585+Parses command line arguments and outputs completion suggestions to stdout.
8686+8787+**Parameters:**
8888+- `args` (string[]): Command line arguments
8989+9090+**Example:**
9191+```ts
9292+await completion.parse(['--port']);
9393+// Outputs completion suggestions to stdout
9494+```
9595+9696+### script
9797+9898+Generates shell completion scripts.
9999+100100+**Parameters:**
101101+- `shell` (string): Target shell ('zsh', 'bash', 'fish', 'powershell')
102102+- `name` (string): CLI tool name
103103+- `execPath` (string): Executable path for the CLI tool
104104+105105+**Example:**
106106+```ts
107107+import { script } from '@bombsh/tab';
108108+109109+script('zsh', 'my-cli', '/usr/bin/node /path/to/my-cli');
110110+```
111111+112112+## Types
113113+114114+### Handler
115115+116116+Function type for completion handlers.
117117+118118+```ts
119119+type Handler = (
120120+ previousArgs: string[],
121121+ toComplete: string,
122122+ endsWithSpace: boolean
123123+) => Item[] | Promise<Item[]>;
124124+```
125125+126126+**Parameters:**
127127+- `previousArgs` (string[]): Previously typed arguments
128128+- `toComplete` (string): The text being completed
129129+- `endsWithSpace` (boolean): Whether the input ends with a space
130130+131131+**Returns:** Item[] | Promise<Item[]>
132132+133133+**Example:**
134134+```ts
135135+const handler: Handler = async (previousArgs, toComplete, endsWithSpace) => {
136136+ // Check if user is typing 'prod' to suggest production
137137+ if (toComplete.startsWith('prod')) {
138138+ return [
139139+ { value: 'production', description: 'Production environment' },
140140+ ];
141141+ }
142142+143143+ return [
144144+ { value: 'development', description: 'Development environment' },
145145+ { value: 'staging', description: 'Staging environment' },
146146+ { value: 'production', description: 'Production environment' },
147147+ ];
148148+};
149149+```
150150+151151+### Item
152152+153153+Object representing a completion suggestion.
154154+155155+```ts
156156+type Item = {
157157+ value: string;
158158+ description: string;
159159+};
160160+```
161161+162162+**Properties:**
163163+- `value` (string): The completion value
164164+- `description` (string): Description of the completion
165165+166166+**Example:**
167167+```ts
168168+const item: Item = {
169169+ value: '3000',
170170+ description: 'Development port'
171171+};
172172+```
173173+174174+### `Positional`
175175+176176+Type for positional argument configuration.
177177+178178+```ts
179179+type Positional = {
180180+ required: boolean;
181181+ variadic: boolean;
182182+ completion: Handler;
183183+};
184184+```
185185+186186+## Constants
187187+188188+### ShellCompRequestCmd
189189+190190+The name of the hidden command used to request completion results.
191191+192192+```ts
193193+export const ShellCompRequestCmd: string = '__complete';
194194+```
195195+196196+### ShellCompNoDescRequestCmd
197197+198198+The name of the hidden command used to request completion results without descriptions.
199199+200200+```ts
201201+export const ShellCompNoDescRequestCmd: string = '__completeNoDesc';
202202+```
203203+204204+### ShellCompDirective
205205+206206+Bit map representing different behaviors the shell can be instructed to have.
207207+208208+```ts
209209+export const ShellCompDirective = {
210210+ ShellCompDirectiveError: 1 << 0,
211211+ ShellCompDirectiveNoSpace: 1 << 1,
212212+ ShellCompDirectiveNoFileComp: 1 << 2,
213213+ ShellCompDirectiveFilterFileExt: 1 << 3,
214214+ ShellCompDirectiveFilterDirs: 1 << 4,
215215+ ShellCompDirectiveKeepOrder: 1 << 5,
216216+ ShellCompDirectiveDefault: 0,
217217+};
218218+```
219219+220220+## Complete Example
221221+222222+Here's a complete example showing all the core API features:
223223+224224+```ts
225225+#!/usr/bin/env node
226226+import { Completion, script } from '@bombsh/tab';
227227+228228+const name = 'my-cli';
229229+const completion = new Completion();
230230+231231+// Add command completions with positional arguments
232232+completion.addCommand('dev', 'Start development server', [false], async (previousArgs, toComplete, endsWithSpace) => {
233233+ if (toComplete.startsWith('dev')) {
234234+ return [
235235+ { value: 'dev', description: 'Start in development mode' },
236236+ ];
237237+ }
238238+239239+ return [
240240+ { value: 'dev', description: 'Start in development mode' },
241241+ { value: 'prod', description: 'Start in production mode' },
242242+ ];
243243+});
244244+245245+// Add option completions
246246+completion.addOption('dev', '--port', 'Port number', async (previousArgs, toComplete, endsWithSpace) => {
247247+ if (toComplete.startsWith('30')) {
248248+ return [
249249+ { value: '3000', description: 'Development port' },
250250+ ];
251251+ }
252252+253253+ return [
254254+ { value: '3000', description: 'Development port' },
255255+ { value: '8080', description: 'Production port' },
256256+ ];
257257+}, 'p');
258258+259259+// Helper function to quote paths with spaces
260260+function quoteIfNeeded(path: string) {
261261+ return path.includes(' ') ? `'${path}'` : path;
262262+}
263263+264264+// Get the executable path for shell completion
265265+const execPath = process.execPath;
266266+const processArgs = process.argv.slice(1);
267267+const quotedExecPath = quoteIfNeeded(execPath);
268268+const quotedProcessArgs = processArgs.map(quoteIfNeeded);
269269+const quotedProcessExecArgs = process.execArgv.map(quoteIfNeeded);
270270+const x = `${quotedExecPath} ${quotedProcessExecArgs.join(' ')} ${quotedProcessArgs[0]}`;
271271+272272+// Handle completion requests
273273+if (process.argv[2] === '--') {
274274+ try {
275275+ await completion.parse(process.argv.slice(2));
276276+ } catch (error) {
277277+ console.error('Completion error:', error.message);
278278+ process.exit(1);
279279+ }
280280+} else {
281281+ const shell = process.argv[2];
282282+ if (['zsh', 'bash', 'fish', 'powershell'].includes(shell)) {
283283+ script(shell, name, x);
284284+ } else {
285285+ console.error(`Unsupported shell: ${shell}`);
286286+ process.exit(1);
287287+ }
288288+}
289289+```
290290+291291+## Next Steps
292292+293293+- Learn about [Framework Adapters](/docs/tab/guides/adapters/) for easier integration
294294+- Check out [Examples](/docs/tab/guides/examples/) for practical use cases
295295+- Explore [Best Practices](/docs/tab/guides/best-practices/) for effective implementations
+169
src/content/docs/tab/basics/getting-started.mdx
···11+---
22+title: Getting Started
33+description: Learn how to add shell autocompletions to your CLI with Tab
44+---
55+66+import { Tabs, TabItem } from '@astrojs/starlight/components';
77+88+Tab provides shell autocompletions for JavaScript CLI tools, making your CLI feel as polished as native tools like git. This guide will walk you through adding autocompletions to your CLI tool.
99+1010+## Installation
1111+1212+Install Tab using your preferred package manager:
1313+1414+<Tabs>
1515+ <TabItem label="npm" icon="seti:npm">
1616+ ```bash
1717+ npm install @bombsh/tab
1818+ ```
1919+ </TabItem>
2020+ <TabItem label="pnpm" icon="pnpm">
2121+ ```bash
2222+ pnpm add @bombsh/tab
2323+ ```
2424+ </TabItem>
2525+ <TabItem label="Yarn" icon="seti:yarn">
2626+ ```bash
2727+ yarn add @bombsh/tab
2828+ ```
2929+ </TabItem>
3030+</Tabs>
3131+3232+## Basic Usage
3333+3434+Here's a simple example of how to add autocompletions to your CLI:
3535+3636+```ts
3737+import { Completion, script } from '@bombsh/tab';
3838+3939+const name = 'my-cli';
4040+const completion = new Completion();
4141+4242+// Add command completions with positional arguments
4343+completion.addCommand(
4444+ 'start',
4545+ 'Start the application',
4646+ [false], // Required argument
4747+ async (previousArgs, toComplete, endsWithSpace) => {
4848+ return [
4949+ { value: 'dev', description: 'Start in development mode' },
5050+ { value: 'prod', description: 'Start in production mode' },
5151+ ];
5252+ }
5353+);
5454+5555+// Add option completions
5656+completion.addOption(
5757+ 'start',
5858+ '--port',
5959+ 'Specify the port number',
6060+ async (previousArgs, toComplete, endsWithSpace) => {
6161+ return [
6262+ { value: '3000', description: 'Development port' },
6363+ { value: '8080', description: 'Production port' },
6464+ ];
6565+ },
6666+ 'p' // Short flag alias
6767+);
6868+6969+// Helper function to quote paths with spaces
7070+function quoteIfNeeded(path: string) {
7171+ return path.includes(' ') ? `'${path}'` : path;
7272+}
7373+7474+// Get the executable path for shell completion
7575+const execPath = process.execPath;
7676+const processArgs = process.argv.slice(1);
7777+const quotedExecPath = quoteIfNeeded(execPath);
7878+const quotedProcessArgs = processArgs.map(quoteIfNeeded);
7979+const quotedProcessExecArgs = process.execArgv.map(quoteIfNeeded);
8080+const x = `${quotedExecPath} ${quotedProcessExecArgs.join(' ')} ${quotedProcessArgs[0]}`;
8181+8282+// Handle completion requests
8383+if (process.argv[2] === '--') {
8484+ // Autocompletion logic
8585+ await completion.parse(process.argv.slice(2));
8686+} else {
8787+ // Generate shell completion script
8888+ script(process.argv[2], name, x);
8989+}
9090+```
9191+9292+## Understanding Positional Arguments
9393+9494+The `args` parameter in `addCommand` is an array of booleans that indicates which arguments are required or optional:
9595+9696+```ts
9797+// Command: "my-cli start <entry>"
9898+completion.addCommand('start', 'Start the app', [false], handler);
9999+100100+// Command: "my-cli serve [entry]"
101101+completion.addCommand('serve', 'Serve the app', [true], handler);
102102+103103+// Command: "my-cli build <entry> [output]"
104104+completion.addCommand('build', 'Build the app', [false, true], handler);
105105+106106+// Command: "my-cli dev [...files]"
107107+completion.addCommand('dev', 'Dev mode', [true], handler); // Variadic argument
108108+```
109109+110110+## Shell Setup
111111+112112+After adding Tab to your CLI, users need to set up shell completion. Here are the recommended approaches for different shells:
113113+114114+### Zsh
115115+116116+```bash
117117+# Generate completion script
118118+my-cli complete zsh > ~/completion-for-my-cli.zsh
119119+120120+# Add to your .zshrc
121121+echo 'source ~/completion-for-my-cli.zsh' >> ~/.zshrc
122122+```
123123+124124+### Bash
125125+126126+```bash
127127+# Generate completion script
128128+my-cli complete bash > ~/.bash_completion.d/my-cli
129129+130130+# Add to your .bashrc
131131+echo 'source ~/.bash_completion.d/my-cli' >> ~/.bashrc
132132+```
133133+134134+### Fish
135135+136136+```bash
137137+# Generate completion script
138138+my-cli complete fish > ~/.config/fish/completions/my-cli.fish
139139+```
140140+141141+### PowerShell
142142+143143+```powershell
144144+# Generate completion script
145145+my-cli complete powershell > $PROFILE.CurrentUserAllHosts
146146+```
147147+148148+## How It Works
149149+150150+Tab works by adding a `complete` command to your CLI that generates shell-specific completion scripts. When users type `my-cli complete zsh`, Tab generates a completion script that the shell can source.
151151+152152+The completion script communicates with your CLI through the autocompletion server. When users press Tab, the shell calls your CLI with special arguments, and Tab returns the appropriate completions.
153153+154154+### Autocompletion Server
155155+156156+Your CLI becomes an autocompletion server when you add Tab. The server responds to completion requests with suggestions and ends its output with `:{Number}` to indicate the number of completions.
157157+158158+For example:
159159+```bash
160160+my-cli complete -- --po
161161+--port Specify the port number
162162+:0
163163+```
164164+165165+## Next Steps
166166+167167+- Learn about [Framework Adapters](/docs/tab/guides/adapters/) for easier integration with CAC, Citty, and Commander.js
168168+- Explore [Best Practices](/docs/tab/guides/best-practices/) for implementing effective autocompletions
169169+- Check out the [API Reference](/docs/tab/api/core/) for detailed documentation
+299
src/content/docs/tab/guides/adapters.mdx
···11+---
22+title: Adapters
33+description: Use Tab with popular CLI frameworks like CAC, Citty, and Commander.js
44+---
55+66+Tab provides adapters for popular CLI frameworks to make integration even easier. These adapters automatically extract commands and options from your CLI framework and allow you to add custom completion handlers.
77+88+## CAC Adapter
99+1010+The CAC adapter automatically detects commands and options from your CAC instance and provides completion handlers for customization.
1111+1212+```ts
1313+import cac from 'cac';
1414+import tab from '@bombsh/tab/cac';
1515+1616+const cli = cac('my-cli');
1717+1818+cli.command('dev', 'Start dev server').option('--port <port>', 'Specify port');
1919+cli.command('build', 'Build for production').option('--mode <mode>', 'Build mode');
2020+2121+const completion = await tab(cli);
2222+2323+// Get the dev command completion handler
2424+const devCommandCompletion = completion.commands.get('dev');
2525+2626+// Get and configure the port option completion handler
2727+const portOptionCompletion = devCommandCompletion.options.get('--port');
2828+portOptionCompletion.handler = async (
2929+ previousArgs,
3030+ toComplete,
3131+ endsWithSpace
3232+) => {
3333+ return [
3434+ { value: '3000', description: 'Development port' },
3535+ { value: '8080', description: 'Production port' },
3636+ ];
3737+};
3838+3939+// Configure build mode completions
4040+const buildCommandCompletion = completion.commands.get('build');
4141+const modeOptionCompletion = buildCommandCompletion.options.get('--mode');
4242+modeOptionCompletion.handler = async () => {
4343+ return [
4444+ { value: 'development', description: 'Development build' },
4545+ { value: 'production', description: 'Production build' },
4646+ ];
4747+};
4848+4949+cli.parse();
5050+```
5151+5252+### How the CAC Adapter Works
5353+5454+The CAC adapter:
5555+5656+1. **Extracts Commands**: Automatically detects all commands defined with `cli.command()`
5757+2. **Extracts Options**: Identifies options defined with `.option()` for each command
5858+3. **Provides Handlers**: Gives you access to completion handlers for each command and option
5959+4. **Maintains Structure**: Preserves the command hierarchy and option relationships
6060+6161+## Citty Adapter
6262+6363+The Citty adapter works with Citty's command definitions and provides a similar interface for adding completions.
6464+6565+```ts
6666+import { defineCommand, createMain } from 'citty';
6767+import tab from '@bombsh/tab/citty';
6868+6969+const main = defineCommand({
7070+ meta: {
7171+ name: 'my-cli',
7272+ description: 'My CLI tool',
7373+ },
7474+});
7575+7676+const devCommand = defineCommand({
7777+ meta: {
7878+ name: 'dev',
7979+ description: 'Start dev server',
8080+ },
8181+ args: {
8282+ port: { type: 'string', description: 'Specify port' },
8383+ host: { type: 'string', description: 'Specify host' },
8484+ },
8585+});
8686+8787+const buildCommand = defineCommand({
8888+ meta: {
8989+ name: 'build',
9090+ description: 'Build for production',
9191+ },
9292+ args: {
9393+ mode: { type: 'string', description: 'Build mode' },
9494+ },
9595+});
9696+9797+main.subCommands = {
9898+ dev: devCommand,
9999+ build: buildCommand,
100100+};
101101+102102+const completion = await tab(main);
103103+104104+// Configure completions
105105+const devCommandCompletion = completion.commands.get('dev');
106106+if (devCommandCompletion) {
107107+ const portOptionCompletion = devCommandCompletion.options.get('--port');
108108+ if (portOptionCompletion) {
109109+ portOptionCompletion.handler = async () => {
110110+ return [
111111+ { value: '3000', description: 'Development port' },
112112+ { value: '8080', description: 'Production port' },
113113+ ];
114114+ };
115115+ }
116116+}
117117+118118+const cli = createMain(main);
119119+cli();
120120+```
121121+122122+### How the Citty Adapter Works
123123+124124+The Citty adapter:
125125+126126+1. **Processes Command Definitions**: Analyzes the command structure defined with `defineCommand()`
127127+2. **Extracts Arguments**: Identifies arguments defined in the `args` object
128128+3. **Handles Subcommands**: Recursively processes nested subcommands
129129+4. **Provides Type Safety**: Leverages Citty's type system for better completion accuracy
130130+131131+## Commander Adapter
132132+133133+The Commander adapter works with Commander.js and provides completion handlers for its command structure.
134134+135135+```ts
136136+import { Command } from 'commander';
137137+import tab from '@bombsh/tab/commander';
138138+139139+const program = new Command();
140140+141141+program
142142+ .name('my-cli')
143143+ .description('My CLI tool');
144144+145145+program
146146+ .command('dev')
147147+ .description('Start development server')
148148+ .option('-p, --port <port>', 'Specify port')
149149+ .option('-h, --host <host>', 'Specify host');
150150+151151+program
152152+ .command('build')
153153+ .description('Build for production')
154154+ .option('-m, --mode <mode>', 'Build mode');
155155+156156+const completion = tab(program);
157157+158158+// Configure completions
159159+const devCommandCompletion = completion.commands.get('dev');
160160+if (devCommandCompletion) {
161161+ const portOptionCompletion = devCommandCompletion.options.get('--port');
162162+ if (portOptionCompletion) {
163163+ portOptionCompletion.handler = async () => {
164164+ return [
165165+ { value: '3000', description: 'Development port' },
166166+ { value: '8080', description: 'Production port' },
167167+ ];
168168+ };
169169+ }
170170+}
171171+172172+program.parse();
173173+```
174174+175175+### How the Commander Adapter Works
176176+177177+The Commander adapter:
178178+179179+1. **Processes Commands**: Analyzes the command structure defined with `program.command()`
180180+2. **Extracts Options**: Identifies options defined with `.option()`
181181+3. **Handles Aliases**: Automatically processes short and long option aliases
182182+4. **Maintains Hierarchy**: Preserves the command hierarchy and option relationships
183183+184184+## Configuration
185185+186186+All adapters support a configuration object to customize completion behavior:
187187+188188+```ts
189189+import { CompletionConfig } from '@bombsh/tab';
190190+191191+const config: CompletionConfig = {
192192+ handler: async (previousArgs, toComplete, endsWithSpace) => {
193193+ // Default handler for all commands
194194+ return [];
195195+ },
196196+ options: {
197197+ port: {
198198+ handler: async () => {
199199+ return [
200200+ { value: '3000', description: 'Development port' },
201201+ { value: '8080', description: 'Production port' },
202202+ ];
203203+ },
204204+ },
205205+ },
206206+ subCommands: {
207207+ dev: {
208208+ handler: async () => {
209209+ return [
210210+ { value: 'dev', description: 'Development mode' },
211211+ ];
212212+ },
213213+ options: {
214214+ port: {
215215+ handler: async () => {
216216+ return [
217217+ { value: '3000', description: 'Dev port' },
218218+ ];
219219+ },
220220+ },
221221+ },
222222+ },
223223+ },
224224+};
225225+226226+// Use with any adapter
227227+const completion = await tab(cli, config);
228228+```
229229+230230+## Best Practices
231231+232232+### 1. Error Handling
233233+234234+Always handle errors gracefully in your completion handlers:
235235+236236+```ts
237237+portOptionCompletion.handler = async () => {
238238+ try {
239239+ // Expensive operation
240240+ const results = await getPorts();
241241+ return results.map(port => ({
242242+ value: port.toString(),
243243+ description: `Port ${port}`
244244+ }));
245245+ } catch (error) {
246246+ // Return empty array instead of throwing
247247+ console.error('Error in completion handler:', error);
248248+ return [];
249249+ }
250250+};
251251+```
252252+253253+### 2. Context-Aware Completions
254254+255255+Make your completions responsive to what the user is typing:
256256+257257+```ts
258258+portOptionCompletion.handler = async (previousArgs, toComplete, endsWithSpace) => {
259259+ if (toComplete.startsWith('30')) {
260260+ return [
261261+ { value: '3000', description: 'Development port' },
262262+ ];
263263+ }
264264+265265+ return [
266266+ { value: '3000', description: 'Development port' },
267267+ { value: '8080', description: 'Production port' },
268268+ { value: '9000', description: 'Alternative port' },
269269+ ];
270270+};
271271+```
272272+273273+### 3. Performance Optimization
274274+275275+Cache expensive operations and limit results:
276276+277277+```ts
278278+let cachedPorts: Item[] | null = null;
279279+280280+portOptionCompletion.handler = async () => {
281281+ if (cachedPorts) {
282282+ return cachedPorts;
283283+ }
284284+285285+ const ports = await getAvailablePorts();
286286+ cachedPorts = ports.slice(0, 20).map(port => ({
287287+ value: port.toString(),
288288+ description: `Port ${port}`
289289+ }));
290290+291291+ return cachedPorts;
292292+};
293293+```
294294+295295+## Next Steps
296296+297297+- Learn about [Best Practices](/docs/tab/guides/best-practices/) for effective autocompletions
298298+- Check out [Examples](/docs/tab/guides/examples/) for practical use cases
299299+- Explore the [API Reference](/docs/tab/api/core/) for advanced usage
+312
src/content/docs/tab/guides/best-practices.mdx
···11+---
22+title: Best Practices
33+description: Learn best practices for implementing effective autocompletions with Tab
44+---
55+66+This guide covers best practices for implementing autocompletions that provide a great user experience and integrate seamlessly with your CLI tool.
77+88+## Completion Handler Design
99+1010+### Keep Handlers Fast
1111+1212+Completion handlers should return results quickly since they're called frequently as users type:
1313+1414+```ts
1515+// ✅ Good: Fast, synchronous completion
1616+completion.addOption('dev', '--port', 'Port number', () => {
1717+ return [
1818+ { value: '3000', description: 'Development port' },
1919+ { value: '8080', description: 'Production port' },
2020+ ];
2121+});
2222+2323+// ❌ Avoid: Slow, async operations in completion handlers
2424+completion.addOption('dev', '--file', 'File path', async () => {
2525+ const files = await fs.readdir('.'); // This can be slow
2626+ return files.map(f => ({ value: f, description: 'File' }));
2727+});
2828+```
2929+3030+### Use Context Appropriately
3131+3232+Leverage the completion context to provide relevant suggestions:
3333+3434+```ts
3535+completion.addOption('deploy', '--env', 'Environment', async (previousArgs, toComplete, endsWithSpace) => {
3636+ // Check if user is completing an environment name
3737+ if (toComplete.startsWith('prod')) {
3838+ return [
3939+ { value: 'production', description: 'Production environment' },
4040+ ];
4141+ }
4242+4343+ return [
4444+ { value: 'development', description: 'Development environment' },
4545+ { value: 'staging', description: 'Staging environment' },
4646+ { value: 'production', description: 'Production environment' },
4747+ ];
4848+});
4949+```
5050+5151+### Provide Meaningful Descriptions
5252+5353+Always include descriptions to help users understand what each completion means:
5454+5555+```ts
5656+// ✅ Good: Clear descriptions
5757+completion.addCommand('deploy', 'Deploy application', () => {
5858+ return [
5959+ { value: 'dev', description: 'Deploy to development environment' },
6060+ { value: 'staging', description: 'Deploy to staging environment' },
6161+ { value: 'prod', description: 'Deploy to production environment' },
6262+ ];
6363+});
6464+6565+// ❌ Avoid: No descriptions
6666+completion.addCommand('deploy', 'Deploy application', () => {
6767+ return [
6868+ { value: 'dev' },
6969+ { value: 'staging' },
7070+ { value: 'prod' },
7171+ ];
7272+});
7373+```
7474+7575+## Shell Integration
7676+7777+### Handle Spaces Correctly
7878+7979+Pay attention to the `endsWithSpace` parameter to provide appropriate completions:
8080+8181+```ts
8282+completion.addOption('dev', '--config', 'Config file', async (previousArgs, toComplete, endsWithSpace) => {
8383+ if (endsWithSpace) {
8484+ // User typed a space, suggest files
8585+ return [
8686+ { value: 'config.json', description: 'JSON config file' },
8787+ { value: 'config.yaml', description: 'YAML config file' },
8888+ ];
8989+ } else {
9090+ // User is typing, filter based on input
9191+ const suggestions = [
9292+ { value: 'config.json', description: 'JSON config file' },
9393+ { value: 'config.yaml', description: 'YAML config file' },
9494+ ];
9595+9696+ return suggestions.filter(s => s.value.startsWith(toComplete));
9797+ }
9898+});
9999+```
100100+101101+### Support Multiple Shells
102102+103103+Test your completions across different shells to ensure compatibility:
104104+105105+```ts
106106+// Generate completion scripts for all supported shells
107107+if (process.argv[2] === '--') {
108108+ await completion.parse(process.argv.slice(2), 'start');
109109+} else {
110110+ const shell = process.argv[2];
111111+ if (['zsh', 'bash', 'fish', 'powershell'].includes(shell)) {
112112+ script(shell, 'my-cli', execPath);
113113+ } else {
114114+ console.error(`Unsupported shell: ${shell}`);
115115+ process.exit(1);
116116+ }
117117+}
118118+```
119119+120120+## User Experience
121121+122122+### Progressive Disclosure
123123+124124+Show relevant completions based on what the user has already typed:
125125+126126+```ts
127127+completion.addCommand('deploy', 'Deploy application', async (previousArgs, toComplete, endsWithSpace) => {
128128+ // If user typed 'prod', only show production-related options
129129+ if (toComplete.startsWith('prod')) {
130130+ return [
131131+ { value: 'production', description: 'Production environment' },
132132+ ];
133133+ }
134134+135135+ // Show all options otherwise
136136+ return [
137137+ { value: 'development', description: 'Development environment' },
138138+ { value: 'staging', description: 'Staging environment' },
139139+ { value: 'production', description: 'Production environment' },
140140+ ];
141141+});
142142+```
143143+144144+### Consistent Naming
145145+146146+Use consistent naming patterns across your CLI:
147147+148148+```ts
149149+// ✅ Good: Consistent naming
150150+completion.addCommand('deploy', 'Deploy application', () => {
151151+ return [
152152+ { value: 'dev', description: 'Deploy to development' },
153153+ { value: 'staging', description: 'Deploy to staging' },
154154+ { value: 'prod', description: 'Deploy to production' },
155155+ ];
156156+});
157157+158158+completion.addCommand('build', 'Build application', () => {
159159+ return [
160160+ { value: 'dev', description: 'Development build' },
161161+ { value: 'staging', description: 'Staging build' },
162162+ { value: 'prod', description: 'Production build' },
163163+ ];
164164+});
165165+```
166166+167167+### Error Handling
168168+169169+Handle errors gracefully in completion handlers:
170170+171171+```ts
172172+completion.addOption('deploy', '--config', 'Config file', async () => {
173173+ try {
174174+ const files = await fs.readdir('.');
175175+ return files
176176+ .filter(f => f.endsWith('.json') || f.endsWith('.yaml'))
177177+ .map(f => ({ value: f, description: `Config file: ${f}` }));
178178+ } catch (error) {
179179+ // Return empty array instead of throwing
180180+ console.error('Error reading config files:', error);
181181+ return [];
182182+ }
183183+});
184184+```
185185+186186+## Performance Considerations
187187+188188+### Cache Expensive Operations
189189+190190+Cache results for expensive operations that don't change frequently:
191191+192192+```ts
193193+let cachedPorts: Array<{ value: string; description: string }> | null = null;
194194+195195+completion.addOption('dev', '--port', 'Port number', async () => {
196196+ if (cachedPorts) {
197197+ return cachedPorts;
198198+ }
199199+200200+ // Expensive operation (e.g., reading from config)
201201+ const ports = await getAvailablePorts();
202202+ cachedPorts = ports.map(p => ({
203203+ value: p.toString(),
204204+ description: `Port ${p}`
205205+ }));
206206+207207+ return cachedPorts;
208208+});
209209+```
210210+211211+### Limit Result Sets
212212+213213+Don't overwhelm users with too many completions:
214214+215215+```ts
216216+completion.addOption('search', '--file', 'File pattern', async (previousArgs, toComplete, endsWithSpace) => {
217217+ const files = await getMatchingFiles(toComplete);
218218+219219+ // Limit to 20 results to avoid overwhelming the user
220220+ return files.slice(0, 20).map(f => ({
221221+ value: f,
222222+ description: `File: ${f}`
223223+ }));
224224+});
225225+```
226226+227227+## Testing
228228+229229+### Test Completion Handlers
230230+231231+Write tests for your completion handlers to ensure they work correctly:
232232+233233+```ts
234234+import { Completion } from '@bombsh/tab';
235235+236236+describe('CLI Completions', () => {
237237+ let completion: Completion;
238238+239239+ beforeEach(() => {
240240+ completion = new Completion();
241241+ // Setup your completion handlers
242242+ });
243243+244244+ test('should suggest ports for --port option', async () => {
245245+ const result = await completion.parse(['--port'], 'dev');
246246+ expect(result).toContainEqual({ value: '3000', description: 'Development port' });
247247+ });
248248+249249+ test('should filter suggestions based on input', async () => {
250250+ const result = await completion.parse(['--port', '30'], 'dev');
251251+ expect(result).toContainEqual({ value: '3000', description: 'Development port' });
252252+ expect(result).not.toContainEqual({ value: '8080', description: 'Production port' });
253253+ });
254254+});
255255+```
256256+257257+### Test Shell Integration
258258+259259+Test that your completion scripts work correctly in different shells:
260260+261261+```bash
262262+# Test zsh completion
263263+source <(my-cli complete zsh)
264264+my-cli dev --po<TAB> # Should suggest --port
265265+266266+# Test bash completion
267267+source <(my-cli complete bash)
268268+my-cli dev --po<TAB> # Should suggest --port
269269+```
270270+271271+## Common Patterns
272272+273273+### File Completions
274274+275275+```ts
276276+completion.addOption('build', '--config', 'Config file', async (previousArgs, toComplete, endsWithSpace) => {
277277+ const files = await fs.readdir('.');
278278+ return files
279279+ .filter(f => f.endsWith('.json') || f.endsWith('.yaml'))
280280+ .map(f => ({ value: f, description: `Config: ${f}` }));
281281+});
282282+```
283283+284284+### Environment Completions
285285+286286+```ts
287287+completion.addOption('deploy', '--env', 'Environment', () => {
288288+ return [
289289+ { value: 'development', description: 'Development environment' },
290290+ { value: 'staging', description: 'Staging environment' },
291291+ { value: 'production', description: 'Production environment' },
292292+ ];
293293+});
294294+```
295295+296296+### Command Completions
297297+298298+```ts
299299+completion.addCommand('deploy', 'Deploy application', () => {
300300+ return [
301301+ { value: 'dev', description: 'Deploy to development' },
302302+ { value: 'staging', description: 'Deploy to staging' },
303303+ { value: 'prod', description: 'Deploy to production' },
304304+ ];
305305+});
306306+```
307307+308308+## Next Steps
309309+310310+- Check out [Examples](/docs/tab/guides/examples/) for more practical use cases
311311+- Explore the [API Reference](/docs/tab/api/core/) for detailed documentation
312312+- Learn about [Framework Adapters](/docs/tab/guides/adapters/) for easier integration
···11+---
22+title: Tab
33+description: Shell autocompletions for JavaScript CLI tools
44+---
55+66+import { CardGrid, Card } from '@astrojs/starlight/components';
77+88+Tab is a powerful tool that brings shell autocompletions to JavaScript CLI tools. Inspired by tools like git and their excellent autocompletion experience, Tab makes the same functionality available for any JavaScript CLI project.
99+1010+## Features
1111+1212+<CardGrid>
1313+1414+ <Card title="Shell Integration" icon="seti:shell">
1515+ Seamless integration with zsh, bash, fish, and PowerShell. Get native autocompletion experience for your CLI tools.
1616+ </Card>
1717+1818+ <Card title="Framework Adapters" icon="seti:smarty">
1919+ Built-in adapters for popular CLI frameworks including CAC, Citty, and Commander.js for easy integration.
2020+ </Card>
2121+2222+ <Card title="Type-Safe" icon="rocket">
2323+ Full TypeScript support with type-safe completion handlers and autocomplete suggestions.
2424+ </Card>
2525+2626+ <Card title="Custom Completions" icon="approve-check">
2727+ Define custom completion logic for commands and options with dynamic suggestions based on context.
2828+ </Card>
2929+3030+ <Card title="Universal Support" icon="information">
3131+ Works with any JavaScript CLI tool, regardless of the underlying framework or architecture.
3232+ </Card>
3333+3434+ <Card title="Easy Setup" icon="sun">
3535+ Simple installation and configuration with minimal code changes to add autocompletion to your CLI.
3636+ </Card>
3737+3838+</CardGrid>
3939+4040+## Quick Start
4141+4242+Add shell autocompletions to your CLI in just a few lines:
4343+4444+```ts
4545+import { Completion, script } from '@bombsh/tab';
4646+4747+const completion = new Completion();
4848+4949+completion.addCommand('dev', 'Start development server', [false], async () => {
5050+ return [
5151+ { value: 'dev', description: 'Start in development mode' },
5252+ { value: 'prod', description: 'Start in production mode' },
5353+ ];
5454+});
5555+5656+completion.addOption('dev', '--port', 'Port number', async () => {
5757+ return [
5858+ { value: '3000', description: 'Development port' },
5959+ { value: '8080', description: 'Production port' },
6060+ ];
6161+}, 'p');
6262+6363+// Generate shell completion script
6464+if (process.argv[2] === '--') {
6565+ await completion.parse(process.argv.slice(2));
6666+} else {
6767+ script(process.argv[2], 'my-cli', process.execPath);
6868+}
6969+```
7070+7171+## What's Next?
7272+7373+- [Getting Started](/docs/tab/basics/getting-started/) - Learn how to add autocompletions to your CLI
7474+- [Framework Adapters](/docs/tab/guides/adapters/) - Use Tab with CAC, Citty, and Commander.js
7575+- [Best Practices](/docs/tab/guides/best-practices/) - Learn best practices for implementing autocompletions
7676+- [API Reference](/docs/tab/api/core/) - Detailed API documentation