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,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 toCamelCase, c as listSubCommands, d as resolveSubcommandWithAlias, f as isLazyCommand, l as resolveLazyCommand, m as resolveSubCommandMeta, n as getAllAliases, o as toKebabCase, r as getExtractedFields, s as listSubCommandNamesWithAliases, t as extractFields } from "./schema-extractor-C50R-1re.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,16 +1922,19 @@ 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);
1719
1929
  }
1720
1930
  }
1931
+ const ddIdx = argv.indexOf("--");
1932
+ const flagScanArgv = ddIdx >= 0 ? argv.slice(0, ddIdx) : argv;
1721
1933
  const hasUserDefinedH = extracted?.fields.some((f) => f.overrideBuiltinAlias === true && getAllAliases(f).includes("H")) ?? false;
1722
1934
  const hasUserDefinedh = extracted?.fields.some((f) => f.overrideBuiltinAlias === true && getAllAliases(f).includes("h")) ?? false;
1723
- const helpAllRequested = argv.includes("--help-all") || !hasUserDefinedH && argv.includes("-H");
1724
- const helpRequested = !helpAllRequested && (argv.includes("--help") || !hasUserDefinedh && argv.includes("-h"));
1725
- const versionRequested = argv.includes("--version");
1935
+ const helpAllRequested = flagScanArgv.includes("--help-all") || !hasUserDefinedH && flagScanArgv.includes("-H");
1936
+ const helpRequested = !helpAllRequested && (flagScanArgv.includes("--help") || !hasUserDefinedh && flagScanArgv.includes("-h"));
1937
+ const versionRequested = flagScanArgv.includes("--version");
1726
1938
  if (helpRequested || helpAllRequested || versionRequested) return {
1727
1939
  helpRequested,
1728
1940
  helpAllRequested,
@@ -1822,7 +2034,7 @@ function separateGlobalArgs(argv, globalExtracted, localExtracted) {
1822
2034
  }
1823
2035
  if (arg.startsWith("--")) {
1824
2036
  const { resolvedName, withoutDashes, isNegated, isGlobal } = resolveGlobalLongOption(arg, lookup);
1825
- const flagName = isNegated ? withoutDashes.slice(3) : withoutDashes;
2037
+ const flagName = resolvedName;
1826
2038
  const isLocalCollision = localFieldNames.has(withoutDashes) || localFieldNames.has(flagName) || localCliNames.has(withoutDashes) || localCliNames.has(flagName) || localAliasMapKeys.has(withoutDashes) || localAliasMapKeys.has(flagName);
1827
2039
  if (isGlobal && !isLocalCollision) {
1828
2040
  i += collectGlobalFlag(argv, i, resolvedName, isNegated, lookup.booleanFlags, globalTokens) - 1;
@@ -2058,12 +2270,36 @@ async function runCommand(command, argv, options = {}) {
2058
2270
  return result;
2059
2271
  }
2060
2272
  /**
2273
+ * Hidden internal subcommands (e.g. `__refresh-completion`) are spawned
2274
+ * by background hooks and must not run user-provided
2275
+ * `setup`/`cleanup`/`prompt` or required `globalArgs`. Those exist for
2276
+ * the foreground CLI run; replaying them in a detached child causes
2277
+ * duplicate side effects, stuck prompts, and validation failures the
2278
+ * user never opted into.
2279
+ *
2280
+ * We treat any registered subcommand whose name starts with `__` as
2281
+ * internal. We use `findFirstPositional` (schema-aware) instead of the
2282
+ * naive "first non-flag token" so an option *value* like
2283
+ * `--name __refresh-completion` doesn't trip the bypass — that would
2284
+ * silently skip lifecycle hooks for ordinary invocations.
2285
+ */
2286
+ function isInternalSubcommandInvocation(command, argv, globalExtracted) {
2287
+ const firstPositional = findFirstPositional(argv, globalExtracted);
2288
+ if (!firstPositional || !firstPositional.startsWith("__")) return false;
2289
+ return Boolean(command.subCommands?.[firstPositional]);
2290
+ }
2291
+ /**
2061
2292
  * Run a CLI command as the main entry point
2062
2293
  *
2063
2294
  * This function:
2064
2295
  * - Uses process.argv for arguments
2065
2296
  * - Handles SIGINT/SIGTERM signals
2066
2297
  * - Calls process.exit with the appropriate exit code
2298
+ * - Invokes `command.runMainHook` once before parsing if set, so plug-ins
2299
+ * like `withCompletionCommand` can fire detached background work
2300
+ * - Bypasses user `setup`/`cleanup`/`prompt` and required `globalArgs`
2301
+ * for registered hidden subcommands whose name starts with `__`
2302
+ * (e.g. `__refresh-completion`)
2067
2303
  *
2068
2304
  * @param command - The command to run
2069
2305
  * @param options - Main options (version, debug)
@@ -2081,38 +2317,51 @@ async function runCommand(command, argv, options = {}) {
2081
2317
  * ```
2082
2318
  */
2083
2319
  async function runMain(command, options = {}) {
2084
- const globalExtracted = extractAndValidateGlobal(options);
2085
- if (options.setup) try {
2086
- await options.setup({});
2320
+ if (command.runMainHook) try {
2321
+ command.runMainHook(process.argv.slice(2));
2322
+ } catch {}
2323
+ const argv = process.argv.slice(2);
2324
+ let globalExtractedForBypass;
2325
+ if (options.globalArgs) try {
2326
+ globalExtractedForBypass = extractFields(options.globalArgs);
2327
+ } catch {}
2328
+ let effectiveOptions = options;
2329
+ if (isInternalSubcommandInvocation(command, argv, globalExtractedForBypass)) {
2330
+ const { setup: _s, cleanup: _c, prompt: _p, globalArgs: _g, ...rest } = options;
2331
+ effectiveOptions = rest;
2332
+ }
2333
+ const globalExtracted = extractAndValidateGlobal(effectiveOptions);
2334
+ if (effectiveOptions.setup) try {
2335
+ await effectiveOptions.setup({});
2087
2336
  } catch (e) {
2088
2337
  const error = e instanceof Error ? e : new Error(String(e));
2089
- if (options.cleanup) try {
2090
- await options.cleanup({ error });
2338
+ if (effectiveOptions.cleanup) try {
2339
+ await effectiveOptions.cleanup({ error });
2091
2340
  } catch {}
2092
2341
  process.exit(1);
2093
2342
  }
2094
- const result = await runCommandInternal(command, process.argv.slice(2), {
2095
- debug: options.debug,
2096
- captureLogs: options.captureLogs,
2097
- skipValidation: options.skipValidation,
2343
+ const result = await runCommandInternal(command, argv, {
2344
+ debug: effectiveOptions.debug,
2345
+ captureLogs: effectiveOptions.captureLogs,
2346
+ skipValidation: effectiveOptions.skipValidation,
2098
2347
  handleSignals: true,
2099
- logger: options.logger,
2100
- globalArgs: options.globalArgs,
2101
- prompt: options.prompt,
2348
+ logger: effectiveOptions.logger,
2349
+ globalArgs: effectiveOptions.globalArgs,
2350
+ prompt: effectiveOptions.prompt,
2102
2351
  _globalExtracted: globalExtracted,
2103
- _globalCleanup: options.cleanup,
2352
+ _globalCleanup: effectiveOptions.cleanup,
2104
2353
  _context: {
2105
2354
  commandPath: [],
2106
2355
  rootName: command.name,
2107
- rootVersion: options.version,
2356
+ rootVersion: effectiveOptions.version,
2108
2357
  globalExtracted
2109
2358
  }
2110
2359
  });
2111
- if ((options.displayErrors ?? true) && !result.success && result.error) (options.logger ?? defaultLogger).error(formatRuntimeError(result.error, options.debug ?? false));
2112
- if (options.cleanup) {
2360
+ if ((effectiveOptions.displayErrors ?? true) && !result.success && result.error) (effectiveOptions.logger ?? defaultLogger).error(formatRuntimeError(result.error, effectiveOptions.debug ?? false));
2361
+ if (effectiveOptions.cleanup) {
2113
2362
  const cleanupCtx = { error: !result.success ? result.error : void 0 };
2114
2363
  try {
2115
- await options.cleanup(cleanupCtx);
2364
+ await effectiveOptions.cleanup(cleanupCtx);
2116
2365
  } catch {}
2117
2366
  }
2118
2367
  if (process.stdout.writableLength > 0) await new Promise((resolve) => process.stdout.once("drain", resolve));
@@ -2188,6 +2437,22 @@ async function runCommandInternal(command, argv, options = {}) {
2188
2437
  };
2189
2438
  }
2190
2439
  if (parseResult.subCommand) {
2440
+ if (parseResult.unknownFlags.length > 0) {
2441
+ const globalMode = context.globalExtracted?.unknownKeysMode ?? "strip";
2442
+ if (globalMode === "strict") {
2443
+ collector?.stop();
2444
+ return {
2445
+ success: false,
2446
+ error: /* @__PURE__ */ new Error(`Unknown flags: ${parseResult.unknownFlags.join(", ")}`),
2447
+ exitCode: 1,
2448
+ logs: getCurrentLogs()
2449
+ };
2450
+ }
2451
+ if (globalMode === "strip") {
2452
+ const knownGlobalFlags = context.globalExtracted?.fields.map((f) => f.name) ?? [];
2453
+ for (const flag of parseResult.unknownFlags) logger.error(formatUnknownFlagWarning(flag, knownGlobalFlags));
2454
+ }
2455
+ }
2191
2456
  const resolved = await resolveSubcommandWithAlias(command, parseResult.subCommand);
2192
2457
  if (resolved) {
2193
2458
  const subContext = {
@@ -2234,7 +2499,8 @@ async function runCommandInternal(command, argv, options = {}) {
2234
2499
  } else if (unknownKeysMode === "strip") for (const flag of parseResult.unknownFlags) logger.error(formatUnknownFlagWarning(flag, knownFlags));
2235
2500
  }
2236
2501
  let validatedGlobalArgs = {};
2237
- if (options.globalArgs && options._globalExtracted) {
2502
+ const isCompletionInvocation = command.name === "__complete";
2503
+ if (options.globalArgs && options._globalExtracted && !isCompletionInvocation) {
2238
2504
  for (const field of options._globalExtracted.fields) if (field.env && accumulatedGlobalArgs[field.name] === void 0) {
2239
2505
  const envNames = Array.isArray(field.env) ? field.env : [field.env];
2240
2506
  for (const envName of envNames) {
@@ -2263,7 +2529,7 @@ async function runCommandInternal(command, argv, options = {}) {
2263
2529
  }
2264
2530
  if (!command.args) {
2265
2531
  const proxiedGlobalArgs = createDualCaseProxy(validatedGlobalArgs);
2266
- if (options._globalExtracted) await runEffects(proxiedGlobalArgs, options._globalExtracted, proxiedGlobalArgs);
2532
+ if (options._globalExtracted && !isCompletionInvocation) await runEffects(proxiedGlobalArgs, options._globalExtracted, proxiedGlobalArgs);
2267
2533
  collector?.stop();
2268
2534
  return await executeLifecycle(command, proxiedGlobalArgs, {
2269
2535
  handleSignals: options.handleSignals,
@@ -2292,8 +2558,8 @@ async function runCommandInternal(command, argv, options = {}) {
2292
2558
  }
2293
2559
  const proxiedCommandArgs = createDualCaseProxy(validationResult.data);
2294
2560
  const proxiedGlobalArgs = createDualCaseProxy(validatedGlobalArgs);
2295
- if (options._globalExtracted) await runEffects(proxiedGlobalArgs, options._globalExtracted, proxiedGlobalArgs);
2296
- if (parseResult.extractedFields) await runEffects(proxiedCommandArgs, parseResult.extractedFields, proxiedGlobalArgs);
2561
+ if (options._globalExtracted && !isCompletionInvocation) await runEffects(proxiedGlobalArgs, options._globalExtracted, proxiedGlobalArgs);
2562
+ if (parseResult.extractedFields && !isCompletionInvocation) await runEffects(proxiedCommandArgs, parseResult.extractedFields, proxiedGlobalArgs);
2297
2563
  const mergedArgs = createDualCaseProxy({
2298
2564
  ...proxiedGlobalArgs,
2299
2565
  ...proxiedCommandArgs
@@ -2328,6 +2594,7 @@ function extractAndValidateGlobal(options) {
2328
2594
  validateDuplicateFields(extracted);
2329
2595
  validateCaseVariantCollisions(extracted);
2330
2596
  validateDuplicateAliases(extracted);
2597
+ validateDuplicateNegations(extracted);
2331
2598
  validateReservedAliases(extracted, true);
2332
2599
  const positionalNames = extracted.fields.filter((f) => f.positional).map((f) => f.name);
2333
2600
  if (positionalNames.length > 0) throw new Error(`Global options schema must not contain positional arguments. Found: ${positionalNames.join(", ")}`);
@@ -2336,5 +2603,5 @@ function extractAndValidateGlobal(options) {
2336
2603
  }
2337
2604
 
2338
2605
  //#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
2606
+ 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 };
2607
+ //# sourceMappingURL=runner-BmSEiD9A.js.map