@optique/core 1.0.0-dev.1810 → 1.0.0-dev.1818

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -51,6 +51,7 @@ function mergeChildExec(parent, child) {
51
51
  trace: child.trace ?? parent.trace,
52
52
  dependencyRuntime: child.dependencyRuntime ?? parent.dependencyRuntime,
53
53
  dependencyRegistry: child.dependencyRegistry ?? parent.dependencyRegistry,
54
+ commandPath: child.commandPath ?? parent.commandPath,
54
55
  preCompletedByParser: child.preCompletedByParser ?? parent.preCompletedByParser,
55
56
  excludedSourceFields: child.excludedSourceFields ?? parent.excludedSourceFields
56
57
  };
@@ -51,6 +51,7 @@ function mergeChildExec(parent, child) {
51
51
  trace: child.trace ?? parent.trace,
52
52
  dependencyRuntime: child.dependencyRuntime ?? parent.dependencyRuntime,
53
53
  dependencyRegistry: child.dependencyRegistry ?? parent.dependencyRegistry,
54
+ commandPath: child.commandPath ?? parent.commandPath,
54
55
  preCompletedByParser: child.preCompletedByParser ?? parent.preCompletedByParser,
55
56
  excludedSourceFields: child.excludedSourceFields ?? parent.excludedSourceFields
56
57
  };
package/dist/facade.cjs CHANGED
@@ -95,11 +95,147 @@ function withPreparedParsedForContext(context, preparedParsed, run) {
95
95
  function isBufferUnchanged(previous, current) {
96
96
  return current.length > 0 && current.length === previous.length && current.every((item, i) => item === previous[i]);
97
97
  }
98
+ function getFailureProgress(args, buffer, consumedInStep) {
99
+ return {
100
+ remainingArgs: buffer.slice(consumedInStep),
101
+ consumedCount: args.length - buffer.length + consumedInStep
102
+ };
103
+ }
104
+ function createParseExec(parser) {
105
+ return {
106
+ usage: parser.usage,
107
+ phase: "parse",
108
+ path: [],
109
+ commandPath: [],
110
+ trace: require_input_trace.createInputTrace()
111
+ };
112
+ }
113
+ function getCommandPath(exec) {
114
+ return exec?.commandPath ?? [];
115
+ }
116
+ function createCompleteExec(exec, context) {
117
+ const runtime = require_dependency_runtime.createDependencyRuntimeContext();
118
+ return {
119
+ ...exec,
120
+ phase: "complete",
121
+ dependencyRuntime: runtime,
122
+ dependencyRegistry: runtime.registry,
123
+ commandPath: getCommandPath(context.exec) ?? exec.commandPath,
124
+ trace: context.exec?.trace ?? context.trace ?? exec.trace
125
+ };
126
+ }
127
+ function attemptParseSync(parser, args, mode = "complete") {
128
+ const shouldUnwrapAnnotatedValue = require_annotations.isInjectedAnnotationWrapper(parser.initialState);
129
+ const exec = createParseExec(parser);
130
+ let context = require_parser.createParserContext({
131
+ buffer: args,
132
+ state: parser.initialState,
133
+ optionsTerminated: false
134
+ }, exec);
135
+ do {
136
+ const result = parser.parse(context);
137
+ if (!result.success) {
138
+ const progress = getFailureProgress(args, context.buffer, result.consumed);
139
+ return {
140
+ kind: "failure",
141
+ error: result.error,
142
+ remainingArgs: progress.remainingArgs,
143
+ consumedCount: progress.consumedCount,
144
+ optionsTerminated: context.optionsTerminated,
145
+ commandPath: getCommandPath(context.exec)
146
+ };
147
+ }
148
+ const previousBuffer = context.buffer;
149
+ context = result.next;
150
+ if (isBufferUnchanged(previousBuffer, context.buffer)) {
151
+ const progress = getFailureProgress(args, previousBuffer, result.consumed.length);
152
+ return {
153
+ kind: "failure",
154
+ error: require_message.message`Unexpected option or argument: ${context.buffer[0]}.`,
155
+ remainingArgs: progress.remainingArgs,
156
+ consumedCount: progress.consumedCount,
157
+ optionsTerminated: context.optionsTerminated,
158
+ commandPath: getCommandPath(context.exec)
159
+ };
160
+ }
161
+ } while (context.buffer.length > 0);
162
+ if (mode === "parse-only") return {
163
+ kind: "success",
164
+ value: void 0
165
+ };
166
+ const endResult = parser.complete(context.state, createCompleteExec(exec, context));
167
+ if (!endResult.success) return {
168
+ kind: "failure",
169
+ error: endResult.error,
170
+ remainingArgs: [],
171
+ consumedCount: args.length,
172
+ optionsTerminated: context.optionsTerminated,
173
+ commandPath: getCommandPath(context.exec)
174
+ };
175
+ return {
176
+ kind: "success",
177
+ value: shouldUnwrapAnnotatedValue ? require_annotations.unwrapInjectedAnnotationWrapper(endResult.value) : endResult.value
178
+ };
179
+ }
180
+ async function attemptParseAsync(parser, args, mode = "complete") {
181
+ const shouldUnwrapAnnotatedValue = require_annotations.isInjectedAnnotationWrapper(parser.initialState);
182
+ const exec = createParseExec(parser);
183
+ let context = require_parser.createParserContext({
184
+ buffer: args,
185
+ state: parser.initialState,
186
+ optionsTerminated: false
187
+ }, exec);
188
+ do {
189
+ const result = await parser.parse(context);
190
+ if (!result.success) {
191
+ const progress = getFailureProgress(args, context.buffer, result.consumed);
192
+ return {
193
+ kind: "failure",
194
+ error: result.error,
195
+ remainingArgs: progress.remainingArgs,
196
+ consumedCount: progress.consumedCount,
197
+ optionsTerminated: context.optionsTerminated,
198
+ commandPath: getCommandPath(context.exec)
199
+ };
200
+ }
201
+ const previousBuffer = context.buffer;
202
+ context = result.next;
203
+ if (isBufferUnchanged(previousBuffer, context.buffer)) {
204
+ const progress = getFailureProgress(args, previousBuffer, result.consumed.length);
205
+ return {
206
+ kind: "failure",
207
+ error: require_message.message`Unexpected option or argument: ${context.buffer[0]}.`,
208
+ remainingArgs: progress.remainingArgs,
209
+ consumedCount: progress.consumedCount,
210
+ optionsTerminated: context.optionsTerminated,
211
+ commandPath: getCommandPath(context.exec)
212
+ };
213
+ }
214
+ } while (context.buffer.length > 0);
215
+ if (mode === "parse-only") return {
216
+ kind: "success",
217
+ value: void 0
218
+ };
219
+ const endResult = await parser.complete(context.state, createCompleteExec(exec, context));
220
+ if (!endResult.success) return {
221
+ kind: "failure",
222
+ error: endResult.error,
223
+ remainingArgs: [],
224
+ consumedCount: args.length,
225
+ optionsTerminated: context.optionsTerminated,
226
+ commandPath: getCommandPath(context.exec)
227
+ };
228
+ return {
229
+ kind: "success",
230
+ value: shouldUnwrapAnnotatedValue ? require_annotations.unwrapInjectedAnnotationWrapper(endResult.value) : endResult.value
231
+ };
232
+ }
98
233
  function createPhase2SeedExec(parser, context) {
99
234
  const exec = {
100
235
  usage: parser.usage,
101
236
  phase: "parse",
102
237
  path: [],
238
+ commandPath: [],
103
239
  trace: require_input_trace.createInputTrace()
104
240
  };
105
241
  const runtime = require_dependency_runtime.createDependencyRuntimeContext();
@@ -108,6 +244,7 @@ function createPhase2SeedExec(parser, context) {
108
244
  phase: "complete",
109
245
  dependencyRuntime: runtime,
110
246
  dependencyRegistry: runtime.registry,
247
+ commandPath: getCommandPath(context.exec),
111
248
  trace: context.exec?.trace ?? context.trace ?? exec.trace
112
249
  };
113
250
  }
@@ -116,6 +253,7 @@ function createPhase2SeedContext(parser, args) {
116
253
  usage: parser.usage,
117
254
  phase: "parse",
118
255
  path: [],
256
+ commandPath: [],
119
257
  trace: require_input_trace.createInputTrace()
120
258
  };
121
259
  return require_parser.createParserContext({
@@ -203,9 +341,6 @@ function createVersionParser(commandConfig, optionConfig) {
203
341
  };
204
342
  }
205
343
  const metaResultBrand = Symbol("@optique/core/facade/meta");
206
- function isMetaParseResult(value$1) {
207
- return typeof value$1 === "object" && value$1 != null && metaResultBrand in value$1;
208
- }
209
344
  /**
210
345
  * Creates completion parsers based on the sub-config.
211
346
  */
@@ -473,54 +608,115 @@ function combineWithHelpVersion(originalParser, helpParsers, versionParsers, com
473
608
  }
474
609
  return combined;
475
610
  }
476
- /**
477
- * Classifies the parsing result into a discriminated union for cleaner
478
- * handling.
479
- */
480
- function classifyResult(result, args, helpOptionNames, helpCommandNames, versionOptionNames, versionCommandNames, completionCommandNames) {
481
- if (!result.success) return {
482
- type: "error",
483
- error: result.error
484
- };
485
- const value$1 = result.value;
486
- if (isMetaParseResult(value$1)) {
487
- const parsedValue = value$1;
488
- const hasVersionOption = versionOptionNames.some((n) => args.includes(n));
489
- const hasVersionCommand = args.length > 0 && versionCommandNames.includes(args[0]);
490
- const hasCompletionCommand = args.length > 0 && completionCommandNames.includes(args[0]);
491
- const hasHelpOption = hasCompletionCommand ? helpOptionNames.length > 0 && args.length >= 2 && helpOptionNames.includes(args[1]) : helpOptionNames.some((n) => args.includes(n));
492
- const hasHelpCommand = args.length > 0 && helpCommandNames.includes(args[0]);
493
- if (hasVersionCommand && hasHelpOption && parsedValue.helpFlag) return {
494
- type: "help",
495
- commands: [args[0]]
611
+ function classifyOptionMeta(scanArgs, fullArgs, helpOptionNames, versionOptionNames, completionOptionNames) {
612
+ let lastHelpVersion;
613
+ for (let i = 0; i < scanArgs.length; i++) {
614
+ const arg = scanArgs[i];
615
+ if (helpOptionNames.includes(arg)) lastHelpVersion = {
616
+ index: i,
617
+ kind: "help"
496
618
  };
497
- if (hasCompletionCommand && hasHelpOption && parsedValue.helpFlag) return {
498
- type: "help",
499
- commands: [args[0]]
619
+ else if (versionOptionNames.includes(arg)) lastHelpVersion = {
620
+ index: i,
621
+ kind: "version"
622
+ };
623
+ const equalsMatch = completionOptionNames.find((name) => arg.startsWith(name + "="));
624
+ if (equalsMatch != null) return {
625
+ lastHelpVersion,
626
+ completion: {
627
+ index: i,
628
+ shell: arg.slice(equalsMatch.length + 1),
629
+ args: fullArgs.slice(i + 1)
630
+ }
500
631
  };
501
- if (parsedValue.help && (hasHelpOption || hasHelpCommand)) {
502
- let commandContext = [];
503
- if (Array.isArray(parsedValue.commands)) commandContext = parsedValue.commands;
504
- else if (typeof parsedValue.commands === "object" && parsedValue.commands != null && "length" in parsedValue.commands) commandContext = parsedValue.commands;
632
+ if (completionOptionNames.includes(arg)) {
633
+ const shell = scanArgs[i + 1] ?? "";
505
634
  return {
506
- type: "help",
507
- commands: commandContext
635
+ lastHelpVersion,
636
+ completion: {
637
+ index: i,
638
+ shell,
639
+ args: shell === "" ? [] : fullArgs.slice(i + 2)
640
+ }
508
641
  };
509
642
  }
510
- if ((hasVersionOption || hasVersionCommand) && (parsedValue.version || parsedValue.versionFlag)) return { type: "version" };
511
- if (parsedValue.completion && parsedValue.completionData) return {
643
+ }
644
+ return { lastHelpVersion };
645
+ }
646
+ function getHelpCommandContext(commandPath, args, helpIndex) {
647
+ const commands = [...commandPath];
648
+ for (let i = 0; i < helpIndex; i++) {
649
+ const arg = args[i];
650
+ if (!arg.startsWith("-")) commands.push(arg);
651
+ }
652
+ return commands;
653
+ }
654
+ function classifyParseFailure(failure, helpOptionNames, helpCommandNames, versionOptionNames, versionCommandNames, completionOptionNames, completionCommandNames) {
655
+ if (failure.remainingArgs.length < 1) return {
656
+ type: "error",
657
+ error: failure.error
658
+ };
659
+ const hasConsumedPrefix = failure.consumedCount > 0;
660
+ const firstArg = failure.remainingArgs[0];
661
+ if (!hasConsumedPrefix && completionCommandNames.includes(firstArg)) {
662
+ const secondArg = failure.remainingArgs[1];
663
+ if (helpOptionNames.includes(secondArg)) return {
664
+ type: "help",
665
+ commands: [firstArg]
666
+ };
667
+ return {
512
668
  type: "completion",
513
- shell: parsedValue.completionData.shell ?? "",
514
- args: parsedValue.completionData.args ?? []
669
+ source: "command",
670
+ shell: secondArg ?? "",
671
+ args: failure.remainingArgs.slice(2)
515
672
  };
673
+ }
674
+ const optionArgs = failure.optionsTerminated ? [] : (() => {
675
+ const terminatorIndex = failure.remainingArgs.indexOf("--");
676
+ return terminatorIndex >= 0 ? failure.remainingArgs.slice(0, terminatorIndex) : failure.remainingArgs;
677
+ })();
678
+ const { lastHelpVersion, completion } = classifyOptionMeta(optionArgs, failure.remainingArgs, helpOptionNames, versionOptionNames, completionOptionNames);
679
+ if (!hasConsumedPrefix && versionCommandNames.includes(firstArg)) {
680
+ const secondArg = failure.remainingArgs[1];
681
+ if (helpOptionNames.includes(secondArg)) return {
682
+ type: "help",
683
+ commands: [firstArg]
684
+ };
685
+ const isCompletionImmediatelyAfter = completion?.index === 1;
686
+ if (secondArg == null || isCompletionImmediatelyAfter || lastHelpVersion?.index === 1 && lastHelpVersion.kind === "version") return { type: "version" };
516
687
  return {
517
- type: "success",
518
- value: "result" in parsedValue ? parsedValue.result : value$1
688
+ type: "error",
689
+ error: failure.error
519
690
  };
520
691
  }
692
+ let commandAction;
693
+ if (!hasConsumedPrefix && helpCommandNames.includes(firstArg)) commandAction = {
694
+ index: 0,
695
+ kind: "help"
696
+ };
697
+ const winner = lastHelpVersion == null ? commandAction : commandAction == null || lastHelpVersion.index >= commandAction.index ? lastHelpVersion : commandAction;
698
+ if (winner?.kind === "help") {
699
+ if (winner === commandAction) return {
700
+ type: "help",
701
+ commands: failure.remainingArgs.slice(1)
702
+ };
703
+ return {
704
+ type: "help",
705
+ commands: getHelpCommandContext(failure.commandPath, failure.remainingArgs, winner.index),
706
+ preferUserCommandDocs: failure.commandPath.length > 0
707
+ };
708
+ }
709
+ if (winner?.kind === "version") return { type: "version" };
710
+ if (completion != null) return {
711
+ type: "completion",
712
+ source: "option",
713
+ shell: completion.shell,
714
+ commandPath: failure.commandPath,
715
+ args: completion.args
716
+ };
521
717
  return {
522
- type: "success",
523
- value: value$1
718
+ type: "error",
719
+ error: failure.error
524
720
  };
525
721
  }
526
722
  /**
@@ -694,12 +890,7 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
694
890
  "completion command",
695
891
  completionCommandNames
696
892
  ]);
697
- require_validate.validateMetaNameCollisions({
698
- leadingNames: parser.leadingNames,
699
- allOptions: require_usage.extractOptionNames(parser.usage, true),
700
- allCommands: require_usage.extractCommandNames(parser.usage, true),
701
- allLiterals: require_usage.extractLiteralValues(parser.usage)
702
- }, activeMetaEntries);
893
+ require_validate.validateMetaNameCollisions(activeMetaEntries);
703
894
  const defaultShells = {
704
895
  bash: require_completion.bash,
705
896
  fish: require_completion.fish,
@@ -723,50 +914,6 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
723
914
  completionCommand: null,
724
915
  completionOption: null
725
916
  };
726
- if (options.completion) {
727
- const helpTerminatorIndex = args.indexOf("--");
728
- const helpOptionArgs = helpTerminatorIndex >= 0 ? args.slice(0, helpTerminatorIndex) : args;
729
- const hasHelpOption = helpOptionConfig ? completionCommandConfig && completionCommandNames.includes(args[0]) ? args.length >= 2 && helpOptionNames.includes(args[1]) : helpOptionNames.some((n) => helpOptionArgs.includes(n)) : false;
730
- if (completionCommandConfig && args.length >= 1 && completionCommandNames.includes(args[0]) && !hasHelpOption) return handleCompletion(args.slice(1), programName, parser, completionParsers.completionCommand, stdout, stderr, onCompletionResult, onErrorResult, availableShells, colors, maxWidth, completionCommandNames[0], completionOptionNames[0], false, sectionOrder);
731
- const hasMetaEntryBefore = (endIndex) => {
732
- const argsBefore = args.slice(0, endIndex);
733
- if (helpOptionConfig && helpOptionNames.some((n) => argsBefore.includes(n))) return true;
734
- if (versionOptionConfig && versionOptionNames.some((n) => argsBefore.includes(n))) return true;
735
- if (endIndex > 0) {
736
- if (helpCommandConfig && helpCommandNames.includes(args[0])) return true;
737
- if (versionCommandConfig && versionCommandNames.includes(args[0])) return true;
738
- }
739
- return false;
740
- };
741
- let completionOptionIndex = -1;
742
- if (completionOptionConfig) {
743
- const loopBound = helpTerminatorIndex >= 0 ? helpTerminatorIndex : args.length;
744
- for (let i = 0; i < loopBound; i++) {
745
- const arg = args[i];
746
- const equalsMatch = completionOptionNames.find((n) => arg.startsWith(n + "="));
747
- if (equalsMatch) {
748
- if (hasMetaEntryBefore(i)) {
749
- completionOptionIndex = i;
750
- break;
751
- }
752
- const shell = arg.slice(equalsMatch.length + 1);
753
- const completionArgs = args.slice(i + 1);
754
- return handleCompletion([shell, ...completionArgs], programName, parser, completionParsers.completionOption, stdout, stderr, onCompletionResult, onErrorResult, availableShells, colors, maxWidth, completionCommandNames[0], completionOptionNames[0], true, sectionOrder);
755
- }
756
- const exactMatch = completionOptionNames.includes(arg);
757
- if (exactMatch) {
758
- if (hasMetaEntryBefore(i)) {
759
- completionOptionIndex = i;
760
- break;
761
- }
762
- const shell = i + 1 < args.length ? args[i + 1] : "";
763
- const completionArgs = i + 1 < args.length ? args.slice(i + 2) : [];
764
- return handleCompletion([shell, ...completionArgs], programName, parser, completionParsers.completionOption, stdout, stderr, onCompletionResult, onErrorResult, availableShells, colors, maxWidth, completionCommandNames[0], completionOptionNames[0], true, sectionOrder);
765
- }
766
- }
767
- }
768
- if (completionOptionIndex >= 0) args = args.slice(0, completionOptionIndex);
769
- }
770
917
  const augmentedParser = !options.help && !options.version && !options.completion ? parser : combineWithHelpVersion(parser, helpParsers, versionParsers, completionParsers, {
771
918
  helpCommandGroup: helpCommandConfig?.group,
772
919
  helpOptionGroup: helpOptionConfig?.group,
@@ -775,14 +922,17 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
775
922
  completionCommandGroup: completionCommandConfig?.group,
776
923
  completionOptionGroup: completionOptionConfig?.group
777
924
  }, helpOptionConfig ? [...helpOptionNames] : void 0, versionOptionConfig ? [...versionOptionNames] : void 0);
778
- const handleResult = (result) => {
779
- const classified = classifyResult(result, args, helpOptionConfig ? [...helpOptionNames] : [], helpCommandConfig ? [...helpCommandNames] : [], versionOptionConfig ? [...versionOptionNames] : [], versionCommandConfig ? [...versionCommandNames] : [], completionCommandConfig ? [...completionCommandNames] : []);
925
+ const handleResult = (classified) => {
780
926
  switch (classified.type) {
781
927
  case "success": return classified.value;
782
928
  case "version":
783
929
  stdout(versionValue);
784
930
  return onVersion(0);
785
- case "completion": throw new RunParserError("Completion should be handled by early return");
931
+ case "completion": return handleCompletion([
932
+ classified.shell,
933
+ ...classified.commandPath ?? [],
934
+ ...classified.args
935
+ ], programName, parser, classified.source === "command" ? completionParsers.completionCommand : completionParsers.completionOption, stdout, stderr, onCompletionResult, onErrorResult, availableShells, colors, maxWidth, completionCommandNames[0], completionOptionNames[0], classified.source === "option", sectionOrder);
786
936
  case "help": {
787
937
  let helpGeneratorParser;
788
938
  const helpAsCommand = helpCommandConfig != null;
@@ -792,9 +942,9 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
792
942
  const versionAsOption = versionOptionConfig != null;
793
943
  const completionAsOption = completionOptionConfig != null;
794
944
  const requestedCommand = classified.commands[0];
795
- if (requestedCommand != null && completionCommandNames.includes(requestedCommand) && completionAsCommand && completionParsers.completionCommand) helpGeneratorParser = completionParsers.completionCommand;
796
- else if (requestedCommand != null && helpCommandNames.includes(requestedCommand) && helpAsCommand && helpParsers.helpCommand) helpGeneratorParser = helpParsers.helpCommand;
797
- else if (requestedCommand != null && versionCommandNames.includes(requestedCommand) && versionAsCommand && versionParsers.versionCommand) helpGeneratorParser = versionParsers.versionCommand;
945
+ if (requestedCommand != null && !classified.preferUserCommandDocs && completionCommandNames.includes(requestedCommand) && completionAsCommand && completionParsers.completionCommand) helpGeneratorParser = completionParsers.completionCommand;
946
+ else if (requestedCommand != null && !classified.preferUserCommandDocs && helpCommandNames.includes(requestedCommand) && helpAsCommand && helpParsers.helpCommand) helpGeneratorParser = helpParsers.helpCommand;
947
+ else if (requestedCommand != null && !classified.preferUserCommandDocs && versionCommandNames.includes(requestedCommand) && versionAsCommand && versionParsers.versionCommand) helpGeneratorParser = versionParsers.versionCommand;
798
948
  else {
799
949
  const commandParsers = [parser];
800
950
  const groupedMeta = {};
@@ -886,8 +1036,8 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
886
1036
  while (validationResult === "continue") {
887
1037
  const stepResult = helpGeneratorParser.parse(validationContext);
888
1038
  if (stepResult instanceof Promise) {
889
- const asyncValidate = async (result$1) => {
890
- let res = processStep(result$1);
1039
+ const asyncValidate = async (result) => {
1040
+ let res = processStep(result);
891
1041
  while (res === "continue") {
892
1042
  const next = helpGeneratorParser.parse(validationContext);
893
1043
  const resolved = next instanceof Promise ? await next : next;
@@ -953,13 +1103,21 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
953
1103
  };
954
1104
  const parserMode = parser.$mode;
955
1105
  return require_mode_dispatch.dispatchByMode(parserMode, () => {
956
- const result = require_parser.parseSync(augmentedParser, args);
957
- const handled = handleResult(result);
1106
+ const attempted = attemptParseSync(parser, args);
1107
+ const classified = attempted.kind === "success" ? {
1108
+ type: "success",
1109
+ value: attempted.value
1110
+ } : classifyParseFailure(attempted, helpOptionConfig ? [...helpOptionNames] : [], helpCommandConfig ? [...helpCommandNames] : [], versionOptionConfig ? [...versionOptionNames] : [], versionCommandConfig ? [...versionCommandNames] : [], completionOptionConfig ? [...completionOptionNames] : [], completionCommandConfig ? [...completionCommandNames] : []);
1111
+ const handled = handleResult(classified);
958
1112
  if (handled instanceof Promise) throw new RunParserError("Synchronous parser returned async result.");
959
1113
  return handled;
960
1114
  }, async () => {
961
- const result = await require_parser.parseAsync(augmentedParser, args);
962
- const handled = handleResult(result);
1115
+ const attempted = await attemptParseAsync(parser, args);
1116
+ const classified = attempted.kind === "success" ? {
1117
+ type: "success",
1118
+ value: attempted.value
1119
+ } : classifyParseFailure(attempted, helpOptionConfig ? [...helpOptionNames] : [], helpCommandConfig ? [...helpCommandNames] : [], versionOptionConfig ? [...versionOptionNames] : [], versionCommandConfig ? [...versionCommandNames] : [], completionOptionConfig ? [...completionOptionNames] : [], completionCommandConfig ? [...completionCommandNames] : []);
1120
+ const handled = handleResult(classified);
963
1121
  return handled instanceof Promise ? await handled : handled;
964
1122
  });
965
1123
  }
@@ -1020,49 +1178,38 @@ var RunParserError = class extends Error {
1020
1178
  function indentLines(text$1, indent) {
1021
1179
  return text$1.split("\n").join("\n" + " ".repeat(indent));
1022
1180
  }
1181
+ function isMetaEarlyExit(classified) {
1182
+ return classified.type === "help" || classified.type === "version" || classified.type === "completion";
1183
+ }
1184
+ function classifyEarlyExitFailure(failure, options) {
1185
+ const norm = (c) => c === true ? {} : c;
1186
+ const helpOptionConfig = norm(options.help?.option);
1187
+ const helpCommandConfig = norm(options.help?.command);
1188
+ const versionOptionConfig = norm(options.version?.option);
1189
+ const versionCommandConfig = norm(options.version?.command);
1190
+ const completionOptionConfig = norm(options.completion?.option);
1191
+ const completionCommandConfig = norm(options.completion?.command);
1192
+ return classifyParseFailure(failure, helpOptionConfig ? [...helpOptionConfig.names ?? ["--help"]] : [], helpCommandConfig ? [...helpCommandConfig.names ?? ["help"]] : [], versionOptionConfig ? [...versionOptionConfig.names ?? ["--version"]] : [], versionCommandConfig ? [...versionCommandConfig.names ?? ["version"]] : [], completionOptionConfig ? [...completionOptionConfig.names ?? ["--completion"]] : [], completionCommandConfig ? [...completionCommandConfig.names ?? ["completion"]] : []);
1193
+ }
1194
+ function shouldProbeEarlyExit(options, needsTwoPhase) {
1195
+ return needsTwoPhase && (options.help != null || options.version != null || options.completion != null);
1196
+ }
1023
1197
  /**
1024
- * Checks if the arguments contain help, version, or completion requests
1025
- * that should be handled immediately without context processing.
1026
- *
1027
- * This enables early exit optimization: when users request help, version,
1028
- * or completion, we skip annotation collection and context processing
1029
- * entirely, delegating directly to runParser().
1030
- *
1031
- * @param args Command-line arguments to check.
1032
- * @param options Run options containing help/version/completion configuration.
1033
- * @returns `true` if early exit should be performed, `false` otherwise.
1198
+ * Checks if the arguments contain parser-visible meta requests that can be
1199
+ * handled without collecting source-context annotations first.
1034
1200
  */
1035
- function needsEarlyExit(args, options) {
1036
- const norm = (c) => c === true ? {} : c;
1037
- const terminatorIndex = args.indexOf("--");
1038
- const optionArgs = terminatorIndex >= 0 ? args.slice(0, terminatorIndex) : args;
1039
- if (options.help) {
1040
- const helpOptionConfig = norm(options.help.option);
1041
- const helpCommandConfig = norm(options.help.command);
1042
- const helpOptionNames = helpOptionConfig?.names ?? ["--help"];
1043
- const helpCommandNames = helpCommandConfig?.names ?? ["help"];
1044
- if (helpOptionConfig && helpOptionNames.some((n) => optionArgs.includes(n))) return true;
1045
- if (helpCommandConfig && helpCommandNames.includes(args[0])) return true;
1046
- }
1047
- if (options.version) {
1048
- const versionOptionConfig = norm(options.version.option);
1049
- const versionCommandConfig = norm(options.version.command);
1050
- const versionOptionNames = versionOptionConfig?.names ?? ["--version"];
1051
- const versionCommandNames = versionCommandConfig?.names ?? ["version"];
1052
- if (versionOptionConfig && versionOptionNames.some((n) => optionArgs.includes(n))) return true;
1053
- if (versionCommandConfig && versionCommandNames.includes(args[0])) return true;
1054
- }
1055
- if (options.completion) {
1056
- const completionCommandConfig = norm(options.completion.command);
1057
- const completionOptionConfig = norm(options.completion.option);
1058
- const completionCommandNames = completionCommandConfig?.names ?? ["completion"];
1059
- const completionOptionNames = completionOptionConfig?.names ?? ["--completion"];
1060
- if (completionCommandConfig && completionCommandNames.includes(args[0])) return true;
1061
- if (completionOptionConfig) {
1062
- for (const arg of optionArgs) for (const name of completionOptionNames) if (arg === name || arg.startsWith(name + "=")) return true;
1063
- }
1064
- }
1065
- return false;
1201
+ function needsEarlyExitSync(parser, args, options) {
1202
+ const attempted = attemptParseSync(parser, args, "parse-only");
1203
+ if (attempted.kind === "success") return false;
1204
+ return isMetaEarlyExit(classifyEarlyExitFailure(attempted, options));
1205
+ }
1206
+ /**
1207
+ * Async variant of {@link needsEarlyExitSync}.
1208
+ */
1209
+ async function needsEarlyExitAsync(parser, args, options) {
1210
+ const attempted = await attemptParseAsync(parser, args, "parse-only");
1211
+ if (attempted.kind === "success") return false;
1212
+ return isMetaEarlyExit(classifyEarlyExitFailure(attempted, options));
1066
1213
  }
1067
1214
  /**
1068
1215
  * Merges multiple annotation objects, with earlier contexts having priority.
@@ -1250,23 +1397,25 @@ function disposeContextsSync(contexts) {
1250
1397
  async function runWithBody(parser, programName, contexts, args, options) {
1251
1398
  require_validate.validateContextIds(contexts);
1252
1399
  validateContextPhases(contexts);
1253
- if (needsEarlyExit(args, options)) {
1254
- if (parser.$mode === "async") return runParser(parser, programName, args, options);
1255
- return Promise.resolve(runParser(parser, programName, args, options));
1256
- }
1257
1400
  const ctxOptions = options.contextOptions;
1258
1401
  const { annotations: phase1Annotations, needsTwoPhase, snapshots: phase1Snapshots } = await collectPhase1Annotations(contexts, ctxOptions);
1259
- if (!needsTwoPhase) {
1260
- const augmentedParser = injectAnnotationsIntoParser(parser, phase1Annotations);
1261
- if (parser.$mode === "async") return runParser(augmentedParser, programName, args, options);
1262
- return Promise.resolve(runParser(augmentedParser, programName, args, options));
1402
+ if (shouldProbeEarlyExit(options, needsTwoPhase)) {
1403
+ const earlyExitParser = injectAnnotationsIntoParser(parser, phase1Annotations);
1404
+ if (await needsEarlyExitAsync(earlyExitParser, args, options)) {
1405
+ if (parser.$mode === "async") return runParser(earlyExitParser, programName, args, options);
1406
+ return Promise.resolve(runParser(earlyExitParser, programName, args, options));
1407
+ }
1263
1408
  }
1264
1409
  const augmentedParser1 = injectAnnotationsIntoParser(parser, phase1Annotations);
1410
+ if (!needsTwoPhase) {
1411
+ if (parser.$mode === "async") return runParser(augmentedParser1, programName, args, options);
1412
+ return Promise.resolve(runParser(augmentedParser1, programName, args, options));
1413
+ }
1265
1414
  const firstPassSeed = await require_mode_dispatch.dispatchByMode(parser.$mode, () => extractPhase2SeedSync(augmentedParser1, args), () => extractPhase2SeedAsync(augmentedParser1, args));
1266
1415
  if (firstPassSeed == null) {
1267
- const augmentedParser = injectAnnotationsIntoParser(parser, phase1Annotations);
1268
- if (parser.$mode === "async") return runParser(augmentedParser, programName, args, options);
1269
- return Promise.resolve(runParser(augmentedParser, programName, args, options));
1416
+ const fallbackParser = injectAnnotationsIntoParser(parser, phase1Annotations);
1417
+ if (parser.$mode === "async") return runParser(fallbackParser, programName, args, options);
1418
+ return Promise.resolve(runParser(fallbackParser, programName, args, options));
1270
1419
  }
1271
1420
  const { annotations: finalAnnotations } = await collectFinalAnnotations(contexts, phase1Snapshots, firstPassSeed.value, ctxOptions, firstPassSeed.deferred, firstPassSeed.deferredKeys);
1272
1421
  const augmentedParser2 = injectAnnotationsIntoParser(parser, finalAnnotations);
@@ -1368,18 +1517,18 @@ async function runWith(parser, programName, contexts, options) {
1368
1517
  function runWithSyncBody(parser, programName, contexts, args, options) {
1369
1518
  require_validate.validateContextIds(contexts);
1370
1519
  validateContextPhases(contexts);
1371
- if (needsEarlyExit(args, options)) return runParser(parser, programName, args, options);
1372
1520
  const ctxOptions = options.contextOptions;
1373
1521
  const { annotations: phase1Annotations, needsTwoPhase, snapshots: phase1Snapshots } = collectPhase1AnnotationsSync(contexts, ctxOptions);
1374
- if (!needsTwoPhase) {
1375
- const augmentedParser = injectAnnotationsIntoParser(parser, phase1Annotations);
1376
- return runParser(augmentedParser, programName, args, options);
1522
+ if (shouldProbeEarlyExit(options, needsTwoPhase)) {
1523
+ const earlyExitParser = injectAnnotationsIntoParser(parser, phase1Annotations);
1524
+ if (needsEarlyExitSync(earlyExitParser, args, options)) return runParser(earlyExitParser, programName, args, options);
1377
1525
  }
1378
1526
  const augmentedParser1 = injectAnnotationsIntoParser(parser, phase1Annotations);
1527
+ if (!needsTwoPhase) return runParser(augmentedParser1, programName, args, options);
1379
1528
  const firstPassSeed = extractPhase2SeedSync(augmentedParser1, args);
1380
1529
  if (firstPassSeed == null) {
1381
- const augmentedParser = injectAnnotationsIntoParser(parser, phase1Annotations);
1382
- return runParser(augmentedParser, programName, args, options);
1530
+ const fallbackParser = injectAnnotationsIntoParser(parser, phase1Annotations);
1531
+ return runParser(fallbackParser, programName, args, options);
1383
1532
  }
1384
1533
  const { annotations: finalAnnotations } = collectFinalAnnotationsSync(contexts, phase1Snapshots, firstPassSeed.value, ctxOptions, firstPassSeed.deferred, firstPassSeed.deferredKeys);
1385
1534
  const augmentedParser2 = injectAnnotationsIntoParser(parser, finalAnnotations);