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,4 +1,4 @@
1
- import { a as resolveSubcommandWithAlias, c as resolveSubCommandMeta, d as getExtractedFields, l as extractFields, m as toKebabCase, n as listSubCommands, o as isLazyCommand, p as toCamelCase, r as resolveLazyCommand, t as listSubCommandNamesWithAliases, u as getAllAliases } from "./subcommand-router-ENeCymvX.js";
1
+ import { a as resolveSubcommandWithAlias, c as resolveSubCommandMeta, d as getExtractedFields, l as extractFields, m as toKebabCase, n as listSubCommands, o as isLazyCommand, p as toCamelCase, r as resolveLazyCommand, t as listSubCommandNamesWithAliases, u as getAllAliases } from "./subcommand-router-XZBWe8HN.js";
2
2
  import { n as emptyLogs, r as mergeLogs, t as createLogCollector } from "./log-collector-Cu6MCtAx.js";
3
3
  import { styleText } from "node:util";
4
4
  import stringWidth from "string-width";
@@ -236,24 +236,45 @@ const symbols = {
236
236
  * Logger for CLI output
237
237
  */
238
238
  const logger = {
239
+ /**
240
+ * Log informational message
241
+ */
239
242
  info(message) {
240
243
  console.log(message);
241
244
  },
245
+ /**
246
+ * Log success message
247
+ */
242
248
  success(message) {
243
249
  console.log(`${symbols.success} ${styles.success(message)}`);
244
250
  },
251
+ /**
252
+ * Log warning message
253
+ */
245
254
  warn(message) {
246
255
  console.warn(`${symbols.warning} ${styles.warning(message)}`);
247
256
  },
257
+ /**
258
+ * Log error message
259
+ */
248
260
  error(message) {
249
261
  console.error(`${symbols.error} ${styles.error(message)}`);
250
262
  },
263
+ /**
264
+ * Log raw message without prefix
265
+ */
251
266
  log(message) {
252
267
  console.log(message);
253
268
  },
269
+ /**
270
+ * Log empty line
271
+ */
254
272
  newline() {
255
273
  console.log("");
256
274
  },
275
+ /**
276
+ * Log debug message with dim color
277
+ */
257
278
  debug(message) {
258
279
  console.log(styles.dim(message));
259
280
  }
@@ -650,10 +671,21 @@ function renderOptions(command, descriptions = {}, context) {
650
671
  const envInfo = formatEnvInfo(opt.env);
651
672
  if (envInfo) desc += ` ${envInfo}`;
652
673
  lines.push(formatOption(flags, desc));
674
+ const negationLine = formatNegationLine(opt);
675
+ if (negationLine) lines.push(negationLine);
653
676
  }
654
677
  return lines.join("\n");
655
678
  }
656
679
  /**
680
+ * Render a separate line for the custom negation option when a
681
+ * `negationDescription` is provided. When no description is given, the
682
+ * negation is shown inline by `formatFlags`.
683
+ */
684
+ function formatNegationLine(opt, indent = 0, extraDescPadding = 0) {
685
+ if (!opt.negationDisplay || !opt.negationDescription) return null;
686
+ return formatOption(styles.option(`--${opt.negationDisplay}`), `${opt.negationDescription} ${styles.dim(`(↔ --${opt.cliName})`)}`, indent, extraDescPadding);
687
+ }
688
+ /**
657
689
  * Render options for discriminated union with variants
658
690
  */
659
691
  function renderDiscriminatedUnionOptions(extracted, _command, lines) {
@@ -682,6 +714,8 @@ function renderDiscriminatedUnionOptions(extracted, _command, lines) {
682
714
  const envInfo = formatEnvInfo(field.env);
683
715
  if (envInfo) desc += ` ${envInfo}`;
684
716
  lines.push(formatOption(flags, desc));
717
+ const negationLine = formatNegationLine(field);
718
+ if (negationLine) lines.push(negationLine);
685
719
  }
686
720
  }
687
721
  for (const variant of variants) {
@@ -697,7 +731,9 @@ function renderDiscriminatedUnionOptions(extracted, _command, lines) {
697
731
  if (field.required) desc += ` ${styles.required("(required)")}`;
698
732
  const envInfo = formatEnvInfo(field.env);
699
733
  if (envInfo) desc += ` ${envInfo}`;
700
- lines.push(formatOption(` ${flags}`, desc));
734
+ lines.push(formatOption(flags, desc, 1));
735
+ const negationLine = formatNegationLine(field, 1);
736
+ if (negationLine) lines.push(negationLine);
701
737
  }
702
738
  }
703
739
  }
@@ -721,6 +757,8 @@ function renderUnionOptions(extracted, _command, lines) {
721
757
  const envInfo = formatEnvInfo(field.env);
722
758
  if (envInfo) desc += ` ${envInfo}`;
723
759
  lines.push(formatOption(flags, desc));
760
+ const negationLine = formatNegationLine(field);
761
+ if (negationLine) lines.push(negationLine);
724
762
  }
725
763
  }
726
764
  for (let i = 0; i < unionOptions.length; i++) {
@@ -738,7 +776,9 @@ function renderUnionOptions(extracted, _command, lines) {
738
776
  if (field.required) desc += ` ${styles.required("(required)")}`;
739
777
  const envInfo = formatEnvInfo(field.env);
740
778
  if (envInfo) desc += ` ${envInfo}`;
741
- lines.push(formatOption(` ${flags}`, desc));
779
+ lines.push(formatOption(flags, desc, 1));
780
+ const negationLine = formatNegationLine(field, 1);
781
+ if (negationLine) lines.push(negationLine);
742
782
  }
743
783
  } else {
744
784
  lines.push("");
@@ -753,16 +793,16 @@ function renderUnionOptions(extracted, _command, lines) {
753
793
  * Uses cliName (kebab-case) for display
754
794
  */
755
795
  function formatFlags(opt) {
756
- const parts = [];
796
+ const aliasParts = [];
757
797
  if (opt.alias) {
758
- for (const alias of opt.alias) if (alias.length === 1) parts.push(styles.option(`-${alias}`));
798
+ for (const alias of opt.alias) if (alias.length === 1) aliasParts.push(styles.option(`-${alias}`));
759
799
  }
760
800
  let longFlag = styles.option(`--${opt.cliName}`);
761
801
  if (opt.type !== "boolean") {
762
802
  const placeholder = opt.placeholder ?? opt.cliName.toUpperCase();
763
803
  longFlag += ` ${styles.placeholder(`<${placeholder}>`)}`;
764
804
  }
765
- parts.push(longFlag);
805
+ aliasParts.push(longFlag);
766
806
  if (opt.alias) {
767
807
  for (const alias of opt.alias) if (alias.length > 1) {
768
808
  let longAlias = styles.option(`--${alias}`);
@@ -770,10 +810,12 @@ function formatFlags(opt) {
770
810
  const placeholder = opt.placeholder ?? opt.cliName.toUpperCase();
771
811
  longAlias += ` ${styles.placeholder(`<${placeholder}>`)}`;
772
812
  }
773
- parts.push(longAlias);
813
+ aliasParts.push(longAlias);
774
814
  }
775
815
  }
776
- return parts.join(", ");
816
+ const aliasStr = aliasParts.join(", ");
817
+ if (opt.type === "boolean" && opt.negationDisplay && !opt.negationDescription) return `${aliasStr} / ${styles.option(`--${opt.negationDisplay}`)}`;
818
+ return aliasStr;
777
819
  }
778
820
  /**
779
821
  * Format environment variable info for help display
@@ -825,7 +867,14 @@ function formatFieldLine(opt, indent = 0, extraDescPadding = 0) {
825
867
  * Render global options section
826
868
  */
827
869
  function renderGlobalOptions(globalExtracted) {
828
- return globalExtracted.fields.filter((a) => !a.positional).map((opt) => formatFieldLine(opt)).join("\n");
870
+ const lines = [];
871
+ for (const opt of globalExtracted.fields) {
872
+ if (opt.positional) continue;
873
+ lines.push(formatFieldLine(opt));
874
+ const negationLine = formatNegationLine(opt);
875
+ if (negationLine) lines.push(negationLine);
876
+ }
877
+ return lines.join("\n");
829
878
  }
830
879
  /**
831
880
  * Render options for a subcommand (used by showSubcommandOptions)
@@ -842,6 +891,8 @@ function renderSubcommandOptionsCompact(command, indent) {
842
891
  const envInfo = formatEnvInfo(opt.env);
843
892
  if (envInfo) desc += ` ${envInfo}`;
844
893
  lines.push(formatOption(flags, desc, indent, 2));
894
+ const negationLine = formatNegationLine(opt, indent, 2);
895
+ if (negationLine) lines.push(negationLine);
845
896
  }
846
897
  }
847
898
  return lines;
@@ -989,6 +1040,17 @@ var CaseVariantCollisionError = class extends Error {
989
1040
  this.name = "CaseVariantCollisionError";
990
1041
  }
991
1042
  };
1043
+ /**
1044
+ * Error thrown when a custom boolean negation name collides with another
1045
+ * field's name, cliName, alias, or another field's negation (including
1046
+ * derived camelCase variants).
1047
+ */
1048
+ var DuplicateNegationError = class extends Error {
1049
+ constructor(message) {
1050
+ super(message);
1051
+ this.name = "DuplicateNegationError";
1052
+ }
1053
+ };
992
1054
 
993
1055
  //#endregion
994
1056
  //#region src/validator/command-validator.ts
@@ -1069,6 +1131,79 @@ function checkDuplicateAliases(extracted, commandPath) {
1069
1131
  return errors;
1070
1132
  }
1071
1133
  /**
1134
+ * Check for collisions involving custom boolean `negation` names
1135
+ */
1136
+ function checkDuplicateNegations(extracted, commandPath) {
1137
+ const errors = [];
1138
+ const claimed = /* @__PURE__ */ new Map();
1139
+ const claim = (name, fieldName, kind) => {
1140
+ if (!claimed.has(name)) claimed.set(name, {
1141
+ field: fieldName,
1142
+ kind
1143
+ });
1144
+ };
1145
+ for (const field of extracted.fields) {
1146
+ claim(field.name, field.name, "field name");
1147
+ if (field.name.includes("-")) {
1148
+ const camelName = toCamelCase(field.name);
1149
+ if (camelName !== field.name) claim(camelName, field.name, "field name");
1150
+ }
1151
+ if (field.cliName !== field.name) claim(field.cliName, field.name, "CLI name");
1152
+ if (field.cliName.includes("-")) {
1153
+ const camelCli = toCamelCase(field.cliName);
1154
+ if (camelCli !== field.cliName) claim(camelCli, field.name, "CLI name");
1155
+ }
1156
+ for (const alias of getAllAliases(field)) {
1157
+ claim(alias, field.name, "alias");
1158
+ if (alias.length > 1 && alias.includes("-")) {
1159
+ const camelVariant = toCamelCase(alias);
1160
+ if (camelVariant !== alias) claim(camelVariant, field.name, "alias");
1161
+ }
1162
+ }
1163
+ if (field.type === "boolean" && field.negation !== false && typeof field.negation !== "string") {
1164
+ const defaultKebab = `no-${field.cliName}`;
1165
+ claim(defaultKebab, field.name, "default negation");
1166
+ const camelBase = toCamelCase(field.cliName);
1167
+ const defaultCamel = `no${camelBase[0]?.toUpperCase() ?? ""}${camelBase.slice(1)}`;
1168
+ if (defaultCamel !== defaultKebab) claim(defaultCamel, field.name, "default negation");
1169
+ }
1170
+ }
1171
+ const seenNegations = /* @__PURE__ */ new Map();
1172
+ const register = (name, fieldName, isDerived) => {
1173
+ const claim = claimed.get(name);
1174
+ if (claim) {
1175
+ const qualifier = isDerived ? " (derived camelCase variant)" : "";
1176
+ const conflict = claim.field === fieldName ? `the same field's own ${claim.kind} "${name}"` : `${claim.kind} "${name}" of field "${claim.field}"`;
1177
+ errors.push({
1178
+ commandPath,
1179
+ type: "duplicate_negation",
1180
+ message: `Negation "${name}"${qualifier} for field "${fieldName}" conflicts with ${conflict}.`,
1181
+ field: fieldName
1182
+ });
1183
+ }
1184
+ const existing = seenNegations.get(name);
1185
+ if (existing && existing !== fieldName) {
1186
+ const qualifier = isDerived ? " (derived camelCase variant)" : "";
1187
+ errors.push({
1188
+ commandPath,
1189
+ type: "duplicate_negation",
1190
+ message: `Duplicate negation "${name}"${qualifier} detected. Both "${existing}" and "${fieldName}" use the same negation name.`,
1191
+ field: fieldName
1192
+ });
1193
+ }
1194
+ seenNegations.set(name, fieldName);
1195
+ };
1196
+ for (const field of extracted.fields) {
1197
+ if (typeof field.negation !== "string") continue;
1198
+ register(field.negation, field.name, false);
1199
+ if (field.negation.includes("-")) {
1200
+ const camelVariant = toCamelCase(field.negation);
1201
+ if (camelVariant !== field.negation) register(camelVariant, field.name, true);
1202
+ }
1203
+ }
1204
+ return errors;
1205
+ }
1206
+ /**
1072
1207
  * Check positional argument configuration
1073
1208
  */
1074
1209
  function checkPositionalConfig(extracted, commandPath) {
@@ -1183,6 +1318,19 @@ function validateReservedAliases(extracted, _hasSubCommands) {
1183
1318
  }
1184
1319
  }
1185
1320
  /**
1321
+ * Validate that custom boolean negation names do not collide with anything
1322
+ *
1323
+ * @param extracted - Extracted fields from schema
1324
+ * @throws {DuplicateNegationError} If a colliding negation is found
1325
+ */
1326
+ function validateDuplicateNegations(extracted) {
1327
+ const errors = checkDuplicateNegations(extracted, []);
1328
+ if (errors.length > 0) {
1329
+ const err = errors[0];
1330
+ throw new DuplicateNegationError(err.message);
1331
+ }
1332
+ }
1333
+ /**
1186
1334
  * Validate that no case-variant collisions exist
1187
1335
  *
1188
1336
  * @param extracted - Extracted fields from schema
@@ -1220,6 +1368,7 @@ function collectSchemaErrors(extracted, _hasSubCommands, commandPath) {
1220
1368
  ...checkDuplicateFields(extracted, commandPath),
1221
1369
  ...checkCaseVariantCollisions(extracted, commandPath),
1222
1370
  ...checkDuplicateAliases(extracted, commandPath),
1371
+ ...checkDuplicateNegations(extracted, commandPath),
1223
1372
  ...checkPositionalConfig(extracted, commandPath),
1224
1373
  ...checkReservedAliases(extracted, commandPath)
1225
1374
  ];
@@ -1352,7 +1501,7 @@ function formatCommandValidationErrors(errors) {
1352
1501
  * @returns Parsed arguments
1353
1502
  */
1354
1503
  function parseArgv(argv, options = {}) {
1355
- const { aliasMap = /* @__PURE__ */ new Map(), booleanFlags = /* @__PURE__ */ new Set(), arrayFlags = /* @__PURE__ */ new Set(), definedNames = /* @__PURE__ */ new Set() } = options;
1504
+ 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;
1356
1505
  const result = {
1357
1506
  options: {},
1358
1507
  positionals: [],
@@ -1383,11 +1532,19 @@ function parseArgv(argv, options = {}) {
1383
1532
  }
1384
1533
  if (arg.startsWith("--")) {
1385
1534
  const withoutDashes = arg.slice(2);
1535
+ if (!withoutDashes.includes("=")) {
1536
+ const negatedField = negationMap.get(withoutDashes);
1537
+ if (negatedField && booleanFlags.has(negatedField)) {
1538
+ setOption(negatedField, false);
1539
+ i++;
1540
+ continue;
1541
+ }
1542
+ }
1386
1543
  if (withoutDashes.startsWith("no-")) {
1387
1544
  const flagName = withoutDashes.slice(3);
1388
1545
  if (flagName === flagName.toLowerCase()) {
1389
1546
  const resolvedName = aliasMap.get(flagName) ?? flagName;
1390
- if (booleanFlags.has(resolvedName)) {
1547
+ if (booleanFlags.has(resolvedName) && !customNegatedFields.has(resolvedName)) {
1391
1548
  const asIsResolved = aliasMap.get(withoutDashes) ?? withoutDashes;
1392
1549
  if (!definedNames.has(asIsResolved)) {
1393
1550
  setOption(flagName, false);
@@ -1400,7 +1557,7 @@ function parseArgv(argv, options = {}) {
1400
1557
  if (withoutDashes.length > 2 && withoutDashes.startsWith("no") && /[A-Z]/.test(withoutDashes[2])) {
1401
1558
  const camelFlagName = withoutDashes[2].toLowerCase() + withoutDashes.slice(3);
1402
1559
  const resolvedName = aliasMap.get(camelFlagName) ?? camelFlagName;
1403
- if (booleanFlags.has(resolvedName)) {
1560
+ if (booleanFlags.has(resolvedName) && !customNegatedFields.has(resolvedName)) {
1404
1561
  const asIsResolved = aliasMap.get(withoutDashes) ?? withoutDashes;
1405
1562
  if (!definedNames.has(asIsResolved)) {
1406
1563
  setOption(camelFlagName, false);
@@ -1473,6 +1630,8 @@ function buildParserOptions(extracted) {
1473
1630
  const booleanFlags = /* @__PURE__ */ new Set();
1474
1631
  const arrayFlags = /* @__PURE__ */ new Set();
1475
1632
  const definedNames = /* @__PURE__ */ new Set();
1633
+ const negationMap = /* @__PURE__ */ new Map();
1634
+ const customNegatedFields = /* @__PURE__ */ new Set();
1476
1635
  for (const field of extracted.fields) definedNames.add(field.name);
1477
1636
  for (const field of extracted.fields) {
1478
1637
  if (field.cliName !== field.name) aliasMap.set(field.cliName, field.name);
@@ -1487,12 +1646,24 @@ function buildParserOptions(extracted) {
1487
1646
  if (camelVariant !== field.name && !definedNames.has(camelVariant) && !aliasMap.has(camelVariant)) aliasMap.set(camelVariant, field.name);
1488
1647
  if (field.type === "boolean") booleanFlags.add(field.name);
1489
1648
  if (field.type === "array") arrayFlags.add(field.name);
1649
+ if (field.type === "boolean" && (typeof field.negation === "string" || field.negation === false)) {
1650
+ customNegatedFields.add(field.name);
1651
+ if (typeof field.negation === "string") {
1652
+ negationMap.set(field.negation, field.name);
1653
+ if (field.negation.includes("-")) {
1654
+ const camelNegation = toCamelCase(field.negation);
1655
+ if (camelNegation !== field.negation) negationMap.set(camelNegation, field.name);
1656
+ }
1657
+ }
1658
+ }
1490
1659
  }
1491
1660
  return {
1492
1661
  aliasMap,
1493
1662
  booleanFlags,
1494
1663
  arrayFlags,
1495
- definedNames
1664
+ definedNames,
1665
+ negationMap,
1666
+ customNegatedFields
1496
1667
  };
1497
1668
  }
1498
1669
  /**
@@ -1523,7 +1694,7 @@ function mergeWithPositionals(parsed, extracted) {
1523
1694
  * Shared by scanForSubcommand, separateGlobalArgs, and findFirstPositional.
1524
1695
  */
1525
1696
  function buildGlobalFlagLookup(globalExtracted) {
1526
- const { aliasMap = /* @__PURE__ */ new Map(), booleanFlags = /* @__PURE__ */ new Set() } = buildParserOptions(globalExtracted);
1697
+ const { aliasMap = /* @__PURE__ */ new Map(), booleanFlags = /* @__PURE__ */ new Set(), negationMap = /* @__PURE__ */ new Map(), customNegatedFields = /* @__PURE__ */ new Set() } = buildParserOptions(globalExtracted);
1527
1698
  const shortAliases = /* @__PURE__ */ new Set();
1528
1699
  for (const field of globalExtracted.fields) for (const alias of getAllAliases(field)) if (alias.length === 1) shortAliases.add(alias);
1529
1700
  return {
@@ -1531,23 +1702,53 @@ function buildGlobalFlagLookup(globalExtracted) {
1531
1702
  booleanFlags,
1532
1703
  flagNames: new Set(globalExtracted.fields.map((f) => f.name)),
1533
1704
  cliNames: new Set(globalExtracted.fields.map((f) => f.cliName)),
1534
- aliases: shortAliases
1705
+ aliases: shortAliases,
1706
+ negationMap,
1707
+ customNegatedFields
1535
1708
  };
1536
1709
  }
1537
1710
  /**
1538
- * Resolve a long option (--flag, --flag=value, --no-flag) against global flag lookup.
1539
- * Returns the resolved camelCase name and whether it is a known global flag.
1711
+ * Resolve a long option (--flag, --flag=value, --no-flag, --custom-negation)
1712
+ * against global flag lookup. Returns the resolved camelCase name and whether
1713
+ * it is a known global flag.
1714
+ *
1715
+ * `isSuppressedNegation` is true when the token matches a default `--no-X`
1716
+ * form that has been suppressed by a custom `negation` on the target field.
1717
+ * The caller may use this to keep argv scanning past such tokens (so a
1718
+ * trailing subcommand is still detected) even though they no longer negate.
1540
1719
  */
1541
1720
  function resolveGlobalLongOption(arg, lookup) {
1542
1721
  const withoutDashes = arg.includes("=") ? arg.slice(2, arg.indexOf("=")) : arg.slice(2);
1543
- const isNegated = withoutDashes.startsWith("no-");
1544
- const flagName = isNegated ? withoutDashes.slice(3) : withoutDashes;
1722
+ const customNegated = !arg.includes("=") ? lookup.negationMap.get(withoutDashes) : void 0;
1723
+ if (customNegated) return {
1724
+ resolvedName: customNegated,
1725
+ withoutDashes,
1726
+ isNegated: true,
1727
+ isGlobal: lookup.flagNames.has(customNegated),
1728
+ isSuppressedNegation: false
1729
+ };
1730
+ const kebabNegated = withoutDashes.startsWith("no-");
1731
+ const camelNegated = !kebabNegated && withoutDashes.length > 2 && withoutDashes.startsWith("no") && /[A-Z]/.test(withoutDashes[2]);
1732
+ if (kebabNegated || camelNegated) {
1733
+ const literalResolved = lookup.aliasMap.get(withoutDashes) ?? withoutDashes;
1734
+ if (lookup.flagNames.has(literalResolved) || lookup.cliNames.has(withoutDashes)) return {
1735
+ resolvedName: literalResolved,
1736
+ withoutDashes,
1737
+ isNegated: false,
1738
+ isGlobal: true,
1739
+ isSuppressedNegation: false
1740
+ };
1741
+ }
1742
+ const defaultIsNegated = kebabNegated || camelNegated;
1743
+ const flagName = kebabNegated ? withoutDashes.slice(3) : camelNegated ? withoutDashes[2].toLowerCase() + withoutDashes.slice(3) : withoutDashes;
1545
1744
  const resolvedName = lookup.aliasMap.get(flagName) ?? flagName;
1745
+ const suppressDefaultNegation = defaultIsNegated && lookup.customNegatedFields.has(resolvedName);
1546
1746
  return {
1547
1747
  resolvedName,
1548
1748
  withoutDashes,
1549
- isNegated,
1550
- isGlobal: lookup.flagNames.has(resolvedName) || lookup.cliNames.has(withoutDashes) || lookup.cliNames.has(flagName)
1749
+ isNegated: defaultIsNegated && !suppressDefaultNegation,
1750
+ isGlobal: !suppressDefaultNegation && (lookup.flagNames.has(resolvedName) || lookup.cliNames.has(withoutDashes) || lookup.cliNames.has(flagName)),
1751
+ isSuppressedNegation: suppressDefaultNegation
1551
1752
  };
1552
1753
  }
1553
1754
  /**
@@ -1595,6 +1796,7 @@ function scanForSubcommand(argv, subCommandNames, globalExtracted) {
1595
1796
  const lookup = buildGlobalFlagLookup(globalExtracted);
1596
1797
  const subCommandNameSet = new Set(subCommandNames);
1597
1798
  const globalTokensBefore = [];
1799
+ const suppressedTokens = [];
1598
1800
  let i = 0;
1599
1801
  while (i < argv.length) {
1600
1802
  const arg = argv[i];
@@ -1602,14 +1804,20 @@ function scanForSubcommand(argv, subCommandNames, globalExtracted) {
1602
1804
  if (!arg.startsWith("-") && subCommandNameSet.has(arg)) return {
1603
1805
  subCommandIndex: i,
1604
1806
  globalTokensBefore,
1605
- tokensAfterSubcommand: argv.slice(i + 1)
1807
+ tokensAfterSubcommand: argv.slice(i + 1),
1808
+ suppressedTokens
1606
1809
  };
1607
1810
  if (arg.startsWith("--")) {
1608
- const { resolvedName, isNegated, isGlobal } = resolveGlobalLongOption(arg, lookup);
1811
+ const { resolvedName, isNegated, isGlobal, isSuppressedNegation } = resolveGlobalLongOption(arg, lookup);
1609
1812
  if (isGlobal) {
1610
1813
  i += collectGlobalFlag(argv, i, resolvedName, isNegated, lookup.booleanFlags, globalTokensBefore);
1611
1814
  continue;
1612
1815
  }
1816
+ if (isSuppressedNegation) {
1817
+ suppressedTokens.push(arg.includes("=") ? arg.slice(2, arg.indexOf("=")) : arg.slice(2));
1818
+ i++;
1819
+ continue;
1820
+ }
1613
1821
  break;
1614
1822
  }
1615
1823
  if (arg.startsWith("-") && arg.length > 1) {
@@ -1628,7 +1836,8 @@ function scanForSubcommand(argv, subCommandNames, globalExtracted) {
1628
1836
  return {
1629
1837
  subCommandIndex: -1,
1630
1838
  globalTokensBefore,
1631
- tokensAfterSubcommand: []
1839
+ tokensAfterSubcommand: [],
1840
+ suppressedTokens
1632
1841
  };
1633
1842
  }
1634
1843
  const BUILTIN_FLAGS = new Set([
@@ -1689,7 +1898,7 @@ function parseArgs(argv, command, options = {}) {
1689
1898
  remainingArgs: scanResult.tokensAfterSubcommand,
1690
1899
  rawArgs: {},
1691
1900
  positionals: [],
1692
- unknownFlags: [],
1901
+ unknownFlags: scanResult.suppressedTokens,
1693
1902
  rawGlobalArgs
1694
1903
  };
1695
1904
  }
@@ -1713,6 +1922,7 @@ function parseArgs(argv, command, options = {}) {
1713
1922
  validateDuplicateFields(extracted);
1714
1923
  validateCaseVariantCollisions(extracted);
1715
1924
  validateDuplicateAliases(extracted);
1925
+ validateDuplicateNegations(extracted);
1716
1926
  validatePositionalConfig(extracted);
1717
1927
  validateReservedAliases(extracted, hasSubCommands);
1718
1928
  if (options.globalExtracted) validateCrossSchemaCollisions(options.globalExtracted, extracted);
@@ -1822,7 +2032,7 @@ function separateGlobalArgs(argv, globalExtracted, localExtracted) {
1822
2032
  }
1823
2033
  if (arg.startsWith("--")) {
1824
2034
  const { resolvedName, withoutDashes, isNegated, isGlobal } = resolveGlobalLongOption(arg, lookup);
1825
- const flagName = isNegated ? withoutDashes.slice(3) : withoutDashes;
2035
+ const flagName = resolvedName;
1826
2036
  const isLocalCollision = localFieldNames.has(withoutDashes) || localFieldNames.has(flagName) || localCliNames.has(withoutDashes) || localCliNames.has(flagName) || localAliasMapKeys.has(withoutDashes) || localAliasMapKeys.has(flagName);
1827
2037
  if (isGlobal && !isLocalCollision) {
1828
2038
  i += collectGlobalFlag(argv, i, resolvedName, isNegated, lookup.booleanFlags, globalTokens) - 1;
@@ -2058,12 +2268,36 @@ async function runCommand(command, argv, options = {}) {
2058
2268
  return result;
2059
2269
  }
2060
2270
  /**
2271
+ * Hidden internal subcommands (e.g. `__refresh-completion`) are spawned
2272
+ * by background hooks and must not run user-provided
2273
+ * `setup`/`cleanup`/`prompt` or required `globalArgs`. Those exist for
2274
+ * the foreground CLI run; replaying them in a detached child causes
2275
+ * duplicate side effects, stuck prompts, and validation failures the
2276
+ * user never opted into.
2277
+ *
2278
+ * We treat any registered subcommand whose name starts with `__` as
2279
+ * internal. We use `findFirstPositional` (schema-aware) instead of the
2280
+ * naive "first non-flag token" so an option *value* like
2281
+ * `--name __refresh-completion` doesn't trip the bypass — that would
2282
+ * silently skip lifecycle hooks for ordinary invocations.
2283
+ */
2284
+ function isInternalSubcommandInvocation(command, argv, globalExtracted) {
2285
+ const firstPositional = findFirstPositional(argv, globalExtracted);
2286
+ if (!firstPositional || !firstPositional.startsWith("__")) return false;
2287
+ return Boolean(command.subCommands?.[firstPositional]);
2288
+ }
2289
+ /**
2061
2290
  * Run a CLI command as the main entry point
2062
2291
  *
2063
2292
  * This function:
2064
2293
  * - Uses process.argv for arguments
2065
2294
  * - Handles SIGINT/SIGTERM signals
2066
2295
  * - Calls process.exit with the appropriate exit code
2296
+ * - Invokes `command.runMainHook` once before parsing if set, so plug-ins
2297
+ * like `withCompletionCommand` can fire detached background work
2298
+ * - Bypasses user `setup`/`cleanup`/`prompt` and required `globalArgs`
2299
+ * for registered hidden subcommands whose name starts with `__`
2300
+ * (e.g. `__refresh-completion`)
2067
2301
  *
2068
2302
  * @param command - The command to run
2069
2303
  * @param options - Main options (version, debug)
@@ -2081,38 +2315,51 @@ async function runCommand(command, argv, options = {}) {
2081
2315
  * ```
2082
2316
  */
2083
2317
  async function runMain(command, options = {}) {
2084
- const globalExtracted = extractAndValidateGlobal(options);
2085
- if (options.setup) try {
2086
- await options.setup({});
2318
+ if (command.runMainHook) try {
2319
+ command.runMainHook(process.argv.slice(2));
2320
+ } catch {}
2321
+ const argv = process.argv.slice(2);
2322
+ let globalExtractedForBypass;
2323
+ if (options.globalArgs) try {
2324
+ globalExtractedForBypass = extractFields(options.globalArgs);
2325
+ } catch {}
2326
+ let effectiveOptions = options;
2327
+ if (isInternalSubcommandInvocation(command, argv, globalExtractedForBypass)) {
2328
+ const { setup: _s, cleanup: _c, prompt: _p, globalArgs: _g, ...rest } = options;
2329
+ effectiveOptions = rest;
2330
+ }
2331
+ const globalExtracted = extractAndValidateGlobal(effectiveOptions);
2332
+ if (effectiveOptions.setup) try {
2333
+ await effectiveOptions.setup({});
2087
2334
  } catch (e) {
2088
2335
  const error = e instanceof Error ? e : new Error(String(e));
2089
- if (options.cleanup) try {
2090
- await options.cleanup({ error });
2336
+ if (effectiveOptions.cleanup) try {
2337
+ await effectiveOptions.cleanup({ error });
2091
2338
  } catch {}
2092
2339
  process.exit(1);
2093
2340
  }
2094
- const result = await runCommandInternal(command, process.argv.slice(2), {
2095
- debug: options.debug,
2096
- captureLogs: options.captureLogs,
2097
- skipValidation: options.skipValidation,
2341
+ const result = await runCommandInternal(command, argv, {
2342
+ debug: effectiveOptions.debug,
2343
+ captureLogs: effectiveOptions.captureLogs,
2344
+ skipValidation: effectiveOptions.skipValidation,
2098
2345
  handleSignals: true,
2099
- logger: options.logger,
2100
- globalArgs: options.globalArgs,
2101
- prompt: options.prompt,
2346
+ logger: effectiveOptions.logger,
2347
+ globalArgs: effectiveOptions.globalArgs,
2348
+ prompt: effectiveOptions.prompt,
2102
2349
  _globalExtracted: globalExtracted,
2103
- _globalCleanup: options.cleanup,
2350
+ _globalCleanup: effectiveOptions.cleanup,
2104
2351
  _context: {
2105
2352
  commandPath: [],
2106
2353
  rootName: command.name,
2107
- rootVersion: options.version,
2354
+ rootVersion: effectiveOptions.version,
2108
2355
  globalExtracted
2109
2356
  }
2110
2357
  });
2111
- if ((options.displayErrors ?? true) && !result.success && result.error) (options.logger ?? defaultLogger).error(formatRuntimeError(result.error, options.debug ?? false));
2112
- if (options.cleanup) {
2358
+ if ((effectiveOptions.displayErrors ?? true) && !result.success && result.error) (effectiveOptions.logger ?? defaultLogger).error(formatRuntimeError(result.error, effectiveOptions.debug ?? false));
2359
+ if (effectiveOptions.cleanup) {
2113
2360
  const cleanupCtx = { error: !result.success ? result.error : void 0 };
2114
2361
  try {
2115
- await options.cleanup(cleanupCtx);
2362
+ await effectiveOptions.cleanup(cleanupCtx);
2116
2363
  } catch {}
2117
2364
  }
2118
2365
  if (process.stdout.writableLength > 0) await new Promise((resolve) => process.stdout.once("drain", resolve));
@@ -2188,6 +2435,22 @@ async function runCommandInternal(command, argv, options = {}) {
2188
2435
  };
2189
2436
  }
2190
2437
  if (parseResult.subCommand) {
2438
+ if (parseResult.unknownFlags.length > 0) {
2439
+ const globalMode = context.globalExtracted?.unknownKeysMode ?? "strip";
2440
+ if (globalMode === "strict") {
2441
+ collector?.stop();
2442
+ return {
2443
+ success: false,
2444
+ error: /* @__PURE__ */ new Error(`Unknown flags: ${parseResult.unknownFlags.join(", ")}`),
2445
+ exitCode: 1,
2446
+ logs: getCurrentLogs()
2447
+ };
2448
+ }
2449
+ if (globalMode === "strip") {
2450
+ const knownGlobalFlags = context.globalExtracted?.fields.map((f) => f.name) ?? [];
2451
+ for (const flag of parseResult.unknownFlags) logger.error(formatUnknownFlagWarning(flag, knownGlobalFlags));
2452
+ }
2453
+ }
2191
2454
  const resolved = await resolveSubcommandWithAlias(command, parseResult.subCommand);
2192
2455
  if (resolved) {
2193
2456
  const subContext = {
@@ -2328,6 +2591,7 @@ function extractAndValidateGlobal(options) {
2328
2591
  validateDuplicateFields(extracted);
2329
2592
  validateCaseVariantCollisions(extracted);
2330
2593
  validateDuplicateAliases(extracted);
2594
+ validateDuplicateNegations(extracted);
2331
2595
  validateReservedAliases(extracted, true);
2332
2596
  const positionalNames = extracted.fields.filter((f) => f.positional).map((f) => f.name);
2333
2597
  if (positionalNames.length > 0) throw new Error(`Global options schema must not contain positional arguments. Found: ${positionalNames.join(", ")}`);
@@ -2336,5 +2600,5 @@ function extractAndValidateGlobal(options) {
2336
2600
  }
2337
2601
 
2338
2602
  //#endregion
2339
- export { logger as C, createDualCaseProxy as D, symbols as E, isColorEnabled as S, styles as T, PositionalConfigError as _, parseArgv as a, renderInline as b, validateCommand as c, validateDuplicateFields as d, validatePositionalConfig as f, DuplicateFieldError as g, DuplicateAliasError as h, formatValidationErrors as i, validateCrossSchemaCollisions as l, CaseVariantCollisionError as m, runMain as n, formatCommandValidationErrors as o, validateReservedAliases as p, runner_exports as r, validateCaseVariantCollisions as s, runCommand as t, validateDuplicateAliases as u, ReservedAliasError as v, setColorEnabled as w, renderMarkdown as x, generateHelp as y };
2340
- //# sourceMappingURL=runner-DKAQBNNh.js.map
2603
+ export { renderMarkdown as C, styles as D, setColorEnabled as E, symbols as O, renderInline as S, logger as T, DuplicateFieldError as _, parseArgv as a, ReservedAliasError as b, validateCommand as c, validateDuplicateFields as d, validateDuplicateNegations as f, DuplicateAliasError as g, CaseVariantCollisionError as h, formatValidationErrors as i, createDualCaseProxy as k, validateCrossSchemaCollisions as l, validateReservedAliases as m, runMain as n, formatCommandValidationErrors as o, validatePositionalConfig as p, runner_exports as r, validateCaseVariantCollisions as s, runCommand as t, validateDuplicateAliases as u, DuplicateNegationError as v, isColorEnabled as w, generateHelp as x, PositionalConfigError as y };
2604
+ //# sourceMappingURL=runner-BHeCMEa5.js.map