politty 0.3.3 → 0.4.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.
@@ -8,6 +8,77 @@ node_fs = require_subcommand_router.__toESM(node_fs);
8
8
  let node_path = require("node:path");
9
9
  node_path = require_subcommand_router.__toESM(node_path);
10
10
 
11
+ //#region src/docs/types.ts
12
+ /**
13
+ * Environment variable name for update mode
14
+ */
15
+ const UPDATE_GOLDEN_ENV = "POLITTY_DOCS_UPDATE";
16
+ /**
17
+ * All section types in rendering order
18
+ */
19
+ const SECTION_TYPES = [
20
+ "heading",
21
+ "description",
22
+ "usage",
23
+ "arguments",
24
+ "options",
25
+ "subcommands",
26
+ "examples",
27
+ "notes"
28
+ ];
29
+ /**
30
+ * Marker prefix for command section markers in generated documentation
31
+ * Format: <!-- politty:command:<scope>:<type>:start --> ... <!-- politty:command:<scope>:<type>:end -->
32
+ */
33
+ const SECTION_MARKER_PREFIX = "politty:command";
34
+ /**
35
+ * Generate start marker for a command section
36
+ */
37
+ function sectionStartMarker(type, scope) {
38
+ return `<!-- ${SECTION_MARKER_PREFIX}:${scope}:${type}:start -->`;
39
+ }
40
+ /**
41
+ * Generate end marker for a command section
42
+ */
43
+ function sectionEndMarker(type, scope) {
44
+ return `<!-- ${SECTION_MARKER_PREFIX}:${scope}:${type}:end -->`;
45
+ }
46
+ /**
47
+ * Marker prefix for global options sections in generated documentation
48
+ * Format: <!-- politty:global-options:start --> ... <!-- politty:global-options:end -->
49
+ */
50
+ const GLOBAL_OPTIONS_MARKER_PREFIX = "politty:global-options";
51
+ /**
52
+ * Generate start marker for a global options section
53
+ */
54
+ function globalOptionsStartMarker() {
55
+ return `<!-- ${GLOBAL_OPTIONS_MARKER_PREFIX}:start -->`;
56
+ }
57
+ /**
58
+ * Generate end marker for a global options section
59
+ */
60
+ function globalOptionsEndMarker() {
61
+ return `<!-- ${GLOBAL_OPTIONS_MARKER_PREFIX}:end -->`;
62
+ }
63
+ /**
64
+ * Marker prefix for index sections in generated documentation
65
+ * Format: <!-- politty:index:<scope>:start --> ... <!-- politty:index:<scope>:end -->
66
+ */
67
+ const INDEX_MARKER_PREFIX = "politty:index";
68
+ /**
69
+ * Generate start marker for an index section
70
+ */
71
+ function indexStartMarker(scope) {
72
+ return `<!-- ${INDEX_MARKER_PREFIX}:${scope}:start -->`;
73
+ }
74
+ /**
75
+ * Generate end marker for an index section
76
+ */
77
+ function indexEndMarker(scope) {
78
+ return `<!-- ${INDEX_MARKER_PREFIX}:${scope}:end -->`;
79
+ }
80
+
81
+ //#endregion
11
82
  //#region src/docs/default-renderers.ts
12
83
  /**
13
84
  * Escape markdown special characters in table cells
@@ -69,15 +140,34 @@ function formatEnvInfo(env) {
69
140
  return ` [env: ${(Array.isArray(env) ? env : [env]).join(", ")}]`;
70
141
  }
71
142
  /**
72
- * Format option flags (uses kebab-case cliName)
143
+ * Resolve placeholder for an option (uses kebab-case cliName)
144
+ */
145
+ function resolvePlaceholder(opt) {
146
+ return opt.placeholder ?? opt.cliName.toUpperCase().replace(/-/g, "_");
147
+ }
148
+ /**
149
+ * Format option name for table display (e.g., `--dry-run` or `--port <PORT>`)
150
+ */
151
+ function formatOptionName(opt) {
152
+ const placeholder = resolvePlaceholder(opt);
153
+ return opt.type === "boolean" ? `\`--${opt.cliName}\`` : `\`--${opt.cliName} <${placeholder}>\``;
154
+ }
155
+ /**
156
+ * Format option flags for list display (uses kebab-case cliName)
73
157
  */
74
158
  function formatOptionFlags(opt) {
75
- const parts = [];
76
- const placeholder = opt.placeholder ?? opt.cliName.toUpperCase().replace(/-/g, "_");
159
+ const placeholder = resolvePlaceholder(opt);
77
160
  const longFlag = opt.type === "boolean" ? `--${opt.cliName}` : `--${opt.cliName} <${placeholder}>`;
78
- if (opt.alias) parts.push(`\`-${opt.alias}\`, \`${longFlag}\``);
79
- else parts.push(`\`${longFlag}\``);
80
- return parts.join("");
161
+ if (opt.alias) return `\`-${opt.alias}\`, \`${longFlag}\``;
162
+ return `\`${longFlag}\``;
163
+ }
164
+ /**
165
+ * Format env variable names for table display
166
+ */
167
+ function formatEnvNames(env) {
168
+ if (!env) return "-";
169
+ if (Array.isArray(env)) return env.map((e) => `\`${e}\``).join(", ");
170
+ return `\`${env}\``;
81
171
  }
82
172
  /**
83
173
  * Render options as markdown table
@@ -105,14 +195,13 @@ function renderOptionsTable(info) {
105
195
  lines.push("|--------|-------|-------------|----------|---------|");
106
196
  }
107
197
  for (const opt of info.options) {
108
- const placeholder = opt.placeholder ?? opt.cliName.toUpperCase().replace(/-/g, "_");
109
- const optionName = opt.type === "boolean" ? `\`--${opt.cliName}\`` : `\`--${opt.cliName} <${placeholder}>\``;
198
+ const optionName = formatOptionName(opt);
110
199
  const alias = opt.alias ? `\`-${opt.alias}\`` : "-";
111
200
  const desc = escapeTableCell$2(opt.description ?? "");
112
201
  const required = opt.required ? "Yes" : "No";
113
202
  const defaultVal = formatDefaultValue$1(opt.defaultValue);
114
203
  if (hasEnv) {
115
- const envNames = opt.env ? Array.isArray(opt.env) ? opt.env.map((e) => `\`${e}\``).join(", ") : `\`${opt.env}\`` : "-";
204
+ const envNames = formatEnvNames(opt.env);
116
205
  lines.push(`| ${optionName} | ${alias} | ${desc} | ${required} | ${defaultVal} | ${envNames} |`);
117
206
  } else lines.push(`| ${optionName} | ${alias} | ${desc} | ${required} | ${defaultVal} |`);
118
207
  }
@@ -199,14 +288,13 @@ function renderOptionsTableFromArray(options) {
199
288
  lines.push("|--------|-------|-------------|----------|---------|");
200
289
  }
201
290
  for (const opt of options) {
202
- const placeholder = opt.placeholder ?? opt.cliName.toUpperCase().replace(/-/g, "_");
203
- const optionName = opt.type === "boolean" ? `\`--${opt.cliName}\`` : `\`--${opt.cliName} <${placeholder}>\``;
291
+ const optionName = formatOptionName(opt);
204
292
  const alias = opt.alias ? `\`-${opt.alias}\`` : "-";
205
293
  const desc = escapeTableCell$2(opt.description ?? "");
206
294
  const required = opt.required ? "Yes" : "No";
207
295
  const defaultVal = formatDefaultValue$1(opt.defaultValue);
208
296
  if (hasEnv) {
209
- const envNames = opt.env ? Array.isArray(opt.env) ? opt.env.map((e) => `\`${e}\``).join(", ") : `\`${opt.env}\`` : "-";
297
+ const envNames = formatEnvNames(opt.env);
210
298
  lines.push(`| ${optionName} | ${alias} | ${desc} | ${required} | ${defaultVal} | ${envNames} |`);
211
299
  } else lines.push(`| ${optionName} | ${alias} | ${desc} | ${required} | ${defaultVal} |`);
212
300
  }
@@ -322,17 +410,23 @@ function renderExamplesDefault(examples, results, opts) {
322
410
  return lines.join("\n");
323
411
  }
324
412
  /**
413
+ * Wrap content with section markers
414
+ */
415
+ function wrapWithMarker(type, scope, content) {
416
+ return `${sectionStartMarker(type, scope)}\n${content}\n${sectionEndMarker(type, scope)}`;
417
+ }
418
+ /**
325
419
  * Create command renderer with options
326
420
  */
327
421
  function createCommandRenderer(options = {}) {
328
422
  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;
329
423
  return (info) => {
330
- const lines = [];
424
+ const sections = [];
425
+ const scope = info.commandPath;
331
426
  const effectiveLevel = Math.min(headingLevel + (info.depth - 1), 6);
332
427
  const h = "#".repeat(effectiveLevel);
333
428
  const title = info.commandPath || info.name;
334
- lines.push(`${h} ${title}`);
335
- lines.push("");
429
+ sections.push(wrapWithMarker("heading", scope, `${h} ${title}`));
336
430
  if (info.description) {
337
431
  const context = {
338
432
  content: info.description,
@@ -340,10 +434,7 @@ function createCommandRenderer(options = {}) {
340
434
  info
341
435
  };
342
436
  const content = customRenderDescription ? customRenderDescription(context) : context.content;
343
- if (content) {
344
- lines.push(content);
345
- lines.push("");
346
- }
437
+ if (content) sections.push(wrapWithMarker("description", scope, content));
347
438
  }
348
439
  {
349
440
  const context = {
@@ -352,10 +443,7 @@ function createCommandRenderer(options = {}) {
352
443
  info
353
444
  };
354
445
  const content = customRenderUsage ? customRenderUsage(context) : context.content;
355
- if (content) {
356
- lines.push(content);
357
- lines.push("");
358
- }
446
+ if (content) sections.push(wrapWithMarker("usage", scope, content));
359
447
  }
360
448
  if (info.positionalArgs.length > 0) {
361
449
  const renderArgs = (args, opts) => {
@@ -371,10 +459,7 @@ function createCommandRenderer(options = {}) {
371
459
  info
372
460
  };
373
461
  const content = customRenderArguments ? customRenderArguments(context) : renderArgs(context.args);
374
- if (content) {
375
- lines.push(content);
376
- lines.push("");
377
- }
462
+ if (content) sections.push(wrapWithMarker("arguments", scope, content));
378
463
  }
379
464
  if (info.options.length > 0) {
380
465
  const renderOpts = (opts, renderOpts) => {
@@ -390,10 +475,7 @@ function createCommandRenderer(options = {}) {
390
475
  info
391
476
  };
392
477
  const content = customRenderOptions ? customRenderOptions(context) : renderOpts(context.options);
393
- if (content) {
394
- lines.push(content);
395
- lines.push("");
396
- }
478
+ if (content) sections.push(wrapWithMarker("options", scope, content));
397
479
  }
398
480
  if (info.subCommands.length > 0) {
399
481
  const effectiveAnchors = generateAnchors && includeSubcommandDetails;
@@ -410,10 +492,7 @@ function createCommandRenderer(options = {}) {
410
492
  info
411
493
  };
412
494
  const content = customRenderSubcommands ? customRenderSubcommands(context) : renderSubs(context.subcommands);
413
- if (content) {
414
- lines.push(content);
415
- lines.push("");
416
- }
495
+ if (content) sections.push(wrapWithMarker("subcommands", scope, content));
417
496
  }
418
497
  if (info.examples && info.examples.length > 0) {
419
498
  const renderEx = (examples, results, opts) => {
@@ -432,10 +511,7 @@ function createCommandRenderer(options = {}) {
432
511
  info
433
512
  };
434
513
  const content = customRenderExamples ? customRenderExamples(context) : renderEx(context.examples, context.results);
435
- if (content) {
436
- lines.push(content);
437
- lines.push("");
438
- }
514
+ if (content) sections.push(wrapWithMarker("examples", scope, content));
439
515
  }
440
516
  if (info.notes) {
441
517
  const context = {
@@ -444,10 +520,7 @@ function createCommandRenderer(options = {}) {
444
520
  info
445
521
  };
446
522
  const content = customRenderNotes ? customRenderNotes(context) : context.content;
447
- if (content) {
448
- lines.push(content);
449
- lines.push("");
450
- }
523
+ if (content) sections.push(wrapWithMarker("notes", scope, content));
451
524
  }
452
525
  {
453
526
  const context = {
@@ -456,14 +529,9 @@ function createCommandRenderer(options = {}) {
456
529
  info
457
530
  };
458
531
  const content = customRenderFooter ? customRenderFooter(context) : context.content;
459
- if (content) {
460
- lines.push(content);
461
- lines.push("");
462
- }
532
+ if (content) sections.push(content);
463
533
  }
464
- while (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
465
- lines.push("");
466
- return lines.join("\n");
534
+ return sections.join("\n\n") + "\n";
467
535
  };
468
536
  }
469
537
  /**
@@ -936,64 +1004,6 @@ async function renderCommandIndex(command, categories, options) {
936
1004
  return sections.join("\n\n");
937
1005
  }
938
1006
 
939
- //#endregion
940
- //#region src/docs/types.ts
941
- /**
942
- * Environment variable name for update mode
943
- */
944
- const UPDATE_GOLDEN_ENV = "POLITTY_DOCS_UPDATE";
945
- /**
946
- * Marker prefix for command sections in generated documentation
947
- * Format: <!-- politty:command:<path>:start --> ... <!-- politty:command:<path>:end -->
948
- */
949
- const COMMAND_MARKER_PREFIX = "politty:command";
950
- /**
951
- * Generate start marker for a command section
952
- */
953
- function commandStartMarker(commandPath) {
954
- return `<!-- ${COMMAND_MARKER_PREFIX}:${commandPath}:start -->`;
955
- }
956
- /**
957
- * Generate end marker for a command section
958
- */
959
- function commandEndMarker(commandPath) {
960
- return `<!-- ${COMMAND_MARKER_PREFIX}:${commandPath}:end -->`;
961
- }
962
- /**
963
- * Marker prefix for global options sections in generated documentation
964
- * Format: <!-- politty:global-options:start --> ... <!-- politty:global-options:end -->
965
- */
966
- const GLOBAL_OPTIONS_MARKER_PREFIX = "politty:global-options";
967
- /**
968
- * Generate start marker for a global options section
969
- */
970
- function globalOptionsStartMarker() {
971
- return `<!-- ${GLOBAL_OPTIONS_MARKER_PREFIX}:start -->`;
972
- }
973
- /**
974
- * Generate end marker for a global options section
975
- */
976
- function globalOptionsEndMarker() {
977
- return `<!-- ${GLOBAL_OPTIONS_MARKER_PREFIX}:end -->`;
978
- }
979
- /**
980
- * Marker prefix for index sections in generated documentation
981
- * Format: <!-- politty:index:start --> ... <!-- politty:index:end -->
982
- */
983
- const INDEX_MARKER_PREFIX = "politty:index";
984
- /**
985
- * Generate start marker for an index section
986
- */
987
- function indexStartMarker() {
988
- return `<!-- ${INDEX_MARKER_PREFIX}:start -->`;
989
- }
990
- /**
991
- * Generate end marker for an index section
992
- */
993
- function indexEndMarker() {
994
- return `<!-- ${INDEX_MARKER_PREFIX}:end -->`;
995
- }
996
-
997
1007
  //#endregion
998
1008
  //#region src/docs/golden-test.ts
999
1009
  /**
@@ -1084,16 +1094,8 @@ function matchesIgnorePattern(path, ignorePattern) {
1084
1094
  */
1085
1095
  function expandCommandPaths(commandPaths, allCommands) {
1086
1096
  const expanded = /* @__PURE__ */ new Set();
1087
- for (const cmdPath of commandPaths) if (containsWildcard(cmdPath)) {
1088
- const matches = expandWildcardPattern(cmdPath, allCommands);
1089
- for (const match of matches) {
1090
- expanded.add(match);
1091
- for (const path of allCommands.keys()) if (isSubcommandOf(path, match)) expanded.add(path);
1092
- }
1093
- } else {
1094
- if (allCommands.has(cmdPath)) expanded.add(cmdPath);
1095
- for (const path of allCommands.keys()) if (isSubcommandOf(path, cmdPath)) expanded.add(path);
1096
- }
1097
+ const resolved = commandPaths.flatMap((cmdPath) => containsWildcard(cmdPath) ? expandWildcardPattern(cmdPath, allCommands) : [cmdPath]);
1098
+ for (const cmdPath of resolved) for (const existingPath of allCommands.keys()) if (isSubcommandOf(existingPath, cmdPath)) expanded.add(existingPath);
1097
1099
  return Array.from(expanded);
1098
1100
  }
1099
1101
  /**
@@ -1236,55 +1238,52 @@ function processFileHeader(existingContent, fileConfig, updateMode) {
1236
1238
  wasUpdated: true
1237
1239
  };
1238
1240
  }
1241
+ function formatCommandPath(commandPath) {
1242
+ return commandPath === "" ? "<root>" : commandPath;
1243
+ }
1239
1244
  /**
1240
- * Extract a command section from content using markers
1241
- * Returns the content between start and end markers (including markers)
1245
+ * Extract a section marker's content from document content.
1246
+ * Returns the content between start and end markers (including markers).
1242
1247
  */
1243
- function extractCommandSection(content, commandPath) {
1244
- const startMarker = commandStartMarker(commandPath);
1245
- const endMarker = commandEndMarker(commandPath);
1246
- const startIndex = content.indexOf(startMarker);
1247
- if (startIndex === -1) return null;
1248
- const endIndex = content.indexOf(endMarker, startIndex);
1249
- if (endIndex === -1) return null;
1250
- return content.slice(startIndex, endIndex + endMarker.length);
1248
+ function extractSectionMarker(content, type, scope) {
1249
+ return extractMarkerSection(content, sectionStartMarker(type, scope), sectionEndMarker(type, scope));
1251
1250
  }
1252
1251
  /**
1253
- * Collect command paths from command start markers in content.
1252
+ * Replace a section marker's content in document content.
1253
+ * Returns updated content, or null if marker not found.
1254
1254
  */
1255
- function collectCommandMarkerPaths(content) {
1256
- const markerPattern = /<!--\s*politty:command:(.*?):start\s*-->/g;
1257
- const paths = [];
1258
- for (const match of content.matchAll(markerPattern)) paths.push(match[1] ?? "");
1259
- return paths;
1255
+ function replaceSectionMarker(content, type, scope, newContent) {
1256
+ return replaceMarkerSection(content, sectionStartMarker(type, scope), sectionEndMarker(type, scope), newContent);
1260
1257
  }
1261
- function formatCommandPath(commandPath) {
1262
- return commandPath === "" ? "<root>" : commandPath;
1258
+ /**
1259
+ * Collect all section types that have markers for a given command path.
1260
+ */
1261
+ function collectSectionMarkers(content, commandPath) {
1262
+ const found = [];
1263
+ for (const type of SECTION_TYPES) if (extractSectionMarker(content, type, commandPath) !== null) found.push(type);
1264
+ return found;
1263
1265
  }
1264
1266
  /**
1265
- * Replace a command section in content using markers
1266
- * Returns the updated content with the new section
1267
+ * Collect all command paths that have any section markers in the content.
1267
1268
  */
1268
- function replaceCommandSection(content, commandPath, newSection) {
1269
- const startMarker = commandStartMarker(commandPath);
1270
- const endMarker = commandEndMarker(commandPath);
1271
- const startIndex = content.indexOf(startMarker);
1272
- if (startIndex === -1) return null;
1273
- const endIndex = content.indexOf(endMarker, startIndex);
1274
- if (endIndex === -1) return null;
1275
- return content.slice(0, startIndex) + newSection + content.slice(endIndex + endMarker.length);
1269
+ function collectSectionMarkerPaths(content) {
1270
+ const sectionTypes = SECTION_TYPES.join("|");
1271
+ const markerPattern = new RegExp(`<!--\\s*politty:command:(.*?):(?:${sectionTypes}):start\\s*-->`, "g");
1272
+ const paths = /* @__PURE__ */ new Set();
1273
+ for (const match of content.matchAll(markerPattern)) paths.add(match[1] ?? "");
1274
+ return Array.from(paths);
1276
1275
  }
1277
1276
  /**
1278
- * Insert a command section at the correct position based on specified order
1279
- * Returns the updated content with the section inserted at the right position
1277
+ * Insert command section markers at the correct position based on specified order.
1278
+ * Uses the heading marker of adjacent commands as reference points.
1280
1279
  */
1281
- function insertCommandSection(content, commandPath, newSection, specifiedOrder) {
1280
+ function insertCommandSections(content, commandPath, newSection, specifiedOrder) {
1282
1281
  const targetIndex = specifiedOrder.indexOf(commandPath);
1283
1282
  if (targetIndex === -1) return content.trimEnd() + "\n\n" + newSection + "\n";
1284
1283
  for (let i = targetIndex + 1; i < specifiedOrder.length; i++) {
1285
1284
  const nextCmd = specifiedOrder[i];
1286
1285
  if (nextCmd === void 0) continue;
1287
- const nextMarker = commandStartMarker(nextCmd);
1286
+ const nextMarker = sectionStartMarker("heading", nextCmd);
1288
1287
  const nextIndex = content.indexOf(nextMarker);
1289
1288
  if (nextIndex !== -1) {
1290
1289
  let insertPos = nextIndex;
@@ -1296,11 +1295,15 @@ function insertCommandSection(content, commandPath, newSection, specifiedOrder)
1296
1295
  for (let i = targetIndex - 1; i >= 0; i--) {
1297
1296
  const prevCmd = specifiedOrder[i];
1298
1297
  if (prevCmd === void 0) continue;
1299
- const prevEndMarker = commandEndMarker(prevCmd);
1300
- const prevEndIndex = content.indexOf(prevEndMarker);
1301
- if (prevEndIndex !== -1) {
1302
- const insertPos = prevEndIndex + prevEndMarker.length;
1303
- return content.slice(0, insertPos) + "\n" + newSection + content.slice(insertPos);
1298
+ const prevMarkers = collectSectionMarkers(content, prevCmd);
1299
+ if (prevMarkers.length > 0) {
1300
+ const lastType = prevMarkers[prevMarkers.length - 1];
1301
+ const prevEndMarker = sectionEndMarker(lastType, prevCmd);
1302
+ const prevEndIndex = content.indexOf(prevEndMarker);
1303
+ if (prevEndIndex !== -1) {
1304
+ const insertPos = prevEndIndex + prevEndMarker.length;
1305
+ return content.slice(0, insertPos) + "\n" + newSection + content.slice(insertPos);
1306
+ }
1304
1307
  }
1305
1308
  }
1306
1309
  return content.trimEnd() + "\n" + newSection + "\n";
@@ -1451,9 +1454,9 @@ function generateGlobalOptionsSection(config) {
1451
1454
  /**
1452
1455
  * Generate index section content with markers
1453
1456
  */
1454
- async function generateIndexSection(categories, command, options) {
1455
- const startMarker = indexStartMarker();
1456
- const endMarker = indexEndMarker();
1457
+ async function generateIndexSection(categories, command, scope, options) {
1458
+ const startMarker = indexStartMarker(scope);
1459
+ const endMarker = indexEndMarker(scope);
1457
1460
  return [
1458
1461
  startMarker,
1459
1462
  await renderCommandIndex(command, categories, options),
@@ -1514,13 +1517,13 @@ async function processGlobalOptionsMarker(existingContent, globalOptionsConfig,
1514
1517
  * Returns result with updated content and any diffs.
1515
1518
  * If the marker is not present in the file, the section is silently skipped.
1516
1519
  */
1517
- async function processIndexMarker(existingContent, categories, command, updateMode, formatter, indexOptions) {
1520
+ async function processIndexMarker(existingContent, categories, command, scope, updateMode, formatter, indexOptions) {
1518
1521
  let content = existingContent;
1519
1522
  const diffs = [];
1520
1523
  let hasError = false;
1521
1524
  let wasUpdated = false;
1522
- const startMarker = indexStartMarker();
1523
- const endMarker = indexEndMarker();
1525
+ const startMarker = indexStartMarker(scope);
1526
+ const endMarker = indexEndMarker(scope);
1524
1527
  const hasStartMarker = content.includes(startMarker);
1525
1528
  const hasEndMarker = content.includes(endMarker);
1526
1529
  if (!hasStartMarker && !hasEndMarker) return {
@@ -1550,7 +1553,7 @@ async function processIndexMarker(existingContent, categories, command, updateMo
1550
1553
  wasUpdated
1551
1554
  };
1552
1555
  }
1553
- const generatedSection = await applyFormatter(await generateIndexSection(categories, command, indexOptions), formatter);
1556
+ const generatedSection = await applyFormatter(await generateIndexSection(categories, command, scope, indexOptions), formatter);
1554
1557
  if (existingSection !== generatedSection) if (updateMode) {
1555
1558
  const updated = replaceMarkerSection(content, startMarker, endMarker, generatedSection);
1556
1559
  if (updated) {
@@ -1598,21 +1601,16 @@ function findTargetCommandsInFile(targetCommands, filePath, files, allCommands,
1598
1601
  return Array.from(expandedTargets);
1599
1602
  }
1600
1603
  /**
1601
- * Generate a single command section with markers
1604
+ * Generate a single command section (already contains section markers from renderer)
1602
1605
  */
1603
1606
  function generateCommandSection(cmdPath, allCommands, render, filePath, fileMap) {
1604
1607
  const info = allCommands.get(cmdPath);
1605
1608
  if (!info) return null;
1606
- const renderedSection = render({
1609
+ return render({
1607
1610
  ...info,
1608
1611
  filePath,
1609
1612
  fileMap
1610
1613
  });
1611
- return [
1612
- commandStartMarker(cmdPath),
1613
- renderedSection,
1614
- commandEndMarker(cmdPath)
1615
- ].join("\n");
1616
1614
  }
1617
1615
  /**
1618
1616
  * Generate markdown for a file containing multiple commands
@@ -1701,12 +1699,13 @@ async function generateDoc(config) {
1701
1699
  for (const targetCommand of fileTargetCommands) {
1702
1700
  const rawSection = generateCommandSection(targetCommand, allCommands, render, filePath, fileMap);
1703
1701
  if (!rawSection) throw new Error(`Target command "${targetCommand}" not found in commands`);
1704
- const header = targetCommand === "" && fileConfig ? generateFileHeader(fileConfig) : null;
1705
- const generatedSection = await applyFormatter(header ? `${header}\n${rawSection}` : rawSection, formatter);
1702
+ const generatedSection = await applyFormatter(rawSection, formatter);
1706
1703
  if (!existingContent) {
1707
1704
  if (updateMode) {
1708
- writeFile(filePath, generatedSection);
1709
- existingContent = generatedSection;
1705
+ const header = targetCommand === "" && fileConfig ? generateFileHeader(fileConfig) : null;
1706
+ const fullContent = header ? `${header}\n${generatedSection}` : generatedSection;
1707
+ writeFile(filePath, fullContent);
1708
+ existingContent = fullContent;
1710
1709
  fileStatus = "created";
1711
1710
  } else {
1712
1711
  hasError = true;
@@ -1715,32 +1714,35 @@ async function generateDoc(config) {
1715
1714
  }
1716
1715
  continue;
1717
1716
  }
1718
- const existingSection = extractCommandSection(existingContent, targetCommand);
1719
- const generatedSectionOnly = extractCommandSection(generatedSection, targetCommand);
1720
- if (!generatedSectionOnly) throw new Error(`Generated content does not contain section for command "${targetCommand}"`);
1721
- if (!existingSection) {
1717
+ const existingMarkers = collectSectionMarkers(existingContent, targetCommand);
1718
+ if (existingMarkers.length === 0) {
1722
1719
  if (updateMode) {
1723
- existingContent = insertCommandSection(existingContent, targetCommand, generatedSectionOnly, specifiedCommands);
1720
+ existingContent = insertCommandSections(existingContent, targetCommand, generatedSection, specifiedCommands);
1724
1721
  writeFile(filePath, existingContent);
1725
1722
  if (fileStatus !== "created") fileStatus = "updated";
1726
1723
  } else {
1727
1724
  hasError = true;
1728
1725
  fileStatus = "diff";
1729
- diffs.push(`Existing file does not contain section for command "${targetCommand}"`);
1726
+ diffs.push(`Existing file does not contain section markers for command "${targetCommand}"`);
1730
1727
  }
1731
1728
  continue;
1732
1729
  }
1733
- if (existingSection !== generatedSectionOnly) if (updateMode) {
1734
- const updatedContent = replaceCommandSection(existingContent, targetCommand, generatedSectionOnly);
1735
- if (updatedContent) {
1736
- existingContent = updatedContent;
1737
- writeFile(filePath, existingContent);
1738
- if (fileStatus !== "created") fileStatus = "updated";
1739
- } else throw new Error(`Failed to replace section for command "${targetCommand}"`);
1740
- } else {
1741
- hasError = true;
1742
- fileStatus = "diff";
1743
- diffs.push(formatDiff(existingSection, generatedSectionOnly));
1730
+ for (const sectionType of existingMarkers) {
1731
+ const existingSection = extractSectionMarker(existingContent, sectionType, targetCommand);
1732
+ const generatedSectionPart = extractSectionMarker(generatedSection, sectionType, targetCommand);
1733
+ if (!existingSection || !generatedSectionPart) continue;
1734
+ if (existingSection !== generatedSectionPart) if (updateMode) {
1735
+ const updated = replaceSectionMarker(existingContent, sectionType, targetCommand, generatedSectionPart);
1736
+ if (updated) {
1737
+ existingContent = updated;
1738
+ writeFile(filePath, existingContent);
1739
+ if (fileStatus !== "created") fileStatus = "updated";
1740
+ } else throw new Error(`Failed to replace ${sectionType} section for command "${targetCommand}"`);
1741
+ } else {
1742
+ hasError = true;
1743
+ fileStatus = "diff";
1744
+ diffs.push(formatDiff(existingSection, generatedSectionPart));
1745
+ }
1744
1746
  }
1745
1747
  }
1746
1748
  } else {
@@ -1782,10 +1784,10 @@ async function generateDoc(config) {
1782
1784
  if (headerResult.diff) rootDocDiffs.push(headerResult.diff);
1783
1785
  if (headerResult.hasError) hasError = true;
1784
1786
  if (headerResult.wasUpdated) markerUpdated = true;
1785
- const unexpectedCommandMarkers = Array.from(new Set(collectCommandMarkerPaths(content)));
1786
- if (unexpectedCommandMarkers.length > 0) {
1787
+ const unexpectedSectionPaths = Array.from(new Set(collectSectionMarkerPaths(content)));
1788
+ if (unexpectedSectionPaths.length > 0) {
1787
1789
  hasError = true;
1788
- rootDocDiffs.push(`Found unexpected command marker sections in rootDoc: ${unexpectedCommandMarkers.map((commandPath) => `"${formatCommandPath(commandPath)}"`).join(", ")}.`);
1790
+ rootDocDiffs.push(`Found unexpected section markers in rootDoc: ${unexpectedSectionPaths.map((commandPath) => `"${formatCommandPath(commandPath)}"`).join(", ")}.`);
1789
1791
  }
1790
1792
  const normalizedGlobalOptions = normalizeGlobalOptions(rootDoc.globalOptions);
1791
1793
  if (normalizedGlobalOptions) {
@@ -1796,7 +1798,8 @@ async function generateDoc(config) {
1796
1798
  if (globalOptionsResult.wasUpdated) markerUpdated = true;
1797
1799
  }
1798
1800
  const derivedCategories = deriveIndexFromFiles(files, rootDocFilePath, allCommands, ignores);
1799
- const indexResult = await processIndexMarker(content, derivedCategories, command, updateMode, formatter, rootDoc.index);
1801
+ const indexScope = node_path.relative(process.cwd(), rootDocFilePath);
1802
+ const indexResult = await processIndexMarker(content, derivedCategories, command, indexScope, updateMode, formatter, rootDoc.index);
1800
1803
  content = indexResult.content;
1801
1804
  rootDocDiffs.push(...indexResult.diffs);
1802
1805
  if (indexResult.hasError) hasError = true;
@@ -1848,15 +1851,14 @@ function initDocFile(config, fileSystem) {
1848
1851
  }
1849
1852
 
1850
1853
  //#endregion
1851
- exports.COMMAND_MARKER_PREFIX = COMMAND_MARKER_PREFIX;
1852
1854
  exports.GLOBAL_OPTIONS_MARKER_PREFIX = GLOBAL_OPTIONS_MARKER_PREFIX;
1853
1855
  exports.INDEX_MARKER_PREFIX = INDEX_MARKER_PREFIX;
1856
+ exports.SECTION_MARKER_PREFIX = SECTION_MARKER_PREFIX;
1857
+ exports.SECTION_TYPES = SECTION_TYPES;
1854
1858
  exports.UPDATE_GOLDEN_ENV = UPDATE_GOLDEN_ENV;
1855
1859
  exports.assertDocMatch = assertDocMatch;
1856
1860
  exports.buildCommandInfo = buildCommandInfo;
1857
1861
  exports.collectAllCommands = collectAllCommands;
1858
- exports.commandEndMarker = commandEndMarker;
1859
- exports.commandStartMarker = commandStartMarker;
1860
1862
  exports.compareWithExisting = compareWithExisting;
1861
1863
  exports.createCommandRenderer = createCommandRenderer;
1862
1864
  exports.defaultRenderers = defaultRenderers;
@@ -1883,5 +1885,7 @@ exports.renderSubcommandsTable = renderSubcommandsTable;
1883
1885
  exports.renderSubcommandsTableFromArray = renderSubcommandsTableFromArray;
1884
1886
  exports.renderUsage = renderUsage;
1885
1887
  exports.resolveLazyCommand = require_subcommand_router.resolveLazyCommand;
1888
+ exports.sectionEndMarker = sectionEndMarker;
1889
+ exports.sectionStartMarker = sectionStartMarker;
1886
1890
  exports.writeFile = writeFile;
1887
1891
  //# sourceMappingURL=index.cjs.map