politty 0.4.15 → 0.5.0

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 (75) hide show
  1. package/dist/{arg-registry-Dw0f11Zc.d.ts → arg-registry--NRaNFJM.d.cts} +238 -6
  2. package/dist/arg-registry--NRaNFJM.d.cts.map +1 -0
  3. package/dist/{arg-registry-CB5gGtzp.d.cts → arg-registry-6E0WHOh_.d.ts} +238 -6
  4. package/dist/arg-registry-6E0WHOh_.d.ts.map +1 -0
  5. package/dist/augment.d.cts +1 -1
  6. package/dist/augment.d.cts.map +1 -1
  7. package/dist/augment.d.ts +1 -1
  8. package/dist/augment.d.ts.map +1 -1
  9. package/dist/completion/index.cjs +2 -1
  10. package/dist/completion/index.d.cts +3 -2
  11. package/dist/completion/index.d.ts +3 -2
  12. package/dist/completion/index.js +2 -2
  13. package/dist/completion-BA5JMvVG.js +4067 -0
  14. package/dist/completion-BA5JMvVG.js.map +1 -0
  15. package/dist/completion-Cqs1Ja7C.cjs +4169 -0
  16. package/dist/completion-Cqs1Ja7C.cjs.map +1 -0
  17. package/dist/docs/index.cjs +89 -29
  18. package/dist/docs/index.cjs.map +1 -1
  19. package/dist/docs/index.d.cts +1 -1
  20. package/dist/docs/index.d.cts.map +1 -1
  21. package/dist/docs/index.d.ts +1 -1
  22. package/dist/docs/index.d.ts.map +1 -1
  23. package/dist/docs/index.js +92 -31
  24. package/dist/docs/index.js.map +1 -1
  25. package/dist/{index-C1gGgUeB.d.cts → index-DBMfKZ34.d.ts} +189 -17
  26. package/dist/index-DBMfKZ34.d.ts.map +1 -0
  27. package/dist/{index-Dg9Fpz0R.d.ts → index-DJp8k5Bq.d.cts} +189 -17
  28. package/dist/index-DJp8k5Bq.d.cts.map +1 -0
  29. package/dist/index.cjs +12 -10
  30. package/dist/index.d.cts +36 -4
  31. package/dist/index.d.cts.map +1 -1
  32. package/dist/index.d.ts +36 -4
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +4 -4
  35. package/dist/log-collector-Cd2_mv87.cjs.map +1 -1
  36. package/dist/log-collector-Cu6MCtAx.js.map +1 -1
  37. package/dist/prompt/clack/index.cjs.map +1 -1
  38. package/dist/prompt/clack/index.d.cts +1 -1
  39. package/dist/prompt/clack/index.d.cts.map +1 -1
  40. package/dist/prompt/clack/index.d.ts +1 -1
  41. package/dist/prompt/clack/index.d.ts.map +1 -1
  42. package/dist/prompt/clack/index.js.map +1 -1
  43. package/dist/prompt/index.d.cts +1 -1
  44. package/dist/prompt/index.d.cts.map +1 -1
  45. package/dist/prompt/index.d.ts +1 -1
  46. package/dist/prompt/index.d.ts.map +1 -1
  47. package/dist/prompt/inquirer/index.cjs.map +1 -1
  48. package/dist/prompt/inquirer/index.d.cts +1 -1
  49. package/dist/prompt/inquirer/index.d.cts.map +1 -1
  50. package/dist/prompt/inquirer/index.d.ts +1 -1
  51. package/dist/prompt/inquirer/index.d.ts.map +1 -1
  52. package/dist/prompt/inquirer/index.js.map +1 -1
  53. package/dist/prompt-BKHqGrFw.js.map +1 -1
  54. package/dist/prompt-aXfSf27y.cjs.map +1 -1
  55. package/dist/{runner-DKAQBNNh.js → runner-BmSEiD9A.js} +319 -52
  56. package/dist/runner-BmSEiD9A.js.map +1 -0
  57. package/dist/{runner-CriXJlm4.cjs → runner-CRZ_7Y9i.cjs} +366 -87
  58. package/dist/runner-CRZ_7Y9i.cjs.map +1 -0
  59. package/dist/{subcommand-router-ENeCymvX.js → schema-extractor-C50R-1re.js} +175 -137
  60. package/dist/schema-extractor-C50R-1re.js.map +1 -0
  61. package/dist/{subcommand-router-CqZX3orq.cjs → schema-extractor-SLPgBNgZ.cjs} +174 -136
  62. package/dist/schema-extractor-SLPgBNgZ.cjs.map +1 -0
  63. package/package.json +16 -16
  64. package/dist/arg-registry-CB5gGtzp.d.cts.map +0 -1
  65. package/dist/arg-registry-Dw0f11Zc.d.ts.map +0 -1
  66. package/dist/completion-B5fgnUGm.cjs +0 -1940
  67. package/dist/completion-B5fgnUGm.cjs.map +0 -1
  68. package/dist/completion-Ca5ESJlG.js +0 -1844
  69. package/dist/completion-Ca5ESJlG.js.map +0 -1
  70. package/dist/index-C1gGgUeB.d.cts.map +0 -1
  71. package/dist/index-Dg9Fpz0R.d.ts.map +0 -1
  72. package/dist/runner-CriXJlm4.cjs.map +0 -1
  73. package/dist/runner-DKAQBNNh.js.map +0 -1
  74. package/dist/subcommand-router-CqZX3orq.cjs.map +0 -1
  75. package/dist/subcommand-router-ENeCymvX.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  const require_log_collector = require('./log-collector-Cd2_mv87.cjs');
2
- const require_subcommand_router = require('./subcommand-router-CqZX3orq.cjs');
2
+ const require_schema_extractor = require('./schema-extractor-SLPgBNgZ.cjs');
3
3
  let node_util = require("node:util");
4
4
  let string_width = require("string-width");
5
5
  string_width = require_log_collector.__toESM(string_width, 1);
@@ -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_subcommand_router.toCamelCase(prop);
22
+ const camel = require_schema_extractor.toCamelCase(prop);
23
23
  if (camel !== prop && camel in target) return Reflect.get(target, camel, receiver);
24
- const kebab = require_subcommand_router.toKebabCase(prop);
24
+ const kebab = require_schema_extractor.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_subcommand_router.toCamelCase(prop);
32
+ const camel = require_schema_extractor.toCamelCase(prop);
33
33
  if (camel !== prop && camel in target) return true;
34
- const kebab = require_subcommand_router.toKebabCase(prop);
34
+ const kebab = require_schema_extractor.toKebabCase(prop);
35
35
  if (kebab !== prop && kebab in target) return true;
36
36
  }
37
37
  return Reflect.has(target, prop);
@@ -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_subcommand_router.getExtractedFields(command);
617
+ const extracted = require_schema_extractor.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_subcommand_router.getExtractedFields(command);
617
- const hasUserDefinedh = extracted?.fields.some((f) => f.overrideBuiltinAlias === true && require_subcommand_router.getAllAliases(f).includes("h")) ?? false;
618
- const hasUserDefinedH = extracted?.fields.some((f) => f.overrideBuiltinAlias === true && require_subcommand_router.getAllAliases(f).includes("H")) ?? false;
637
+ const extracted = require_schema_extractor.getExtractedFields(command);
638
+ const hasUserDefinedh = extracted?.fields.some((f) => f.overrideBuiltinAlias === true && require_schema_extractor.getAllAliases(f).includes("h")) ?? false;
639
+ const hasUserDefinedH = extracted?.fields.some((f) => f.overrideBuiltinAlias === true && require_schema_extractor.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_subcommand_router.getExtractedFields(command);
868
+ const extracted = require_schema_extractor.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,7 +887,7 @@ 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_subcommand_router.resolveSubCommandMeta(subCmd);
890
+ const cmd = require_schema_extractor.resolveSubCommandMeta(subCmd);
840
891
  const fullPath = parentPath ? `${parentPath} ${name}` : name;
841
892
  const desc = cmd?.description ?? "";
842
893
  const aliases = cmd?.aliases;
@@ -888,7 +939,7 @@ function generateHelp(command, options) {
888
939
  } else {
889
940
  const subLines = [];
890
941
  for (const [name, subCmd] of Object.entries(visibleSubCommands)) {
891
- const cmd = require_subcommand_router.resolveSubCommandMeta(subCmd);
942
+ const cmd = require_schema_extractor.resolveSubCommandMeta(subCmd);
892
943
  const desc = cmd?.description ?? "";
893
944
  const fullName = currentPath ? `${currentPath} ${name}` : name;
894
945
  const aliases = cmd?.aliases;
@@ -973,6 +1024,17 @@ var CaseVariantCollisionError = class extends Error {
973
1024
  this.name = "CaseVariantCollisionError";
974
1025
  }
975
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
+ };
976
1038
 
977
1039
  //#endregion
978
1040
  //#region src/validator/command-validator.ts
@@ -1000,7 +1062,7 @@ function checkCaseVariantCollisions(extracted, commandPath) {
1000
1062
  const errors = [];
1001
1063
  const canonicalMap = /* @__PURE__ */ new Map();
1002
1064
  for (const field of extracted.fields) {
1003
- const camel = require_subcommand_router.toCamelCase(field.name);
1065
+ const camel = require_schema_extractor.toCamelCase(field.name);
1004
1066
  const existing = canonicalMap.get(camel);
1005
1067
  if (existing && existing !== field.name) errors.push({
1006
1068
  commandPath,
@@ -1040,12 +1102,12 @@ function checkDuplicateAliases(extracted, commandPath) {
1040
1102
  seenAliases.set(alias, fieldName);
1041
1103
  };
1042
1104
  for (const field of extracted.fields) {
1043
- const allAliases = require_subcommand_router.getAllAliases(field);
1105
+ const allAliases = require_schema_extractor.getAllAliases(field);
1044
1106
  if (allAliases.length === 0) continue;
1045
1107
  for (const alias of allAliases) {
1046
1108
  registerAlias(alias, field.name, false);
1047
1109
  if (alias.length > 1 && alias.includes("-")) {
1048
- const camelVariant = require_subcommand_router.toCamelCase(alias);
1110
+ const camelVariant = require_schema_extractor.toCamelCase(alias);
1049
1111
  if (camelVariant !== alias && !fieldNames.has(camelVariant)) registerAlias(camelVariant, field.name, true);
1050
1112
  }
1051
1113
  }
@@ -1053,6 +1115,79 @@ function checkDuplicateAliases(extracted, commandPath) {
1053
1115
  return errors;
1054
1116
  }
1055
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_schema_extractor.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_schema_extractor.toCamelCase(field.cliName);
1138
+ if (camelCli !== field.cliName) claim(camelCli, field.name, "CLI name");
1139
+ }
1140
+ for (const alias of require_schema_extractor.getAllAliases(field)) {
1141
+ claim(alias, field.name, "alias");
1142
+ if (alias.length > 1 && alias.includes("-")) {
1143
+ const camelVariant = require_schema_extractor.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_schema_extractor.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_schema_extractor.toCamelCase(field.negation);
1185
+ if (camelVariant !== field.negation) register(camelVariant, field.name, true);
1186
+ }
1187
+ }
1188
+ return errors;
1189
+ }
1190
+ /**
1056
1191
  * Check positional argument configuration
1057
1192
  */
1058
1193
  function checkPositionalConfig(extracted, commandPath) {
@@ -1091,7 +1226,7 @@ function checkReservedAliases(extracted, commandPath) {
1091
1226
  const errors = [];
1092
1227
  for (const field of extracted.fields) {
1093
1228
  if (field.overrideBuiltinAlias === true) continue;
1094
- for (const alias of require_subcommand_router.getAllAliases(field)) if (alias === "h" || alias === "H") errors.push({
1229
+ for (const alias of require_schema_extractor.getAllAliases(field)) if (alias === "h" || alias === "H") errors.push({
1095
1230
  commandPath,
1096
1231
  type: "reserved_alias",
1097
1232
  message: `Alias "${alias}" is reserved for --${alias === "h" ? "help" : "help-all"}.`,
@@ -1162,11 +1297,24 @@ function validateReservedAliases(extracted, _hasSubCommands) {
1162
1297
  if (errors.length > 0) {
1163
1298
  const field = errors[0].field ?? "unknown";
1164
1299
  const found = extracted.fields.find((f) => f.name === field);
1165
- const alias = (found ? require_subcommand_router.getAllAliases(found) : []).find((a) => a === "h" || a === "H") ?? "h";
1300
+ const alias = (found ? require_schema_extractor.getAllAliases(found) : []).find((a) => a === "h" || a === "H") ?? "h";
1166
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).`);
1167
1302
  }
1168
1303
  }
1169
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
+ /**
1170
1318
  * Validate that no case-variant collisions exist
1171
1319
  *
1172
1320
  * @param extracted - Extracted fields from schema
@@ -1189,9 +1337,9 @@ function validateCaseVariantCollisions(extracted) {
1189
1337
  */
1190
1338
  function validateCrossSchemaCollisions(extractedA, extractedB) {
1191
1339
  const canonicalMap = /* @__PURE__ */ new Map();
1192
- for (const field of extractedA.fields) canonicalMap.set(require_subcommand_router.toCamelCase(field.name), field.name);
1340
+ for (const field of extractedA.fields) canonicalMap.set(require_schema_extractor.toCamelCase(field.name), field.name);
1193
1341
  for (const field of extractedB.fields) {
1194
- const camel = require_subcommand_router.toCamelCase(field.name);
1342
+ const camel = require_schema_extractor.toCamelCase(field.name);
1195
1343
  const existing = canonicalMap.get(camel);
1196
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.`);
1197
1345
  }
@@ -1204,6 +1352,7 @@ function collectSchemaErrors(extracted, _hasSubCommands, commandPath) {
1204
1352
  ...checkDuplicateFields(extracted, commandPath),
1205
1353
  ...checkCaseVariantCollisions(extracted, commandPath),
1206
1354
  ...checkDuplicateAliases(extracted, commandPath),
1355
+ ...checkDuplicateNegations(extracted, commandPath),
1207
1356
  ...checkPositionalConfig(extracted, commandPath),
1208
1357
  ...checkReservedAliases(extracted, commandPath)
1209
1358
  ];
@@ -1219,7 +1368,7 @@ function checkSubCommandAliasConflicts(command, commandPath) {
1219
1368
  const nameToOwner = /* @__PURE__ */ new Map();
1220
1369
  for (const [name] of Object.entries(command.subCommands)) nameToOwner.set(name, name);
1221
1370
  for (const [name, subCmdValue] of Object.entries(command.subCommands)) {
1222
- const resolved = require_subcommand_router.isLazyCommand(subCmdValue) ? subCmdValue.meta : typeof subCmdValue !== "function" ? subCmdValue : null;
1371
+ const resolved = require_schema_extractor.isLazyCommand(subCmdValue) ? subCmdValue.meta : typeof subCmdValue !== "function" ? subCmdValue : null;
1223
1372
  if (!resolved?.aliases) continue;
1224
1373
  const subCommandPath = [...commandPath, name];
1225
1374
  for (const alias of resolved.aliases) {
@@ -1282,12 +1431,12 @@ async function validateCommand(command, options = {}) {
1282
1431
  const errors = [];
1283
1432
  const hasSubCommands = command.subCommands ? Object.keys(command.subCommands).length > 0 : false;
1284
1433
  if (command.args) {
1285
- const extracted = require_subcommand_router.extractFields(command.args);
1434
+ const extracted = require_schema_extractor.extractFields(command.args);
1286
1435
  errors.push(...collectSchemaErrors(extracted, hasSubCommands, commandPath));
1287
1436
  }
1288
1437
  errors.push(...checkSubCommandAliasConflicts(command, commandPath));
1289
1438
  if (command.subCommands) for (const [name, subCmd] of Object.entries(command.subCommands)) {
1290
- const subResult = await validateCommand(await require_subcommand_router.resolveLazyCommand(subCmd), { commandPath: [...commandPath, name] });
1439
+ const subResult = await validateCommand(await require_schema_extractor.resolveLazyCommand(subCmd), { commandPath: [...commandPath, name] });
1291
1440
  if (!subResult.valid) errors.push(...subResult.errors);
1292
1441
  }
1293
1442
  if (errors.length === 0) return { valid: true };
@@ -1336,7 +1485,7 @@ function formatCommandValidationErrors(errors) {
1336
1485
  * @returns Parsed arguments
1337
1486
  */
1338
1487
  function parseArgv(argv, options = {}) {
1339
- 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;
1340
1489
  const result = {
1341
1490
  options: {},
1342
1491
  positionals: [],
@@ -1367,11 +1516,19 @@ function parseArgv(argv, options = {}) {
1367
1516
  }
1368
1517
  if (arg.startsWith("--")) {
1369
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
+ }
1370
1527
  if (withoutDashes.startsWith("no-")) {
1371
1528
  const flagName = withoutDashes.slice(3);
1372
1529
  if (flagName === flagName.toLowerCase()) {
1373
1530
  const resolvedName = aliasMap.get(flagName) ?? flagName;
1374
- if (booleanFlags.has(resolvedName)) {
1531
+ if (booleanFlags.has(resolvedName) && !customNegatedFields.has(resolvedName)) {
1375
1532
  const asIsResolved = aliasMap.get(withoutDashes) ?? withoutDashes;
1376
1533
  if (!definedNames.has(asIsResolved)) {
1377
1534
  setOption(flagName, false);
@@ -1384,7 +1541,7 @@ function parseArgv(argv, options = {}) {
1384
1541
  if (withoutDashes.length > 2 && withoutDashes.startsWith("no") && /[A-Z]/.test(withoutDashes[2])) {
1385
1542
  const camelFlagName = withoutDashes[2].toLowerCase() + withoutDashes.slice(3);
1386
1543
  const resolvedName = aliasMap.get(camelFlagName) ?? camelFlagName;
1387
- if (booleanFlags.has(resolvedName)) {
1544
+ if (booleanFlags.has(resolvedName) && !customNegatedFields.has(resolvedName)) {
1388
1545
  const asIsResolved = aliasMap.get(withoutDashes) ?? withoutDashes;
1389
1546
  if (!definedNames.has(asIsResolved)) {
1390
1547
  setOption(camelFlagName, false);
@@ -1457,26 +1614,40 @@ function buildParserOptions(extracted) {
1457
1614
  const booleanFlags = /* @__PURE__ */ new Set();
1458
1615
  const arrayFlags = /* @__PURE__ */ new Set();
1459
1616
  const definedNames = /* @__PURE__ */ new Set();
1617
+ const negationMap = /* @__PURE__ */ new Map();
1618
+ const customNegatedFields = /* @__PURE__ */ new Set();
1460
1619
  for (const field of extracted.fields) definedNames.add(field.name);
1461
1620
  for (const field of extracted.fields) {
1462
1621
  if (field.cliName !== field.name) aliasMap.set(field.cliName, field.name);
1463
- for (const alias of require_subcommand_router.getAllAliases(field)) {
1622
+ for (const alias of require_schema_extractor.getAllAliases(field)) {
1464
1623
  aliasMap.set(alias, field.name);
1465
1624
  if (alias.length > 1 && alias.includes("-")) {
1466
- const camelAlias = require_subcommand_router.toCamelCase(alias);
1625
+ const camelAlias = require_schema_extractor.toCamelCase(alias);
1467
1626
  if (camelAlias !== alias && !definedNames.has(camelAlias) && !aliasMap.has(camelAlias)) aliasMap.set(camelAlias, field.name);
1468
1627
  }
1469
1628
  }
1470
- const camelVariant = require_subcommand_router.toCamelCase(field.name);
1629
+ const camelVariant = require_schema_extractor.toCamelCase(field.name);
1471
1630
  if (camelVariant !== field.name && !definedNames.has(camelVariant) && !aliasMap.has(camelVariant)) aliasMap.set(camelVariant, field.name);
1472
1631
  if (field.type === "boolean") booleanFlags.add(field.name);
1473
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_schema_extractor.toCamelCase(field.negation);
1639
+ if (camelNegation !== field.negation) negationMap.set(camelNegation, field.name);
1640
+ }
1641
+ }
1642
+ }
1474
1643
  }
1475
1644
  return {
1476
1645
  aliasMap,
1477
1646
  booleanFlags,
1478
1647
  arrayFlags,
1479
- definedNames
1648
+ definedNames,
1649
+ negationMap,
1650
+ customNegatedFields
1480
1651
  };
1481
1652
  }
1482
1653
  /**
@@ -1507,31 +1678,61 @@ function mergeWithPositionals(parsed, extracted) {
1507
1678
  * Shared by scanForSubcommand, separateGlobalArgs, and findFirstPositional.
1508
1679
  */
1509
1680
  function buildGlobalFlagLookup(globalExtracted) {
1510
- 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);
1511
1682
  const shortAliases = /* @__PURE__ */ new Set();
1512
- for (const field of globalExtracted.fields) for (const alias of require_subcommand_router.getAllAliases(field)) if (alias.length === 1) shortAliases.add(alias);
1683
+ for (const field of globalExtracted.fields) for (const alias of require_schema_extractor.getAllAliases(field)) if (alias.length === 1) shortAliases.add(alias);
1513
1684
  return {
1514
1685
  aliasMap,
1515
1686
  booleanFlags,
1516
1687
  flagNames: new Set(globalExtracted.fields.map((f) => f.name)),
1517
1688
  cliNames: new Set(globalExtracted.fields.map((f) => f.cliName)),
1518
- aliases: shortAliases
1689
+ aliases: shortAliases,
1690
+ negationMap,
1691
+ customNegatedFields
1519
1692
  };
1520
1693
  }
1521
1694
  /**
1522
- * Resolve a long option (--flag, --flag=value, --no-flag) against global flag lookup.
1523
- * 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.
1524
1703
  */
1525
1704
  function resolveGlobalLongOption(arg, lookup) {
1526
1705
  const withoutDashes = arg.includes("=") ? arg.slice(2, arg.indexOf("=")) : arg.slice(2);
1527
- const isNegated = withoutDashes.startsWith("no-");
1528
- 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;
1529
1728
  const resolvedName = lookup.aliasMap.get(flagName) ?? flagName;
1729
+ const suppressDefaultNegation = defaultIsNegated && lookup.customNegatedFields.has(resolvedName);
1530
1730
  return {
1531
1731
  resolvedName,
1532
1732
  withoutDashes,
1533
- isNegated,
1534
- 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
1535
1736
  };
1536
1737
  }
1537
1738
  /**
@@ -1579,6 +1780,7 @@ function scanForSubcommand(argv, subCommandNames, globalExtracted) {
1579
1780
  const lookup = buildGlobalFlagLookup(globalExtracted);
1580
1781
  const subCommandNameSet = new Set(subCommandNames);
1581
1782
  const globalTokensBefore = [];
1783
+ const suppressedTokens = [];
1582
1784
  let i = 0;
1583
1785
  while (i < argv.length) {
1584
1786
  const arg = argv[i];
@@ -1586,14 +1788,20 @@ function scanForSubcommand(argv, subCommandNames, globalExtracted) {
1586
1788
  if (!arg.startsWith("-") && subCommandNameSet.has(arg)) return {
1587
1789
  subCommandIndex: i,
1588
1790
  globalTokensBefore,
1589
- tokensAfterSubcommand: argv.slice(i + 1)
1791
+ tokensAfterSubcommand: argv.slice(i + 1),
1792
+ suppressedTokens
1590
1793
  };
1591
1794
  if (arg.startsWith("--")) {
1592
- const { resolvedName, isNegated, isGlobal } = resolveGlobalLongOption(arg, lookup);
1795
+ const { resolvedName, isNegated, isGlobal, isSuppressedNegation } = resolveGlobalLongOption(arg, lookup);
1593
1796
  if (isGlobal) {
1594
1797
  i += collectGlobalFlag(argv, i, resolvedName, isNegated, lookup.booleanFlags, globalTokensBefore);
1595
1798
  continue;
1596
1799
  }
1800
+ if (isSuppressedNegation) {
1801
+ suppressedTokens.push(arg.includes("=") ? arg.slice(2, arg.indexOf("=")) : arg.slice(2));
1802
+ i++;
1803
+ continue;
1804
+ }
1597
1805
  break;
1598
1806
  }
1599
1807
  if (arg.startsWith("-") && arg.length > 1) {
@@ -1612,7 +1820,8 @@ function scanForSubcommand(argv, subCommandNames, globalExtracted) {
1612
1820
  return {
1613
1821
  subCommandIndex: -1,
1614
1822
  globalTokensBefore,
1615
- tokensAfterSubcommand: []
1823
+ tokensAfterSubcommand: [],
1824
+ suppressedTokens
1616
1825
  };
1617
1826
  }
1618
1827
  const BUILTIN_FLAGS = new Set([
@@ -1658,7 +1867,7 @@ function findFirstPositional(argv, globalExtracted) {
1658
1867
  * @returns Parse result
1659
1868
  */
1660
1869
  function parseArgs(argv, command, options = {}) {
1661
- const subCommandNameSet = require_subcommand_router.listSubCommandNamesWithAliases(command);
1870
+ const subCommandNameSet = require_schema_extractor.listSubCommandNamesWithAliases(command);
1662
1871
  const subCommandNames = [...subCommandNameSet];
1663
1872
  const hasSubCommands = subCommandNames.length > 0;
1664
1873
  if (hasSubCommands && argv.length > 0) if (options.globalExtracted) {
@@ -1673,7 +1882,7 @@ function parseArgs(argv, command, options = {}) {
1673
1882
  remainingArgs: scanResult.tokensAfterSubcommand,
1674
1883
  rawArgs: {},
1675
1884
  positionals: [],
1676
- unknownFlags: [],
1885
+ unknownFlags: scanResult.suppressedTokens,
1677
1886
  rawGlobalArgs
1678
1887
  };
1679
1888
  }
@@ -1692,21 +1901,24 @@ function parseArgs(argv, command, options = {}) {
1692
1901
  }
1693
1902
  let extracted;
1694
1903
  if (command.args) {
1695
- extracted = require_subcommand_router.extractFields(command.args);
1904
+ extracted = require_schema_extractor.extractFields(command.args);
1696
1905
  if (!options.skipValidation) {
1697
1906
  validateDuplicateFields(extracted);
1698
1907
  validateCaseVariantCollisions(extracted);
1699
1908
  validateDuplicateAliases(extracted);
1909
+ validateDuplicateNegations(extracted);
1700
1910
  validatePositionalConfig(extracted);
1701
1911
  validateReservedAliases(extracted, hasSubCommands);
1702
1912
  if (options.globalExtracted) validateCrossSchemaCollisions(options.globalExtracted, extracted);
1703
1913
  }
1704
1914
  }
1705
- const hasUserDefinedH = extracted?.fields.some((f) => f.overrideBuiltinAlias === true && require_subcommand_router.getAllAliases(f).includes("H")) ?? false;
1706
- const hasUserDefinedh = extracted?.fields.some((f) => f.overrideBuiltinAlias === true && require_subcommand_router.getAllAliases(f).includes("h")) ?? false;
1707
- const helpAllRequested = argv.includes("--help-all") || !hasUserDefinedH && argv.includes("-H");
1708
- const helpRequested = !helpAllRequested && (argv.includes("--help") || !hasUserDefinedh && argv.includes("-h"));
1709
- const versionRequested = argv.includes("--version");
1915
+ const ddIdx = argv.indexOf("--");
1916
+ const flagScanArgv = ddIdx >= 0 ? argv.slice(0, ddIdx) : argv;
1917
+ const hasUserDefinedH = extracted?.fields.some((f) => f.overrideBuiltinAlias === true && require_schema_extractor.getAllAliases(f).includes("H")) ?? false;
1918
+ const hasUserDefinedh = extracted?.fields.some((f) => f.overrideBuiltinAlias === true && require_schema_extractor.getAllAliases(f).includes("h")) ?? false;
1919
+ const helpAllRequested = flagScanArgv.includes("--help-all") || !hasUserDefinedH && flagScanArgv.includes("-H");
1920
+ const helpRequested = !helpAllRequested && (flagScanArgv.includes("--help") || !hasUserDefinedh && flagScanArgv.includes("-h"));
1921
+ const versionRequested = flagScanArgv.includes("--version");
1710
1922
  if (helpRequested || helpAllRequested || versionRequested) return {
1711
1923
  helpRequested,
1712
1924
  helpAllRequested,
@@ -1751,11 +1963,11 @@ function parseArgs(argv, command, options = {}) {
1751
1963
  const knownFlags = new Set(extracted.fields.map((f) => f.name));
1752
1964
  const knownCliNames = new Set(extracted.fields.map((f) => f.cliName));
1753
1965
  const knownAliases = /* @__PURE__ */ new Set();
1754
- for (const f of extracted.fields) for (const alias of require_subcommand_router.getAllAliases(f)) knownAliases.add(alias);
1966
+ for (const f of extracted.fields) for (const alias of require_schema_extractor.getAllAliases(f)) knownAliases.add(alias);
1755
1967
  if (options.globalExtracted) for (const f of options.globalExtracted.fields) {
1756
1968
  knownFlags.add(f.name);
1757
1969
  knownCliNames.add(f.cliName);
1758
- for (const alias of require_subcommand_router.getAllAliases(f)) knownAliases.add(alias);
1970
+ for (const alias of require_schema_extractor.getAllAliases(f)) knownAliases.add(alias);
1759
1971
  }
1760
1972
  const unknownFlags = [];
1761
1973
  for (const key of Object.keys(parsed.options)) if (!knownFlags.has(key) && !knownCliNames.has(key) && !knownAliases.has(key)) unknownFlags.push(key);
@@ -1806,7 +2018,7 @@ function separateGlobalArgs(argv, globalExtracted, localExtracted) {
1806
2018
  }
1807
2019
  if (arg.startsWith("--")) {
1808
2020
  const { resolvedName, withoutDashes, isNegated, isGlobal } = resolveGlobalLongOption(arg, lookup);
1809
- const flagName = isNegated ? withoutDashes.slice(3) : withoutDashes;
2021
+ const flagName = resolvedName;
1810
2022
  const isLocalCollision = localFieldNames.has(withoutDashes) || localFieldNames.has(flagName) || localCliNames.has(withoutDashes) || localCliNames.has(flagName) || localAliasMapKeys.has(withoutDashes) || localAliasMapKeys.has(flagName);
1811
2023
  if (isGlobal && !isLocalCollision) {
1812
2024
  i += collectGlobalFlag(argv, i, resolvedName, isNegated, lookup.booleanFlags, globalTokens) - 1;
@@ -2042,12 +2254,36 @@ async function runCommand(command, argv, options = {}) {
2042
2254
  return result;
2043
2255
  }
2044
2256
  /**
2257
+ * Hidden internal subcommands (e.g. `__refresh-completion`) are spawned
2258
+ * by background hooks and must not run user-provided
2259
+ * `setup`/`cleanup`/`prompt` or required `globalArgs`. Those exist for
2260
+ * the foreground CLI run; replaying them in a detached child causes
2261
+ * duplicate side effects, stuck prompts, and validation failures the
2262
+ * user never opted into.
2263
+ *
2264
+ * We treat any registered subcommand whose name starts with `__` as
2265
+ * internal. We use `findFirstPositional` (schema-aware) instead of the
2266
+ * naive "first non-flag token" so an option *value* like
2267
+ * `--name __refresh-completion` doesn't trip the bypass — that would
2268
+ * silently skip lifecycle hooks for ordinary invocations.
2269
+ */
2270
+ function isInternalSubcommandInvocation(command, argv, globalExtracted) {
2271
+ const firstPositional = findFirstPositional(argv, globalExtracted);
2272
+ if (!firstPositional || !firstPositional.startsWith("__")) return false;
2273
+ return Boolean(command.subCommands?.[firstPositional]);
2274
+ }
2275
+ /**
2045
2276
  * Run a CLI command as the main entry point
2046
2277
  *
2047
2278
  * This function:
2048
2279
  * - Uses process.argv for arguments
2049
2280
  * - Handles SIGINT/SIGTERM signals
2050
2281
  * - Calls process.exit with the appropriate exit code
2282
+ * - Invokes `command.runMainHook` once before parsing if set, so plug-ins
2283
+ * like `withCompletionCommand` can fire detached background work
2284
+ * - Bypasses user `setup`/`cleanup`/`prompt` and required `globalArgs`
2285
+ * for registered hidden subcommands whose name starts with `__`
2286
+ * (e.g. `__refresh-completion`)
2051
2287
  *
2052
2288
  * @param command - The command to run
2053
2289
  * @param options - Main options (version, debug)
@@ -2065,38 +2301,51 @@ async function runCommand(command, argv, options = {}) {
2065
2301
  * ```
2066
2302
  */
2067
2303
  async function runMain(command, options = {}) {
2068
- const globalExtracted = extractAndValidateGlobal(options);
2069
- if (options.setup) try {
2070
- await options.setup({});
2304
+ if (command.runMainHook) try {
2305
+ command.runMainHook(process.argv.slice(2));
2306
+ } catch {}
2307
+ const argv = process.argv.slice(2);
2308
+ let globalExtractedForBypass;
2309
+ if (options.globalArgs) try {
2310
+ globalExtractedForBypass = require_schema_extractor.extractFields(options.globalArgs);
2311
+ } catch {}
2312
+ let effectiveOptions = options;
2313
+ if (isInternalSubcommandInvocation(command, argv, globalExtractedForBypass)) {
2314
+ const { setup: _s, cleanup: _c, prompt: _p, globalArgs: _g, ...rest } = options;
2315
+ effectiveOptions = rest;
2316
+ }
2317
+ const globalExtracted = extractAndValidateGlobal(effectiveOptions);
2318
+ if (effectiveOptions.setup) try {
2319
+ await effectiveOptions.setup({});
2071
2320
  } catch (e) {
2072
2321
  const error = e instanceof Error ? e : new Error(String(e));
2073
- if (options.cleanup) try {
2074
- await options.cleanup({ error });
2322
+ if (effectiveOptions.cleanup) try {
2323
+ await effectiveOptions.cleanup({ error });
2075
2324
  } catch {}
2076
2325
  process.exit(1);
2077
2326
  }
2078
- const result = await runCommandInternal(command, process.argv.slice(2), {
2079
- debug: options.debug,
2080
- captureLogs: options.captureLogs,
2081
- skipValidation: options.skipValidation,
2327
+ const result = await runCommandInternal(command, argv, {
2328
+ debug: effectiveOptions.debug,
2329
+ captureLogs: effectiveOptions.captureLogs,
2330
+ skipValidation: effectiveOptions.skipValidation,
2082
2331
  handleSignals: true,
2083
- logger: options.logger,
2084
- globalArgs: options.globalArgs,
2085
- prompt: options.prompt,
2332
+ logger: effectiveOptions.logger,
2333
+ globalArgs: effectiveOptions.globalArgs,
2334
+ prompt: effectiveOptions.prompt,
2086
2335
  _globalExtracted: globalExtracted,
2087
- _globalCleanup: options.cleanup,
2336
+ _globalCleanup: effectiveOptions.cleanup,
2088
2337
  _context: {
2089
2338
  commandPath: [],
2090
2339
  rootName: command.name,
2091
- rootVersion: options.version,
2340
+ rootVersion: effectiveOptions.version,
2092
2341
  globalExtracted
2093
2342
  }
2094
2343
  });
2095
- if ((options.displayErrors ?? true) && !result.success && result.error) (options.logger ?? defaultLogger).error(formatRuntimeError(result.error, options.debug ?? false));
2096
- if (options.cleanup) {
2344
+ if ((effectiveOptions.displayErrors ?? true) && !result.success && result.error) (effectiveOptions.logger ?? defaultLogger).error(formatRuntimeError(result.error, effectiveOptions.debug ?? false));
2345
+ if (effectiveOptions.cleanup) {
2097
2346
  const cleanupCtx = { error: !result.success ? result.error : void 0 };
2098
2347
  try {
2099
- await options.cleanup(cleanupCtx);
2348
+ await effectiveOptions.cleanup(cleanupCtx);
2100
2349
  } catch {}
2101
2350
  }
2102
2351
  if (process.stdout.writableLength > 0) await new Promise((resolve) => process.stdout.once("drain", resolve));
@@ -2129,8 +2378,8 @@ async function runCommandInternal(command, argv, options = {}) {
2129
2378
  };
2130
2379
  if (parseResult.helpRequested || parseResult.helpAllRequested) {
2131
2380
  let hasUnknownSubcommand = false;
2132
- const subCmdNames = require_subcommand_router.listSubCommands(command);
2133
- const allSubCmdNameSet = require_subcommand_router.listSubCommandNamesWithAliases(command);
2381
+ const subCmdNames = require_schema_extractor.listSubCommands(command);
2382
+ const allSubCmdNameSet = require_schema_extractor.listSubCommandNamesWithAliases(command);
2134
2383
  if (subCmdNames.length > 0) {
2135
2384
  const potentialSubCmd = findFirstPositional(argv, context.globalExtracted);
2136
2385
  if (potentialSubCmd && !allSubCmdNameSet.has(potentialSubCmd)) hasUnknownSubcommand = true;
@@ -2172,7 +2421,23 @@ async function runCommandInternal(command, argv, options = {}) {
2172
2421
  };
2173
2422
  }
2174
2423
  if (parseResult.subCommand) {
2175
- const resolved = await require_subcommand_router.resolveSubcommandWithAlias(command, parseResult.subCommand);
2424
+ if (parseResult.unknownFlags.length > 0) {
2425
+ const globalMode = context.globalExtracted?.unknownKeysMode ?? "strip";
2426
+ if (globalMode === "strict") {
2427
+ collector?.stop();
2428
+ return {
2429
+ success: false,
2430
+ error: /* @__PURE__ */ new Error(`Unknown flags: ${parseResult.unknownFlags.join(", ")}`),
2431
+ exitCode: 1,
2432
+ logs: getCurrentLogs()
2433
+ };
2434
+ }
2435
+ if (globalMode === "strip") {
2436
+ const knownGlobalFlags = context.globalExtracted?.fields.map((f) => f.name) ?? [];
2437
+ for (const flag of parseResult.unknownFlags) logger.error(formatUnknownFlagWarning(flag, knownGlobalFlags));
2438
+ }
2439
+ }
2440
+ const resolved = await require_schema_extractor.resolveSubcommandWithAlias(command, parseResult.subCommand);
2176
2441
  if (resolved) {
2177
2442
  const subContext = {
2178
2443
  commandPath: [...context.commandPath ?? [], parseResult.subCommand],
@@ -2190,7 +2455,7 @@ async function runCommandInternal(command, argv, options = {}) {
2190
2455
  });
2191
2456
  }
2192
2457
  }
2193
- if (require_subcommand_router.listSubCommands(command).length > 0 && !parseResult.subCommand && !command.run) {
2458
+ if (require_schema_extractor.listSubCommands(command).length > 0 && !parseResult.subCommand && !command.run) {
2194
2459
  const help = generateHelp(command, {
2195
2460
  showSubcommands: options.showSubcommands ?? true,
2196
2461
  context
@@ -2218,7 +2483,8 @@ async function runCommandInternal(command, argv, options = {}) {
2218
2483
  } else if (unknownKeysMode === "strip") for (const flag of parseResult.unknownFlags) logger.error(formatUnknownFlagWarning(flag, knownFlags));
2219
2484
  }
2220
2485
  let validatedGlobalArgs = {};
2221
- if (options.globalArgs && options._globalExtracted) {
2486
+ const isCompletionInvocation = command.name === "__complete";
2487
+ if (options.globalArgs && options._globalExtracted && !isCompletionInvocation) {
2222
2488
  for (const field of options._globalExtracted.fields) if (field.env && accumulatedGlobalArgs[field.name] === void 0) {
2223
2489
  const envNames = Array.isArray(field.env) ? field.env : [field.env];
2224
2490
  for (const envName of envNames) {
@@ -2247,7 +2513,7 @@ async function runCommandInternal(command, argv, options = {}) {
2247
2513
  }
2248
2514
  if (!command.args) {
2249
2515
  const proxiedGlobalArgs = createDualCaseProxy(validatedGlobalArgs);
2250
- if (options._globalExtracted) await runEffects(proxiedGlobalArgs, options._globalExtracted, proxiedGlobalArgs);
2516
+ if (options._globalExtracted && !isCompletionInvocation) await runEffects(proxiedGlobalArgs, options._globalExtracted, proxiedGlobalArgs);
2251
2517
  collector?.stop();
2252
2518
  return await executeLifecycle(command, proxiedGlobalArgs, {
2253
2519
  handleSignals: options.handleSignals,
@@ -2276,8 +2542,8 @@ async function runCommandInternal(command, argv, options = {}) {
2276
2542
  }
2277
2543
  const proxiedCommandArgs = createDualCaseProxy(validationResult.data);
2278
2544
  const proxiedGlobalArgs = createDualCaseProxy(validatedGlobalArgs);
2279
- if (options._globalExtracted) await runEffects(proxiedGlobalArgs, options._globalExtracted, proxiedGlobalArgs);
2280
- if (parseResult.extractedFields) await runEffects(proxiedCommandArgs, parseResult.extractedFields, proxiedGlobalArgs);
2545
+ if (options._globalExtracted && !isCompletionInvocation) await runEffects(proxiedGlobalArgs, options._globalExtracted, proxiedGlobalArgs);
2546
+ if (parseResult.extractedFields && !isCompletionInvocation) await runEffects(proxiedCommandArgs, parseResult.extractedFields, proxiedGlobalArgs);
2281
2547
  const mergedArgs = createDualCaseProxy({
2282
2548
  ...proxiedGlobalArgs,
2283
2549
  ...proxiedCommandArgs
@@ -2307,11 +2573,12 @@ async function runCommandInternal(command, argv, options = {}) {
2307
2573
  */
2308
2574
  function extractAndValidateGlobal(options) {
2309
2575
  if (!options.globalArgs) return void 0;
2310
- const extracted = require_subcommand_router.extractFields(options.globalArgs);
2576
+ const extracted = require_schema_extractor.extractFields(options.globalArgs);
2311
2577
  if (!options.skipValidation) {
2312
2578
  validateDuplicateFields(extracted);
2313
2579
  validateCaseVariantCollisions(extracted);
2314
2580
  validateDuplicateAliases(extracted);
2581
+ validateDuplicateNegations(extracted);
2315
2582
  validateReservedAliases(extracted, true);
2316
2583
  const positionalNames = extracted.fields.filter((f) => f.positional).map((f) => f.name);
2317
2584
  if (positionalNames.length > 0) throw new Error(`Global options schema must not contain positional arguments. Found: ${positionalNames.join(", ")}`);
@@ -2338,6 +2605,12 @@ Object.defineProperty(exports, 'DuplicateFieldError', {
2338
2605
  return DuplicateFieldError;
2339
2606
  }
2340
2607
  });
2608
+ Object.defineProperty(exports, 'DuplicateNegationError', {
2609
+ enumerable: true,
2610
+ get: function () {
2611
+ return DuplicateNegationError;
2612
+ }
2613
+ });
2341
2614
  Object.defineProperty(exports, 'PositionalConfigError', {
2342
2615
  enumerable: true,
2343
2616
  get: function () {
@@ -2470,6 +2743,12 @@ Object.defineProperty(exports, 'validateDuplicateFields', {
2470
2743
  return validateDuplicateFields;
2471
2744
  }
2472
2745
  });
2746
+ Object.defineProperty(exports, 'validateDuplicateNegations', {
2747
+ enumerable: true,
2748
+ get: function () {
2749
+ return validateDuplicateNegations;
2750
+ }
2751
+ });
2473
2752
  Object.defineProperty(exports, 'validatePositionalConfig', {
2474
2753
  enumerable: true,
2475
2754
  get: function () {
@@ -2482,4 +2761,4 @@ Object.defineProperty(exports, 'validateReservedAliases', {
2482
2761
  return validateReservedAliases;
2483
2762
  }
2484
2763
  });
2485
- //# sourceMappingURL=runner-CriXJlm4.cjs.map
2764
+ //# sourceMappingURL=runner-CRZ_7Y9i.cjs.map