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,5 +1,5 @@
1
- import { a as getExtractedFields, i as extractFields } from "../lazy-BrEg8SgI.js";
2
- import { i as createLogCollector, n as resolveLazyCommand } from "../subcommand-router-CAzBsLSI.js";
1
+ import { a as getExtractedFields, i as extractFields } from "../lazy-DSyfzR-F.js";
2
+ import { i as createLogCollector, n as resolveLazyCommand } from "../subcommand-router-CKuy6D2b.js";
3
3
  import { z } from "zod";
4
4
  import { isDeepStrictEqual } from "node:util";
5
5
  import * as fs from "node:fs";
@@ -58,6 +58,26 @@ function globalOptionsEndMarker() {
58
58
  return `<!-- ${GLOBAL_OPTIONS_MARKER_PREFIX}:end -->`;
59
59
  }
60
60
  /**
61
+ * Marker prefix for root header sections in generated documentation
62
+ */
63
+ const ROOT_HEADER_MARKER_PREFIX = "politty:root-header";
64
+ function rootHeaderStartMarker() {
65
+ return `<!-- ${ROOT_HEADER_MARKER_PREFIX}:start -->`;
66
+ }
67
+ function rootHeaderEndMarker() {
68
+ return `<!-- ${ROOT_HEADER_MARKER_PREFIX}:end -->`;
69
+ }
70
+ /**
71
+ * Marker prefix for root footer sections in generated documentation
72
+ */
73
+ const ROOT_FOOTER_MARKER_PREFIX = "politty:root-footer";
74
+ function rootFooterStartMarker() {
75
+ return `<!-- ${ROOT_FOOTER_MARKER_PREFIX}:start -->`;
76
+ }
77
+ function rootFooterEndMarker() {
78
+ return `<!-- ${ROOT_FOOTER_MARKER_PREFIX}:end -->`;
79
+ }
80
+ /**
61
81
  * Marker prefix for index sections in generated documentation
62
82
  * Format: <!-- politty:index:<scope>:start --> ... <!-- politty:index:<scope>:end -->
63
83
  */
@@ -298,6 +318,68 @@ function renderOptionsTableFromArray(options) {
298
318
  return lines.join("\n");
299
319
  }
300
320
  /**
321
+ * Render union/xor options as markdown with variant grouping
322
+ */
323
+ function renderUnionOptionsMarkdown(extracted, style = "table") {
324
+ const unionOptions = extracted.unionOptions ?? [];
325
+ if (unionOptions.length === 0) return "";
326
+ const sections = [];
327
+ const allFieldNames = /* @__PURE__ */ new Set();
328
+ for (const option of unionOptions) for (const field of option.fields) allFieldNames.add(field.name);
329
+ const commonFieldNames = /* @__PURE__ */ new Set();
330
+ for (const fieldName of allFieldNames) if (unionOptions.every((o) => o.fields.some((f) => f.name === fieldName))) commonFieldNames.add(fieldName);
331
+ const commonFields = extracted.fields.filter((f) => commonFieldNames.has(f.name) && !f.positional);
332
+ if (commonFields.length > 0) sections.push(style === "table" ? renderOptionsTableFromArray(commonFields) : renderOptionsListFromArray(commonFields));
333
+ sections.push("> One of the following option groups is required:");
334
+ for (let i = 0; i < unionOptions.length; i++) {
335
+ const option = unionOptions[i];
336
+ if (!option) continue;
337
+ const uniqueFields = option.fields.filter((f) => !commonFieldNames.has(f.name) && !f.positional);
338
+ if (uniqueFields.length === 0) continue;
339
+ const label = option.description ?? `Variant ${i + 1}`;
340
+ const rendered = style === "table" ? renderOptionsTableFromArray(uniqueFields) : renderOptionsListFromArray(uniqueFields);
341
+ sections.push(`**${label}:**\n\n${rendered}`);
342
+ }
343
+ return sections.join("\n\n");
344
+ }
345
+ /**
346
+ * Render discriminatedUnion options as markdown with variant grouping
347
+ */
348
+ function renderDiscriminatedUnionOptionsMarkdown(extracted, style = "table") {
349
+ const discriminator = extracted.discriminator;
350
+ const variants = extracted.variants ?? [];
351
+ if (!discriminator || variants.length === 0) return "";
352
+ const sections = [];
353
+ const allFieldNames = /* @__PURE__ */ new Set();
354
+ for (const variant of variants) for (const field of variant.fields) allFieldNames.add(field.name);
355
+ const commonFieldNames = /* @__PURE__ */ new Set();
356
+ for (const fieldName of allFieldNames) {
357
+ if (fieldName === discriminator) continue;
358
+ if (variants.every((v) => v.fields.some((f) => f.name === fieldName))) commonFieldNames.add(fieldName);
359
+ }
360
+ const discriminatorField = extracted.fields.find((f) => f.name === discriminator);
361
+ const variantValues = variants.map((v) => v.discriminatorValue).join("\\|");
362
+ const topFields = [];
363
+ if (discriminatorField) topFields.push({
364
+ ...discriminatorField,
365
+ placeholder: variantValues
366
+ });
367
+ for (const fieldName of commonFieldNames) {
368
+ const field = extracted.fields.find((f) => f.name === fieldName);
369
+ if (field && !field.positional) topFields.push(field);
370
+ }
371
+ if (topFields.length > 0) sections.push(style === "table" ? renderOptionsTableFromArray(topFields) : renderOptionsListFromArray(topFields));
372
+ for (const variant of variants) {
373
+ const uniqueFields = variant.fields.filter((f) => f.name !== discriminator && !commonFieldNames.has(f.name) && !f.positional);
374
+ if (uniqueFields.length === 0) continue;
375
+ const descSuffix = variant.description ? ` ${variant.description}` : "";
376
+ const label = `**When \`${discriminator}\` = \`${variant.discriminatorValue}\`:**${descSuffix}`;
377
+ const rendered = style === "table" ? renderOptionsTableFromArray(uniqueFields) : renderOptionsListFromArray(uniqueFields);
378
+ sections.push(`${label}\n\n${rendered}`);
379
+ }
380
+ return sections.join("\n\n");
381
+ }
382
+ /**
301
383
  * Render options from array as list
302
384
  */
303
385
  function renderOptionsListFromArray(options) {
@@ -413,8 +495,13 @@ function wrapWithMarker(type, scope, content) {
413
495
  return `${sectionStartMarker(type, scope)}\n${content}\n${sectionEndMarker(type, scope)}`;
414
496
  }
415
497
  /**
416
- * Create command renderer with options
498
+ * Generate a "See Global Options" link for subcommand documentation.
499
+ * Returns null for root command or when no global options exist.
417
500
  */
501
+ function getGlobalOptionsLink(info) {
502
+ if (!info.hasGlobalOptions || info.commandPath === "") return null;
503
+ 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.`;
504
+ }
418
505
  function createCommandRenderer(options = {}) {
419
506
  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;
420
507
  return (info) => {
@@ -462,7 +549,11 @@ function createCommandRenderer(options = {}) {
462
549
  const renderOpts = (opts, renderOpts) => {
463
550
  const style = renderOpts?.style ?? optionStyle;
464
551
  const withHeading = renderOpts?.withHeading ?? true;
465
- const content = style === "table" ? renderOptionsTableFromArray(opts) : renderOptionsListFromArray(opts);
552
+ const extracted = info.extracted;
553
+ let content;
554
+ if (extracted && (extracted.schemaType === "union" || extracted.schemaType === "xor") && extracted.unionOptions) content = renderUnionOptionsMarkdown(extracted, style);
555
+ else if (extracted && extracted.schemaType === "discriminatedUnion" && extracted.discriminator) content = renderDiscriminatedUnionOptionsMarkdown(extracted, style);
556
+ else content = style === "table" ? renderOptionsTableFromArray(opts) : renderOptionsListFromArray(opts);
466
557
  return withHeading ? `**Options**\n\n${content}` : content;
467
558
  };
468
559
  const context = {
@@ -474,6 +565,10 @@ function createCommandRenderer(options = {}) {
474
565
  const content = customRenderOptions ? customRenderOptions(context) : renderOpts(context.options);
475
566
  if (content) sections.push(wrapWithMarker("options", scope, content));
476
567
  }
568
+ {
569
+ const globalLink = getGlobalOptionsLink(info);
570
+ if (globalLink) sections.push(globalLink);
571
+ }
477
572
  if (info.subCommands.length > 0) {
478
573
  const effectiveAnchors = generateAnchors && includeSubcommandDetails;
479
574
  const renderSubs = (subs, opts) => {
@@ -720,7 +815,7 @@ async function executeSingleExample(example, rootCommand, commandPath) {
720
815
  collector.start();
721
816
  let success = true;
722
817
  try {
723
- const { runCommand } = await import("../runner-D6k4BgB4.js").then((n) => n.r);
818
+ const { runCommand } = await import("../runner-D4ByDT5I.js").then((n) => n.r);
724
819
  const result = await runCommand(rootCommand, argv);
725
820
  success = result.success;
726
821
  if (!result.success && result.error) console.error(result.error.message);
@@ -1124,7 +1219,7 @@ function resolveConfiguredCommandPaths(fileConfigRaw, allCommands, ignores) {
1124
1219
  return {
1125
1220
  fileConfig,
1126
1221
  specifiedCommands,
1127
- commandPaths: filterIgnoredCommands(expandCommandPaths(specifiedCommands, allCommands), ignores),
1222
+ commandPaths: filterIgnoredCommands(fileConfig.noExpand ? specifiedCommands.filter((p) => allCommands.has(p)) : expandCommandPaths(specifiedCommands, allCommands), ignores),
1128
1223
  topLevelCommands: filterIgnoredCommands(resolveTopLevelCommands(specifiedCommands, allCommands), ignores)
1129
1224
  };
1130
1225
  }
@@ -1444,6 +1539,7 @@ function generateGlobalOptionsSection(config) {
1444
1539
  const endMarker = globalOptionsEndMarker();
1445
1540
  return [
1446
1541
  startMarker,
1542
+ "<a id=\"global-options\"></a>",
1447
1543
  renderArgsTable(config.args, config.options),
1448
1544
  endMarker
1449
1545
  ].join("\n");
@@ -1470,7 +1566,7 @@ function normalizeDocPathForComparison(filePath) {
1470
1566
  * Process global options marker in file content
1471
1567
  * Returns result with updated content and any diffs
1472
1568
  */
1473
- async function processGlobalOptionsMarker(existingContent, globalOptionsConfig, updateMode, formatter) {
1569
+ async function processGlobalOptionsMarker(existingContent, globalOptionsConfig, updateMode, formatter, autoInsertIfMissing) {
1474
1570
  let content = existingContent;
1475
1571
  const diffs = [];
1476
1572
  let hasError = false;
@@ -1480,6 +1576,16 @@ async function processGlobalOptionsMarker(existingContent, globalOptionsConfig,
1480
1576
  const generatedSection = await applyFormatter(generateGlobalOptionsSection(globalOptionsConfig), formatter);
1481
1577
  const existingSection = extractMarkerSection(content, startMarker, endMarker);
1482
1578
  if (!existingSection) {
1579
+ if (updateMode && autoInsertIfMissing) {
1580
+ content = content.trimEnd() + "\n\n" + generatedSection + "\n";
1581
+ wasUpdated = true;
1582
+ return {
1583
+ content,
1584
+ diffs,
1585
+ hasError,
1586
+ wasUpdated
1587
+ };
1588
+ }
1483
1589
  hasError = true;
1484
1590
  diffs.push(`Global options marker not found in file. Expected markers:\n${startMarker}\n...\n${endMarker}`);
1485
1591
  return {
@@ -1510,6 +1616,61 @@ async function processGlobalOptionsMarker(existingContent, globalOptionsConfig,
1510
1616
  };
1511
1617
  }
1512
1618
  /**
1619
+ * Process a static content marker (root-header or root-footer).
1620
+ * Inserts/updates the marker section with the given content.
1621
+ */
1622
+ async function processStaticMarker(existingContent, markerLabel, startMarker, endMarker, rawContent, updateMode, formatter, autoInsertIfMissing) {
1623
+ let content = existingContent;
1624
+ const diffs = [];
1625
+ let hasError = false;
1626
+ let wasUpdated = false;
1627
+ const generatedSection = [
1628
+ startMarker,
1629
+ await applyFormatter(rawContent, formatter),
1630
+ endMarker
1631
+ ].join("\n");
1632
+ const existingSection = extractMarkerSection(content, startMarker, endMarker);
1633
+ if (!existingSection) {
1634
+ if (updateMode && autoInsertIfMissing) {
1635
+ content = content.trimEnd() + "\n\n" + generatedSection + "\n";
1636
+ wasUpdated = true;
1637
+ return {
1638
+ content,
1639
+ diffs,
1640
+ hasError,
1641
+ wasUpdated
1642
+ };
1643
+ }
1644
+ hasError = true;
1645
+ diffs.push(`${markerLabel} marker not found in file. Expected markers:\n${startMarker}\n...\n${endMarker}`);
1646
+ return {
1647
+ content,
1648
+ diffs,
1649
+ hasError,
1650
+ wasUpdated
1651
+ };
1652
+ }
1653
+ if (existingSection !== generatedSection) if (updateMode) {
1654
+ const updated = replaceMarkerSection(content, startMarker, endMarker, generatedSection);
1655
+ if (updated) {
1656
+ content = updated;
1657
+ wasUpdated = true;
1658
+ } else {
1659
+ hasError = true;
1660
+ diffs.push(`Failed to replace ${markerLabel} section`);
1661
+ }
1662
+ } else {
1663
+ hasError = true;
1664
+ diffs.push(formatDiff(existingSection, generatedSection));
1665
+ }
1666
+ return {
1667
+ content,
1668
+ diffs,
1669
+ hasError,
1670
+ wasUpdated
1671
+ };
1672
+ }
1673
+ /**
1513
1674
  * Process index marker in file content
1514
1675
  * Returns result with updated content and any diffs.
1515
1676
  * If the marker is not present in the file, the section is silently skipped.
@@ -1600,26 +1761,29 @@ function findTargetCommandsInFile(targetCommands, filePath, files, allCommands,
1600
1761
  /**
1601
1762
  * Generate a single command section (already contains section markers from renderer)
1602
1763
  */
1603
- function generateCommandSection(cmdPath, allCommands, render, filePath, fileMap) {
1764
+ function generateCommandSection(cmdPath, allCommands, render, filePath, fileMap, rootDocPath, hasGlobalOptions) {
1604
1765
  const info = allCommands.get(cmdPath);
1605
1766
  if (!info) return null;
1606
- return render({
1767
+ const enriched = {
1607
1768
  ...info,
1608
1769
  filePath,
1609
- fileMap
1610
- });
1770
+ fileMap,
1771
+ rootDocPath
1772
+ };
1773
+ if (hasGlobalOptions !== void 0) enriched.hasGlobalOptions = hasGlobalOptions;
1774
+ return render(enriched);
1611
1775
  }
1612
1776
  /**
1613
1777
  * Generate markdown for a file containing multiple commands
1614
1778
  * Each command section is wrapped with markers for partial validation
1615
1779
  */
1616
- function generateFileMarkdown(commandPaths, allCommands, render, filePath, fileMap, specifiedOrder, fileConfig) {
1780
+ function generateFileMarkdown(commandPaths, allCommands, render, filePath, fileMap, specifiedOrder, fileConfig, rootDocPath, hasGlobalOptions) {
1617
1781
  const sections = [];
1618
1782
  const header = fileConfig ? generateFileHeader(fileConfig) : null;
1619
1783
  if (header) sections.push(header);
1620
1784
  const sortedPaths = sortDepthFirst(commandPaths, specifiedOrder ?? []);
1621
1785
  for (const cmdPath of sortedPaths) {
1622
- const section = generateCommandSection(cmdPath, allCommands, render, filePath, fileMap);
1786
+ const section = generateCommandSection(cmdPath, allCommands, render, filePath, fileMap, rootDocPath, hasGlobalOptions);
1623
1787
  if (section) sections.push(section);
1624
1788
  }
1625
1789
  return `${sections.join("\n")}\n`;
@@ -1648,16 +1812,72 @@ async function executeConfiguredExamples(allCommands, examplesConfig, rootComman
1648
1812
  }
1649
1813
  }
1650
1814
  /**
1815
+ * Convert PathConfig to FileMapping with explicit command paths.
1816
+ * Uses noExpand to prevent subcommand expansion since paths are pre-resolved.
1817
+ */
1818
+ function pathToFiles(pathConfig, allCommands) {
1819
+ if (typeof pathConfig === "string") return {
1820
+ files: { [pathConfig]: Array.from(allCommands.keys()) },
1821
+ rootDocPath: pathConfig
1822
+ };
1823
+ const { root, commands = {} } = pathConfig;
1824
+ const files = {};
1825
+ const assignedToOtherFiles = /* @__PURE__ */ new Set();
1826
+ const sortedEntries = Object.entries(commands).sort(([a], [b]) => b.split(" ").length - a.split(" ").length);
1827
+ for (const [cmdPath, filePath] of sortedEntries) {
1828
+ if (!files[filePath]) files[filePath] = {
1829
+ commands: [],
1830
+ noExpand: true
1831
+ };
1832
+ const fc = files[filePath];
1833
+ for (const existingPath of allCommands.keys()) if ((existingPath === cmdPath || existingPath.startsWith(cmdPath + " ")) && !assignedToOtherFiles.has(existingPath)) {
1834
+ fc.commands.push(existingPath);
1835
+ assignedToOtherFiles.add(existingPath);
1836
+ }
1837
+ }
1838
+ files[root] = {
1839
+ commands: Array.from(allCommands.keys()).filter((p) => !assignedToOtherFiles.has(p)),
1840
+ noExpand: true
1841
+ };
1842
+ return {
1843
+ files,
1844
+ rootDocPath: root
1845
+ };
1846
+ }
1847
+ /**
1651
1848
  * Generate documentation from command definition
1652
1849
  */
1653
1850
  async function generateDoc(config) {
1654
- const { command, rootDoc, files, ignores = [], format = {}, formatter, examples: examplesConfig, targetCommands } = config;
1851
+ const { command, ignores = [], format = {}, formatter, examples: examplesConfig, targetCommands, globalArgs } = config;
1852
+ const allCommands = await collectAllCommands(command);
1853
+ let files;
1854
+ let usingPathConfig = false;
1855
+ let resolvedRootDocPath;
1856
+ if (config.path !== void 0) {
1857
+ if (config.files !== void 0) throw new Error("Cannot specify both \"path\" and \"files\". Use one or the other.");
1858
+ const converted = pathToFiles(config.path, allCommands);
1859
+ files = converted.files;
1860
+ resolvedRootDocPath = converted.rootDocPath;
1861
+ usingPathConfig = true;
1862
+ } else if (config.files !== void 0) files = config.files;
1863
+ else throw new Error("Either \"path\" or \"files\" must be specified.");
1864
+ let rootDoc = config.rootDoc;
1865
+ if (!rootDoc && usingPathConfig && (globalArgs || config.rootInfo)) rootDoc = { path: resolvedRootDocPath };
1866
+ if (globalArgs && rootDoc && !rootDoc.globalOptions) {
1867
+ const optionFields = extractFields(globalArgs).fields.filter((f) => !f.positional);
1868
+ if (optionFields.length > 0) {
1869
+ const globalShape = Object.fromEntries(optionFields.map((f) => [f.name, f.schema]));
1870
+ rootDoc = {
1871
+ ...rootDoc,
1872
+ globalOptions: globalShape
1873
+ };
1874
+ }
1875
+ }
1655
1876
  const updateMode = isUpdateMode();
1656
- if (rootDoc) {
1877
+ if (rootDoc && !usingPathConfig) {
1657
1878
  const normalizedRootDocPath = normalizeDocPathForComparison(rootDoc.path);
1658
1879
  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.`);
1659
1880
  }
1660
- const allCommands = await collectAllCommands(command);
1661
1881
  if (examplesConfig) await executeConfiguredExamples(allCommands, examplesConfig, command);
1662
1882
  const hasTargetCommands = targetCommands !== void 0 && targetCommands.length > 0;
1663
1883
  if (hasTargetCommands) {
@@ -1691,11 +1911,13 @@ async function generateDoc(config) {
1691
1911
  headingLevel: adjustedHeadingLevel
1692
1912
  });
1693
1913
  const render = fileConfig.render ?? fileRenderer;
1694
- if (hasTargetCommands) {
1914
+ const isRootDocFile = usingPathConfig && rootDoc && normalizeDocPathForComparison(filePath) === normalizeDocPathForComparison(rootDoc.path);
1915
+ if (hasTargetCommands || isRootDocFile) {
1695
1916
  let existingContent = readFile(filePath);
1696
1917
  const sortedCommandPaths = sortDepthFirst(commandPaths, specifiedCommands);
1697
- for (const targetCommand of fileTargetCommands) {
1698
- const rawSection = generateCommandSection(targetCommand, allCommands, render, filePath, fileMap);
1918
+ const effectiveTargetCommands = hasTargetCommands ? fileTargetCommands : commandPaths;
1919
+ for (const targetCommand of effectiveTargetCommands) {
1920
+ const rawSection = generateCommandSection(targetCommand, allCommands, render, filePath, fileMap, rootDoc?.path, globalOptionDefinitions.size > 0);
1699
1921
  if (!rawSection) throw new Error(`Target command "${targetCommand}" not found in commands`);
1700
1922
  const generatedSection = await applyFormatter(rawSection, formatter);
1701
1923
  if (!existingContent) {
@@ -1744,7 +1966,7 @@ async function generateDoc(config) {
1744
1966
  }
1745
1967
  }
1746
1968
  } else {
1747
- const generatedMarkdown = await applyFormatter(generateFileMarkdown(commandPaths, allCommands, render, filePath, fileMap, specifiedCommands, fileConfig), formatter);
1969
+ const generatedMarkdown = await applyFormatter(generateFileMarkdown(commandPaths, allCommands, render, filePath, fileMap, specifiedCommands, fileConfig, rootDoc?.path, globalOptionDefinitions.size > 0), formatter);
1748
1970
  const comparison = compareWithExisting(generatedMarkdown, filePath);
1749
1971
  if (comparison.match) {} else if (updateMode) {
1750
1972
  writeFile(filePath, generatedMarkdown);
@@ -1774,22 +1996,33 @@ async function generateDoc(config) {
1774
1996
  } else {
1775
1997
  let content = existingContent;
1776
1998
  let markerUpdated = false;
1777
- const rootDocFileConfig = { title: command.name };
1999
+ const rootInfo = config.rootInfo;
2000
+ const rootDocFileConfig = { title: rootInfo?.title ?? command.name };
1778
2001
  if (rootDoc.headingLevel !== void 0) rootDocFileConfig.headingLevel = rootDoc.headingLevel;
1779
- if (command.description !== void 0) rootDocFileConfig.description = command.description;
2002
+ const rootDescription = rootInfo?.description ?? command.description;
2003
+ if (rootDescription !== void 0) rootDocFileConfig.description = rootDescription;
1780
2004
  const headerResult = processFileHeader(content, rootDocFileConfig, updateMode);
1781
2005
  content = headerResult.content;
1782
2006
  if (headerResult.diff) rootDocDiffs.push(headerResult.diff);
1783
2007
  if (headerResult.hasError) hasError = true;
1784
2008
  if (headerResult.wasUpdated) markerUpdated = true;
1785
- const unexpectedSectionPaths = Array.from(new Set(collectSectionMarkerPaths(content)));
1786
- if (unexpectedSectionPaths.length > 0) {
1787
- hasError = true;
1788
- rootDocDiffs.push(`Found unexpected section markers in rootDoc: ${unexpectedSectionPaths.map((commandPath) => `"${formatCommandPath(commandPath)}"`).join(", ")}.`);
2009
+ if (rootInfo?.header) {
2010
+ const headerMarkerResult = await processStaticMarker(content, "Root header", rootHeaderStartMarker(), rootHeaderEndMarker(), rootInfo.header, updateMode, formatter, usingPathConfig);
2011
+ content = headerMarkerResult.content;
2012
+ rootDocDiffs.push(...headerMarkerResult.diffs);
2013
+ if (headerMarkerResult.hasError) hasError = true;
2014
+ if (headerMarkerResult.wasUpdated) markerUpdated = true;
2015
+ }
2016
+ if (!usingPathConfig) {
2017
+ const unexpectedSectionPaths = Array.from(new Set(collectSectionMarkerPaths(content)));
2018
+ if (unexpectedSectionPaths.length > 0) {
2019
+ hasError = true;
2020
+ rootDocDiffs.push(`Found unexpected section markers in rootDoc: ${unexpectedSectionPaths.map((commandPath) => `"${formatCommandPath(commandPath)}"`).join(", ")}.`);
2021
+ }
1789
2022
  }
1790
2023
  const normalizedGlobalOptions = normalizeGlobalOptions(rootDoc.globalOptions);
1791
2024
  if (normalizedGlobalOptions) {
1792
- const globalOptionsResult = await processGlobalOptionsMarker(content, normalizedGlobalOptions, updateMode, formatter);
2025
+ const globalOptionsResult = await processGlobalOptionsMarker(content, normalizedGlobalOptions, updateMode, formatter, usingPathConfig);
1793
2026
  content = globalOptionsResult.content;
1794
2027
  rootDocDiffs.push(...globalOptionsResult.diffs);
1795
2028
  if (globalOptionsResult.hasError) hasError = true;
@@ -1802,6 +2035,13 @@ async function generateDoc(config) {
1802
2035
  rootDocDiffs.push(...indexResult.diffs);
1803
2036
  if (indexResult.hasError) hasError = true;
1804
2037
  if (indexResult.wasUpdated) markerUpdated = true;
2038
+ if (rootInfo?.footer) {
2039
+ const footerMarkerResult = await processStaticMarker(content, "Root footer", rootFooterStartMarker(), rootFooterEndMarker(), rootInfo.footer, updateMode, formatter, usingPathConfig);
2040
+ content = footerMarkerResult.content;
2041
+ rootDocDiffs.push(...footerMarkerResult.diffs);
2042
+ if (footerMarkerResult.hasError) hasError = true;
2043
+ if (footerMarkerResult.wasUpdated) markerUpdated = true;
2044
+ }
1805
2045
  if (updateMode && markerUpdated) {
1806
2046
  writeFile(rootDocFilePath, content);
1807
2047
  if (rootDocStatus === "match") rootDocStatus = "updated";
@@ -1845,9 +2085,9 @@ async function assertDocMatch(config) {
1845
2085
  function initDocFile(config, fileSystem) {
1846
2086
  if (!isUpdateMode()) return;
1847
2087
  if (typeof config === "string") deleteFile(config, fileSystem);
1848
- else for (const filePath of Object.keys(config.files)) deleteFile(filePath, fileSystem);
2088
+ else if (config.files) for (const filePath of Object.keys(config.files)) deleteFile(filePath, fileSystem);
1849
2089
  }
1850
2090
 
1851
2091
  //#endregion
1852
- export { GLOBAL_OPTIONS_MARKER_PREFIX, INDEX_MARKER_PREFIX, SECTION_MARKER_PREFIX, SECTION_TYPES, UPDATE_GOLDEN_ENV, assertDocMatch, buildCommandInfo, collectAllCommands, compareWithExisting, createCommandRenderer, defaultRenderers, executeExamples, formatDiff, generateDoc, globalOptionsEndMarker, globalOptionsStartMarker, indexEndMarker, indexStartMarker, initDocFile, renderArgsTable, renderArgumentsList, renderArgumentsListFromArray, renderArgumentsTable, renderArgumentsTableFromArray, renderCommandIndex, renderExamplesDefault, renderOptionsList, renderOptionsListFromArray, renderOptionsTable, renderOptionsTableFromArray, renderSubcommandsTable, renderSubcommandsTableFromArray, renderUsage, resolveLazyCommand, sectionEndMarker, sectionStartMarker, writeFile };
2092
+ export { GLOBAL_OPTIONS_MARKER_PREFIX, INDEX_MARKER_PREFIX, ROOT_FOOTER_MARKER_PREFIX, ROOT_HEADER_MARKER_PREFIX, SECTION_MARKER_PREFIX, SECTION_TYPES, UPDATE_GOLDEN_ENV, assertDocMatch, buildCommandInfo, collectAllCommands, compareWithExisting, createCommandRenderer, defaultRenderers, executeExamples, formatDiff, generateDoc, globalOptionsEndMarker, globalOptionsStartMarker, indexEndMarker, indexStartMarker, initDocFile, renderArgsTable, renderArgumentsList, renderArgumentsListFromArray, renderArgumentsTable, renderArgumentsTableFromArray, renderCommandIndex, renderExamplesDefault, renderOptionsList, renderOptionsListFromArray, renderOptionsTable, renderOptionsTableFromArray, renderSubcommandsTable, renderSubcommandsTableFromArray, renderUsage, resolveLazyCommand, rootFooterEndMarker, rootFooterStartMarker, rootHeaderEndMarker, rootHeaderStartMarker, sectionEndMarker, sectionStartMarker, writeFile };
1853
2093
  //# sourceMappingURL=index.js.map