politty 0.4.3 → 0.4.6

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 (60) hide show
  1. package/dist/{arg-registry-BUUhZ7JR.d.ts → arg-registry-2m40k1Et.d.ts} +1 -1
  2. package/dist/{arg-registry-BUUhZ7JR.d.ts.map → arg-registry-2m40k1Et.d.ts.map} +1 -1
  3. package/dist/augment.d.ts +1 -1
  4. package/dist/completion/index.cjs +16 -168
  5. package/dist/completion/index.d.cts +2 -77
  6. package/dist/completion/index.d.ts +2 -77
  7. package/dist/completion/index.js +3 -154
  8. package/dist/{zsh-CASZWn0o.cjs → completion-Df0eZ70u.cjs} +326 -37
  9. package/dist/completion-Df0eZ70u.cjs.map +1 -0
  10. package/dist/{zsh-hjvdI8uZ.js → completion-_AnQsWh9.js} +298 -27
  11. package/dist/completion-_AnQsWh9.js.map +1 -0
  12. package/dist/docs/index.cjs +276 -29
  13. package/dist/docs/index.cjs.map +1 -1
  14. package/dist/docs/index.d.cts +62 -7
  15. package/dist/docs/index.d.cts.map +1 -1
  16. package/dist/docs/index.d.ts +62 -7
  17. package/dist/docs/index.d.ts.map +1 -1
  18. package/dist/docs/index.js +271 -30
  19. package/dist/docs/index.js.map +1 -1
  20. package/dist/{value-completion-resolver-BQgHsX7b.d.cts → index-BZalbMeu.d.ts} +83 -4
  21. package/dist/index-BZalbMeu.d.ts.map +1 -0
  22. package/dist/{value-completion-resolver-C9LTGr0O.d.ts → index-C5-0RXiH.d.cts} +83 -4
  23. package/dist/index-C5-0RXiH.d.cts.map +1 -0
  24. package/dist/index.cjs +8 -7
  25. package/dist/index.d.cts +67 -13
  26. package/dist/index.d.cts.map +1 -1
  27. package/dist/index.d.ts +68 -14
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +4 -5
  30. package/dist/{lazy-BEDnSR0m.cjs → lazy-DHlvJiQQ.cjs} +44 -8
  31. package/dist/lazy-DHlvJiQQ.cjs.map +1 -0
  32. package/dist/{lazy-BrEg8SgI.js → lazy-DSyfzR-F.js} +38 -8
  33. package/dist/lazy-DSyfzR-F.js.map +1 -0
  34. package/dist/{runner-C4fSHJMe.cjs → runner-C1Aah5c5.cjs} +365 -23
  35. package/dist/runner-C1Aah5c5.cjs.map +1 -0
  36. package/dist/{runner-D6k4BgB4.js → runner-DjG0uBxQ.js} +365 -23
  37. package/dist/runner-DjG0uBxQ.js.map +1 -0
  38. package/dist/{schema-extractor-n9288WJ6.d.ts → schema-extractor-C9APqeSL.d.ts} +30 -3
  39. package/dist/schema-extractor-C9APqeSL.d.ts.map +1 -0
  40. package/dist/{schema-extractor-DFaAZzaY.d.cts → schema-extractor-CVf0J4An.d.cts} +29 -2
  41. package/dist/schema-extractor-CVf0J4An.d.cts.map +1 -0
  42. package/dist/{subcommand-router-CAzBsLSI.js → subcommand-router-CKuy6D2b.js} +2 -2
  43. package/dist/{subcommand-router-CAzBsLSI.js.map → subcommand-router-CKuy6D2b.js.map} +1 -1
  44. package/dist/{subcommand-router-ZjNjFaUL.cjs → subcommand-router-sZHhUP7b.cjs} +2 -2
  45. package/dist/{subcommand-router-ZjNjFaUL.cjs.map → subcommand-router-sZHhUP7b.cjs.map} +1 -1
  46. package/package.json +9 -9
  47. package/dist/completion/index.cjs.map +0 -1
  48. package/dist/completion/index.d.cts.map +0 -1
  49. package/dist/completion/index.d.ts.map +0 -1
  50. package/dist/completion/index.js.map +0 -1
  51. package/dist/lazy-BEDnSR0m.cjs.map +0 -1
  52. package/dist/lazy-BrEg8SgI.js.map +0 -1
  53. package/dist/runner-C4fSHJMe.cjs.map +0 -1
  54. package/dist/runner-D6k4BgB4.js.map +0 -1
  55. package/dist/schema-extractor-DFaAZzaY.d.cts.map +0 -1
  56. package/dist/schema-extractor-n9288WJ6.d.ts.map +0 -1
  57. package/dist/value-completion-resolver-BQgHsX7b.d.cts.map +0 -1
  58. package/dist/value-completion-resolver-C9LTGr0O.d.ts.map +0 -1
  59. package/dist/zsh-CASZWn0o.cjs.map +0 -1
  60. package/dist/zsh-hjvdI8uZ.js.map +0 -1
@@ -1,5 +1,5 @@
1
- import { a as getExtractedFields, i as extractFields, r as resolveSubCommandMeta } from "./lazy-BrEg8SgI.js";
2
- import { a as emptyLogs, i as createLogCollector, n as resolveLazyCommand, o as mergeLogs, r as resolveSubcommand, t as listSubCommands } from "./subcommand-router-CAzBsLSI.js";
1
+ import { a as getExtractedFields, i as extractFields, r as resolveSubCommandMeta, s as toCamelCase } from "./lazy-DSyfzR-F.js";
2
+ import { a as emptyLogs, i as createLogCollector, n as resolveLazyCommand, o as mergeLogs, r as resolveSubcommand, t as listSubCommands } from "./subcommand-router-CKuy6D2b.js";
3
3
  import { z } from "zod";
4
4
  import { styleText } from "node:util";
5
5
  import stringWidth from "string-width";
@@ -555,6 +555,7 @@ function renderUsageLine(command, context) {
555
555
  const parts = [];
556
556
  const name = buildUsageCommandName(command, context);
557
557
  parts.push(styles.commandName(name));
558
+ if (context?.globalExtracted?.fields.length) parts.push(styles.placeholder("[global options]"));
558
559
  const extracted = getExtractedFields(command);
559
560
  if (extracted) {
560
561
  const positionals = extracted.fields.filter((a) => a.positional);
@@ -740,6 +741,24 @@ function formatOption(flags, description, indent = 0, extraDescPadding = 0) {
740
741
  return `${indentStr} ${padEndVisual(flags, effectiveFlagWidth)}${description}`;
741
742
  }
742
743
  /**
744
+ * Format a single option field as a help line
745
+ */
746
+ function formatFieldLine(opt, indent = 0, extraDescPadding = 0) {
747
+ const flags = formatFlags(opt);
748
+ let desc = opt.description ?? "";
749
+ if (opt.defaultValue !== void 0) desc += ` ${styles.defaultValue(`(default: ${JSON.stringify(opt.defaultValue)})`)}`;
750
+ if (opt.required) desc += ` ${styles.required("(required)")}`;
751
+ const envInfo = formatEnvInfo(opt.env);
752
+ if (envInfo) desc += ` ${envInfo}`;
753
+ return formatOption(flags, desc, indent, extraDescPadding);
754
+ }
755
+ /**
756
+ * Render global options section
757
+ */
758
+ function renderGlobalOptions(globalExtracted) {
759
+ return globalExtracted.fields.filter((a) => !a.positional).map((opt) => formatFieldLine(opt)).join("\n");
760
+ }
761
+ /**
743
762
  * Render options for a subcommand (used by showSubcommandOptions)
744
763
  */
745
764
  function renderSubcommandOptionsCompact(command, indent) {
@@ -802,6 +821,7 @@ function generateHelp(command, options) {
802
821
  sections.push(`${styles.sectionHeader("Usage:")} ${renderUsageLine(command, context)}`);
803
822
  const optionsText = renderOptions(command, options.descriptions, context);
804
823
  if (optionsText) sections.push(`${styles.sectionHeader("Options:")}\n${optionsText}`);
824
+ if (context?.globalExtracted?.fields.length) sections.push(`${styles.sectionHeader("Global Options:")}\n${renderGlobalOptions(context.globalExtracted)}`);
805
825
  if (options.showSubcommands !== false && command.subCommands && getVisibleSubcommandEntries(command.subCommands).length > 0) {
806
826
  const currentPath = context?.commandPath?.join(" ") ?? "";
807
827
  const visibleSubCommands = Object.fromEntries(getVisibleSubcommandEntries(command.subCommands));
@@ -1114,13 +1134,20 @@ function formatCommandValidationErrors(errors) {
1114
1134
  * - Combined short options: -abc (treated as -a -b -c if all are boolean)
1115
1135
  * - Positional arguments
1116
1136
  * - -- to stop parsing options
1137
+ * - Boolean negation: --no-flag, --noFlag (requires `booleanFlags`)
1138
+ *
1139
+ * **Note:** When using negation detection (`--noFlag` / `--no-flag`),
1140
+ * supply `definedNames` so that options whose names happen to start with
1141
+ * "no" (e.g. `noDryRun`) are not mistaken for negation of another flag.
1142
+ * Without `definedNames`, all `--noX` forms matching a boolean flag will
1143
+ * be treated as negation.
1117
1144
  *
1118
1145
  * @param argv - Command line arguments
1119
1146
  * @param options - Parser options
1120
1147
  * @returns Parsed arguments
1121
1148
  */
1122
1149
  function parseArgv(argv, options = {}) {
1123
- const { aliasMap = /* @__PURE__ */ new Map(), booleanFlags = /* @__PURE__ */ new Set(), arrayFlags = /* @__PURE__ */ new Set() } = options;
1150
+ const { aliasMap = /* @__PURE__ */ new Map(), booleanFlags = /* @__PURE__ */ new Set(), arrayFlags = /* @__PURE__ */ new Set(), definedNames = /* @__PURE__ */ new Set() } = options;
1124
1151
  const result = {
1125
1152
  options: {},
1126
1153
  positionals: [],
@@ -1153,11 +1180,28 @@ function parseArgv(argv, options = {}) {
1153
1180
  const withoutDashes = arg.slice(2);
1154
1181
  if (withoutDashes.startsWith("no-")) {
1155
1182
  const flagName = withoutDashes.slice(3);
1156
- const resolvedName = aliasMap.get(flagName) ?? flagName;
1183
+ if (flagName === flagName.toLowerCase()) {
1184
+ const resolvedName = aliasMap.get(flagName) ?? flagName;
1185
+ if (booleanFlags.has(resolvedName)) {
1186
+ const asIsResolved = aliasMap.get(withoutDashes) ?? withoutDashes;
1187
+ if (!definedNames.has(asIsResolved)) {
1188
+ setOption(flagName, false);
1189
+ i++;
1190
+ continue;
1191
+ }
1192
+ }
1193
+ }
1194
+ }
1195
+ if (withoutDashes.length > 2 && withoutDashes.startsWith("no") && /[A-Z]/.test(withoutDashes[2])) {
1196
+ const camelFlagName = withoutDashes[2].toLowerCase() + withoutDashes.slice(3);
1197
+ const resolvedName = aliasMap.get(camelFlagName) ?? camelFlagName;
1157
1198
  if (booleanFlags.has(resolvedName)) {
1158
- setOption(flagName, false);
1159
- i++;
1160
- continue;
1199
+ const asIsResolved = aliasMap.get(withoutDashes) ?? withoutDashes;
1200
+ if (!definedNames.has(asIsResolved)) {
1201
+ setOption(camelFlagName, false);
1202
+ i++;
1203
+ continue;
1204
+ }
1161
1205
  }
1162
1206
  }
1163
1207
  const eqIndex = withoutDashes.indexOf("=");
@@ -1223,16 +1267,21 @@ function buildParserOptions(extracted) {
1223
1267
  const aliasMap = /* @__PURE__ */ new Map();
1224
1268
  const booleanFlags = /* @__PURE__ */ new Set();
1225
1269
  const arrayFlags = /* @__PURE__ */ new Set();
1270
+ const definedNames = /* @__PURE__ */ new Set();
1271
+ for (const field of extracted.fields) definedNames.add(field.name);
1226
1272
  for (const field of extracted.fields) {
1227
1273
  if (field.cliName !== field.name) aliasMap.set(field.cliName, field.name);
1228
1274
  if (field.alias) aliasMap.set(field.alias, field.name);
1275
+ const camelVariant = toCamelCase(field.name);
1276
+ if (camelVariant !== field.name && !definedNames.has(camelVariant) && !aliasMap.has(camelVariant)) aliasMap.set(camelVariant, field.name);
1229
1277
  if (field.type === "boolean") booleanFlags.add(field.name);
1230
1278
  if (field.type === "array") arrayFlags.add(field.name);
1231
1279
  }
1232
1280
  return {
1233
1281
  aliasMap,
1234
1282
  booleanFlags,
1235
- arrayFlags
1283
+ arrayFlags,
1284
+ definedNames
1236
1285
  };
1237
1286
  }
1238
1287
  /**
@@ -1256,6 +1305,151 @@ function mergeWithPositionals(parsed, extracted) {
1256
1305
  return result;
1257
1306
  }
1258
1307
 
1308
+ //#endregion
1309
+ //#region src/parser/subcommand-scanner.ts
1310
+ /**
1311
+ * Build lookup tables from extracted global schema fields.
1312
+ * Shared by scanForSubcommand, separateGlobalArgs, and findFirstPositional.
1313
+ */
1314
+ function buildGlobalFlagLookup(globalExtracted) {
1315
+ const { aliasMap = /* @__PURE__ */ new Map(), booleanFlags = /* @__PURE__ */ new Set() } = buildParserOptions(globalExtracted);
1316
+ return {
1317
+ aliasMap,
1318
+ booleanFlags,
1319
+ flagNames: new Set(globalExtracted.fields.map((f) => f.name)),
1320
+ cliNames: new Set(globalExtracted.fields.map((f) => f.cliName)),
1321
+ aliases: new Set(globalExtracted.fields.filter((f) => f.alias).map((f) => f.alias))
1322
+ };
1323
+ }
1324
+ /**
1325
+ * Resolve a long option (--flag, --flag=value, --no-flag) against global flag lookup.
1326
+ * Returns the resolved camelCase name and whether it is a known global flag.
1327
+ */
1328
+ function resolveGlobalLongOption(arg, lookup) {
1329
+ const withoutDashes = arg.includes("=") ? arg.slice(2, arg.indexOf("=")) : arg.slice(2);
1330
+ const isNegated = withoutDashes.startsWith("no-");
1331
+ const flagName = isNegated ? withoutDashes.slice(3) : withoutDashes;
1332
+ const resolvedName = lookup.aliasMap.get(flagName) ?? flagName;
1333
+ return {
1334
+ resolvedName,
1335
+ withoutDashes,
1336
+ isNegated,
1337
+ isGlobal: lookup.flagNames.has(resolvedName) || lookup.cliNames.has(withoutDashes) || lookup.cliNames.has(flagName)
1338
+ };
1339
+ }
1340
+ /**
1341
+ * Check whether a non-boolean flag should consume the next argv token as its value.
1342
+ * Returns true when the next token exists, is not a flag, and the current flag
1343
+ * is not boolean / negated / using = syntax.
1344
+ */
1345
+ function shouldConsumeValue(arg, resolvedName, isNegated, nextArg, booleanFlags) {
1346
+ return !arg.includes("=") && !booleanFlags.has(resolvedName) && !isNegated && nextArg !== void 0 && !nextArg.startsWith("-");
1347
+ }
1348
+ /**
1349
+ * Collect a recognized global flag (and its value if applicable) into `dest`,
1350
+ * returning how many argv positions were consumed (1 or 2).
1351
+ */
1352
+ function collectGlobalFlag(argv, i, resolvedName, isNegated, booleanFlags, dest) {
1353
+ const arg = argv[i];
1354
+ dest.push(arg);
1355
+ if (shouldConsumeValue(arg, resolvedName, isNegated, argv[i + 1], booleanFlags)) {
1356
+ dest.push(argv[i + 1]);
1357
+ return 2;
1358
+ }
1359
+ return 1;
1360
+ }
1361
+ /**
1362
+ * Scan argv to find the subcommand position, skipping over global flags.
1363
+ *
1364
+ * Walks argv and recognizes global flags (long, short, --no-*) so that
1365
+ * `my-cli --verbose build --output dist` correctly identifies `build` as
1366
+ * the subcommand (index 1) rather than treating `--verbose` as the subcommand.
1367
+ *
1368
+ * Limitation: flags appearing before the subcommand name are matched only
1369
+ * against the global schema. If a flag is defined in both global and a
1370
+ * subcommand's local schema, the pre-subcommand occurrence is always treated
1371
+ * as global because the local schema is not available until the subcommand is
1372
+ * identified (lazy-loaded commands make eager checking infeasible). Place
1373
+ * colliding flags after the subcommand name so that `separateGlobalArgs` can
1374
+ * apply local-precedence logic.
1375
+ *
1376
+ * @param argv - Command line arguments
1377
+ * @param subCommandNames - Valid subcommand names
1378
+ * @param globalExtracted - Extracted fields from global args schema
1379
+ * @returns Scan result with subcommand position and token separation
1380
+ */
1381
+ function scanForSubcommand(argv, subCommandNames, globalExtracted) {
1382
+ const lookup = buildGlobalFlagLookup(globalExtracted);
1383
+ const subCommandNameSet = new Set(subCommandNames);
1384
+ const globalTokensBefore = [];
1385
+ let i = 0;
1386
+ while (i < argv.length) {
1387
+ const arg = argv[i];
1388
+ if (arg === "--" || BUILTIN_FLAGS.has(arg)) break;
1389
+ if (!arg.startsWith("-") && subCommandNameSet.has(arg)) return {
1390
+ subCommandIndex: i,
1391
+ globalTokensBefore,
1392
+ tokensAfterSubcommand: argv.slice(i + 1)
1393
+ };
1394
+ if (arg.startsWith("--")) {
1395
+ const { resolvedName, isNegated, isGlobal } = resolveGlobalLongOption(arg, lookup);
1396
+ if (isGlobal) {
1397
+ i += collectGlobalFlag(argv, i, resolvedName, isNegated, lookup.booleanFlags, globalTokensBefore);
1398
+ continue;
1399
+ }
1400
+ break;
1401
+ }
1402
+ if (arg.startsWith("-") && arg.length > 1) {
1403
+ const withoutDash = arg.includes("=") ? arg.slice(1, arg.indexOf("=")) : arg.slice(1);
1404
+ if (withoutDash.length === 1) {
1405
+ const resolvedName = lookup.aliasMap.get(withoutDash) ?? withoutDash;
1406
+ if (lookup.aliases.has(withoutDash) || lookup.flagNames.has(resolvedName)) {
1407
+ i += collectGlobalFlag(argv, i, resolvedName, false, lookup.booleanFlags, globalTokensBefore);
1408
+ continue;
1409
+ }
1410
+ }
1411
+ break;
1412
+ }
1413
+ break;
1414
+ }
1415
+ return {
1416
+ subCommandIndex: -1,
1417
+ globalTokensBefore,
1418
+ tokensAfterSubcommand: []
1419
+ };
1420
+ }
1421
+ const BUILTIN_FLAGS = new Set([
1422
+ "--help",
1423
+ "-h",
1424
+ "--help-all",
1425
+ "-H",
1426
+ "--version"
1427
+ ]);
1428
+ /**
1429
+ * Find the first positional argument in argv, properly skipping global flag values.
1430
+ * Without globalExtracted, falls back to the first non-flag token.
1431
+ */
1432
+ function findFirstPositional(argv, globalExtracted) {
1433
+ if (!globalExtracted) return argv.find((arg) => !arg.startsWith("-"));
1434
+ const lookup = buildGlobalFlagLookup(globalExtracted);
1435
+ for (let i = 0; i < argv.length; i++) {
1436
+ const arg = argv[i];
1437
+ if (!arg.startsWith("-")) return arg;
1438
+ if (arg === "--") return void 0;
1439
+ if (arg.startsWith("--")) {
1440
+ const { resolvedName, isNegated, isGlobal } = resolveGlobalLongOption(arg, lookup);
1441
+ if (isGlobal && shouldConsumeValue(arg, resolvedName, isNegated, argv[i + 1], lookup.booleanFlags)) i++;
1442
+ continue;
1443
+ }
1444
+ if (arg.length === 2) {
1445
+ const ch = arg[1];
1446
+ if (lookup.aliases.has(ch)) {
1447
+ if (shouldConsumeValue(arg, lookup.aliasMap.get(ch) ?? ch, false, argv[i + 1], lookup.booleanFlags)) i++;
1448
+ }
1449
+ }
1450
+ }
1451
+ }
1452
+
1259
1453
  //#endregion
1260
1454
  //#region src/parser/arg-parser.ts
1261
1455
  /**
@@ -1269,7 +1463,23 @@ function mergeWithPositionals(parsed, extracted) {
1269
1463
  function parseArgs(argv, command, options = {}) {
1270
1464
  const subCommandNames = command.subCommands ? Object.keys(command.subCommands) : [];
1271
1465
  const hasSubCommands = subCommandNames.length > 0;
1272
- if (hasSubCommands && argv.length > 0) {
1466
+ if (hasSubCommands && argv.length > 0) if (options.globalExtracted) {
1467
+ const scanResult = scanForSubcommand(argv, subCommandNames, options.globalExtracted);
1468
+ if (scanResult.subCommandIndex >= 0) {
1469
+ const rawGlobalArgs = parseGlobalArgs(scanResult.globalTokensBefore, options.globalExtracted);
1470
+ return {
1471
+ helpRequested: false,
1472
+ helpAllRequested: false,
1473
+ versionRequested: false,
1474
+ subCommand: argv[scanResult.subCommandIndex],
1475
+ remainingArgs: scanResult.tokensAfterSubcommand,
1476
+ rawArgs: {},
1477
+ positionals: [],
1478
+ unknownFlags: [],
1479
+ rawGlobalArgs
1480
+ };
1481
+ }
1482
+ } else {
1273
1483
  const firstArg = argv[0];
1274
1484
  if (firstArg && !firstArg.startsWith("-") && subCommandNames.includes(firstArg)) return {
1275
1485
  helpRequested: false,
@@ -1307,6 +1517,13 @@ function parseArgs(argv, command, options = {}) {
1307
1517
  positionals: [],
1308
1518
  unknownFlags: []
1309
1519
  };
1520
+ let commandArgv = argv;
1521
+ let rawGlobalArgs;
1522
+ if (options.globalExtracted) {
1523
+ const { separated, globalParsed } = separateGlobalArgs(argv, options.globalExtracted, extracted);
1524
+ commandArgv = separated;
1525
+ rawGlobalArgs = globalParsed;
1526
+ }
1310
1527
  if (!extracted) return {
1311
1528
  helpRequested: false,
1312
1529
  helpAllRequested: false,
@@ -1315,9 +1532,11 @@ function parseArgs(argv, command, options = {}) {
1315
1532
  remainingArgs: [],
1316
1533
  rawArgs: {},
1317
1534
  positionals: [],
1318
- unknownFlags: []
1535
+ unknownFlags: [],
1536
+ rawGlobalArgs
1319
1537
  };
1320
- const parsed = parseArgv(argv, buildParserOptions(extracted));
1538
+ const parserOptions = buildParserOptions(extracted);
1539
+ const parsed = parseArgv(commandArgv, parserOptions);
1321
1540
  const rawArgs = mergeWithPositionals(parsed, extracted);
1322
1541
  for (const field of extracted.fields) if (field.env && rawArgs[field.name] === void 0) {
1323
1542
  const envNames = Array.isArray(field.env) ? field.env : [field.env];
@@ -1332,6 +1551,11 @@ function parseArgs(argv, command, options = {}) {
1332
1551
  const knownFlags = new Set(extracted.fields.map((f) => f.name));
1333
1552
  const knownCliNames = new Set(extracted.fields.map((f) => f.cliName));
1334
1553
  const knownAliases = new Set(extracted.fields.filter((f) => f.alias).map((f) => f.alias));
1554
+ if (options.globalExtracted) for (const f of options.globalExtracted.fields) {
1555
+ knownFlags.add(f.name);
1556
+ knownCliNames.add(f.cliName);
1557
+ if (f.alias) knownAliases.add(f.alias);
1558
+ }
1335
1559
  const unknownFlags = [];
1336
1560
  for (const key of Object.keys(parsed.options)) if (!knownFlags.has(key) && !knownCliNames.has(key) && !knownAliases.has(key)) unknownFlags.push(key);
1337
1561
  return {
@@ -1343,7 +1567,64 @@ function parseArgs(argv, command, options = {}) {
1343
1567
  rawArgs,
1344
1568
  positionals: parsed.positionals,
1345
1569
  unknownFlags,
1346
- extractedFields: extracted
1570
+ extractedFields: extracted,
1571
+ rawGlobalArgs
1572
+ };
1573
+ }
1574
+ /**
1575
+ * Parse global args from a list of tokens (e.g., tokens before the subcommand).
1576
+ * Env fallbacks are applied later in the runner on the accumulated global args.
1577
+ */
1578
+ function parseGlobalArgs(tokens, globalExtracted) {
1579
+ if (tokens.length === 0) return {};
1580
+ return mergeWithPositionals(parseArgv(tokens, buildParserOptions(globalExtracted)), globalExtracted);
1581
+ }
1582
+ /**
1583
+ * Separate global flags from command-local args in argv.
1584
+ * Global flags mixed with command args (e.g., `build --verbose --output dist`)
1585
+ * are extracted and returned separately.
1586
+ * When a flag is defined in both global and local schemas, the local definition
1587
+ * takes precedence (the flag stays in the command tokens).
1588
+ *
1589
+ * Note: Combined short flags (e.g., `-vq`) are not decomposed here; only
1590
+ * single-character short options are recognized as global. The underlying
1591
+ * `parseArgv` handles combined shorts for command-local parsing.
1592
+ */
1593
+ function separateGlobalArgs(argv, globalExtracted, localExtracted) {
1594
+ const lookup = buildGlobalFlagLookup(globalExtracted);
1595
+ const localCliNames = new Set(localExtracted?.fields.map((f) => f.cliName) ?? []);
1596
+ const localAliases = new Set(localExtracted?.fields.filter((f) => f.alias).map((f) => f.alias) ?? []);
1597
+ const globalTokens = [];
1598
+ const commandTokens = [];
1599
+ for (let i = 0; i < argv.length; i++) {
1600
+ const arg = argv[i];
1601
+ if (arg === "--") {
1602
+ commandTokens.push(...argv.slice(i));
1603
+ break;
1604
+ }
1605
+ if (arg.startsWith("--")) {
1606
+ const { resolvedName, withoutDashes, isNegated, isGlobal } = resolveGlobalLongOption(arg, lookup);
1607
+ const flagName = isNegated ? withoutDashes.slice(3) : withoutDashes;
1608
+ const isLocalCollision = localCliNames.has(withoutDashes) || localCliNames.has(flagName);
1609
+ if (isGlobal && !isLocalCollision) {
1610
+ i += collectGlobalFlag(argv, i, resolvedName, isNegated, lookup.booleanFlags, globalTokens) - 1;
1611
+ continue;
1612
+ }
1613
+ } else if (arg.startsWith("-") && arg.length > 1) {
1614
+ const withoutDash = arg.includes("=") ? arg.slice(1, arg.indexOf("=")) : arg.slice(1);
1615
+ if (withoutDash.length === 1) {
1616
+ const resolvedName = lookup.aliasMap.get(withoutDash) ?? withoutDash;
1617
+ if ((lookup.aliases.has(withoutDash) || lookup.flagNames.has(resolvedName)) && !localAliases.has(withoutDash)) {
1618
+ i += collectGlobalFlag(argv, i, resolvedName, false, lookup.booleanFlags, globalTokens) - 1;
1619
+ continue;
1620
+ }
1621
+ }
1622
+ }
1623
+ commandTokens.push(arg);
1624
+ }
1625
+ return {
1626
+ separated: commandTokens,
1627
+ globalParsed: parseGlobalArgs(globalTokens, globalExtracted)
1347
1628
  };
1348
1629
  }
1349
1630
 
@@ -1526,11 +1807,11 @@ const defaultLogger = {
1526
1807
  * ```
1527
1808
  */
1528
1809
  async function runCommand(command, argv, options = {}) {
1810
+ const globalExtracted = extractAndValidateGlobal(options);
1529
1811
  return runCommandInternal(command, argv, {
1530
1812
  ...options,
1531
1813
  handleSignals: false,
1532
- skipValidation: options.skipValidation,
1533
- logger: options.logger
1814
+ _globalExtracted: globalExtracted
1534
1815
  });
1535
1816
  }
1536
1817
  /**
@@ -1557,16 +1838,20 @@ async function runCommand(command, argv, options = {}) {
1557
1838
  * ```
1558
1839
  */
1559
1840
  async function runMain(command, options = {}) {
1841
+ const globalExtracted = extractAndValidateGlobal(options);
1560
1842
  const result = await runCommandInternal(command, process.argv.slice(2), {
1561
1843
  debug: options.debug,
1562
1844
  captureLogs: options.captureLogs,
1563
1845
  skipValidation: options.skipValidation,
1564
1846
  handleSignals: true,
1565
1847
  logger: options.logger,
1848
+ globalArgs: options.globalArgs,
1849
+ _globalExtracted: globalExtracted,
1566
1850
  _context: {
1567
1851
  commandPath: [],
1568
1852
  rootName: command.name,
1569
- rootVersion: options.version
1853
+ rootVersion: options.version,
1854
+ globalExtracted
1570
1855
  }
1571
1856
  });
1572
1857
  if (process.stdout.writableLength > 0) await new Promise((resolve) => process.stdout.once("drain", resolve));
@@ -1579,7 +1864,8 @@ async function runCommandInternal(command, argv, options = {}) {
1579
1864
  const logger = options.logger ?? defaultLogger;
1580
1865
  const context = options._context ?? {
1581
1866
  commandPath: [],
1582
- rootName: command.name
1867
+ rootName: command.name,
1868
+ globalExtracted: options._globalExtracted
1583
1869
  };
1584
1870
  const collector = options.captureLogs ?? false ? createLogCollector() : null;
1585
1871
  collector?.start();
@@ -1587,12 +1873,19 @@ async function runCommandInternal(command, argv, options = {}) {
1587
1873
  return mergeLogs(options._existingLogs ?? emptyLogs(), collector?.getLogs() ?? emptyLogs());
1588
1874
  };
1589
1875
  try {
1590
- const parseResult = parseArgs(argv, command, { skipValidation: options.skipValidation });
1876
+ const parseResult = parseArgs(argv, command, {
1877
+ skipValidation: options.skipValidation,
1878
+ globalExtracted: options._globalExtracted
1879
+ });
1880
+ const accumulatedGlobalArgs = {
1881
+ ...options._parsedGlobalArgs,
1882
+ ...parseResult.rawGlobalArgs
1883
+ };
1591
1884
  if (parseResult.helpRequested || parseResult.helpAllRequested) {
1592
1885
  let hasUnknownSubcommand = false;
1593
1886
  const subCmdNames = listSubCommands(command);
1594
1887
  if (subCmdNames.length > 0) {
1595
- const potentialSubCmd = argv.find((arg) => !arg.startsWith("-"));
1888
+ const potentialSubCmd = findFirstPositional(argv, context.globalExtracted);
1596
1889
  if (potentialSubCmd && !subCmdNames.includes(potentialSubCmd)) {
1597
1890
  logger.error(formatUnknownSubcommand(potentialSubCmd, subCmdNames));
1598
1891
  logger.error("");
@@ -1636,13 +1929,15 @@ async function runCommandInternal(command, argv, options = {}) {
1636
1929
  const subContext = {
1637
1930
  commandPath: [...context.commandPath ?? [], parseResult.subCommand],
1638
1931
  rootName: context.rootName,
1639
- rootVersion: context.rootVersion
1932
+ rootVersion: context.rootVersion,
1933
+ globalExtracted: context.globalExtracted
1640
1934
  };
1641
1935
  collector?.stop();
1642
1936
  return runCommandInternal(subCmd, parseResult.remainingArgs, {
1643
1937
  ...options,
1644
1938
  _context: subContext,
1645
- _existingLogs: getCurrentLogs()
1939
+ _existingLogs: getCurrentLogs(),
1940
+ _parsedGlobalArgs: accumulatedGlobalArgs
1646
1941
  });
1647
1942
  }
1648
1943
  }
@@ -1674,9 +1969,35 @@ async function runCommandInternal(command, argv, options = {}) {
1674
1969
  };
1675
1970
  } else if (unknownKeysMode === "strip") for (const flag of parseResult.unknownFlags) logger.error(formatUnknownFlagWarning(flag, knownFlags));
1676
1971
  }
1972
+ let validatedGlobalArgs = {};
1973
+ if (options.globalArgs && options._globalExtracted) {
1974
+ for (const field of options._globalExtracted.fields) if (field.env && accumulatedGlobalArgs[field.name] === void 0) {
1975
+ const envNames = Array.isArray(field.env) ? field.env : [field.env];
1976
+ for (const envName of envNames) {
1977
+ const envValue = process.env[envName];
1978
+ if (envValue !== void 0) {
1979
+ accumulatedGlobalArgs[field.name] = envValue;
1980
+ break;
1981
+ }
1982
+ }
1983
+ }
1984
+ const globalValidation = validateArgs(accumulatedGlobalArgs, options.globalArgs);
1985
+ if (!globalValidation.success) {
1986
+ const errorMessage = formatValidationErrors$1(globalValidation.errors);
1987
+ logger.error(errorMessage);
1988
+ collector?.stop();
1989
+ return {
1990
+ success: false,
1991
+ error: new Error(errorMessage),
1992
+ exitCode: 1,
1993
+ logs: getCurrentLogs()
1994
+ };
1995
+ }
1996
+ validatedGlobalArgs = globalValidation.data;
1997
+ }
1677
1998
  if (!command.args) {
1678
1999
  collector?.stop();
1679
- return await executeLifecycle(command, {}, {
2000
+ return await executeLifecycle(command, validatedGlobalArgs, {
1680
2001
  handleSignals: options.handleSignals,
1681
2002
  captureLogs: options.captureLogs,
1682
2003
  existingLogs: getCurrentLogs()
@@ -1693,8 +2014,12 @@ async function runCommandInternal(command, argv, options = {}) {
1693
2014
  logs: getCurrentLogs()
1694
2015
  };
1695
2016
  }
2017
+ const mergedArgs = {
2018
+ ...validatedGlobalArgs,
2019
+ ...validationResult.data
2020
+ };
1696
2021
  collector?.stop();
1697
- return await executeLifecycle(command, validationResult.data, {
2022
+ return await executeLifecycle(command, mergedArgs, {
1698
2023
  handleSignals: options.handleSignals,
1699
2024
  captureLogs: options.captureLogs,
1700
2025
  existingLogs: getCurrentLogs()
@@ -1711,7 +2036,24 @@ async function runCommandInternal(command, argv, options = {}) {
1711
2036
  };
1712
2037
  }
1713
2038
  }
2039
+ /**
2040
+ * Extract global fields from options.globalArgs and validate the schema upfront.
2041
+ * Rejects positional fields since global options must be flags.
2042
+ * Returns undefined when no globalArgs is provided.
2043
+ */
2044
+ function extractAndValidateGlobal(options) {
2045
+ if (!options.globalArgs) return void 0;
2046
+ const extracted = extractFields(options.globalArgs);
2047
+ if (!options.skipValidation) {
2048
+ validateDuplicateFields(extracted);
2049
+ validateDuplicateAliases(extracted);
2050
+ validateReservedAliases(extracted, true);
2051
+ const positionalNames = extracted.fields.filter((f) => f.positional).map((f) => f.name);
2052
+ if (positionalNames.length > 0) throw new Error(`Global options schema must not contain positional arguments. Found: ${positionalNames.join(", ")}`);
2053
+ }
2054
+ return extracted;
2055
+ }
1714
2056
 
1715
2057
  //#endregion
1716
2058
  export { symbols as C, styles as S, renderInline as _, parseArgv as a, logger as b, validateDuplicateAliases as c, validateReservedAliases as d, DuplicateAliasError as f, generateHelp as g, ReservedAliasError as h, formatValidationErrors as i, validateDuplicateFields as l, PositionalConfigError as m, runMain as n, formatCommandValidationErrors as o, DuplicateFieldError as p, runner_exports as r, validateCommand as s, runCommand as t, validatePositionalConfig as u, renderMarkdown as v, setColorEnabled as x, isColorEnabled as y };
1717
- //# sourceMappingURL=runner-D6k4BgB4.js.map
2059
+ //# sourceMappingURL=runner-DjG0uBxQ.js.map