politty 0.4.15 → 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 (73) hide show
  1. package/dist/{arg-registry-CB5gGtzp.d.cts → arg-registry-Cd6xnjHa.d.ts} +116 -4
  2. package/dist/arg-registry-Cd6xnjHa.d.ts.map +1 -0
  3. package/dist/{arg-registry-Dw0f11Zc.d.ts → arg-registry-MVWOAcvw.d.cts} +116 -4
  4. package/dist/arg-registry-MVWOAcvw.d.cts.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 +2 -2
  11. package/dist/completion/index.d.ts +2 -2
  12. package/dist/completion/index.js +2 -2
  13. package/dist/{completion-Ca5ESJlG.js → completion-B04iiki9.js} +512 -18
  14. package/dist/completion-B04iiki9.js.map +1 -0
  15. package/dist/{completion-B5fgnUGm.cjs → completion-BlZxMSeU.cjs} +516 -16
  16. package/dist/completion-BlZxMSeU.cjs.map +1 -0
  17. package/dist/docs/index.cjs +82 -22
  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-Dg9Fpz0R.d.ts → index-CPebddth.d.cts} +56 -4
  26. package/dist/index-CPebddth.d.cts.map +1 -0
  27. package/dist/{index-C1gGgUeB.d.cts → index-DR9HLxIP.d.ts} +56 -4
  28. package/dist/index-DR9HLxIP.d.ts.map +1 -0
  29. package/dist/index.cjs +5 -3
  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-BHeCMEa5.js} +309 -45
  56. package/dist/runner-BHeCMEa5.js.map +1 -0
  57. package/dist/{runner-CriXJlm4.cjs → runner-BcyR6Z8r.cjs} +320 -44
  58. package/dist/runner-BcyR6Z8r.cjs.map +1 -0
  59. package/dist/{subcommand-router-CqZX3orq.cjs → subcommand-router-DQy0KZU-.cjs} +41 -3
  60. package/dist/subcommand-router-DQy0KZU-.cjs.map +1 -0
  61. package/dist/{subcommand-router-ENeCymvX.js → subcommand-router-XZBWe8HN.js} +41 -3
  62. package/dist/subcommand-router-XZBWe8HN.js.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.map +0 -1
  67. package/dist/completion-Ca5ESJlG.js.map +0 -1
  68. package/dist/index-C1gGgUeB.d.cts.map +0 -1
  69. package/dist/index-Dg9Fpz0R.d.ts.map +0 -1
  70. package/dist/runner-CriXJlm4.cjs.map +0 -1
  71. package/dist/runner-DKAQBNNh.js.map +0 -1
  72. package/dist/subcommand-router-CqZX3orq.cjs.map +0 -1
  73. 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_subcommand_router = require('./subcommand-router-DQy0KZU-.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);
@@ -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
  }
@@ -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,7 +851,14 @@ 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)
@@ -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;
@@ -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
@@ -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_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
+ /**
1056
1191
  * Check positional argument configuration
1057
1192
  */
1058
1193
  function checkPositionalConfig(extracted, commandPath) {
@@ -1167,6 +1302,19 @@ function validateReservedAliases(extracted, _hasSubCommands) {
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
@@ -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
  ];
@@ -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,6 +1614,8 @@ 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);
@@ -1471,12 +1630,24 @@ function buildParserOptions(extracted) {
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_subcommand_router.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,7 +1678,7 @@ 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
1683
  for (const field of globalExtracted.fields) for (const alias of require_subcommand_router.getAllAliases(field)) if (alias.length === 1) shortAliases.add(alias);
1513
1684
  return {
@@ -1515,23 +1686,53 @@ function buildGlobalFlagLookup(globalExtracted) {
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([
@@ -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
  }
@@ -1697,6 +1906,7 @@ function parseArgs(argv, command, options = {}) {
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);
@@ -1806,7 +2016,7 @@ function separateGlobalArgs(argv, globalExtracted, localExtracted) {
1806
2016
  }
1807
2017
  if (arg.startsWith("--")) {
1808
2018
  const { resolvedName, withoutDashes, isNegated, isGlobal } = resolveGlobalLongOption(arg, lookup);
1809
- const flagName = isNegated ? withoutDashes.slice(3) : withoutDashes;
2019
+ const flagName = resolvedName;
1810
2020
  const isLocalCollision = localFieldNames.has(withoutDashes) || localFieldNames.has(flagName) || localCliNames.has(withoutDashes) || localCliNames.has(flagName) || localAliasMapKeys.has(withoutDashes) || localAliasMapKeys.has(flagName);
1811
2021
  if (isGlobal && !isLocalCollision) {
1812
2022
  i += collectGlobalFlag(argv, i, resolvedName, isNegated, lookup.booleanFlags, globalTokens) - 1;
@@ -2042,12 +2252,36 @@ async function runCommand(command, argv, options = {}) {
2042
2252
  return result;
2043
2253
  }
2044
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
+ /**
2045
2274
  * Run a CLI command as the main entry point
2046
2275
  *
2047
2276
  * This function:
2048
2277
  * - Uses process.argv for arguments
2049
2278
  * - Handles SIGINT/SIGTERM signals
2050
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`)
2051
2285
  *
2052
2286
  * @param command - The command to run
2053
2287
  * @param options - Main options (version, debug)
@@ -2065,38 +2299,51 @@ async function runCommand(command, argv, options = {}) {
2065
2299
  * ```
2066
2300
  */
2067
2301
  async function runMain(command, options = {}) {
2068
- const globalExtracted = extractAndValidateGlobal(options);
2069
- if (options.setup) try {
2070
- 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({});
2071
2318
  } catch (e) {
2072
2319
  const error = e instanceof Error ? e : new Error(String(e));
2073
- if (options.cleanup) try {
2074
- await options.cleanup({ error });
2320
+ if (effectiveOptions.cleanup) try {
2321
+ await effectiveOptions.cleanup({ error });
2075
2322
  } catch {}
2076
2323
  process.exit(1);
2077
2324
  }
2078
- const result = await runCommandInternal(command, process.argv.slice(2), {
2079
- debug: options.debug,
2080
- captureLogs: options.captureLogs,
2081
- skipValidation: options.skipValidation,
2325
+ const result = await runCommandInternal(command, argv, {
2326
+ debug: effectiveOptions.debug,
2327
+ captureLogs: effectiveOptions.captureLogs,
2328
+ skipValidation: effectiveOptions.skipValidation,
2082
2329
  handleSignals: true,
2083
- logger: options.logger,
2084
- globalArgs: options.globalArgs,
2085
- prompt: options.prompt,
2330
+ logger: effectiveOptions.logger,
2331
+ globalArgs: effectiveOptions.globalArgs,
2332
+ prompt: effectiveOptions.prompt,
2086
2333
  _globalExtracted: globalExtracted,
2087
- _globalCleanup: options.cleanup,
2334
+ _globalCleanup: effectiveOptions.cleanup,
2088
2335
  _context: {
2089
2336
  commandPath: [],
2090
2337
  rootName: command.name,
2091
- rootVersion: options.version,
2338
+ rootVersion: effectiveOptions.version,
2092
2339
  globalExtracted
2093
2340
  }
2094
2341
  });
2095
- if ((options.displayErrors ?? true) && !result.success && result.error) (options.logger ?? defaultLogger).error(formatRuntimeError(result.error, options.debug ?? false));
2096
- 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) {
2097
2344
  const cleanupCtx = { error: !result.success ? result.error : void 0 };
2098
2345
  try {
2099
- await options.cleanup(cleanupCtx);
2346
+ await effectiveOptions.cleanup(cleanupCtx);
2100
2347
  } catch {}
2101
2348
  }
2102
2349
  if (process.stdout.writableLength > 0) await new Promise((resolve) => process.stdout.once("drain", resolve));
@@ -2172,6 +2419,22 @@ async function runCommandInternal(command, argv, options = {}) {
2172
2419
  };
2173
2420
  }
2174
2421
  if (parseResult.subCommand) {
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
+ }
2175
2438
  const resolved = await require_subcommand_router.resolveSubcommandWithAlias(command, parseResult.subCommand);
2176
2439
  if (resolved) {
2177
2440
  const subContext = {
@@ -2312,6 +2575,7 @@ function extractAndValidateGlobal(options) {
2312
2575
  validateDuplicateFields(extracted);
2313
2576
  validateCaseVariantCollisions(extracted);
2314
2577
  validateDuplicateAliases(extracted);
2578
+ validateDuplicateNegations(extracted);
2315
2579
  validateReservedAliases(extracted, true);
2316
2580
  const positionalNames = extracted.fields.filter((f) => f.positional).map((f) => f.name);
2317
2581
  if (positionalNames.length > 0) throw new Error(`Global options schema must not contain positional arguments. Found: ${positionalNames.join(", ")}`);
@@ -2338,6 +2602,12 @@ Object.defineProperty(exports, 'DuplicateFieldError', {
2338
2602
  return DuplicateFieldError;
2339
2603
  }
2340
2604
  });
2605
+ Object.defineProperty(exports, 'DuplicateNegationError', {
2606
+ enumerable: true,
2607
+ get: function () {
2608
+ return DuplicateNegationError;
2609
+ }
2610
+ });
2341
2611
  Object.defineProperty(exports, 'PositionalConfigError', {
2342
2612
  enumerable: true,
2343
2613
  get: function () {
@@ -2470,6 +2740,12 @@ Object.defineProperty(exports, 'validateDuplicateFields', {
2470
2740
  return validateDuplicateFields;
2471
2741
  }
2472
2742
  });
2743
+ Object.defineProperty(exports, 'validateDuplicateNegations', {
2744
+ enumerable: true,
2745
+ get: function () {
2746
+ return validateDuplicateNegations;
2747
+ }
2748
+ });
2473
2749
  Object.defineProperty(exports, 'validatePositionalConfig', {
2474
2750
  enumerable: true,
2475
2751
  get: function () {
@@ -2482,4 +2758,4 @@ Object.defineProperty(exports, 'validateReservedAliases', {
2482
2758
  return validateReservedAliases;
2483
2759
  }
2484
2760
  });
2485
- //# sourceMappingURL=runner-CriXJlm4.cjs.map
2761
+ //# sourceMappingURL=runner-BcyR6Z8r.cjs.map