@@ -57,20 +57,22 @@ export class Option {
5757 command : Command ;
5858 handler ?: OptionHandler ;
5959 alias ?: string ;
60- // TODO: handle boolean options
60+ isBoolean ?: boolean ;
6161
6262 constructor (
6363 command : Command ,
6464 value : string ,
6565 description : string ,
6666 handler ?: OptionHandler ,
67- alias ?: string
67+ alias ?: string ,
68+ isBoolean ?: boolean
6869 ) {
6970 this . command = command ;
7071 this . value = value ;
7172 this . description = description ;
7273 this . handler = handler ;
7374 this . alias = alias ;
75+ this . isBoolean = isBoolean ;
7476 }
7577}
7678
@@ -90,9 +92,17 @@ export class Command {
9092 value : string ,
9193 description : string ,
9294 handler ?: OptionHandler ,
93- alias ?: string
95+ alias ?: string ,
96+ isBoolean ?: boolean
9497 ) {
95- const option = new Option ( this , value , description , handler , alias ) ;
98+ const option = new Option (
99+ this ,
100+ value ,
101+ description ,
102+ handler ,
103+ alias ,
104+ isBoolean
105+ ) ;
96106 this . options . set ( value , option ) ;
97107 return this ;
98108 }
@@ -135,7 +145,28 @@ export class RootCommand extends Command {
135145
136146 if ( arg . startsWith ( '-' ) ) {
137147 i ++ ; // Skip the option
138- if ( i < args . length && ! args [ i ] . startsWith ( '-' ) ) {
148+
149+ // Check if this option expects a value (not boolean)
150+ // We need to check across all commands since we don't know which command context we're in yet
151+ let isBoolean = false ;
152+
153+ // Check root command options
154+ const rootOption = this . findOption ( this , arg ) ;
155+ if ( rootOption ) {
156+ isBoolean = rootOption . isBoolean ?? false ;
157+ } else {
158+ // Check all subcommand options
159+ for ( const [ , command ] of this . commands ) {
160+ const option = this . findOption ( command , arg ) ;
161+ if ( option ) {
162+ isBoolean = option . isBoolean ?? false ;
163+ break ;
164+ }
165+ }
166+ }
167+
168+ // Only skip the next argument if this is not a boolean option and the next arg doesn't start with -
169+ if ( ! isBoolean && i < args . length && ! args [ i ] . startsWith ( '-' ) ) {
139170 i ++ ; // Skip the option value
140171 }
141172 } else {
@@ -176,7 +207,33 @@ export class RootCommand extends Command {
176207 toComplete : string ,
177208 endsWithSpace : boolean
178209 ) : boolean {
179- return lastPrevArg ?. startsWith ( '-' ) || toComplete . startsWith ( '-' ) ;
210+ // Always complete if the current token starts with a dash
211+ if ( toComplete . startsWith ( '-' ) ) {
212+ return true ;
213+ }
214+
215+ // If the previous argument was an option, check if it expects a value
216+ if ( lastPrevArg ?. startsWith ( '-' ) ) {
217+ // Find the option to check if it's boolean
218+ let option = this . findOption ( this , lastPrevArg ) ;
219+ if ( ! option ) {
220+ // Check all subcommand options
221+ for ( const [ , command ] of this . commands ) {
222+ option = this . findOption ( command , lastPrevArg ) ;
223+ if ( option ) break ;
224+ }
225+ }
226+
227+ // If it's a boolean option, don't try to complete its value
228+ if ( option && option . isBoolean ) {
229+ return false ;
230+ }
231+
232+ // Non-boolean options expect values
233+ return true ;
234+ }
235+
236+ return false ;
180237 }
181238
182239 // Determine if we should complete commands
@@ -276,7 +333,7 @@ export class RootCommand extends Command {
276333
277334 // Handle command completion
278335 private handleCommandCompletion ( previousArgs : string [ ] , toComplete : string ) {
279- const commandParts = previousArgs . filter ( Boolean ) ;
336+ const commandParts = this . stripOptions ( previousArgs ) ;
280337
281338 for ( const [ k , command ] of this . commands ) {
282339 if ( k === '' ) continue ;
@@ -373,7 +430,9 @@ export class RootCommand extends Command {
373430 const previousArgs = args . slice ( 0 , - 1 ) ;
374431
375432 if ( endsWithSpace ) {
376- previousArgs . push ( toComplete ) ;
433+ if ( toComplete !== '' ) {
434+ previousArgs . push ( toComplete ) ;
435+ }
377436 toComplete = '' ;
378437 }
379438
@@ -390,11 +449,31 @@ export class RootCommand extends Command {
390449 lastPrevArg
391450 ) ;
392451 } else {
452+ // Check if we just finished a boolean option with no value expected
453+ // In this case, don't complete anything
454+ if ( lastPrevArg ?. startsWith ( '-' ) && toComplete === '' && endsWithSpace ) {
455+ let option = this . findOption ( this , lastPrevArg ) ;
456+ if ( ! option ) {
457+ // Check all subcommand options
458+ for ( const [ , command ] of this . commands ) {
459+ option = this . findOption ( command , lastPrevArg ) ;
460+ if ( option ) break ;
461+ }
462+ }
463+
464+ // If it's a boolean option followed by empty space, don't complete anything
465+ if ( option && option . isBoolean ) {
466+ // Don't add any completions, just output the directive
467+ this . complete ( toComplete ) ;
468+ return ;
469+ }
470+ }
471+
393472 // 2. Handle command/subcommand completion
394473 if ( this . shouldCompleteCommands ( toComplete , endsWithSpace ) ) {
395474 this . handleCommandCompletion ( previousArgs , toComplete ) ;
396475 }
397- // 3. Handle positional arguments
476+ // 3. Handle positional arguments - always check for root command arguments
398477 if ( matchedCommand && matchedCommand . arguments . size > 0 ) {
399478 this . handlePositionalCompletion (
400479 matchedCommand ,
0 commit comments