Bug Description
Positional argument completion doesn't work when flags precede the argument. For example, with a command like:
mycli session logs -f <TAB>
No argument completions are shown, even though <TAB> without the -f flag works correctly.
Root Causes
There are three interacting bugs:
1. handlePositionalCompletion doesn't strip options from args
handlePositionalCompletion(command, args) uses args.length to calculate the positional argument index, but args still contains flags (e.g., ['session', 'logs', '-f']). This inflates the index past the registered arguments array.
Fix: Call this.stripOptions(t) at the top of handlePositionalCompletion before calculating the index.
2. parse() early-returns after boolean flags
In parse(), when the previous arg is a boolean flag and the current completion target is empty (TAB pressed), there's an early return that skips both handleCommandCompletion and handlePositionalCompletion:
if(e && e.isBoolean) {
this.complete(n);
return; // <-- skips positional completion
}
Fix: Remove the early return to let control fall through to positional completion.
3. Commander adapter marks all options as isBoolean=true
The commander adapter's option registration logic marks every option as boolean, even value-taking ones like --output <format>:
// In Command.option():
typeof n == 'string' ? (i=void 0, a=n, o=true) : // alias string → isBoolean=true always
This means stripOptions doesn't skip the value argument for options like --output json, causing the positional index to be wrong.
Fix: In the commander adapter, detect < or [ in the flag syntax string and set isBoolean=false for value-taking options.
Reproduction
import t from '@bomb.sh/tab';
import createTabFromCommander from '@bomb.sh/tab/commander';
import { Command } from 'commander';
const program = new Command('mycli');
const session = program.command('session').description('Manage');
const logs = session.command('logs').description('Print logs').argument('<id>');
logs.option('-f, --follow', 'Follow output');
createTabFromCommander(program);
const logsTab = t.commands.get('session logs');
logsTab.argument('id', (complete) => {
complete('test-session', 'A session');
});
// Works:
t.parse(['session', 'logs', '']);
// Output: test-session
// Broken (no output):
t.completions = [];
t.parse(['session', 'logs', '-f', '']);
// Output: (nothing)
Environment
@bomb.sh/tab: 0.0.14
- Runtime: Bun 1.3.10
- Commander: 13.1.0
Bug Description
Positional argument completion doesn't work when flags precede the argument. For example, with a command like:
No argument completions are shown, even though
<TAB>without the-fflag works correctly.Root Causes
There are three interacting bugs:
1.
handlePositionalCompletiondoesn't strip options from argshandlePositionalCompletion(command, args)usesargs.lengthto calculate the positional argument index, butargsstill contains flags (e.g.,['session', 'logs', '-f']). This inflates the index past the registered arguments array.Fix: Call
this.stripOptions(t)at the top ofhandlePositionalCompletionbefore calculating the index.2.
parse()early-returns after boolean flagsIn
parse(), when the previous arg is a boolean flag and the current completion target is empty (TAB pressed), there's an earlyreturnthat skips bothhandleCommandCompletionandhandlePositionalCompletion:Fix: Remove the early return to let control fall through to positional completion.
3. Commander adapter marks all options as
isBoolean=trueThe commander adapter's option registration logic marks every option as boolean, even value-taking ones like
--output <format>:This means
stripOptionsdoesn't skip the value argument for options like--output json, causing the positional index to be wrong.Fix: In the commander adapter, detect
<or[in the flag syntax string and setisBoolean=falsefor value-taking options.Reproduction
Environment
@bomb.sh/tab: 0.0.14