politty 0.4.14 → 0.4.16

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.
Files changed (80) hide show
  1. package/README.md +7 -1
  2. package/dist/{arg-registry-CkPDokIu.d.ts → arg-registry-Cd6xnjHa.d.ts} +118 -4
  3. package/dist/arg-registry-Cd6xnjHa.d.ts.map +1 -0
  4. package/dist/{arg-registry-r5wYN6qd.d.cts → arg-registry-MVWOAcvw.d.cts} +118 -4
  5. package/dist/arg-registry-MVWOAcvw.d.cts.map +1 -0
  6. package/dist/augment.d.cts +1 -1
  7. package/dist/augment.d.cts.map +1 -1
  8. package/dist/augment.d.ts +1 -1
  9. package/dist/augment.d.ts.map +1 -1
  10. package/dist/completion/index.cjs +2 -1
  11. package/dist/completion/index.d.cts +2 -2
  12. package/dist/completion/index.d.ts +2 -2
  13. package/dist/completion/index.js +2 -2
  14. package/dist/{completion-yHz8Pdr7.js → completion-B04iiki9.js} +580 -31
  15. package/dist/completion-B04iiki9.js.map +1 -0
  16. package/dist/{completion-CAekGYS4.cjs → completion-BlZxMSeU.cjs} +598 -43
  17. package/dist/completion-BlZxMSeU.cjs.map +1 -0
  18. package/dist/docs/index.cjs +121 -65
  19. package/dist/docs/index.cjs.map +1 -1
  20. package/dist/docs/index.d.cts +5 -1
  21. package/dist/docs/index.d.cts.map +1 -1
  22. package/dist/docs/index.d.ts +5 -1
  23. package/dist/docs/index.d.ts.map +1 -1
  24. package/dist/docs/index.js +124 -67
  25. package/dist/docs/index.js.map +1 -1
  26. package/dist/{index-DPswv0Vt.d.cts → index-CPebddth.d.cts} +58 -4
  27. package/dist/index-CPebddth.d.cts.map +1 -0
  28. package/dist/{index-BLySW_2k.d.ts → index-DR9HLxIP.d.ts} +58 -4
  29. package/dist/index-DR9HLxIP.d.ts.map +1 -0
  30. package/dist/index.cjs +12 -10
  31. package/dist/index.d.cts +39 -4
  32. package/dist/index.d.cts.map +1 -1
  33. package/dist/index.d.ts +39 -4
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +4 -4
  36. package/dist/{subcommand-router-C9ONv6Nq.cjs → log-collector-Cd2_mv87.cjs} +1 -59
  37. package/dist/log-collector-Cd2_mv87.cjs.map +1 -0
  38. package/dist/{subcommand-router--EUt6ftA.js → log-collector-Cu6MCtAx.js} +2 -43
  39. package/dist/log-collector-Cu6MCtAx.js.map +1 -0
  40. package/dist/prompt/clack/index.cjs +1 -1
  41. package/dist/prompt/clack/index.cjs.map +1 -1
  42. package/dist/prompt/clack/index.d.cts +1 -1
  43. package/dist/prompt/clack/index.d.cts.map +1 -1
  44. package/dist/prompt/clack/index.d.ts +1 -1
  45. package/dist/prompt/clack/index.d.ts.map +1 -1
  46. package/dist/prompt/clack/index.js.map +1 -1
  47. package/dist/prompt/index.d.cts +1 -1
  48. package/dist/prompt/index.d.cts.map +1 -1
  49. package/dist/prompt/index.d.ts +1 -1
  50. package/dist/prompt/index.d.ts.map +1 -1
  51. package/dist/prompt/inquirer/index.cjs +1 -1
  52. package/dist/prompt/inquirer/index.cjs.map +1 -1
  53. package/dist/prompt/inquirer/index.d.cts +1 -1
  54. package/dist/prompt/inquirer/index.d.cts.map +1 -1
  55. package/dist/prompt/inquirer/index.d.ts +1 -1
  56. package/dist/prompt/inquirer/index.d.ts.map +1 -1
  57. package/dist/prompt/inquirer/index.js.map +1 -1
  58. package/dist/prompt-BKHqGrFw.js.map +1 -1
  59. package/dist/prompt-aXfSf27y.cjs.map +1 -1
  60. package/dist/{runner-DSZw1AsW.js → runner-BHeCMEa5.js} +383 -57
  61. package/dist/runner-BHeCMEa5.js.map +1 -0
  62. package/dist/{runner-CY5fOsSh.cjs → runner-BcyR6Z8r.cjs} +434 -96
  63. package/dist/runner-BcyR6Z8r.cjs.map +1 -0
  64. package/dist/{lazy-AGV9Pkt5.cjs → subcommand-router-DQy0KZU-.cjs} +148 -4
  65. package/dist/subcommand-router-DQy0KZU-.cjs.map +1 -0
  66. package/dist/{lazy-DiMJSDMB.js → subcommand-router-XZBWe8HN.js} +118 -4
  67. package/dist/subcommand-router-XZBWe8HN.js.map +1 -0
  68. package/package.json +16 -16
  69. package/dist/arg-registry-CkPDokIu.d.ts.map +0 -1
  70. package/dist/arg-registry-r5wYN6qd.d.cts.map +0 -1
  71. package/dist/completion-CAekGYS4.cjs.map +0 -1
  72. package/dist/completion-yHz8Pdr7.js.map +0 -1
  73. package/dist/index-BLySW_2k.d.ts.map +0 -1
  74. package/dist/index-DPswv0Vt.d.cts.map +0 -1
  75. package/dist/lazy-AGV9Pkt5.cjs.map +0 -1
  76. package/dist/lazy-DiMJSDMB.js.map +0 -1
  77. package/dist/runner-CY5fOsSh.cjs.map +0 -1
  78. package/dist/runner-DSZw1AsW.js.map +0 -1
  79. package/dist/subcommand-router--EUt6ftA.js.map +0 -1
  80. package/dist/subcommand-router-C9ONv6Nq.cjs.map +0 -1
@@ -1,8 +1,8 @@
1
- const require_subcommand_router = require('./subcommand-router-C9ONv6Nq.cjs');
2
- const require_lazy = require('./lazy-AGV9Pkt5.cjs');
1
+ const require_log_collector = require('./log-collector-Cd2_mv87.cjs');
2
+ const require_subcommand_router = require('./subcommand-router-DQy0KZU-.cjs');
3
3
  let node_util = require("node:util");
4
4
  let string_width = require("string-width");
5
- string_width = require_subcommand_router.__toESM(string_width);
5
+ string_width = require_log_collector.__toESM(string_width, 1);
6
6
 
7
7
  //#region src/core/case-proxy.ts
8
8
  /**
@@ -19,9 +19,9 @@ function createDualCaseProxy(obj) {
19
19
  get(target, prop, receiver) {
20
20
  if (typeof prop === "string") {
21
21
  if (prop in target) return Reflect.get(target, prop, receiver);
22
- const camel = require_lazy.toCamelCase(prop);
22
+ const camel = require_subcommand_router.toCamelCase(prop);
23
23
  if (camel !== prop && camel in target) return Reflect.get(target, camel, receiver);
24
- const kebab = require_lazy.toKebabCase(prop);
24
+ const kebab = require_subcommand_router.toKebabCase(prop);
25
25
  if (kebab !== prop && kebab in target) return Reflect.get(target, kebab, receiver);
26
26
  }
27
27
  return Reflect.get(target, prop, receiver);
@@ -29,9 +29,9 @@ function createDualCaseProxy(obj) {
29
29
  has(target, prop) {
30
30
  if (typeof prop === "string") {
31
31
  if (prop in target) return true;
32
- const camel = require_lazy.toCamelCase(prop);
32
+ const camel = require_subcommand_router.toCamelCase(prop);
33
33
  if (camel !== prop && camel in target) return true;
34
- const kebab = require_lazy.toKebabCase(prop);
34
+ const kebab = require_subcommand_router.toKebabCase(prop);
35
35
  if (kebab !== prop && kebab in target) return true;
36
36
  }
37
37
  return Reflect.has(target, prop);
@@ -56,7 +56,7 @@ function createDualCaseProxy(obj) {
56
56
  async function executeLifecycle(command, args, _options = {}) {
57
57
  let error;
58
58
  let result;
59
- const collector = _options.captureLogs ?? false ? require_subcommand_router.createLogCollector() : null;
59
+ const collector = _options.captureLogs ?? false ? require_log_collector.createLogCollector() : null;
60
60
  collector?.start();
61
61
  const setupContext = { args };
62
62
  const cleanupContext = {
@@ -119,7 +119,7 @@ async function executeLifecycle(command, args, _options = {}) {
119
119
  }
120
120
  }
121
121
  collector?.stop();
122
- const logs = require_subcommand_router.mergeLogs(_options.existingLogs ?? require_subcommand_router.emptyLogs(), collector?.getLogs() ?? require_subcommand_router.emptyLogs());
122
+ const logs = require_log_collector.mergeLogs(_options.existingLogs ?? require_log_collector.emptyLogs(), collector?.getLogs() ?? require_log_collector.emptyLogs());
123
123
  if (error) return {
124
124
  success: false,
125
125
  error,
@@ -220,24 +220,45 @@ const symbols = {
220
220
  * Logger for CLI output
221
221
  */
222
222
  const logger = {
223
+ /**
224
+ * Log informational message
225
+ */
223
226
  info(message) {
224
227
  console.log(message);
225
228
  },
229
+ /**
230
+ * Log success message
231
+ */
226
232
  success(message) {
227
233
  console.log(`${symbols.success} ${styles.success(message)}`);
228
234
  },
235
+ /**
236
+ * Log warning message
237
+ */
229
238
  warn(message) {
230
239
  console.warn(`${symbols.warning} ${styles.warning(message)}`);
231
240
  },
241
+ /**
242
+ * Log error message
243
+ */
232
244
  error(message) {
233
245
  console.error(`${symbols.error} ${styles.error(message)}`);
234
246
  },
247
+ /**
248
+ * Log raw message without prefix
249
+ */
235
250
  log(message) {
236
251
  console.log(message);
237
252
  },
253
+ /**
254
+ * Log empty line
255
+ */
238
256
  newline() {
239
257
  console.log("");
240
258
  },
259
+ /**
260
+ * Log debug message with dim color
261
+ */
241
262
  debug(message) {
242
263
  console.log(styles.dim(message));
243
264
  }
@@ -593,7 +614,7 @@ function renderUsageLine(command, context) {
593
614
  const name = buildUsageCommandName(command, context);
594
615
  parts.push(styles.commandName(name));
595
616
  if (context?.globalExtracted?.fields.length) parts.push(styles.placeholder("[global options]"));
596
- const extracted = require_lazy.getExtractedFields(command);
617
+ const extracted = require_subcommand_router.getExtractedFields(command);
597
618
  if (extracted) {
598
619
  const positionals = extracted.fields.filter((a) => a.positional);
599
620
  if (extracted.fields.filter((a) => !a.positional).length > 0) parts.push(styles.placeholder("[options]"));
@@ -613,9 +634,9 @@ function renderOptions(command, descriptions = {}, context) {
613
634
  helpAll: descriptions.helpAll ?? defaultBuiltinDescriptions.helpAll,
614
635
  version: descriptions.version ?? defaultBuiltinDescriptions.version
615
636
  };
616
- const extracted = require_lazy.getExtractedFields(command);
617
- const hasUserDefinedh = extracted?.fields.some((f) => f.overrideBuiltinAlias === true && require_lazy.getAllAliases(f).includes("h")) ?? false;
618
- const hasUserDefinedH = extracted?.fields.some((f) => f.overrideBuiltinAlias === true && require_lazy.getAllAliases(f).includes("H")) ?? false;
637
+ const extracted = require_subcommand_router.getExtractedFields(command);
638
+ const hasUserDefinedh = extracted?.fields.some((f) => f.overrideBuiltinAlias === true && require_subcommand_router.getAllAliases(f).includes("h")) ?? false;
639
+ const hasUserDefinedH = extracted?.fields.some((f) => f.overrideBuiltinAlias === true && require_subcommand_router.getAllAliases(f).includes("H")) ?? false;
619
640
  if (hasUserDefinedh) lines.push(formatOption(styles.option("--help"), desc.help));
620
641
  else lines.push(formatOption(`${styles.option("-h")}, ${styles.option("--help")}`, desc.help));
621
642
  if (hasUserDefinedH) lines.push(formatOption(styles.option("--help-all"), desc.helpAll));
@@ -634,10 +655,21 @@ function renderOptions(command, descriptions = {}, context) {
634
655
  const envInfo = formatEnvInfo(opt.env);
635
656
  if (envInfo) desc += ` ${envInfo}`;
636
657
  lines.push(formatOption(flags, desc));
658
+ const negationLine = formatNegationLine(opt);
659
+ if (negationLine) lines.push(negationLine);
637
660
  }
638
661
  return lines.join("\n");
639
662
  }
640
663
  /**
664
+ * Render a separate line for the custom negation option when a
665
+ * `negationDescription` is provided. When no description is given, the
666
+ * negation is shown inline by `formatFlags`.
667
+ */
668
+ function formatNegationLine(opt, indent = 0, extraDescPadding = 0) {
669
+ if (!opt.negationDisplay || !opt.negationDescription) return null;
670
+ return formatOption(styles.option(`--${opt.negationDisplay}`), `${opt.negationDescription} ${styles.dim(`(↔ --${opt.cliName})`)}`, indent, extraDescPadding);
671
+ }
672
+ /**
641
673
  * Render options for discriminated union with variants
642
674
  */
643
675
  function renderDiscriminatedUnionOptions(extracted, _command, lines) {
@@ -666,6 +698,8 @@ function renderDiscriminatedUnionOptions(extracted, _command, lines) {
666
698
  const envInfo = formatEnvInfo(field.env);
667
699
  if (envInfo) desc += ` ${envInfo}`;
668
700
  lines.push(formatOption(flags, desc));
701
+ const negationLine = formatNegationLine(field);
702
+ if (negationLine) lines.push(negationLine);
669
703
  }
670
704
  }
671
705
  for (const variant of variants) {
@@ -681,7 +715,9 @@ function renderDiscriminatedUnionOptions(extracted, _command, lines) {
681
715
  if (field.required) desc += ` ${styles.required("(required)")}`;
682
716
  const envInfo = formatEnvInfo(field.env);
683
717
  if (envInfo) desc += ` ${envInfo}`;
684
- lines.push(formatOption(` ${flags}`, desc));
718
+ lines.push(formatOption(flags, desc, 1));
719
+ const negationLine = formatNegationLine(field, 1);
720
+ if (negationLine) lines.push(negationLine);
685
721
  }
686
722
  }
687
723
  }
@@ -705,6 +741,8 @@ function renderUnionOptions(extracted, _command, lines) {
705
741
  const envInfo = formatEnvInfo(field.env);
706
742
  if (envInfo) desc += ` ${envInfo}`;
707
743
  lines.push(formatOption(flags, desc));
744
+ const negationLine = formatNegationLine(field);
745
+ if (negationLine) lines.push(negationLine);
708
746
  }
709
747
  }
710
748
  for (let i = 0; i < unionOptions.length; i++) {
@@ -722,7 +760,9 @@ function renderUnionOptions(extracted, _command, lines) {
722
760
  if (field.required) desc += ` ${styles.required("(required)")}`;
723
761
  const envInfo = formatEnvInfo(field.env);
724
762
  if (envInfo) desc += ` ${envInfo}`;
725
- lines.push(formatOption(` ${flags}`, desc));
763
+ lines.push(formatOption(flags, desc, 1));
764
+ const negationLine = formatNegationLine(field, 1);
765
+ if (negationLine) lines.push(negationLine);
726
766
  }
727
767
  } else {
728
768
  lines.push("");
@@ -737,16 +777,16 @@ function renderUnionOptions(extracted, _command, lines) {
737
777
  * Uses cliName (kebab-case) for display
738
778
  */
739
779
  function formatFlags(opt) {
740
- const parts = [];
780
+ const aliasParts = [];
741
781
  if (opt.alias) {
742
- for (const alias of opt.alias) if (alias.length === 1) parts.push(styles.option(`-${alias}`));
782
+ for (const alias of opt.alias) if (alias.length === 1) aliasParts.push(styles.option(`-${alias}`));
743
783
  }
744
784
  let longFlag = styles.option(`--${opt.cliName}`);
745
785
  if (opt.type !== "boolean") {
746
786
  const placeholder = opt.placeholder ?? opt.cliName.toUpperCase();
747
787
  longFlag += ` ${styles.placeholder(`<${placeholder}>`)}`;
748
788
  }
749
- parts.push(longFlag);
789
+ aliasParts.push(longFlag);
750
790
  if (opt.alias) {
751
791
  for (const alias of opt.alias) if (alias.length > 1) {
752
792
  let longAlias = styles.option(`--${alias}`);
@@ -754,10 +794,12 @@ function formatFlags(opt) {
754
794
  const placeholder = opt.placeholder ?? opt.cliName.toUpperCase();
755
795
  longAlias += ` ${styles.placeholder(`<${placeholder}>`)}`;
756
796
  }
757
- parts.push(longAlias);
797
+ aliasParts.push(longAlias);
758
798
  }
759
799
  }
760
- return parts.join(", ");
800
+ const aliasStr = aliasParts.join(", ");
801
+ if (opt.type === "boolean" && opt.negationDisplay && !opt.negationDescription) return `${aliasStr} / ${styles.option(`--${opt.negationDisplay}`)}`;
802
+ return aliasStr;
761
803
  }
762
804
  /**
763
805
  * Format environment variable info for help display
@@ -809,14 +851,21 @@ function formatFieldLine(opt, indent = 0, extraDescPadding = 0) {
809
851
  * Render global options section
810
852
  */
811
853
  function renderGlobalOptions(globalExtracted) {
812
- return globalExtracted.fields.filter((a) => !a.positional).map((opt) => formatFieldLine(opt)).join("\n");
854
+ const lines = [];
855
+ for (const opt of globalExtracted.fields) {
856
+ if (opt.positional) continue;
857
+ lines.push(formatFieldLine(opt));
858
+ const negationLine = formatNegationLine(opt);
859
+ if (negationLine) lines.push(negationLine);
860
+ }
861
+ return lines.join("\n");
813
862
  }
814
863
  /**
815
864
  * Render options for a subcommand (used by showSubcommandOptions)
816
865
  */
817
866
  function renderSubcommandOptionsCompact(command, indent) {
818
867
  const lines = [];
819
- const extracted = require_lazy.getExtractedFields(command);
868
+ const extracted = require_subcommand_router.getExtractedFields(command);
820
869
  if (extracted) {
821
870
  const options = extracted.fields.filter((a) => !a.positional);
822
871
  for (const opt of options) {
@@ -826,6 +875,8 @@ function renderSubcommandOptionsCompact(command, indent) {
826
875
  const envInfo = formatEnvInfo(opt.env);
827
876
  if (envInfo) desc += ` ${envInfo}`;
828
877
  lines.push(formatOption(flags, desc, indent, 2));
878
+ const negationLine = formatNegationLine(opt, indent, 2);
879
+ if (negationLine) lines.push(negationLine);
829
880
  }
830
881
  }
831
882
  return lines;
@@ -836,10 +887,12 @@ function renderSubcommandOptionsCompact(command, indent) {
836
887
  function renderSubcommandsWithOptions(subCommands, parentPath, baseIndent) {
837
888
  const lines = [];
838
889
  for (const [name, subCmd] of getVisibleSubcommandEntries(subCommands)) {
839
- const cmd = require_lazy.resolveSubCommandMeta(subCmd);
890
+ const cmd = require_subcommand_router.resolveSubCommandMeta(subCmd);
840
891
  const fullPath = parentPath ? `${parentPath} ${name}` : name;
841
892
  const desc = cmd?.description ?? "";
842
- lines.push(formatOption(styles.command(fullPath), desc, baseIndent));
893
+ const aliases = cmd?.aliases;
894
+ const displayName = aliases && aliases.length > 0 ? `${fullPath}, ${aliases.join(", ")}` : fullPath;
895
+ lines.push(formatOption(styles.command(displayName), desc, baseIndent));
843
896
  if (cmd) {
844
897
  const optionLines = renderSubcommandOptionsCompact(cmd, baseIndent + 1);
845
898
  lines.push(...optionLines);
@@ -870,7 +923,9 @@ function generateHelp(command, options) {
870
923
  else if (context?.rootVersion) header += ` ${styles.version(`v${context.rootVersion}`)}`;
871
924
  sections.push(header);
872
925
  }
926
+ if (context?.aliasFor) sections.push(styles.dim(`Alias for ${styles.commandName(context.aliasFor)}`));
873
927
  if (command.description) sections.push(command.description);
928
+ if (!context?.aliasFor && command.aliases && command.aliases.length > 0) sections.push(`${styles.sectionHeader("Aliases:")} ${command.aliases.map((a) => styles.command(a)).join(", ")}`);
874
929
  sections.push(`${styles.sectionHeader("Usage:")} ${renderUsageLine(command, context)}`);
875
930
  const optionsText = renderOptions(command, options.descriptions, context);
876
931
  if (optionsText) sections.push(`${styles.sectionHeader("Options:")}\n${optionsText}`);
@@ -884,9 +939,12 @@ function generateHelp(command, options) {
884
939
  } else {
885
940
  const subLines = [];
886
941
  for (const [name, subCmd] of Object.entries(visibleSubCommands)) {
887
- const desc = require_lazy.resolveSubCommandMeta(subCmd)?.description ?? "";
942
+ const cmd = require_subcommand_router.resolveSubCommandMeta(subCmd);
943
+ const desc = cmd?.description ?? "";
888
944
  const fullName = currentPath ? `${currentPath} ${name}` : name;
889
- subLines.push(formatOption(styles.command(fullName), desc));
945
+ const aliases = cmd?.aliases;
946
+ const displayName = aliases && aliases.length > 0 ? `${fullName}, ${aliases.join(", ")}` : fullName;
947
+ subLines.push(formatOption(styles.command(displayName), desc));
890
948
  }
891
949
  sections.push(`${styles.sectionHeader("Commands:")}\n${subLines.join("\n")}`);
892
950
  }
@@ -966,6 +1024,17 @@ var CaseVariantCollisionError = class extends Error {
966
1024
  this.name = "CaseVariantCollisionError";
967
1025
  }
968
1026
  };
1027
+ /**
1028
+ * Error thrown when a custom boolean negation name collides with another
1029
+ * field's name, cliName, alias, or another field's negation (including
1030
+ * derived camelCase variants).
1031
+ */
1032
+ var DuplicateNegationError = class extends Error {
1033
+ constructor(message) {
1034
+ super(message);
1035
+ this.name = "DuplicateNegationError";
1036
+ }
1037
+ };
969
1038
 
970
1039
  //#endregion
971
1040
  //#region src/validator/command-validator.ts
@@ -993,7 +1062,7 @@ function checkCaseVariantCollisions(extracted, commandPath) {
993
1062
  const errors = [];
994
1063
  const canonicalMap = /* @__PURE__ */ new Map();
995
1064
  for (const field of extracted.fields) {
996
- const camel = require_lazy.toCamelCase(field.name);
1065
+ const camel = require_subcommand_router.toCamelCase(field.name);
997
1066
  const existing = canonicalMap.get(camel);
998
1067
  if (existing && existing !== field.name) errors.push({
999
1068
  commandPath,
@@ -1033,12 +1102,12 @@ function checkDuplicateAliases(extracted, commandPath) {
1033
1102
  seenAliases.set(alias, fieldName);
1034
1103
  };
1035
1104
  for (const field of extracted.fields) {
1036
- const allAliases = require_lazy.getAllAliases(field);
1105
+ const allAliases = require_subcommand_router.getAllAliases(field);
1037
1106
  if (allAliases.length === 0) continue;
1038
1107
  for (const alias of allAliases) {
1039
1108
  registerAlias(alias, field.name, false);
1040
1109
  if (alias.length > 1 && alias.includes("-")) {
1041
- const camelVariant = require_lazy.toCamelCase(alias);
1110
+ const camelVariant = require_subcommand_router.toCamelCase(alias);
1042
1111
  if (camelVariant !== alias && !fieldNames.has(camelVariant)) registerAlias(camelVariant, field.name, true);
1043
1112
  }
1044
1113
  }
@@ -1046,6 +1115,79 @@ function checkDuplicateAliases(extracted, commandPath) {
1046
1115
  return errors;
1047
1116
  }
1048
1117
  /**
1118
+ * Check for collisions involving custom boolean `negation` names
1119
+ */
1120
+ function checkDuplicateNegations(extracted, commandPath) {
1121
+ const errors = [];
1122
+ const claimed = /* @__PURE__ */ new Map();
1123
+ const claim = (name, fieldName, kind) => {
1124
+ if (!claimed.has(name)) claimed.set(name, {
1125
+ field: fieldName,
1126
+ kind
1127
+ });
1128
+ };
1129
+ for (const field of extracted.fields) {
1130
+ claim(field.name, field.name, "field name");
1131
+ if (field.name.includes("-")) {
1132
+ const camelName = require_subcommand_router.toCamelCase(field.name);
1133
+ if (camelName !== field.name) claim(camelName, field.name, "field name");
1134
+ }
1135
+ if (field.cliName !== field.name) claim(field.cliName, field.name, "CLI name");
1136
+ if (field.cliName.includes("-")) {
1137
+ const camelCli = require_subcommand_router.toCamelCase(field.cliName);
1138
+ if (camelCli !== field.cliName) claim(camelCli, field.name, "CLI name");
1139
+ }
1140
+ for (const alias of require_subcommand_router.getAllAliases(field)) {
1141
+ claim(alias, field.name, "alias");
1142
+ if (alias.length > 1 && alias.includes("-")) {
1143
+ const camelVariant = require_subcommand_router.toCamelCase(alias);
1144
+ if (camelVariant !== alias) claim(camelVariant, field.name, "alias");
1145
+ }
1146
+ }
1147
+ if (field.type === "boolean" && field.negation !== false && typeof field.negation !== "string") {
1148
+ const defaultKebab = `no-${field.cliName}`;
1149
+ claim(defaultKebab, field.name, "default negation");
1150
+ const camelBase = require_subcommand_router.toCamelCase(field.cliName);
1151
+ const defaultCamel = `no${camelBase[0]?.toUpperCase() ?? ""}${camelBase.slice(1)}`;
1152
+ if (defaultCamel !== defaultKebab) claim(defaultCamel, field.name, "default negation");
1153
+ }
1154
+ }
1155
+ const seenNegations = /* @__PURE__ */ new Map();
1156
+ const register = (name, fieldName, isDerived) => {
1157
+ const claim = claimed.get(name);
1158
+ if (claim) {
1159
+ const qualifier = isDerived ? " (derived camelCase variant)" : "";
1160
+ const conflict = claim.field === fieldName ? `the same field's own ${claim.kind} "${name}"` : `${claim.kind} "${name}" of field "${claim.field}"`;
1161
+ errors.push({
1162
+ commandPath,
1163
+ type: "duplicate_negation",
1164
+ message: `Negation "${name}"${qualifier} for field "${fieldName}" conflicts with ${conflict}.`,
1165
+ field: fieldName
1166
+ });
1167
+ }
1168
+ const existing = seenNegations.get(name);
1169
+ if (existing && existing !== fieldName) {
1170
+ const qualifier = isDerived ? " (derived camelCase variant)" : "";
1171
+ errors.push({
1172
+ commandPath,
1173
+ type: "duplicate_negation",
1174
+ message: `Duplicate negation "${name}"${qualifier} detected. Both "${existing}" and "${fieldName}" use the same negation name.`,
1175
+ field: fieldName
1176
+ });
1177
+ }
1178
+ seenNegations.set(name, fieldName);
1179
+ };
1180
+ for (const field of extracted.fields) {
1181
+ if (typeof field.negation !== "string") continue;
1182
+ register(field.negation, field.name, false);
1183
+ if (field.negation.includes("-")) {
1184
+ const camelVariant = require_subcommand_router.toCamelCase(field.negation);
1185
+ if (camelVariant !== field.negation) register(camelVariant, field.name, true);
1186
+ }
1187
+ }
1188
+ return errors;
1189
+ }
1190
+ /**
1049
1191
  * Check positional argument configuration
1050
1192
  */
1051
1193
  function checkPositionalConfig(extracted, commandPath) {
@@ -1084,7 +1226,7 @@ function checkReservedAliases(extracted, commandPath) {
1084
1226
  const errors = [];
1085
1227
  for (const field of extracted.fields) {
1086
1228
  if (field.overrideBuiltinAlias === true) continue;
1087
- for (const alias of require_lazy.getAllAliases(field)) if (alias === "h" || alias === "H") errors.push({
1229
+ for (const alias of require_subcommand_router.getAllAliases(field)) if (alias === "h" || alias === "H") errors.push({
1088
1230
  commandPath,
1089
1231
  type: "reserved_alias",
1090
1232
  message: `Alias "${alias}" is reserved for --${alias === "h" ? "help" : "help-all"}.`,
@@ -1155,11 +1297,24 @@ function validateReservedAliases(extracted, _hasSubCommands) {
1155
1297
  if (errors.length > 0) {
1156
1298
  const field = errors[0].field ?? "unknown";
1157
1299
  const found = extracted.fields.find((f) => f.name === field);
1158
- const alias = (found ? require_lazy.getAllAliases(found) : []).find((a) => a === "h" || a === "H") ?? "h";
1300
+ const alias = (found ? require_subcommand_router.getAllAliases(found) : []).find((a) => a === "h" || a === "H") ?? "h";
1159
1301
  throw new ReservedAliasError(`Alias "${alias}" is reserved for --${alias === "h" ? "help" : "help-all"}. To override this, set { overrideBuiltinAlias: true } for "${field}" and keep the alias where it is currently defined (in alias or hiddenAlias).`);
1160
1302
  }
1161
1303
  }
1162
1304
  /**
1305
+ * Validate that custom boolean negation names do not collide with anything
1306
+ *
1307
+ * @param extracted - Extracted fields from schema
1308
+ * @throws {DuplicateNegationError} If a colliding negation is found
1309
+ */
1310
+ function validateDuplicateNegations(extracted) {
1311
+ const errors = checkDuplicateNegations(extracted, []);
1312
+ if (errors.length > 0) {
1313
+ const err = errors[0];
1314
+ throw new DuplicateNegationError(err.message);
1315
+ }
1316
+ }
1317
+ /**
1163
1318
  * Validate that no case-variant collisions exist
1164
1319
  *
1165
1320
  * @param extracted - Extracted fields from schema
@@ -1182,9 +1337,9 @@ function validateCaseVariantCollisions(extracted) {
1182
1337
  */
1183
1338
  function validateCrossSchemaCollisions(extractedA, extractedB) {
1184
1339
  const canonicalMap = /* @__PURE__ */ new Map();
1185
- for (const field of extractedA.fields) canonicalMap.set(require_lazy.toCamelCase(field.name), field.name);
1340
+ for (const field of extractedA.fields) canonicalMap.set(require_subcommand_router.toCamelCase(field.name), field.name);
1186
1341
  for (const field of extractedB.fields) {
1187
- const camel = require_lazy.toCamelCase(field.name);
1342
+ const camel = require_subcommand_router.toCamelCase(field.name);
1188
1343
  const existing = canonicalMap.get(camel);
1189
1344
  if (existing && existing !== field.name) throw new CaseVariantCollisionError(`Global field "${existing}" and command field "${field.name}" are case variants of each other and would collide.`);
1190
1345
  }
@@ -1197,11 +1352,63 @@ function collectSchemaErrors(extracted, _hasSubCommands, commandPath) {
1197
1352
  ...checkDuplicateFields(extracted, commandPath),
1198
1353
  ...checkCaseVariantCollisions(extracted, commandPath),
1199
1354
  ...checkDuplicateAliases(extracted, commandPath),
1355
+ ...checkDuplicateNegations(extracted, commandPath),
1200
1356
  ...checkPositionalConfig(extracted, commandPath),
1201
1357
  ...checkReservedAliases(extracted, commandPath)
1202
1358
  ];
1203
1359
  }
1204
1360
  /**
1361
+ * Check for alias conflicts within subcommands
1362
+ * - Aliases must not conflict with subcommand names
1363
+ * - Aliases must not conflict with other aliases
1364
+ */
1365
+ function checkSubCommandAliasConflicts(command, commandPath) {
1366
+ const errors = [];
1367
+ if (!command.subCommands) return errors;
1368
+ const nameToOwner = /* @__PURE__ */ new Map();
1369
+ for (const [name] of Object.entries(command.subCommands)) nameToOwner.set(name, name);
1370
+ for (const [name, subCmdValue] of Object.entries(command.subCommands)) {
1371
+ const resolved = require_subcommand_router.isLazyCommand(subCmdValue) ? subCmdValue.meta : typeof subCmdValue !== "function" ? subCmdValue : null;
1372
+ if (!resolved?.aliases) continue;
1373
+ const subCommandPath = [...commandPath, name];
1374
+ for (const alias of resolved.aliases) {
1375
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(alias)) {
1376
+ errors.push({
1377
+ commandPath: subCommandPath,
1378
+ type: "invalid_alias",
1379
+ message: `Alias "${alias}" is invalid. Aliases must start with an alphanumeric character and contain only alphanumeric characters, hyphens, or underscores.`,
1380
+ field: name
1381
+ });
1382
+ continue;
1383
+ }
1384
+ if (alias === name) {
1385
+ errors.push({
1386
+ commandPath: subCommandPath,
1387
+ type: "duplicate_alias",
1388
+ message: `Alias "${alias}" conflicts with its own name.`,
1389
+ field: name
1390
+ });
1391
+ continue;
1392
+ }
1393
+ const existing = nameToOwner.get(alias);
1394
+ if (existing) if (existing === name) errors.push({
1395
+ commandPath: subCommandPath,
1396
+ type: "duplicate_alias",
1397
+ message: `Alias "${alias}" is duplicated within the alias list.`,
1398
+ field: name
1399
+ });
1400
+ else errors.push({
1401
+ commandPath: subCommandPath,
1402
+ type: "duplicate_alias",
1403
+ message: `Alias "${alias}" conflicts with existing subcommand or alias "${existing}".`,
1404
+ field: name
1405
+ });
1406
+ else nameToOwner.set(alias, name);
1407
+ }
1408
+ }
1409
+ return errors;
1410
+ }
1411
+ /**
1205
1412
  * Validate a command and all its subcommands recursively
1206
1413
  *
1207
1414
  * This function collects all validation errors without throwing,
@@ -1224,9 +1431,10 @@ async function validateCommand(command, options = {}) {
1224
1431
  const errors = [];
1225
1432
  const hasSubCommands = command.subCommands ? Object.keys(command.subCommands).length > 0 : false;
1226
1433
  if (command.args) {
1227
- const extracted = require_lazy.extractFields(command.args);
1434
+ const extracted = require_subcommand_router.extractFields(command.args);
1228
1435
  errors.push(...collectSchemaErrors(extracted, hasSubCommands, commandPath));
1229
1436
  }
1437
+ errors.push(...checkSubCommandAliasConflicts(command, commandPath));
1230
1438
  if (command.subCommands) for (const [name, subCmd] of Object.entries(command.subCommands)) {
1231
1439
  const subResult = await validateCommand(await require_subcommand_router.resolveLazyCommand(subCmd), { commandPath: [...commandPath, name] });
1232
1440
  if (!subResult.valid) errors.push(...subResult.errors);
@@ -1277,7 +1485,7 @@ function formatCommandValidationErrors(errors) {
1277
1485
  * @returns Parsed arguments
1278
1486
  */
1279
1487
  function parseArgv(argv, options = {}) {
1280
- const { aliasMap = /* @__PURE__ */ new Map(), booleanFlags = /* @__PURE__ */ new Set(), arrayFlags = /* @__PURE__ */ new Set(), definedNames = /* @__PURE__ */ new Set() } = options;
1488
+ const { aliasMap = /* @__PURE__ */ new Map(), booleanFlags = /* @__PURE__ */ new Set(), arrayFlags = /* @__PURE__ */ new Set(), definedNames = /* @__PURE__ */ new Set(), negationMap = /* @__PURE__ */ new Map(), customNegatedFields = /* @__PURE__ */ new Set() } = options;
1281
1489
  const result = {
1282
1490
  options: {},
1283
1491
  positionals: [],
@@ -1308,11 +1516,19 @@ function parseArgv(argv, options = {}) {
1308
1516
  }
1309
1517
  if (arg.startsWith("--")) {
1310
1518
  const withoutDashes = arg.slice(2);
1519
+ if (!withoutDashes.includes("=")) {
1520
+ const negatedField = negationMap.get(withoutDashes);
1521
+ if (negatedField && booleanFlags.has(negatedField)) {
1522
+ setOption(negatedField, false);
1523
+ i++;
1524
+ continue;
1525
+ }
1526
+ }
1311
1527
  if (withoutDashes.startsWith("no-")) {
1312
1528
  const flagName = withoutDashes.slice(3);
1313
1529
  if (flagName === flagName.toLowerCase()) {
1314
1530
  const resolvedName = aliasMap.get(flagName) ?? flagName;
1315
- if (booleanFlags.has(resolvedName)) {
1531
+ if (booleanFlags.has(resolvedName) && !customNegatedFields.has(resolvedName)) {
1316
1532
  const asIsResolved = aliasMap.get(withoutDashes) ?? withoutDashes;
1317
1533
  if (!definedNames.has(asIsResolved)) {
1318
1534
  setOption(flagName, false);
@@ -1325,7 +1541,7 @@ function parseArgv(argv, options = {}) {
1325
1541
  if (withoutDashes.length > 2 && withoutDashes.startsWith("no") && /[A-Z]/.test(withoutDashes[2])) {
1326
1542
  const camelFlagName = withoutDashes[2].toLowerCase() + withoutDashes.slice(3);
1327
1543
  const resolvedName = aliasMap.get(camelFlagName) ?? camelFlagName;
1328
- if (booleanFlags.has(resolvedName)) {
1544
+ if (booleanFlags.has(resolvedName) && !customNegatedFields.has(resolvedName)) {
1329
1545
  const asIsResolved = aliasMap.get(withoutDashes) ?? withoutDashes;
1330
1546
  if (!definedNames.has(asIsResolved)) {
1331
1547
  setOption(camelFlagName, false);
@@ -1398,26 +1614,40 @@ function buildParserOptions(extracted) {
1398
1614
  const booleanFlags = /* @__PURE__ */ new Set();
1399
1615
  const arrayFlags = /* @__PURE__ */ new Set();
1400
1616
  const definedNames = /* @__PURE__ */ new Set();
1617
+ const negationMap = /* @__PURE__ */ new Map();
1618
+ const customNegatedFields = /* @__PURE__ */ new Set();
1401
1619
  for (const field of extracted.fields) definedNames.add(field.name);
1402
1620
  for (const field of extracted.fields) {
1403
1621
  if (field.cliName !== field.name) aliasMap.set(field.cliName, field.name);
1404
- for (const alias of require_lazy.getAllAliases(field)) {
1622
+ for (const alias of require_subcommand_router.getAllAliases(field)) {
1405
1623
  aliasMap.set(alias, field.name);
1406
1624
  if (alias.length > 1 && alias.includes("-")) {
1407
- const camelAlias = require_lazy.toCamelCase(alias);
1625
+ const camelAlias = require_subcommand_router.toCamelCase(alias);
1408
1626
  if (camelAlias !== alias && !definedNames.has(camelAlias) && !aliasMap.has(camelAlias)) aliasMap.set(camelAlias, field.name);
1409
1627
  }
1410
1628
  }
1411
- const camelVariant = require_lazy.toCamelCase(field.name);
1629
+ const camelVariant = require_subcommand_router.toCamelCase(field.name);
1412
1630
  if (camelVariant !== field.name && !definedNames.has(camelVariant) && !aliasMap.has(camelVariant)) aliasMap.set(camelVariant, field.name);
1413
1631
  if (field.type === "boolean") booleanFlags.add(field.name);
1414
1632
  if (field.type === "array") arrayFlags.add(field.name);
1633
+ if (field.type === "boolean" && (typeof field.negation === "string" || field.negation === false)) {
1634
+ customNegatedFields.add(field.name);
1635
+ if (typeof field.negation === "string") {
1636
+ negationMap.set(field.negation, field.name);
1637
+ if (field.negation.includes("-")) {
1638
+ const camelNegation = require_subcommand_router.toCamelCase(field.negation);
1639
+ if (camelNegation !== field.negation) negationMap.set(camelNegation, field.name);
1640
+ }
1641
+ }
1642
+ }
1415
1643
  }
1416
1644
  return {
1417
1645
  aliasMap,
1418
1646
  booleanFlags,
1419
1647
  arrayFlags,
1420
- definedNames
1648
+ definedNames,
1649
+ negationMap,
1650
+ customNegatedFields
1421
1651
  };
1422
1652
  }
1423
1653
  /**
@@ -1448,31 +1678,61 @@ function mergeWithPositionals(parsed, extracted) {
1448
1678
  * Shared by scanForSubcommand, separateGlobalArgs, and findFirstPositional.
1449
1679
  */
1450
1680
  function buildGlobalFlagLookup(globalExtracted) {
1451
- const { aliasMap = /* @__PURE__ */ new Map(), booleanFlags = /* @__PURE__ */ new Set() } = buildParserOptions(globalExtracted);
1681
+ const { aliasMap = /* @__PURE__ */ new Map(), booleanFlags = /* @__PURE__ */ new Set(), negationMap = /* @__PURE__ */ new Map(), customNegatedFields = /* @__PURE__ */ new Set() } = buildParserOptions(globalExtracted);
1452
1682
  const shortAliases = /* @__PURE__ */ new Set();
1453
- for (const field of globalExtracted.fields) for (const alias of require_lazy.getAllAliases(field)) if (alias.length === 1) shortAliases.add(alias);
1683
+ for (const field of globalExtracted.fields) for (const alias of require_subcommand_router.getAllAliases(field)) if (alias.length === 1) shortAliases.add(alias);
1454
1684
  return {
1455
1685
  aliasMap,
1456
1686
  booleanFlags,
1457
1687
  flagNames: new Set(globalExtracted.fields.map((f) => f.name)),
1458
1688
  cliNames: new Set(globalExtracted.fields.map((f) => f.cliName)),
1459
- aliases: shortAliases
1689
+ aliases: shortAliases,
1690
+ negationMap,
1691
+ customNegatedFields
1460
1692
  };
1461
1693
  }
1462
1694
  /**
1463
- * Resolve a long option (--flag, --flag=value, --no-flag) against global flag lookup.
1464
- * Returns the resolved camelCase name and whether it is a known global flag.
1695
+ * Resolve a long option (--flag, --flag=value, --no-flag, --custom-negation)
1696
+ * against global flag lookup. Returns the resolved camelCase name and whether
1697
+ * it is a known global flag.
1698
+ *
1699
+ * `isSuppressedNegation` is true when the token matches a default `--no-X`
1700
+ * form that has been suppressed by a custom `negation` on the target field.
1701
+ * The caller may use this to keep argv scanning past such tokens (so a
1702
+ * trailing subcommand is still detected) even though they no longer negate.
1465
1703
  */
1466
1704
  function resolveGlobalLongOption(arg, lookup) {
1467
1705
  const withoutDashes = arg.includes("=") ? arg.slice(2, arg.indexOf("=")) : arg.slice(2);
1468
- const isNegated = withoutDashes.startsWith("no-");
1469
- const flagName = isNegated ? withoutDashes.slice(3) : withoutDashes;
1706
+ const customNegated = !arg.includes("=") ? lookup.negationMap.get(withoutDashes) : void 0;
1707
+ if (customNegated) return {
1708
+ resolvedName: customNegated,
1709
+ withoutDashes,
1710
+ isNegated: true,
1711
+ isGlobal: lookup.flagNames.has(customNegated),
1712
+ isSuppressedNegation: false
1713
+ };
1714
+ const kebabNegated = withoutDashes.startsWith("no-");
1715
+ const camelNegated = !kebabNegated && withoutDashes.length > 2 && withoutDashes.startsWith("no") && /[A-Z]/.test(withoutDashes[2]);
1716
+ if (kebabNegated || camelNegated) {
1717
+ const literalResolved = lookup.aliasMap.get(withoutDashes) ?? withoutDashes;
1718
+ if (lookup.flagNames.has(literalResolved) || lookup.cliNames.has(withoutDashes)) return {
1719
+ resolvedName: literalResolved,
1720
+ withoutDashes,
1721
+ isNegated: false,
1722
+ isGlobal: true,
1723
+ isSuppressedNegation: false
1724
+ };
1725
+ }
1726
+ const defaultIsNegated = kebabNegated || camelNegated;
1727
+ const flagName = kebabNegated ? withoutDashes.slice(3) : camelNegated ? withoutDashes[2].toLowerCase() + withoutDashes.slice(3) : withoutDashes;
1470
1728
  const resolvedName = lookup.aliasMap.get(flagName) ?? flagName;
1729
+ const suppressDefaultNegation = defaultIsNegated && lookup.customNegatedFields.has(resolvedName);
1471
1730
  return {
1472
1731
  resolvedName,
1473
1732
  withoutDashes,
1474
- isNegated,
1475
- isGlobal: lookup.flagNames.has(resolvedName) || lookup.cliNames.has(withoutDashes) || lookup.cliNames.has(flagName)
1733
+ isNegated: defaultIsNegated && !suppressDefaultNegation,
1734
+ isGlobal: !suppressDefaultNegation && (lookup.flagNames.has(resolvedName) || lookup.cliNames.has(withoutDashes) || lookup.cliNames.has(flagName)),
1735
+ isSuppressedNegation: suppressDefaultNegation
1476
1736
  };
1477
1737
  }
1478
1738
  /**
@@ -1520,6 +1780,7 @@ function scanForSubcommand(argv, subCommandNames, globalExtracted) {
1520
1780
  const lookup = buildGlobalFlagLookup(globalExtracted);
1521
1781
  const subCommandNameSet = new Set(subCommandNames);
1522
1782
  const globalTokensBefore = [];
1783
+ const suppressedTokens = [];
1523
1784
  let i = 0;
1524
1785
  while (i < argv.length) {
1525
1786
  const arg = argv[i];
@@ -1527,14 +1788,20 @@ function scanForSubcommand(argv, subCommandNames, globalExtracted) {
1527
1788
  if (!arg.startsWith("-") && subCommandNameSet.has(arg)) return {
1528
1789
  subCommandIndex: i,
1529
1790
  globalTokensBefore,
1530
- tokensAfterSubcommand: argv.slice(i + 1)
1791
+ tokensAfterSubcommand: argv.slice(i + 1),
1792
+ suppressedTokens
1531
1793
  };
1532
1794
  if (arg.startsWith("--")) {
1533
- const { resolvedName, isNegated, isGlobal } = resolveGlobalLongOption(arg, lookup);
1795
+ const { resolvedName, isNegated, isGlobal, isSuppressedNegation } = resolveGlobalLongOption(arg, lookup);
1534
1796
  if (isGlobal) {
1535
1797
  i += collectGlobalFlag(argv, i, resolvedName, isNegated, lookup.booleanFlags, globalTokensBefore);
1536
1798
  continue;
1537
1799
  }
1800
+ if (isSuppressedNegation) {
1801
+ suppressedTokens.push(arg.includes("=") ? arg.slice(2, arg.indexOf("=")) : arg.slice(2));
1802
+ i++;
1803
+ continue;
1804
+ }
1538
1805
  break;
1539
1806
  }
1540
1807
  if (arg.startsWith("-") && arg.length > 1) {
@@ -1553,7 +1820,8 @@ function scanForSubcommand(argv, subCommandNames, globalExtracted) {
1553
1820
  return {
1554
1821
  subCommandIndex: -1,
1555
1822
  globalTokensBefore,
1556
- tokensAfterSubcommand: []
1823
+ tokensAfterSubcommand: [],
1824
+ suppressedTokens
1557
1825
  };
1558
1826
  }
1559
1827
  const BUILTIN_FLAGS = new Set([
@@ -1599,7 +1867,8 @@ function findFirstPositional(argv, globalExtracted) {
1599
1867
  * @returns Parse result
1600
1868
  */
1601
1869
  function parseArgs(argv, command, options = {}) {
1602
- const subCommandNames = command.subCommands ? Object.keys(command.subCommands) : [];
1870
+ const subCommandNameSet = require_subcommand_router.listSubCommandNamesWithAliases(command);
1871
+ const subCommandNames = [...subCommandNameSet];
1603
1872
  const hasSubCommands = subCommandNames.length > 0;
1604
1873
  if (hasSubCommands && argv.length > 0) if (options.globalExtracted) {
1605
1874
  const scanResult = scanForSubcommand(argv, subCommandNames, options.globalExtracted);
@@ -1613,13 +1882,13 @@ function parseArgs(argv, command, options = {}) {
1613
1882
  remainingArgs: scanResult.tokensAfterSubcommand,
1614
1883
  rawArgs: {},
1615
1884
  positionals: [],
1616
- unknownFlags: [],
1885
+ unknownFlags: scanResult.suppressedTokens,
1617
1886
  rawGlobalArgs
1618
1887
  };
1619
1888
  }
1620
1889
  } else {
1621
1890
  const firstArg = argv[0];
1622
- if (firstArg && !firstArg.startsWith("-") && subCommandNames.includes(firstArg)) return {
1891
+ if (firstArg && !firstArg.startsWith("-") && subCommandNameSet.has(firstArg)) return {
1623
1892
  helpRequested: false,
1624
1893
  helpAllRequested: false,
1625
1894
  versionRequested: false,
@@ -1632,18 +1901,19 @@ function parseArgs(argv, command, options = {}) {
1632
1901
  }
1633
1902
  let extracted;
1634
1903
  if (command.args) {
1635
- extracted = require_lazy.extractFields(command.args);
1904
+ extracted = require_subcommand_router.extractFields(command.args);
1636
1905
  if (!options.skipValidation) {
1637
1906
  validateDuplicateFields(extracted);
1638
1907
  validateCaseVariantCollisions(extracted);
1639
1908
  validateDuplicateAliases(extracted);
1909
+ validateDuplicateNegations(extracted);
1640
1910
  validatePositionalConfig(extracted);
1641
1911
  validateReservedAliases(extracted, hasSubCommands);
1642
1912
  if (options.globalExtracted) validateCrossSchemaCollisions(options.globalExtracted, extracted);
1643
1913
  }
1644
1914
  }
1645
- const hasUserDefinedH = extracted?.fields.some((f) => f.overrideBuiltinAlias === true && require_lazy.getAllAliases(f).includes("H")) ?? false;
1646
- const hasUserDefinedh = extracted?.fields.some((f) => f.overrideBuiltinAlias === true && require_lazy.getAllAliases(f).includes("h")) ?? false;
1915
+ const hasUserDefinedH = extracted?.fields.some((f) => f.overrideBuiltinAlias === true && require_subcommand_router.getAllAliases(f).includes("H")) ?? false;
1916
+ const hasUserDefinedh = extracted?.fields.some((f) => f.overrideBuiltinAlias === true && require_subcommand_router.getAllAliases(f).includes("h")) ?? false;
1647
1917
  const helpAllRequested = argv.includes("--help-all") || !hasUserDefinedH && argv.includes("-H");
1648
1918
  const helpRequested = !helpAllRequested && (argv.includes("--help") || !hasUserDefinedh && argv.includes("-h"));
1649
1919
  const versionRequested = argv.includes("--version");
@@ -1691,11 +1961,11 @@ function parseArgs(argv, command, options = {}) {
1691
1961
  const knownFlags = new Set(extracted.fields.map((f) => f.name));
1692
1962
  const knownCliNames = new Set(extracted.fields.map((f) => f.cliName));
1693
1963
  const knownAliases = /* @__PURE__ */ new Set();
1694
- for (const f of extracted.fields) for (const alias of require_lazy.getAllAliases(f)) knownAliases.add(alias);
1964
+ for (const f of extracted.fields) for (const alias of require_subcommand_router.getAllAliases(f)) knownAliases.add(alias);
1695
1965
  if (options.globalExtracted) for (const f of options.globalExtracted.fields) {
1696
1966
  knownFlags.add(f.name);
1697
1967
  knownCliNames.add(f.cliName);
1698
- for (const alias of require_lazy.getAllAliases(f)) knownAliases.add(alias);
1968
+ for (const alias of require_subcommand_router.getAllAliases(f)) knownAliases.add(alias);
1699
1969
  }
1700
1970
  const unknownFlags = [];
1701
1971
  for (const key of Object.keys(parsed.options)) if (!knownFlags.has(key) && !knownCliNames.has(key) && !knownAliases.has(key)) unknownFlags.push(key);
@@ -1746,7 +2016,7 @@ function separateGlobalArgs(argv, globalExtracted, localExtracted) {
1746
2016
  }
1747
2017
  if (arg.startsWith("--")) {
1748
2018
  const { resolvedName, withoutDashes, isNegated, isGlobal } = resolveGlobalLongOption(arg, lookup);
1749
- const flagName = isNegated ? withoutDashes.slice(3) : withoutDashes;
2019
+ const flagName = resolvedName;
1750
2020
  const isLocalCollision = localFieldNames.has(withoutDashes) || localFieldNames.has(flagName) || localCliNames.has(withoutDashes) || localCliNames.has(flagName) || localAliasMapKeys.has(withoutDashes) || localAliasMapKeys.has(flagName);
1751
2021
  if (isGlobal && !isLocalCollision) {
1752
2022
  i += collectGlobalFlag(argv, i, resolvedName, isNegated, lookup.booleanFlags, globalTokens) - 1;
@@ -1887,7 +2157,7 @@ async function runEffects(validatedArgs, extracted, globalArgs) {
1887
2157
 
1888
2158
  //#endregion
1889
2159
  //#region src/core/runner.ts
1890
- var runner_exports = /* @__PURE__ */ require_subcommand_router.__exportAll({
2160
+ var runner_exports = /* @__PURE__ */ require_log_collector.__exportAll({
1891
2161
  runCommand: () => runCommand,
1892
2162
  runMain: () => runMain
1893
2163
  });
@@ -1927,7 +2197,7 @@ const defaultLogger = {
1927
2197
  async function runCommand(command, argv, options = {}) {
1928
2198
  const globalExtracted = extractAndValidateGlobal(options);
1929
2199
  const shouldCaptureLogs = options.captureLogs ?? false;
1930
- const globalCollector = shouldCaptureLogs ? require_subcommand_router.createLogCollector() : null;
2200
+ const globalCollector = shouldCaptureLogs ? require_log_collector.createLogCollector() : null;
1931
2201
  if (options.setup) {
1932
2202
  globalCollector?.start();
1933
2203
  try {
@@ -1942,7 +2212,7 @@ async function runCommand(command, argv, options = {}) {
1942
2212
  success: false,
1943
2213
  error,
1944
2214
  exitCode: 1,
1945
- logs: globalCollector?.getLogs() ?? require_subcommand_router.emptyLogs()
2215
+ logs: globalCollector?.getLogs() ?? require_log_collector.emptyLogs()
1946
2216
  };
1947
2217
  }
1948
2218
  globalCollector?.stop();
@@ -1955,7 +2225,7 @@ async function runCommand(command, argv, options = {}) {
1955
2225
  _existingLogs: globalCollector?.getLogs()
1956
2226
  });
1957
2227
  if (options.cleanup) {
1958
- const cleanupCollector = shouldCaptureLogs ? require_subcommand_router.createLogCollector() : null;
2228
+ const cleanupCollector = shouldCaptureLogs ? require_log_collector.createLogCollector() : null;
1959
2229
  cleanupCollector?.start();
1960
2230
  const cleanupCtx = { error: !result.success ? result.error : void 0 };
1961
2231
  try {
@@ -1968,26 +2238,50 @@ async function runCommand(command, argv, options = {}) {
1968
2238
  success: false,
1969
2239
  error,
1970
2240
  exitCode: 1,
1971
- logs: require_subcommand_router.mergeLogs(result.logs, cleanupCollector?.getLogs() ?? require_subcommand_router.emptyLogs())
2241
+ logs: require_log_collector.mergeLogs(result.logs, cleanupCollector?.getLogs() ?? require_log_collector.emptyLogs())
1972
2242
  };
1973
2243
  }
1974
2244
  }
1975
2245
  cleanupCollector?.stop();
1976
- const cleanupLogs = cleanupCollector?.getLogs() ?? require_subcommand_router.emptyLogs();
2246
+ const cleanupLogs = cleanupCollector?.getLogs() ?? require_log_collector.emptyLogs();
1977
2247
  if (cleanupLogs.entries.length > 0) return {
1978
2248
  ...result,
1979
- logs: require_subcommand_router.mergeLogs(result.logs, cleanupLogs)
2249
+ logs: require_log_collector.mergeLogs(result.logs, cleanupLogs)
1980
2250
  };
1981
2251
  }
1982
2252
  return result;
1983
2253
  }
1984
2254
  /**
2255
+ * Hidden internal subcommands (e.g. `__refresh-completion`) are spawned
2256
+ * by background hooks and must not run user-provided
2257
+ * `setup`/`cleanup`/`prompt` or required `globalArgs`. Those exist for
2258
+ * the foreground CLI run; replaying them in a detached child causes
2259
+ * duplicate side effects, stuck prompts, and validation failures the
2260
+ * user never opted into.
2261
+ *
2262
+ * We treat any registered subcommand whose name starts with `__` as
2263
+ * internal. We use `findFirstPositional` (schema-aware) instead of the
2264
+ * naive "first non-flag token" so an option *value* like
2265
+ * `--name __refresh-completion` doesn't trip the bypass — that would
2266
+ * silently skip lifecycle hooks for ordinary invocations.
2267
+ */
2268
+ function isInternalSubcommandInvocation(command, argv, globalExtracted) {
2269
+ const firstPositional = findFirstPositional(argv, globalExtracted);
2270
+ if (!firstPositional || !firstPositional.startsWith("__")) return false;
2271
+ return Boolean(command.subCommands?.[firstPositional]);
2272
+ }
2273
+ /**
1985
2274
  * Run a CLI command as the main entry point
1986
2275
  *
1987
2276
  * This function:
1988
2277
  * - Uses process.argv for arguments
1989
2278
  * - Handles SIGINT/SIGTERM signals
1990
2279
  * - Calls process.exit with the appropriate exit code
2280
+ * - Invokes `command.runMainHook` once before parsing if set, so plug-ins
2281
+ * like `withCompletionCommand` can fire detached background work
2282
+ * - Bypasses user `setup`/`cleanup`/`prompt` and required `globalArgs`
2283
+ * for registered hidden subcommands whose name starts with `__`
2284
+ * (e.g. `__refresh-completion`)
1991
2285
  *
1992
2286
  * @param command - The command to run
1993
2287
  * @param options - Main options (version, debug)
@@ -2005,38 +2299,51 @@ async function runCommand(command, argv, options = {}) {
2005
2299
  * ```
2006
2300
  */
2007
2301
  async function runMain(command, options = {}) {
2008
- const globalExtracted = extractAndValidateGlobal(options);
2009
- if (options.setup) try {
2010
- await options.setup({});
2302
+ if (command.runMainHook) try {
2303
+ command.runMainHook(process.argv.slice(2));
2304
+ } catch {}
2305
+ const argv = process.argv.slice(2);
2306
+ let globalExtractedForBypass;
2307
+ if (options.globalArgs) try {
2308
+ globalExtractedForBypass = require_subcommand_router.extractFields(options.globalArgs);
2309
+ } catch {}
2310
+ let effectiveOptions = options;
2311
+ if (isInternalSubcommandInvocation(command, argv, globalExtractedForBypass)) {
2312
+ const { setup: _s, cleanup: _c, prompt: _p, globalArgs: _g, ...rest } = options;
2313
+ effectiveOptions = rest;
2314
+ }
2315
+ const globalExtracted = extractAndValidateGlobal(effectiveOptions);
2316
+ if (effectiveOptions.setup) try {
2317
+ await effectiveOptions.setup({});
2011
2318
  } catch (e) {
2012
2319
  const error = e instanceof Error ? e : new Error(String(e));
2013
- if (options.cleanup) try {
2014
- await options.cleanup({ error });
2320
+ if (effectiveOptions.cleanup) try {
2321
+ await effectiveOptions.cleanup({ error });
2015
2322
  } catch {}
2016
2323
  process.exit(1);
2017
2324
  }
2018
- const result = await runCommandInternal(command, process.argv.slice(2), {
2019
- debug: options.debug,
2020
- captureLogs: options.captureLogs,
2021
- skipValidation: options.skipValidation,
2325
+ const result = await runCommandInternal(command, argv, {
2326
+ debug: effectiveOptions.debug,
2327
+ captureLogs: effectiveOptions.captureLogs,
2328
+ skipValidation: effectiveOptions.skipValidation,
2022
2329
  handleSignals: true,
2023
- logger: options.logger,
2024
- globalArgs: options.globalArgs,
2025
- prompt: options.prompt,
2330
+ logger: effectiveOptions.logger,
2331
+ globalArgs: effectiveOptions.globalArgs,
2332
+ prompt: effectiveOptions.prompt,
2026
2333
  _globalExtracted: globalExtracted,
2027
- _globalCleanup: options.cleanup,
2334
+ _globalCleanup: effectiveOptions.cleanup,
2028
2335
  _context: {
2029
2336
  commandPath: [],
2030
2337
  rootName: command.name,
2031
- rootVersion: options.version,
2338
+ rootVersion: effectiveOptions.version,
2032
2339
  globalExtracted
2033
2340
  }
2034
2341
  });
2035
- if ((options.displayErrors ?? true) && !result.success && result.error) (options.logger ?? defaultLogger).error(formatRuntimeError(result.error, options.debug ?? false));
2036
- if (options.cleanup) {
2342
+ if ((effectiveOptions.displayErrors ?? true) && !result.success && result.error) (effectiveOptions.logger ?? defaultLogger).error(formatRuntimeError(result.error, effectiveOptions.debug ?? false));
2343
+ if (effectiveOptions.cleanup) {
2037
2344
  const cleanupCtx = { error: !result.success ? result.error : void 0 };
2038
2345
  try {
2039
- await options.cleanup(cleanupCtx);
2346
+ await effectiveOptions.cleanup(cleanupCtx);
2040
2347
  } catch {}
2041
2348
  }
2042
2349
  if (process.stdout.writableLength > 0) await new Promise((resolve) => process.stdout.once("drain", resolve));
@@ -2053,10 +2360,10 @@ async function runCommandInternal(command, argv, options = {}) {
2053
2360
  rootName: command.name,
2054
2361
  globalExtracted: options._globalExtracted
2055
2362
  };
2056
- const collector = options.captureLogs ?? false ? require_subcommand_router.createLogCollector() : null;
2363
+ const collector = options.captureLogs ?? false ? require_log_collector.createLogCollector() : null;
2057
2364
  collector?.start();
2058
2365
  const getCurrentLogs = () => {
2059
- return require_subcommand_router.mergeLogs(options._existingLogs ?? require_subcommand_router.emptyLogs(), collector?.getLogs() ?? require_subcommand_router.emptyLogs());
2366
+ return require_log_collector.mergeLogs(options._existingLogs ?? require_log_collector.emptyLogs(), collector?.getLogs() ?? require_log_collector.emptyLogs());
2060
2367
  };
2061
2368
  try {
2062
2369
  const parseResult = parseArgs(argv, command, {
@@ -2070,9 +2377,10 @@ async function runCommandInternal(command, argv, options = {}) {
2070
2377
  if (parseResult.helpRequested || parseResult.helpAllRequested) {
2071
2378
  let hasUnknownSubcommand = false;
2072
2379
  const subCmdNames = require_subcommand_router.listSubCommands(command);
2380
+ const allSubCmdNameSet = require_subcommand_router.listSubCommandNamesWithAliases(command);
2073
2381
  if (subCmdNames.length > 0) {
2074
2382
  const potentialSubCmd = findFirstPositional(argv, context.globalExtracted);
2075
- if (potentialSubCmd && !subCmdNames.includes(potentialSubCmd)) hasUnknownSubcommand = true;
2383
+ if (potentialSubCmd && !allSubCmdNameSet.has(potentialSubCmd)) hasUnknownSubcommand = true;
2076
2384
  }
2077
2385
  const help = generateHelp(command, {
2078
2386
  showSubcommands: options.showSubcommands ?? true,
@@ -2083,7 +2391,7 @@ async function runCommandInternal(command, argv, options = {}) {
2083
2391
  collector?.stop();
2084
2392
  if (hasUnknownSubcommand) {
2085
2393
  const unknownCmd = findFirstPositional(argv, context.globalExtracted) ?? "";
2086
- const similar = findSimilar(unknownCmd, subCmdNames);
2394
+ const similar = findSimilar(unknownCmd, [...allSubCmdNameSet]);
2087
2395
  const suggestion = similar.length > 0 ? ` Did you mean: ${similar.join(", ")}?` : "";
2088
2396
  return {
2089
2397
  success: false,
@@ -2111,16 +2419,33 @@ async function runCommandInternal(command, argv, options = {}) {
2111
2419
  };
2112
2420
  }
2113
2421
  if (parseResult.subCommand) {
2114
- const subCmd = await require_subcommand_router.resolveSubcommand(command, parseResult.subCommand);
2115
- if (subCmd) {
2422
+ if (parseResult.unknownFlags.length > 0) {
2423
+ const globalMode = context.globalExtracted?.unknownKeysMode ?? "strip";
2424
+ if (globalMode === "strict") {
2425
+ collector?.stop();
2426
+ return {
2427
+ success: false,
2428
+ error: /* @__PURE__ */ new Error(`Unknown flags: ${parseResult.unknownFlags.join(", ")}`),
2429
+ exitCode: 1,
2430
+ logs: getCurrentLogs()
2431
+ };
2432
+ }
2433
+ if (globalMode === "strip") {
2434
+ const knownGlobalFlags = context.globalExtracted?.fields.map((f) => f.name) ?? [];
2435
+ for (const flag of parseResult.unknownFlags) logger.error(formatUnknownFlagWarning(flag, knownGlobalFlags));
2436
+ }
2437
+ }
2438
+ const resolved = await require_subcommand_router.resolveSubcommandWithAlias(command, parseResult.subCommand);
2439
+ if (resolved) {
2116
2440
  const subContext = {
2117
2441
  commandPath: [...context.commandPath ?? [], parseResult.subCommand],
2118
2442
  rootName: context.rootName,
2119
2443
  rootVersion: context.rootVersion,
2120
- globalExtracted: context.globalExtracted
2444
+ globalExtracted: context.globalExtracted,
2445
+ aliasFor: resolved.aliasFor
2121
2446
  };
2122
2447
  collector?.stop();
2123
- return runCommandInternal(subCmd, parseResult.remainingArgs, {
2448
+ return runCommandInternal(resolved.command, parseResult.remainingArgs, {
2124
2449
  ...options,
2125
2450
  _context: subContext,
2126
2451
  _existingLogs: getCurrentLogs(),
@@ -2245,11 +2570,12 @@ async function runCommandInternal(command, argv, options = {}) {
2245
2570
  */
2246
2571
  function extractAndValidateGlobal(options) {
2247
2572
  if (!options.globalArgs) return void 0;
2248
- const extracted = require_lazy.extractFields(options.globalArgs);
2573
+ const extracted = require_subcommand_router.extractFields(options.globalArgs);
2249
2574
  if (!options.skipValidation) {
2250
2575
  validateDuplicateFields(extracted);
2251
2576
  validateCaseVariantCollisions(extracted);
2252
2577
  validateDuplicateAliases(extracted);
2578
+ validateDuplicateNegations(extracted);
2253
2579
  validateReservedAliases(extracted, true);
2254
2580
  const positionalNames = extracted.fields.filter((f) => f.positional).map((f) => f.name);
2255
2581
  if (positionalNames.length > 0) throw new Error(`Global options schema must not contain positional arguments. Found: ${positionalNames.join(", ")}`);
@@ -2276,6 +2602,12 @@ Object.defineProperty(exports, 'DuplicateFieldError', {
2276
2602
  return DuplicateFieldError;
2277
2603
  }
2278
2604
  });
2605
+ Object.defineProperty(exports, 'DuplicateNegationError', {
2606
+ enumerable: true,
2607
+ get: function () {
2608
+ return DuplicateNegationError;
2609
+ }
2610
+ });
2279
2611
  Object.defineProperty(exports, 'PositionalConfigError', {
2280
2612
  enumerable: true,
2281
2613
  get: function () {
@@ -2408,6 +2740,12 @@ Object.defineProperty(exports, 'validateDuplicateFields', {
2408
2740
  return validateDuplicateFields;
2409
2741
  }
2410
2742
  });
2743
+ Object.defineProperty(exports, 'validateDuplicateNegations', {
2744
+ enumerable: true,
2745
+ get: function () {
2746
+ return validateDuplicateNegations;
2747
+ }
2748
+ });
2411
2749
  Object.defineProperty(exports, 'validatePositionalConfig', {
2412
2750
  enumerable: true,
2413
2751
  get: function () {
@@ -2420,4 +2758,4 @@ Object.defineProperty(exports, 'validateReservedAliases', {
2420
2758
  return validateReservedAliases;
2421
2759
  }
2422
2760
  });
2423
- //# sourceMappingURL=runner-CY5fOsSh.cjs.map
2761
+ //# sourceMappingURL=runner-BcyR6Z8r.cjs.map