politty 0.4.4 → 0.4.7

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-DR47Ccac.cjs → completion-Df0eZ70u.cjs} +230 -18
  9. package/dist/completion-Df0eZ70u.cjs.map +1 -0
  10. package/dist/{zsh-XbRzR8FW.js → completion-_AnQsWh9.js} +202 -8
  11. package/dist/completion-_AnQsWh9.js.map +1 -0
  12. package/dist/docs/index.cjs +274 -28
  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 +269 -29
  19. package/dist/docs/index.js.map +1 -1
  20. package/dist/{value-completion-resolver-C9LTGr0O.d.ts → index-BA0GkZQx.d.cts} +83 -4
  21. package/dist/index-BA0GkZQx.d.cts.map +1 -0
  22. package/dist/{value-completion-resolver-BQgHsX7b.d.cts → index-rMDe9hp1.d.ts} +83 -4
  23. package/dist/index-rMDe9hp1.d.ts.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-Cn6Oq4ZZ.cjs} +453 -26
  35. package/dist/runner-Cn6Oq4ZZ.cjs.map +1 -0
  36. package/dist/{runner-D6k4BgB4.js → runner-D4ByDT5I.js} +453 -26
  37. package/dist/runner-D4ByDT5I.js.map +1 -0
  38. package/dist/{schema-extractor-DFaAZzaY.d.cts → schema-extractor-BoWkcP9a.d.cts} +48 -2
  39. package/dist/schema-extractor-BoWkcP9a.d.cts.map +1 -0
  40. package/dist/{schema-extractor-n9288WJ6.d.ts → schema-extractor-DoDO4M_i.d.ts} +49 -3
  41. package/dist/schema-extractor-DoDO4M_i.d.ts.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-DR47Ccac.cjs.map +0 -1
  60. package/dist/zsh-XbRzR8FW.js.map +0 -1
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
- const require_subcommand_router = require('../subcommand-router-ZjNjFaUL.cjs');
3
- const require_lazy = require('../lazy-BEDnSR0m.cjs');
2
+ const require_subcommand_router = require('../subcommand-router-sZHhUP7b.cjs');
3
+ const require_lazy = require('../lazy-DHlvJiQQ.cjs');
4
4
  let zod = require("zod");
5
5
  let node_util = require("node:util");
6
6
  let node_fs = require("node:fs");
@@ -61,6 +61,26 @@ function globalOptionsEndMarker() {
61
61
  return `<!-- ${GLOBAL_OPTIONS_MARKER_PREFIX}:end -->`;
62
62
  }
63
63
  /**
64
+ * Marker prefix for root header sections in generated documentation
65
+ */
66
+ const ROOT_HEADER_MARKER_PREFIX = "politty:root-header";
67
+ function rootHeaderStartMarker() {
68
+ return `<!-- ${ROOT_HEADER_MARKER_PREFIX}:start -->`;
69
+ }
70
+ function rootHeaderEndMarker() {
71
+ return `<!-- ${ROOT_HEADER_MARKER_PREFIX}:end -->`;
72
+ }
73
+ /**
74
+ * Marker prefix for root footer sections in generated documentation
75
+ */
76
+ const ROOT_FOOTER_MARKER_PREFIX = "politty:root-footer";
77
+ function rootFooterStartMarker() {
78
+ return `<!-- ${ROOT_FOOTER_MARKER_PREFIX}:start -->`;
79
+ }
80
+ function rootFooterEndMarker() {
81
+ return `<!-- ${ROOT_FOOTER_MARKER_PREFIX}:end -->`;
82
+ }
83
+ /**
64
84
  * Marker prefix for index sections in generated documentation
65
85
  * Format: <!-- politty:index:<scope>:start --> ... <!-- politty:index:<scope>:end -->
66
86
  */
@@ -301,6 +321,68 @@ function renderOptionsTableFromArray(options) {
301
321
  return lines.join("\n");
302
322
  }
303
323
  /**
324
+ * Render union/xor options as markdown with variant grouping
325
+ */
326
+ function renderUnionOptionsMarkdown(extracted, style = "table") {
327
+ const unionOptions = extracted.unionOptions ?? [];
328
+ if (unionOptions.length === 0) return "";
329
+ const sections = [];
330
+ const allFieldNames = /* @__PURE__ */ new Set();
331
+ for (const option of unionOptions) for (const field of option.fields) allFieldNames.add(field.name);
332
+ const commonFieldNames = /* @__PURE__ */ new Set();
333
+ for (const fieldName of allFieldNames) if (unionOptions.every((o) => o.fields.some((f) => f.name === fieldName))) commonFieldNames.add(fieldName);
334
+ const commonFields = extracted.fields.filter((f) => commonFieldNames.has(f.name) && !f.positional);
335
+ if (commonFields.length > 0) sections.push(style === "table" ? renderOptionsTableFromArray(commonFields) : renderOptionsListFromArray(commonFields));
336
+ sections.push("> One of the following option groups is required:");
337
+ for (let i = 0; i < unionOptions.length; i++) {
338
+ const option = unionOptions[i];
339
+ if (!option) continue;
340
+ const uniqueFields = option.fields.filter((f) => !commonFieldNames.has(f.name) && !f.positional);
341
+ if (uniqueFields.length === 0) continue;
342
+ const label = option.description ?? `Variant ${i + 1}`;
343
+ const rendered = style === "table" ? renderOptionsTableFromArray(uniqueFields) : renderOptionsListFromArray(uniqueFields);
344
+ sections.push(`**${label}:**\n\n${rendered}`);
345
+ }
346
+ return sections.join("\n\n");
347
+ }
348
+ /**
349
+ * Render discriminatedUnion options as markdown with variant grouping
350
+ */
351
+ function renderDiscriminatedUnionOptionsMarkdown(extracted, style = "table") {
352
+ const discriminator = extracted.discriminator;
353
+ const variants = extracted.variants ?? [];
354
+ if (!discriminator || variants.length === 0) return "";
355
+ const sections = [];
356
+ const allFieldNames = /* @__PURE__ */ new Set();
357
+ for (const variant of variants) for (const field of variant.fields) allFieldNames.add(field.name);
358
+ const commonFieldNames = /* @__PURE__ */ new Set();
359
+ for (const fieldName of allFieldNames) {
360
+ if (fieldName === discriminator) continue;
361
+ if (variants.every((v) => v.fields.some((f) => f.name === fieldName))) commonFieldNames.add(fieldName);
362
+ }
363
+ const discriminatorField = extracted.fields.find((f) => f.name === discriminator);
364
+ const variantValues = variants.map((v) => v.discriminatorValue).join("\\|");
365
+ const topFields = [];
366
+ if (discriminatorField) topFields.push({
367
+ ...discriminatorField,
368
+ placeholder: variantValues
369
+ });
370
+ for (const fieldName of commonFieldNames) {
371
+ const field = extracted.fields.find((f) => f.name === fieldName);
372
+ if (field && !field.positional) topFields.push(field);
373
+ }
374
+ if (topFields.length > 0) sections.push(style === "table" ? renderOptionsTableFromArray(topFields) : renderOptionsListFromArray(topFields));
375
+ for (const variant of variants) {
376
+ const uniqueFields = variant.fields.filter((f) => f.name !== discriminator && !commonFieldNames.has(f.name) && !f.positional);
377
+ if (uniqueFields.length === 0) continue;
378
+ const descSuffix = variant.description ? ` ${variant.description}` : "";
379
+ const label = `**When \`${discriminator}\` = \`${variant.discriminatorValue}\`:**${descSuffix}`;
380
+ const rendered = style === "table" ? renderOptionsTableFromArray(uniqueFields) : renderOptionsListFromArray(uniqueFields);
381
+ sections.push(`${label}\n\n${rendered}`);
382
+ }
383
+ return sections.join("\n\n");
384
+ }
385
+ /**
304
386
  * Render options from array as list
305
387
  */
306
388
  function renderOptionsListFromArray(options) {
@@ -416,8 +498,13 @@ function wrapWithMarker(type, scope, content) {
416
498
  return `${sectionStartMarker(type, scope)}\n${content}\n${sectionEndMarker(type, scope)}`;
417
499
  }
418
500
  /**
419
- * Create command renderer with options
501
+ * Generate a "See Global Options" link for subcommand documentation.
502
+ * Returns null for root command or when no global options exist.
420
503
  */
504
+ function getGlobalOptionsLink(info) {
505
+ if (!info.hasGlobalOptions || info.commandPath === "") return null;
506
+ return `See [Global Options](${info.rootDocPath && info.filePath && info.filePath !== info.rootDocPath ? `${getRelativePath(info.filePath, info.rootDocPath)}#global-options` : "#global-options"}) for options available to all commands.`;
507
+ }
421
508
  function createCommandRenderer(options = {}) {
422
509
  const { headingLevel = 1, optionStyle = "table", generateAnchors = true, includeSubcommandDetails = true, renderDescription: customRenderDescription, renderUsage: customRenderUsage, renderArguments: customRenderArguments, renderOptions: customRenderOptions, renderSubcommands: customRenderSubcommands, renderNotes: customRenderNotes, renderFooter: customRenderFooter, renderExamples: customRenderExamples } = options;
423
510
  return (info) => {
@@ -465,7 +552,11 @@ function createCommandRenderer(options = {}) {
465
552
  const renderOpts = (opts, renderOpts) => {
466
553
  const style = renderOpts?.style ?? optionStyle;
467
554
  const withHeading = renderOpts?.withHeading ?? true;
468
- const content = style === "table" ? renderOptionsTableFromArray(opts) : renderOptionsListFromArray(opts);
555
+ const extracted = info.extracted;
556
+ let content;
557
+ if (extracted && (extracted.schemaType === "union" || extracted.schemaType === "xor") && extracted.unionOptions) content = renderUnionOptionsMarkdown(extracted, style);
558
+ else if (extracted && extracted.schemaType === "discriminatedUnion" && extracted.discriminator) content = renderDiscriminatedUnionOptionsMarkdown(extracted, style);
559
+ else content = style === "table" ? renderOptionsTableFromArray(opts) : renderOptionsListFromArray(opts);
469
560
  return withHeading ? `**Options**\n\n${content}` : content;
470
561
  };
471
562
  const context = {
@@ -477,6 +568,10 @@ function createCommandRenderer(options = {}) {
477
568
  const content = customRenderOptions ? customRenderOptions(context) : renderOpts(context.options);
478
569
  if (content) sections.push(wrapWithMarker("options", scope, content));
479
570
  }
571
+ {
572
+ const globalLink = getGlobalOptionsLink(info);
573
+ if (globalLink) sections.push(globalLink);
574
+ }
480
575
  if (info.subCommands.length > 0) {
481
576
  const effectiveAnchors = generateAnchors && includeSubcommandDetails;
482
577
  const renderSubs = (subs, opts) => {
@@ -723,7 +818,7 @@ async function executeSingleExample(example, rootCommand, commandPath) {
723
818
  collector.start();
724
819
  let success = true;
725
820
  try {
726
- const { runCommand } = await Promise.resolve().then(() => require("../runner-C4fSHJMe.cjs")).then((n) => n.runner_exports);
821
+ const { runCommand } = await Promise.resolve().then(() => require("../runner-Cn6Oq4ZZ.cjs")).then((n) => n.runner_exports);
727
822
  const result = await runCommand(rootCommand, argv);
728
823
  success = result.success;
729
824
  if (!result.success && result.error) console.error(result.error.message);
@@ -1127,7 +1222,7 @@ function resolveConfiguredCommandPaths(fileConfigRaw, allCommands, ignores) {
1127
1222
  return {
1128
1223
  fileConfig,
1129
1224
  specifiedCommands,
1130
- commandPaths: filterIgnoredCommands(expandCommandPaths(specifiedCommands, allCommands), ignores),
1225
+ commandPaths: filterIgnoredCommands(fileConfig.noExpand ? specifiedCommands.filter((p) => allCommands.has(p)) : expandCommandPaths(specifiedCommands, allCommands), ignores),
1131
1226
  topLevelCommands: filterIgnoredCommands(resolveTopLevelCommands(specifiedCommands, allCommands), ignores)
1132
1227
  };
1133
1228
  }
@@ -1447,6 +1542,7 @@ function generateGlobalOptionsSection(config) {
1447
1542
  const endMarker = globalOptionsEndMarker();
1448
1543
  return [
1449
1544
  startMarker,
1545
+ "<a id=\"global-options\"></a>",
1450
1546
  renderArgsTable(config.args, config.options),
1451
1547
  endMarker
1452
1548
  ].join("\n");
@@ -1473,7 +1569,7 @@ function normalizeDocPathForComparison(filePath) {
1473
1569
  * Process global options marker in file content
1474
1570
  * Returns result with updated content and any diffs
1475
1571
  */
1476
- async function processGlobalOptionsMarker(existingContent, globalOptionsConfig, updateMode, formatter) {
1572
+ async function processGlobalOptionsMarker(existingContent, globalOptionsConfig, updateMode, formatter, autoInsertIfMissing) {
1477
1573
  let content = existingContent;
1478
1574
  const diffs = [];
1479
1575
  let hasError = false;
@@ -1483,6 +1579,16 @@ async function processGlobalOptionsMarker(existingContent, globalOptionsConfig,
1483
1579
  const generatedSection = await applyFormatter(generateGlobalOptionsSection(globalOptionsConfig), formatter);
1484
1580
  const existingSection = extractMarkerSection(content, startMarker, endMarker);
1485
1581
  if (!existingSection) {
1582
+ if (updateMode && autoInsertIfMissing) {
1583
+ content = content.trimEnd() + "\n\n" + generatedSection + "\n";
1584
+ wasUpdated = true;
1585
+ return {
1586
+ content,
1587
+ diffs,
1588
+ hasError,
1589
+ wasUpdated
1590
+ };
1591
+ }
1486
1592
  hasError = true;
1487
1593
  diffs.push(`Global options marker not found in file. Expected markers:\n${startMarker}\n...\n${endMarker}`);
1488
1594
  return {
@@ -1513,6 +1619,61 @@ async function processGlobalOptionsMarker(existingContent, globalOptionsConfig,
1513
1619
  };
1514
1620
  }
1515
1621
  /**
1622
+ * Process a static content marker (root-header or root-footer).
1623
+ * Inserts/updates the marker section with the given content.
1624
+ */
1625
+ async function processStaticMarker(existingContent, markerLabel, startMarker, endMarker, rawContent, updateMode, formatter, autoInsertIfMissing) {
1626
+ let content = existingContent;
1627
+ const diffs = [];
1628
+ let hasError = false;
1629
+ let wasUpdated = false;
1630
+ const generatedSection = [
1631
+ startMarker,
1632
+ await applyFormatter(rawContent, formatter),
1633
+ endMarker
1634
+ ].join("\n");
1635
+ const existingSection = extractMarkerSection(content, startMarker, endMarker);
1636
+ if (!existingSection) {
1637
+ if (updateMode && autoInsertIfMissing) {
1638
+ content = content.trimEnd() + "\n\n" + generatedSection + "\n";
1639
+ wasUpdated = true;
1640
+ return {
1641
+ content,
1642
+ diffs,
1643
+ hasError,
1644
+ wasUpdated
1645
+ };
1646
+ }
1647
+ hasError = true;
1648
+ diffs.push(`${markerLabel} marker not found in file. Expected markers:\n${startMarker}\n...\n${endMarker}`);
1649
+ return {
1650
+ content,
1651
+ diffs,
1652
+ hasError,
1653
+ wasUpdated
1654
+ };
1655
+ }
1656
+ if (existingSection !== generatedSection) if (updateMode) {
1657
+ const updated = replaceMarkerSection(content, startMarker, endMarker, generatedSection);
1658
+ if (updated) {
1659
+ content = updated;
1660
+ wasUpdated = true;
1661
+ } else {
1662
+ hasError = true;
1663
+ diffs.push(`Failed to replace ${markerLabel} section`);
1664
+ }
1665
+ } else {
1666
+ hasError = true;
1667
+ diffs.push(formatDiff(existingSection, generatedSection));
1668
+ }
1669
+ return {
1670
+ content,
1671
+ diffs,
1672
+ hasError,
1673
+ wasUpdated
1674
+ };
1675
+ }
1676
+ /**
1516
1677
  * Process index marker in file content
1517
1678
  * Returns result with updated content and any diffs.
1518
1679
  * If the marker is not present in the file, the section is silently skipped.
@@ -1603,26 +1764,29 @@ function findTargetCommandsInFile(targetCommands, filePath, files, allCommands,
1603
1764
  /**
1604
1765
  * Generate a single command section (already contains section markers from renderer)
1605
1766
  */
1606
- function generateCommandSection(cmdPath, allCommands, render, filePath, fileMap) {
1767
+ function generateCommandSection(cmdPath, allCommands, render, filePath, fileMap, rootDocPath, hasGlobalOptions) {
1607
1768
  const info = allCommands.get(cmdPath);
1608
1769
  if (!info) return null;
1609
- return render({
1770
+ const enriched = {
1610
1771
  ...info,
1611
1772
  filePath,
1612
- fileMap
1613
- });
1773
+ fileMap,
1774
+ rootDocPath
1775
+ };
1776
+ if (hasGlobalOptions !== void 0) enriched.hasGlobalOptions = hasGlobalOptions;
1777
+ return render(enriched);
1614
1778
  }
1615
1779
  /**
1616
1780
  * Generate markdown for a file containing multiple commands
1617
1781
  * Each command section is wrapped with markers for partial validation
1618
1782
  */
1619
- function generateFileMarkdown(commandPaths, allCommands, render, filePath, fileMap, specifiedOrder, fileConfig) {
1783
+ function generateFileMarkdown(commandPaths, allCommands, render, filePath, fileMap, specifiedOrder, fileConfig, rootDocPath, hasGlobalOptions) {
1620
1784
  const sections = [];
1621
1785
  const header = fileConfig ? generateFileHeader(fileConfig) : null;
1622
1786
  if (header) sections.push(header);
1623
1787
  const sortedPaths = sortDepthFirst(commandPaths, specifiedOrder ?? []);
1624
1788
  for (const cmdPath of sortedPaths) {
1625
- const section = generateCommandSection(cmdPath, allCommands, render, filePath, fileMap);
1789
+ const section = generateCommandSection(cmdPath, allCommands, render, filePath, fileMap, rootDocPath, hasGlobalOptions);
1626
1790
  if (section) sections.push(section);
1627
1791
  }
1628
1792
  return `${sections.join("\n")}\n`;
@@ -1651,16 +1815,72 @@ async function executeConfiguredExamples(allCommands, examplesConfig, rootComman
1651
1815
  }
1652
1816
  }
1653
1817
  /**
1818
+ * Convert PathConfig to FileMapping with explicit command paths.
1819
+ * Uses noExpand to prevent subcommand expansion since paths are pre-resolved.
1820
+ */
1821
+ function pathToFiles(pathConfig, allCommands) {
1822
+ if (typeof pathConfig === "string") return {
1823
+ files: { [pathConfig]: Array.from(allCommands.keys()) },
1824
+ rootDocPath: pathConfig
1825
+ };
1826
+ const { root, commands = {} } = pathConfig;
1827
+ const files = {};
1828
+ const assignedToOtherFiles = /* @__PURE__ */ new Set();
1829
+ const sortedEntries = Object.entries(commands).sort(([a], [b]) => b.split(" ").length - a.split(" ").length);
1830
+ for (const [cmdPath, filePath] of sortedEntries) {
1831
+ if (!files[filePath]) files[filePath] = {
1832
+ commands: [],
1833
+ noExpand: true
1834
+ };
1835
+ const fc = files[filePath];
1836
+ for (const existingPath of allCommands.keys()) if ((existingPath === cmdPath || existingPath.startsWith(cmdPath + " ")) && !assignedToOtherFiles.has(existingPath)) {
1837
+ fc.commands.push(existingPath);
1838
+ assignedToOtherFiles.add(existingPath);
1839
+ }
1840
+ }
1841
+ files[root] = {
1842
+ commands: Array.from(allCommands.keys()).filter((p) => !assignedToOtherFiles.has(p)),
1843
+ noExpand: true
1844
+ };
1845
+ return {
1846
+ files,
1847
+ rootDocPath: root
1848
+ };
1849
+ }
1850
+ /**
1654
1851
  * Generate documentation from command definition
1655
1852
  */
1656
1853
  async function generateDoc(config) {
1657
- const { command, rootDoc, files, ignores = [], format = {}, formatter, examples: examplesConfig, targetCommands } = config;
1854
+ const { command, ignores = [], format = {}, formatter, examples: examplesConfig, targetCommands, globalArgs } = config;
1855
+ const allCommands = await collectAllCommands(command);
1856
+ let files;
1857
+ let usingPathConfig = false;
1858
+ let resolvedRootDocPath;
1859
+ if (config.path !== void 0) {
1860
+ if (config.files !== void 0) throw new Error("Cannot specify both \"path\" and \"files\". Use one or the other.");
1861
+ const converted = pathToFiles(config.path, allCommands);
1862
+ files = converted.files;
1863
+ resolvedRootDocPath = converted.rootDocPath;
1864
+ usingPathConfig = true;
1865
+ } else if (config.files !== void 0) files = config.files;
1866
+ else throw new Error("Either \"path\" or \"files\" must be specified.");
1867
+ let rootDoc = config.rootDoc;
1868
+ if (!rootDoc && usingPathConfig && (globalArgs || config.rootInfo)) rootDoc = { path: resolvedRootDocPath };
1869
+ if (globalArgs && rootDoc && !rootDoc.globalOptions) {
1870
+ const optionFields = require_lazy.extractFields(globalArgs).fields.filter((f) => !f.positional);
1871
+ if (optionFields.length > 0) {
1872
+ const globalShape = Object.fromEntries(optionFields.map((f) => [f.name, f.schema]));
1873
+ rootDoc = {
1874
+ ...rootDoc,
1875
+ globalOptions: globalShape
1876
+ };
1877
+ }
1878
+ }
1658
1879
  const updateMode = isUpdateMode();
1659
- if (rootDoc) {
1880
+ if (rootDoc && !usingPathConfig) {
1660
1881
  const normalizedRootDocPath = normalizeDocPathForComparison(rootDoc.path);
1661
1882
  if (Object.keys(files).some((filePath) => normalizeDocPathForComparison(filePath) === normalizedRootDocPath)) throw new Error(`rootDoc.path "${rootDoc.path}" must not also appear as a key in files.`);
1662
1883
  }
1663
- const allCommands = await collectAllCommands(command);
1664
1884
  if (examplesConfig) await executeConfiguredExamples(allCommands, examplesConfig, command);
1665
1885
  const hasTargetCommands = targetCommands !== void 0 && targetCommands.length > 0;
1666
1886
  if (hasTargetCommands) {
@@ -1694,11 +1914,13 @@ async function generateDoc(config) {
1694
1914
  headingLevel: adjustedHeadingLevel
1695
1915
  });
1696
1916
  const render = fileConfig.render ?? fileRenderer;
1697
- if (hasTargetCommands) {
1917
+ const isRootDocFile = usingPathConfig && rootDoc && normalizeDocPathForComparison(filePath) === normalizeDocPathForComparison(rootDoc.path);
1918
+ if (hasTargetCommands || isRootDocFile) {
1698
1919
  let existingContent = readFile(filePath);
1699
1920
  const sortedCommandPaths = sortDepthFirst(commandPaths, specifiedCommands);
1700
- for (const targetCommand of fileTargetCommands) {
1701
- const rawSection = generateCommandSection(targetCommand, allCommands, render, filePath, fileMap);
1921
+ const effectiveTargetCommands = hasTargetCommands ? fileTargetCommands : commandPaths;
1922
+ for (const targetCommand of effectiveTargetCommands) {
1923
+ const rawSection = generateCommandSection(targetCommand, allCommands, render, filePath, fileMap, rootDoc?.path, globalOptionDefinitions.size > 0);
1702
1924
  if (!rawSection) throw new Error(`Target command "${targetCommand}" not found in commands`);
1703
1925
  const generatedSection = await applyFormatter(rawSection, formatter);
1704
1926
  if (!existingContent) {
@@ -1747,7 +1969,7 @@ async function generateDoc(config) {
1747
1969
  }
1748
1970
  }
1749
1971
  } else {
1750
- const generatedMarkdown = await applyFormatter(generateFileMarkdown(commandPaths, allCommands, render, filePath, fileMap, specifiedCommands, fileConfig), formatter);
1972
+ const generatedMarkdown = await applyFormatter(generateFileMarkdown(commandPaths, allCommands, render, filePath, fileMap, specifiedCommands, fileConfig, rootDoc?.path, globalOptionDefinitions.size > 0), formatter);
1751
1973
  const comparison = compareWithExisting(generatedMarkdown, filePath);
1752
1974
  if (comparison.match) {} else if (updateMode) {
1753
1975
  writeFile(filePath, generatedMarkdown);
@@ -1777,22 +1999,33 @@ async function generateDoc(config) {
1777
1999
  } else {
1778
2000
  let content = existingContent;
1779
2001
  let markerUpdated = false;
1780
- const rootDocFileConfig = { title: command.name };
2002
+ const rootInfo = config.rootInfo;
2003
+ const rootDocFileConfig = { title: rootInfo?.title ?? command.name };
1781
2004
  if (rootDoc.headingLevel !== void 0) rootDocFileConfig.headingLevel = rootDoc.headingLevel;
1782
- if (command.description !== void 0) rootDocFileConfig.description = command.description;
2005
+ const rootDescription = rootInfo?.description ?? command.description;
2006
+ if (rootDescription !== void 0) rootDocFileConfig.description = rootDescription;
1783
2007
  const headerResult = processFileHeader(content, rootDocFileConfig, updateMode);
1784
2008
  content = headerResult.content;
1785
2009
  if (headerResult.diff) rootDocDiffs.push(headerResult.diff);
1786
2010
  if (headerResult.hasError) hasError = true;
1787
2011
  if (headerResult.wasUpdated) markerUpdated = true;
1788
- const unexpectedSectionPaths = Array.from(new Set(collectSectionMarkerPaths(content)));
1789
- if (unexpectedSectionPaths.length > 0) {
1790
- hasError = true;
1791
- rootDocDiffs.push(`Found unexpected section markers in rootDoc: ${unexpectedSectionPaths.map((commandPath) => `"${formatCommandPath(commandPath)}"`).join(", ")}.`);
2012
+ if (rootInfo?.header) {
2013
+ const headerMarkerResult = await processStaticMarker(content, "Root header", rootHeaderStartMarker(), rootHeaderEndMarker(), rootInfo.header, updateMode, formatter, usingPathConfig);
2014
+ content = headerMarkerResult.content;
2015
+ rootDocDiffs.push(...headerMarkerResult.diffs);
2016
+ if (headerMarkerResult.hasError) hasError = true;
2017
+ if (headerMarkerResult.wasUpdated) markerUpdated = true;
2018
+ }
2019
+ if (!usingPathConfig) {
2020
+ const unexpectedSectionPaths = Array.from(new Set(collectSectionMarkerPaths(content)));
2021
+ if (unexpectedSectionPaths.length > 0) {
2022
+ hasError = true;
2023
+ rootDocDiffs.push(`Found unexpected section markers in rootDoc: ${unexpectedSectionPaths.map((commandPath) => `"${formatCommandPath(commandPath)}"`).join(", ")}.`);
2024
+ }
1792
2025
  }
1793
2026
  const normalizedGlobalOptions = normalizeGlobalOptions(rootDoc.globalOptions);
1794
2027
  if (normalizedGlobalOptions) {
1795
- const globalOptionsResult = await processGlobalOptionsMarker(content, normalizedGlobalOptions, updateMode, formatter);
2028
+ const globalOptionsResult = await processGlobalOptionsMarker(content, normalizedGlobalOptions, updateMode, formatter, usingPathConfig);
1796
2029
  content = globalOptionsResult.content;
1797
2030
  rootDocDiffs.push(...globalOptionsResult.diffs);
1798
2031
  if (globalOptionsResult.hasError) hasError = true;
@@ -1805,6 +2038,13 @@ async function generateDoc(config) {
1805
2038
  rootDocDiffs.push(...indexResult.diffs);
1806
2039
  if (indexResult.hasError) hasError = true;
1807
2040
  if (indexResult.wasUpdated) markerUpdated = true;
2041
+ if (rootInfo?.footer) {
2042
+ const footerMarkerResult = await processStaticMarker(content, "Root footer", rootFooterStartMarker(), rootFooterEndMarker(), rootInfo.footer, updateMode, formatter, usingPathConfig);
2043
+ content = footerMarkerResult.content;
2044
+ rootDocDiffs.push(...footerMarkerResult.diffs);
2045
+ if (footerMarkerResult.hasError) hasError = true;
2046
+ if (footerMarkerResult.wasUpdated) markerUpdated = true;
2047
+ }
1808
2048
  if (updateMode && markerUpdated) {
1809
2049
  writeFile(rootDocFilePath, content);
1810
2050
  if (rootDocStatus === "match") rootDocStatus = "updated";
@@ -1848,12 +2088,14 @@ async function assertDocMatch(config) {
1848
2088
  function initDocFile(config, fileSystem) {
1849
2089
  if (!isUpdateMode()) return;
1850
2090
  if (typeof config === "string") deleteFile(config, fileSystem);
1851
- else for (const filePath of Object.keys(config.files)) deleteFile(filePath, fileSystem);
2091
+ else if (config.files) for (const filePath of Object.keys(config.files)) deleteFile(filePath, fileSystem);
1852
2092
  }
1853
2093
 
1854
2094
  //#endregion
1855
2095
  exports.GLOBAL_OPTIONS_MARKER_PREFIX = GLOBAL_OPTIONS_MARKER_PREFIX;
1856
2096
  exports.INDEX_MARKER_PREFIX = INDEX_MARKER_PREFIX;
2097
+ exports.ROOT_FOOTER_MARKER_PREFIX = ROOT_FOOTER_MARKER_PREFIX;
2098
+ exports.ROOT_HEADER_MARKER_PREFIX = ROOT_HEADER_MARKER_PREFIX;
1857
2099
  exports.SECTION_MARKER_PREFIX = SECTION_MARKER_PREFIX;
1858
2100
  exports.SECTION_TYPES = SECTION_TYPES;
1859
2101
  exports.UPDATE_GOLDEN_ENV = UPDATE_GOLDEN_ENV;
@@ -1886,6 +2128,10 @@ exports.renderSubcommandsTable = renderSubcommandsTable;
1886
2128
  exports.renderSubcommandsTableFromArray = renderSubcommandsTableFromArray;
1887
2129
  exports.renderUsage = renderUsage;
1888
2130
  exports.resolveLazyCommand = require_subcommand_router.resolveLazyCommand;
2131
+ exports.rootFooterEndMarker = rootFooterEndMarker;
2132
+ exports.rootFooterStartMarker = rootFooterStartMarker;
2133
+ exports.rootHeaderEndMarker = rootHeaderEndMarker;
2134
+ exports.rootHeaderStartMarker = rootHeaderStartMarker;
1889
2135
  exports.sectionEndMarker = sectionEndMarker;
1890
2136
  exports.sectionStartMarker = sectionStartMarker;
1891
2137
  exports.writeFile = writeFile;