@redpanda-data/docs-extensions-and-macros 4.12.5 → 4.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.adoc +33 -1064
  2. package/bin/doc-tools-mcp.js +720 -0
  3. package/bin/doc-tools.js +1050 -50
  4. package/bin/mcp-tools/antora.js +153 -0
  5. package/bin/mcp-tools/cache.js +89 -0
  6. package/bin/mcp-tools/cloud-regions.js +127 -0
  7. package/bin/mcp-tools/content-review.js +196 -0
  8. package/bin/mcp-tools/crd-docs.js +153 -0
  9. package/bin/mcp-tools/frontmatter.js +138 -0
  10. package/bin/mcp-tools/generated-docs-review.js +887 -0
  11. package/bin/mcp-tools/helm-docs.js +152 -0
  12. package/bin/mcp-tools/index.js +245 -0
  13. package/bin/mcp-tools/job-queue.js +468 -0
  14. package/bin/mcp-tools/mcp-validation.js +266 -0
  15. package/bin/mcp-tools/metrics-docs.js +146 -0
  16. package/bin/mcp-tools/openapi.js +174 -0
  17. package/bin/mcp-tools/prompt-discovery.js +283 -0
  18. package/bin/mcp-tools/property-docs.js +157 -0
  19. package/bin/mcp-tools/rpcn-docs.js +113 -0
  20. package/bin/mcp-tools/rpk-docs.js +141 -0
  21. package/bin/mcp-tools/telemetry.js +211 -0
  22. package/bin/mcp-tools/utils.js +131 -0
  23. package/bin/mcp-tools/versions.js +168 -0
  24. package/cli-utils/convert-doc-links.js +1 -1
  25. package/cli-utils/github-token.js +58 -0
  26. package/cli-utils/self-managed-docs-branch.js +2 -2
  27. package/cli-utils/setup-mcp.js +313 -0
  28. package/docker-compose/25.1/transactions.md +1 -1
  29. package/docker-compose/transactions.md +1 -1
  30. package/extensions/DEVELOPMENT.adoc +464 -0
  31. package/extensions/README.adoc +124 -0
  32. package/extensions/REFERENCE.adoc +768 -0
  33. package/extensions/USER_GUIDE.adoc +339 -0
  34. package/extensions/generate-rp-connect-info.js +3 -4
  35. package/extensions/version-fetcher/get-latest-console-version.js +38 -27
  36. package/extensions/version-fetcher/get-latest-redpanda-helm-version-from-operator.js +1 -1
  37. package/extensions/version-fetcher/get-latest-redpanda-version.js +65 -54
  38. package/extensions/version-fetcher/retry-util.js +88 -0
  39. package/extensions/version-fetcher/set-latest-version.js +6 -3
  40. package/macros/DEVELOPMENT.adoc +377 -0
  41. package/macros/README.adoc +105 -0
  42. package/macros/REFERENCE.adoc +222 -0
  43. package/macros/USER_GUIDE.adoc +220 -0
  44. package/macros/rp-connect-components.js +6 -6
  45. package/package.json +12 -3
  46. package/tools/bundle-openapi.js +20 -10
  47. package/tools/cloud-regions/generate-cloud-regions.js +1 -1
  48. package/tools/fetch-from-github.js +18 -4
  49. package/tools/gen-rpk-ascii.py +3 -1
  50. package/tools/generate-cli-docs.js +325 -0
  51. package/tools/get-console-version.js +4 -2
  52. package/tools/get-redpanda-version.js +4 -2
  53. package/tools/metrics/metrics.py +19 -7
  54. package/tools/property-extractor/Makefile +7 -1
  55. package/tools/property-extractor/cloud_config.py +4 -4
  56. package/tools/property-extractor/constant_resolver.py +11 -11
  57. package/tools/property-extractor/property_extractor.py +18 -16
  58. package/tools/property-extractor/topic_property_extractor.py +2 -2
  59. package/tools/property-extractor/transformers.py +7 -7
  60. package/tools/property-extractor/type_definition_extractor.py +4 -4
  61. package/tools/redpanda-connect/README.adoc +1 -1
  62. package/tools/redpanda-connect/generate-rpcn-connector-docs.js +5 -3
package/bin/doc-tools.js CHANGED
@@ -57,7 +57,7 @@ function fail(msg) {
57
57
  *
58
58
  * Attempts to execute the tool with a version flag to verify its presence. If the tool is missing or fails to run, the process exits with an error message and optional installation hint.
59
59
  *
60
- * @param {string} cmd - The name of the tool to check (e.g., 'docker', 'helm-docs').
60
+ * @param {string} cmd - The name of the tool to check (for example, 'docker', 'helm-docs').
61
61
  * @param {object} [opts] - Optional settings.
62
62
  * @param {string} [opts.versionFlag='--version'] - The flag used to test the tool's execution.
63
63
  * @param {string} [opts.help] - An optional hint or installation instruction shown on failure.
@@ -110,7 +110,12 @@ function requirePython(minMajor = 3, minMinor = 10) {
110
110
  }
111
111
  fail(
112
112
  `Python ${minMajor}.${minMinor}+ not found or too old.
113
- → Install from your package manager or https://python.org`
113
+
114
+ **Quick Install (Recommended):**
115
+ Run the automated installer to set up all dependencies:
116
+ npm run install-test-dependencies
117
+
118
+ Or install manually from your package manager or https://python.org`
114
119
  );
115
120
  }
116
121
 
@@ -124,7 +129,13 @@ function requireDockerDaemon() {
124
129
  try {
125
130
  execSync('docker info', { stdio: 'ignore' });
126
131
  } catch {
127
- fail('Docker daemon does not appear to be running. Please start Docker.');
132
+ fail(`Docker daemon does not appear to be running.
133
+
134
+ **Quick Install (Recommended):**
135
+ Run the automated installer to set up all dependencies:
136
+ npm run install-test-dependencies
137
+
138
+ Or install and start Docker manually: https://docs.docker.com/get-docker/`);
128
139
  }
129
140
  }
130
141
 
@@ -143,7 +154,11 @@ function verifyCrdDependencies() {
143
154
  `
144
155
  The 'crd-ref-docs' command is required but was not found.
145
156
 
146
- To install it, follow these steps (for macOS):
157
+ **Quick Install (Recommended):**
158
+ Run the automated installer to set up all dependencies:
159
+ npm run install-test-dependencies
160
+
161
+ Or install manually (for macOS):
147
162
 
148
163
  1. Determine your architecture:
149
164
  Run: \`uname -m\`
@@ -170,7 +185,11 @@ For more details, visit: https://github.com/elastic/crd-ref-docs
170
185
  `
171
186
  The 'go' command (Golang) is required but was not found.
172
187
 
173
- To install it on macOS:
188
+ **Quick Install (Recommended):**
189
+ Run the automated installer to set up all dependencies:
190
+ npm run install-test-dependencies
191
+
192
+ Or install manually on macOS:
174
193
 
175
194
  Option 1: Install via Homebrew (recommended):
176
195
  brew install go
@@ -200,7 +219,11 @@ function verifyHelmDependencies() {
200
219
  `
201
220
  The 'helm-docs' command is required but was not found.
202
221
 
203
- To install it, follow these steps (for macOS):
222
+ **Quick Install (Recommended):**
223
+ Run the automated installer to set up all dependencies:
224
+ npm run install-test-dependencies
225
+
226
+ Or install manually (for macOS):
204
227
 
205
228
  1. Determine your architecture:
206
229
  Run: \`uname -m\`
@@ -240,7 +263,7 @@ function verifyPropertyDependencies() {
240
263
  requirePython();
241
264
 
242
265
  // Check for Node.js (required for Handlebars templates)
243
- requireCmd('node', 'https://nodejs.org/en/download/ or use your package manager (e.g., brew install node)');
266
+ requireCmd('node', 'https://nodejs.org/en/download/ or use your package manager (for example, brew install node)');
244
267
  requireCmd('npm', 'Usually installed with Node.js');
245
268
 
246
269
  // Check for C++ compiler
@@ -255,6 +278,12 @@ function verifyPropertyDependencies() {
255
278
  } catch {
256
279
  fail(`A C++ compiler (gcc or clang) is required for tree-sitter compilation.
257
280
 
281
+ **Quick Install (Recommended):**
282
+ Run the automated installer to set up all dependencies:
283
+ npm run install-test-dependencies
284
+
285
+ Or install manually:
286
+
258
287
  On macOS, install Xcode Command Line Tools:
259
288
  xcode-select --install
260
289
 
@@ -296,6 +325,12 @@ After installation, verify with:
296
325
  }
297
326
  fail(`C++ standard library headers are missing or incomplete.
298
327
 
328
+ **Quick Install (Recommended):**
329
+ Run the automated installer to set up all dependencies:
330
+ npm run install-test-dependencies
331
+
332
+ Or fix manually:
333
+
299
334
  1. **Test if the issue exists**:
300
335
  echo '#include <functional>' | ${compileCmd} -x c++ -fsyntax-only -
301
336
 
@@ -342,6 +377,32 @@ programCli
342
377
  .version(pkg.version);
343
378
 
344
379
  // Top-level commands.
380
+
381
+ /**
382
+ * install-test-dependencies
383
+ *
384
+ * @description
385
+ * Installs all packages and dependencies required for documentation testing workflows.
386
+ * This includes Redpanda Docker images, Python virtual environments for property extraction,
387
+ * and other test dependencies.
388
+ *
389
+ * @why
390
+ * Setting up a documentation environment requires multiple dependencies across different
391
+ * package managers (npm, pip, Docker). This command automates the entire setup process.
392
+ *
393
+ * @example
394
+ * # Set up a new documentation environment
395
+ * npx doc-tools install-test-dependencies
396
+ *
397
+ * # Use in CI/CD before running doc tests
398
+ * - run: npx doc-tools install-test-dependencies
399
+ * - run: npm test
400
+ *
401
+ * @requirements
402
+ * - Node.js and npm
403
+ * - Python 3.9 or higher
404
+ * - Docker (for some dependencies)
405
+ */
345
406
  programCli
346
407
  .command('install-test-dependencies')
347
408
  .description('Install packages for doc test workflows')
@@ -351,6 +412,41 @@ programCli
351
412
  process.exit(result.status);
352
413
  });
353
414
 
415
+ /**
416
+ * get-redpanda-version
417
+ *
418
+ * @description
419
+ * Fetches the latest Redpanda version from GitHub releases. Can retrieve either stable
420
+ * releases or beta/RC versions. Returns the version in format "v25.3.1" which can be
421
+ * used directly with other doc-tools commands.
422
+ *
423
+ * @why
424
+ * Documentation must reference the correct current version. This command ensures version
425
+ * numbers are accurate and can be used in CI/CD pipelines or before generating
426
+ * version-specific documentation. The version is fetched from GitHub releases, which is
427
+ * the source of truth for Redpanda releases.
428
+ *
429
+ * @example
430
+ * # Get latest stable version
431
+ * npx doc-tools get-redpanda-version
432
+ * # Output: v25.3.1
433
+ *
434
+ * # Get latest beta/RC version
435
+ * npx doc-tools get-redpanda-version --beta
436
+ * # Output: v26.1.1-rc1
437
+ *
438
+ * # Auto-detect from antora.yml prerelease flag
439
+ * cd docs-site
440
+ * npx doc-tools get-redpanda-version --from-antora
441
+ *
442
+ * # Use in CI/CD or scripts
443
+ * VERSION=$(npx doc-tools get-redpanda-version)
444
+ * npx doc-tools generate property-docs --tag $VERSION
445
+ *
446
+ * @requirements
447
+ * - Internet connection to access GitHub API
448
+ * - GitHub API rate limits apply (60 requests/hour unauthenticated)
449
+ */
354
450
  programCli
355
451
  .command('get-redpanda-version')
356
452
  .description('Print the latest Redpanda version')
@@ -365,6 +461,40 @@ programCli
365
461
  }
366
462
  });
367
463
 
464
+ /**
465
+ * get-console-version
466
+ *
467
+ * @description
468
+ * Fetches the latest Redpanda Console version from GitHub releases. Can retrieve either
469
+ * stable releases or beta versions. Returns the version in format "v2.7.2" which can be
470
+ * used for documentation references and Docker image tags.
471
+ *
472
+ * @why
473
+ * Console is released separately from Redpanda core. This command keeps Console
474
+ * documentation in sync with releases and provides the correct version for Docker
475
+ * Compose files and deployment documentation.
476
+ *
477
+ * @example
478
+ * # Get latest stable Console version
479
+ * npx doc-tools get-console-version
480
+ * # Output: v2.7.2
481
+ *
482
+ * # Get latest beta version
483
+ * npx doc-tools get-console-version --beta
484
+ * # Output: v2.8.0-beta1
485
+ *
486
+ * # Auto-detect from antora.yml prerelease flag
487
+ * cd docs-site
488
+ * npx doc-tools get-console-version --from-antora
489
+ *
490
+ * # Use in Docker Compose documentation
491
+ * CONSOLE_VERSION=$(npx doc-tools get-console-version)
492
+ * echo "image: redpandadata/console:$CONSOLE_VERSION"
493
+ *
494
+ * @requirements
495
+ * - Internet connection to access GitHub API
496
+ * - GitHub API rate limits apply (60 requests/hour unauthenticated)
497
+ */
368
498
  programCli
369
499
  .command('get-console-version')
370
500
  .description('Print the latest Console version')
@@ -379,6 +509,40 @@ programCli
379
509
  }
380
510
  });
381
511
 
512
+ /**
513
+ * link-readme
514
+ *
515
+ * @description
516
+ * Creates a symbolic link from a project's README.adoc file into the Antora documentation
517
+ * structure. This allows project README files to be included in the documentation site
518
+ * without duplication. The command creates the necessary directory structure and establishes
519
+ * a symlink in docs/modules/<module>/pages/ that points to the project's README.adoc.
520
+ *
521
+ * @why
522
+ * Documentation repositories often contain multiple sub-projects (like labs or examples)
523
+ * that have their own README files. Rather than manually copying these files into the
524
+ * Antora structure (which creates maintenance burden), symlinks keep the content in one
525
+ * place while making it available to Antora. Changes to the project README automatically
526
+ * appear in the docs site.
527
+ *
528
+ * @example
529
+ * # Link a lab project README into documentation
530
+ * npx doc-tools link-readme \\
531
+ * --subdir labs/docker-compose \\
532
+ * --target docker-compose-lab.adoc
533
+ *
534
+ * # Link multiple lab READMEs
535
+ * npx doc-tools link-readme -s labs/kubernetes -t k8s-lab.adoc
536
+ * npx doc-tools link-readme -s labs/terraform -t terraform-lab.adoc
537
+ *
538
+ * # The symlink structure created:
539
+ * # docs/modules/labs/pages/docker-compose-lab.adoc -> ../../../../labs/docker-compose/README.adoc
540
+ *
541
+ * @requirements
542
+ * - Must run from repository root
543
+ * - Target project must have README.adoc file
544
+ * - Operating system must support symbolic links
545
+ */
382
546
  programCli
383
547
  .command('link-readme')
384
548
  .description('Symlink a README.adoc into docs/modules/<module>/pages/')
@@ -419,6 +583,49 @@ programCli
419
583
  }
420
584
  });
421
585
 
586
+ /**
587
+ * fetch
588
+ *
589
+ * @description
590
+ * Downloads specific files or directories from GitHub repositories without cloning the entire
591
+ * repository. Uses the GitHub API to fetch content and saves it to a local directory. Useful
592
+ * for grabbing examples, configuration files, or documentation snippets from other repositories.
593
+ * Supports both individual files and entire directories.
594
+ *
595
+ * @why
596
+ * Documentation often needs to reference or include files from other repositories (examples,
597
+ * configuration templates, code samples). Cloning entire repositories is inefficient when you
598
+ * only need specific files. This command provides targeted fetching, saving bandwidth and time.
599
+ * It's particularly useful in CI/CD pipelines where you need specific assets without full clones.
600
+ *
601
+ * @example
602
+ * # Fetch a specific configuration file
603
+ * npx doc-tools fetch \\
604
+ * --owner redpanda-data \\
605
+ * --repo redpanda \\
606
+ * --remote-path docker/docker-compose.yml \\
607
+ * --save-dir examples/
608
+ *
609
+ * # Fetch an entire directory of examples
610
+ * npx doc-tools fetch \\
611
+ * -o redpanda-data \\
612
+ * -r connect-examples \\
613
+ * -p pipelines/mongodb \\
614
+ * -d docs/modules/examples/attachments/
615
+ *
616
+ * # Fetch with custom filename
617
+ * npx doc-tools fetch \\
618
+ * -o redpanda-data \\
619
+ * -r helm-charts \\
620
+ * -p charts/redpanda/values.yaml \\
621
+ * -d examples/ \\
622
+ * --filename redpanda-values-example.yaml
623
+ *
624
+ * @requirements
625
+ * - Internet connection to access GitHub API
626
+ * - GitHub API rate limits apply (60 requests/hour unauthenticated, 5000 with token)
627
+ * - For private repositories: GitHub token with repo permissions
628
+ */
422
629
  programCli
423
630
  .command('fetch')
424
631
  .description('Fetch a file or directory from GitHub and save it locally')
@@ -443,6 +650,83 @@ programCli
443
650
  }
444
651
  });
445
652
 
653
+ /**
654
+ * setup-mcp
655
+ *
656
+ * @description
657
+ * Configures the Redpanda Docs MCP (Model Context Protocol) server for Claude Code or
658
+ * Claude Desktop. Automatically detects the installed application, updates the appropriate
659
+ * configuration file, and enables Claude to use doc-tools commands through natural conversation.
660
+ * Supports both production (npm package) and local development modes.
661
+ *
662
+ * @why
663
+ * Manual MCP configuration requires editing JSON configuration files in the correct location
664
+ * with the correct schema. This command handles all setup automatically, including path
665
+ * detection, configuration merging, and validation. It enables AI-assisted documentation
666
+ * workflows where writers can use natural language to run doc-tools commands.
667
+ *
668
+ * @example
669
+ * # Auto-detect and configure for Claude Code or Desktop
670
+ * npx doc-tools setup-mcp
671
+ *
672
+ * # Configure for local development (run from this repository)
673
+ * cd /path/to/docs-extensions-and-macros
674
+ * npx doc-tools setup-mcp --local
675
+ *
676
+ * # Force update existing configuration
677
+ * npx doc-tools setup-mcp --force
678
+ *
679
+ * # Target specific application
680
+ * npx doc-tools setup-mcp --target code
681
+ * npx doc-tools setup-mcp --target desktop
682
+ *
683
+ * # Check current configuration status
684
+ * npx doc-tools setup-mcp --status
685
+ *
686
+ * # After setup, restart Claude Code and use natural language
687
+ * "What's the latest Redpanda version?"
688
+ * "Generate property docs for v25.3.1"
689
+ *
690
+ * @requirements
691
+ * - Claude Code or Claude Desktop must be installed
692
+ * - For --local mode: must run from docs-extensions-and-macros repository
693
+ * - After setup: restart Claude Code/Desktop to load the MCP server
694
+ */
695
+ programCli
696
+ .command('setup-mcp')
697
+ .description('Configure the Redpanda Docs MCP server for Claude Code/Desktop')
698
+ .option('--force', 'Force update even if already configured', false)
699
+ .option('--target <type>', 'Target application: auto, code, or desktop', 'auto')
700
+ .option('--local', 'Use local development mode (requires running from this repo)', false)
701
+ .option('--status', 'Show current MCP server configuration status', false)
702
+ .action(async (options) => {
703
+ try {
704
+ const { setupMCP, showStatus, printNextSteps } = require('../cli-utils/setup-mcp.js');
705
+
706
+ if (options.status) {
707
+ showStatus();
708
+ return;
709
+ }
710
+
711
+ const result = await setupMCP({
712
+ force: options.force,
713
+ target: options.target,
714
+ local: options.local
715
+ });
716
+
717
+ if (result.success) {
718
+ printNextSteps(result);
719
+ process.exit(0);
720
+ } else {
721
+ console.error(`❌ Setup failed: ${result.error}`);
722
+ process.exit(1);
723
+ }
724
+ } catch (err) {
725
+ console.error(`❌ ${err.message}`);
726
+ process.exit(1);
727
+ }
728
+ });
729
+
446
730
  // Create an "automation" subcommand group.
447
731
  const automation = new Command('generate').description('Run docs automations');
448
732
 
@@ -465,7 +749,7 @@ const commonOptions = {
465
749
  * process; if the script exits with a non-zero status, this function will terminate
466
750
  * the Node.js process with that status code.
467
751
  *
468
- * @param {string} mode - Operation mode passed to the script (e.g., "generate" or "clean").
752
+ * @param {string} mode - Operation mode passed to the script (for example, "generate" or "clean").
469
753
  * @param {string} tag - Release tag or version to generate docs for.
470
754
  * @param {Object} options - Runtime options.
471
755
  * @param {string} options.dockerRepo - Docker repository used by the script.
@@ -592,7 +876,7 @@ function generatePropertyComparisonReport(oldTag, newTag, outputDir) {
592
876
  * provided temporary directories. On missing inputs or if the diff subprocess
593
877
  * fails to spawn, the process exits with a non-zero status.
594
878
  *
595
- * @param {string} kind - Logical category for the diff (e.g., "metrics" or "rpk"); used in the output path.
879
+ * @param {string} kind - Logical category for the diff (for example, "metrics" or "rpk"); used in the output path.
596
880
  * @param {string} oldTag - Identifier for the "old" version (used in the output path).
597
881
  * @param {string} newTag - Identifier for the "new" version (used in the output path).
598
882
  * @param {string} oldTempDir - Path to the existing temporary directory containing the old output; must exist.
@@ -662,10 +946,52 @@ function diffDirs(kind, oldTag, newTag, oldTempDir, newTempDir) {
662
946
  }
663
947
  }
664
948
 
949
+ /**
950
+ * generate metrics-docs
951
+ *
952
+ * @description
953
+ * Generates comprehensive metrics reference documentation by running Redpanda in Docker and
954
+ * scraping the `/public_metrics` Prometheus endpoint. Starts a Redpanda cluster with the
955
+ * specified version, waits for it to be ready, collects all exposed metrics, parses the
956
+ * Prometheus format, and generates categorized AsciiDoc documentation. Optionally compares
957
+ * metrics between versions to identify new, removed, or changed metrics.
958
+ *
959
+ * @why
960
+ * Redpanda exposes hundreds of metrics for monitoring and observability. Manual documentation
961
+ * of metrics is error-prone and becomes outdated as new metrics are added or existing ones
962
+ * change. This automation ensures metrics documentation accurately reflects what Redpanda
963
+ * actually exports at each version. Running Redpanda in Docker and scraping metrics directly
964
+ * is the only reliable way to capture the complete and accurate metrics set.
965
+ *
966
+ * @example
967
+ * # Basic: Generate metrics docs for a specific version
968
+ * npx doc-tools generate metrics-docs --tag v25.3.1
969
+ *
970
+ * # Compare metrics between versions to see what changed
971
+ * npx doc-tools generate metrics-docs \\
972
+ * --tag v25.3.1 \\
973
+ * --diff v25.2.1
974
+ *
975
+ * # Use custom Docker repository
976
+ * npx doc-tools generate metrics-docs \\
977
+ * --tag v25.3.1 \\
978
+ * --docker-repo docker.redpanda.com/redpandadata/redpanda
979
+ *
980
+ * # Full workflow: document new release
981
+ * VERSION=$(npx doc-tools get-redpanda-version)
982
+ * npx doc-tools generate metrics-docs --tag $VERSION
983
+ *
984
+ * @requirements
985
+ * - Docker must be installed and running
986
+ * - Port 9644 must be available (Redpanda metrics endpoint)
987
+ * - Sufficient disk space for Docker image
988
+ * - Internet connection to pull Docker images
989
+ */
665
990
  automation
666
991
  .command('metrics-docs')
667
- .description('Generate JSON and AsciiDoc documentation for Redpanda metrics')
668
- .requiredOption('-t, --tag <tag>', 'Redpanda version to use when starting Redpanda in Docker')
992
+ .description('Generate JSON and AsciiDoc documentation for Redpanda metrics. Defaults to branch "dev" if neither --tag nor --branch is specified.')
993
+ .option('-t, --tag <tag>', 'Git tag for released content (GA/beta)')
994
+ .option('-b, --branch <branch>', 'Branch name for in-progress content')
669
995
  .option(
670
996
  '--docker-repo <repo>',
671
997
  'Docker repository to use when starting Redpanda in Docker',
@@ -685,7 +1011,14 @@ automation
685
1011
  .action((options) => {
686
1012
  verifyMetricsDependencies();
687
1013
 
688
- const newTag = options.tag;
1014
+ // Validate that tag and branch are mutually exclusive
1015
+ if (options.tag && options.branch) {
1016
+ console.error('❌ Error: Cannot specify both --tag and --branch');
1017
+ process.exit(1);
1018
+ }
1019
+
1020
+ // Default to 'dev' branch if neither specified
1021
+ const newTag = options.tag || options.branch || 'dev';
689
1022
  const oldTag = options.diff;
690
1023
 
691
1024
  if (oldTag) {
@@ -706,6 +1039,48 @@ automation
706
1039
  process.exit(0);
707
1040
  });
708
1041
 
1042
+ /**
1043
+ * generate rpcn-connector-docs
1044
+ *
1045
+ * @description
1046
+ * Generates complete reference documentation for Redpanda Connect (formerly Benthos) connectors,
1047
+ * processors, and components. Clones the Redpanda Connect repository, parses component templates
1048
+ * and configuration schemas embedded in Go code, reads connector metadata from CSV, and generates
1049
+ * AsciiDoc documentation for each component. Supports diffing changes between versions and
1050
+ * automatically updating what's new documentation. Can also generate Bloblang function documentation.
1051
+ *
1052
+ * @why
1053
+ * Redpanda Connect has hundreds of connectors (inputs, outputs, processors) with complex
1054
+ * configuration schemas. Each component's documentation lives in its Go source code as struct
1055
+ * tags and comments. Manual documentation is impossible to maintain. This automation extracts
1056
+ * documentation directly from code, ensuring accuracy and completeness. The diff capability
1057
+ * automatically identifies new connectors and changed configurations for release notes.
1058
+ *
1059
+ * @example
1060
+ * # Basic: Generate all connector docs
1061
+ * npx doc-tools generate rpcn-connector-docs
1062
+ *
1063
+ * # Generate docs and automatically update what's new page
1064
+ * npx doc-tools generate rpcn-connector-docs --update-whats-new
1065
+ *
1066
+ * # Include Bloblang function documentation
1067
+ * npx doc-tools generate rpcn-connector-docs --include-bloblang
1068
+ *
1069
+ * # Generate with custom metadata CSV
1070
+ * npx doc-tools generate rpcn-connector-docs \\
1071
+ * --csv custom/connector-metadata.csv
1072
+ *
1073
+ * # Full workflow with diff and what's new update
1074
+ * npx doc-tools generate rpcn-connector-docs \\
1075
+ * --update-whats-new \\
1076
+ * --include-bloblang
1077
+ *
1078
+ * @requirements
1079
+ * - Git to clone Redpanda Connect repository
1080
+ * - Internet connection to clone repository
1081
+ * - Node.js for parsing and generation
1082
+ * - Sufficient disk space for repository clone (~500MB)
1083
+ */
709
1084
  automation
710
1085
  .command('rpcn-connector-docs')
711
1086
  .description('Generate RPCN connector docs and diff changes since the last version')
@@ -720,7 +1095,7 @@ automation
720
1095
  .option('--template-fields <path>', 'Fields section partial template', path.resolve(__dirname, '../tools/redpanda-connect/templates/fields-partials.hbs'))
721
1096
  .option('--template-examples <path>', 'Examples section partial template', path.resolve(__dirname, '../tools/redpanda-connect/templates/examples-partials.hbs'))
722
1097
  .option('--template-bloblang <path>', 'Custom Handlebars template for bloblang function/method partials')
723
- .option('--overrides <path>', 'Optional JSON file with overrides')
1098
+ .option('--overrides <path>', 'Optional JSON file with overrides', 'docs-data/overrides.json')
724
1099
  .option('--include-bloblang', 'Include Bloblang functions and methods in generation')
725
1100
  .action(async (options) => {
726
1101
  requireTool('rpk', {
@@ -1477,16 +1852,72 @@ automation
1477
1852
  process.exit(0);
1478
1853
  });
1479
1854
 
1855
+ /**
1856
+ * generate property-docs
1857
+ *
1858
+ * @description
1859
+ * Generates comprehensive reference documentation for Redpanda cluster and topic configuration
1860
+ * properties. Clones the Redpanda repository at a specified version, runs a Python extractor
1861
+ * to parse C++ configuration code, and outputs JSON data files with all property metadata
1862
+ * (descriptions, types, defaults, constraints). Optionally generates consolidated AsciiDoc
1863
+ * partials for direct inclusion in documentation sites.
1864
+ *
1865
+ * @why
1866
+ * Property definitions in the C++ source code are the single source of truth for Redpanda
1867
+ * configuration. Manual documentation becomes outdated quickly. This automation ensures docs
1868
+ * stay perfectly in sync with implementation by extracting properties directly from code,
1869
+ * including type information, default values, and constraints that would be error-prone to
1870
+ * maintain manually.
1871
+ *
1872
+ * @example
1873
+ * # Basic: Extract properties to JSON only (default)
1874
+ * npx doc-tools generate property-docs --tag v25.3.1
1875
+ *
1876
+ * # Generate AsciiDoc partials for documentation site
1877
+ * npx doc-tools generate property-docs --tag v25.3.1 --generate-partials
1878
+ *
1879
+ * # Include Cloud support tags (requires GitHub token)
1880
+ * export GITHUB_TOKEN=ghp_xxx
1881
+ * npx doc-tools generate property-docs \\
1882
+ * --tag v25.3.1 \\
1883
+ * --generate-partials \\
1884
+ * --cloud-support
1885
+ *
1886
+ * # Compare properties between versions
1887
+ * npx doc-tools generate property-docs \\
1888
+ * --tag v25.3.1 \\
1889
+ * --diff v25.2.1
1890
+ *
1891
+ * # Use custom output directory
1892
+ * npx doc-tools generate property-docs \\
1893
+ * --tag v25.3.1 \\
1894
+ * --output-dir docs/modules/reference
1895
+ *
1896
+ * # Full workflow: document new release
1897
+ * VERSION=$(npx doc-tools get-redpanda-version)
1898
+ * npx doc-tools generate property-docs \\
1899
+ * --tag $VERSION \\
1900
+ * --generate-partials \\
1901
+ * --cloud-support
1902
+ *
1903
+ * @requirements
1904
+ * - Python 3.9 or higher
1905
+ * - Git
1906
+ * - Internet connection to clone Redpanda repository
1907
+ * - For --cloud-support: GitHub token with repo permissions (GITHUB_TOKEN env var)
1908
+ * - For --cloud-support: Python packages pyyaml and requests
1909
+ */
1480
1910
  automation
1481
1911
  .command('property-docs')
1482
1912
  .description(
1483
1913
  'Generate JSON and consolidated AsciiDoc partials for Redpanda configuration properties. ' +
1484
1914
  'By default, only extracts properties to JSON. Use --generate-partials to create consolidated ' +
1485
- 'AsciiDoc partials (including deprecated properties).'
1915
+ 'AsciiDoc partials (including deprecated properties). Defaults to branch "dev" if neither --tag nor --branch is specified.'
1486
1916
  )
1487
- .option('--tag <tag>', 'Git tag or branch to extract from', 'dev')
1488
- .option('--diff <oldTag>', 'Also diff autogenerated properties from <oldTag> to <tag>')
1489
- .option('--overrides <path>', 'Optional JSON file with property description overrides')
1917
+ .option('-t, --tag <tag>', 'Git tag for released content (GA/beta)')
1918
+ .option('-b, --branch <branch>', 'Branch name for in-progress content')
1919
+ .option('--diff <oldTag>', 'Also diff autogenerated properties from <oldTag> to current tag/branch')
1920
+ .option('--overrides <path>', 'Optional JSON file with property description overrides', 'docs-data/property-overrides.json')
1490
1921
  .option('--output-dir <dir>', 'Where to write all generated files', 'modules/reference')
1491
1922
  .option('--cloud-support', 'Add AsciiDoc tags to generated property docs to indicate which ones are supported in Redpanda Cloud. This data is fetched from the cloudv2 repository so requires a GitHub token with repo permissions. Set the token as an environment variable using GITHUB_TOKEN, GH_TOKEN, or REDPANDA_GITHUB_TOKEN')
1492
1923
  .option('--template-property <path>', 'Custom Handlebars template for individual property sections')
@@ -1499,12 +1930,22 @@ automation
1499
1930
  .action((options) => {
1500
1931
  verifyPropertyDependencies();
1501
1932
 
1933
+ // Validate that tag and branch are mutually exclusive
1934
+ if (options.tag && options.branch) {
1935
+ console.error('❌ Error: Cannot specify both --tag and --branch');
1936
+ process.exit(1);
1937
+ }
1938
+
1939
+ // Default to 'dev' branch if neither specified
1940
+ const newTag = options.tag || options.branch || 'dev';
1941
+
1502
1942
  // Validate cloud support dependencies if requested
1503
1943
  if (options.cloudSupport) {
1504
1944
  console.log('🔍 Validating cloud support dependencies...');
1505
- const token = process.env.GITHUB_TOKEN || process.env.GH_TOKEN || process.env.REDPANDA_GITHUB_TOKEN;
1945
+ const { getGitHubToken } = require('../cli-utils/github-token');
1946
+ const token = getGitHubToken();
1506
1947
  if (!token) {
1507
- console.error('❌ Cloud support requires GITHUB_TOKEN, GH_TOKEN, or REDPANDA_GITHUB_TOKEN environment variable');
1948
+ console.error('❌ Cloud support requires a GitHub token');
1508
1949
  console.error(' Set up GitHub token:');
1509
1950
  console.error(' 1. Go to https://github.com/settings/tokens');
1510
1951
  console.error(' 2. Generate token with "repo" scope');
@@ -1520,8 +1961,6 @@ automation
1520
1961
  console.log(' Required packages: pyyaml, requests');
1521
1962
  console.log('✅ GitHub token validated');
1522
1963
  }
1523
-
1524
- const newTag = options.tag;
1525
1964
  let oldTag = options.diff;
1526
1965
  const overridesPath = options.overrides;
1527
1966
  const outputDir = options.outputDir;
@@ -1611,10 +2050,51 @@ automation
1611
2050
  process.exit(0);
1612
2051
  });
1613
2052
 
2053
+ /**
2054
+ * generate rpk-docs
2055
+ *
2056
+ * @description
2057
+ * Generates comprehensive CLI reference documentation for RPK (Redpanda Keeper), the official
2058
+ * Redpanda command-line tool. Starts Redpanda in Docker (RPK is bundled with Redpanda), executes
2059
+ * `rpk --help` for all commands and subcommands recursively, parses the help output, and generates
2060
+ * structured AsciiDoc documentation for each command with usage, flags, and descriptions.
2061
+ * Optionally compares RPK commands between versions to identify new or changed commands.
2062
+ *
2063
+ * @why
2064
+ * RPK has dozens of commands and subcommands with complex flags and options. The built-in help
2065
+ * text is the source of truth for RPK's CLI interface. Manual documentation becomes outdated as
2066
+ * RPK evolves. This automation extracts documentation directly from RPK's help output, ensuring
2067
+ * accuracy. Running RPK from Docker guarantees the exact version being documented, and diffing
2068
+ * between versions automatically highlights CLI changes for release notes.
2069
+ *
2070
+ * @example
2071
+ * # Basic: Generate RPK docs for a specific version
2072
+ * npx doc-tools generate rpk-docs --tag v25.3.1
2073
+ *
2074
+ * # Compare RPK commands between versions
2075
+ * npx doc-tools generate rpk-docs \\
2076
+ * --tag v25.3.1 \\
2077
+ * --diff v25.2.1
2078
+ *
2079
+ * # Use custom Docker repository
2080
+ * npx doc-tools generate rpk-docs \\
2081
+ * --tag v25.3.1 \\
2082
+ * --docker-repo docker.redpanda.com/redpandadata/redpanda
2083
+ *
2084
+ * # Full workflow: document new release
2085
+ * VERSION=$(npx doc-tools get-redpanda-version)
2086
+ * npx doc-tools generate rpk-docs --tag $VERSION
2087
+ *
2088
+ * @requirements
2089
+ * - Docker must be installed and running
2090
+ * - Sufficient disk space for Docker image
2091
+ * - Internet connection to pull Docker images
2092
+ */
1614
2093
  automation
1615
2094
  .command('rpk-docs')
1616
- .description('Generate AsciiDoc documentation for rpk CLI commands')
1617
- .requiredOption('-t, --tag <tag>', 'Redpanda version to use when starting Redpanda in Docker')
2095
+ .description('Generate AsciiDoc documentation for rpk CLI commands. Defaults to branch "dev" if neither --tag nor --branch is specified.')
2096
+ .option('-t, --tag <tag>', 'Git tag for released content (GA/beta)')
2097
+ .option('-b, --branch <branch>', 'Branch name for in-progress content')
1618
2098
  .option(
1619
2099
  '--docker-repo <repo>',
1620
2100
  'Docker repository to use when starting Redpanda in Docker',
@@ -1634,7 +2114,14 @@ automation
1634
2114
  .action((options) => {
1635
2115
  verifyMetricsDependencies();
1636
2116
 
1637
- const newTag = options.tag;
2117
+ // Validate that tag and branch are mutually exclusive
2118
+ if (options.tag && options.branch) {
2119
+ console.error('❌ Error: Cannot specify both --tag and --branch');
2120
+ process.exit(1);
2121
+ }
2122
+
2123
+ // Default to 'dev' branch if neither specified
2124
+ const newTag = options.tag || options.branch || 'dev';
1638
2125
  const oldTag = options.diff;
1639
2126
 
1640
2127
  if (oldTag) {
@@ -1655,17 +2142,59 @@ automation
1655
2142
  process.exit(0);
1656
2143
  });
1657
2144
 
2145
+ /**
2146
+ * generate helm-spec
2147
+ *
2148
+ * @description
2149
+ * Generates Helm chart reference documentation by parsing values.yaml files and README.md
2150
+ * documentation from Helm chart repositories. Supports both local chart directories and
2151
+ * GitHub URLs. Extracts all configuration options with their types, defaults, and descriptions,
2152
+ * and generates comprehensive AsciiDoc documentation. Can process single charts or entire
2153
+ * chart repositories with multiple charts.
2154
+ *
2155
+ * @why
2156
+ * Helm charts have complex configuration with hundreds of values. The values.yaml file and
2157
+ * chart README contain the configuration options, but they're not in a documentation-friendly
2158
+ * format. This automation parses the YAML structure and README documentation to generate
2159
+ * comprehensive reference documentation. Supporting both local and GitHub sources allows
2160
+ * documenting charts from any source without manual cloning.
2161
+ *
2162
+ * @example
2163
+ * # Generate docs from GitHub repository
2164
+ * npx doc-tools generate helm-spec \\
2165
+ * --chart-dir https://github.com/redpanda-data/helm-charts \\
2166
+ * --tag v5.9.0 \\
2167
+ * --output-dir modules/deploy/pages
2168
+ *
2169
+ * # Generate docs from local chart directory
2170
+ * npx doc-tools generate helm-spec \\
2171
+ * --chart-dir ./charts/redpanda \\
2172
+ * --output-dir docs/modules/deploy/pages
2173
+ *
2174
+ * # Use custom README and output suffix
2175
+ * npx doc-tools generate helm-spec \\
2176
+ * --chart-dir https://github.com/redpanda-data/helm-charts \\
2177
+ * --tag v5.9.0 \\
2178
+ * --readme docs/README.md \\
2179
+ * --output-suffix -values.adoc
2180
+ *
2181
+ * @requirements
2182
+ * - For GitHub URLs: Git and internet connection
2183
+ * - For local charts: Chart directory must contain Chart.yaml
2184
+ * - README.md file in chart directory (optional but recommended)
2185
+ */
1658
2186
  automation
1659
2187
  .command('helm-spec')
1660
2188
  .description(
1661
- `Generate AsciiDoc documentation for one or more Helm charts (supports local dirs or GitHub URLs)`
2189
+ `Generate AsciiDoc documentation for one or more Helm charts (supports local dirs or GitHub URLs). When using GitHub URLs, requires either --tag or --branch to be specified.`
1662
2190
  )
1663
2191
  .option(
1664
2192
  '--chart-dir <dir|url>',
1665
2193
  'Chart directory (contains Chart.yaml) or a root containing multiple charts, or a GitHub URL',
1666
2194
  'https://github.com/redpanda-data/redpanda-operator/charts'
1667
2195
  )
1668
- .requiredOption('-t, --tag <tag>', 'Branch or tag to clone when using a GitHub URL for the chart-dir')
2196
+ .option('-t, --tag <tag>', 'Git tag for released content when using GitHub URL (auto-prepends "operator/" for redpanda-operator repository)')
2197
+ .option('-b, --branch <branch>', 'Branch name for in-progress content when using GitHub URL')
1669
2198
  .option('--readme <file>', 'Relative README.md path inside each chart dir', 'README.md')
1670
2199
  .option('--output-dir <dir>', 'Where to write all generated AsciiDoc files', 'modules/reference/pages')
1671
2200
  .option('--output-suffix <suffix>', 'Suffix to append to each chart name (including extension)', '-helm-spec.adoc')
@@ -1677,10 +2206,24 @@ automation
1677
2206
  let tmpClone = null;
1678
2207
 
1679
2208
  if (/^https?:\/\/github\.com\//.test(root)) {
1680
- if (!opts.tag) {
1681
- console.error('❌ When using a GitHub URL you must pass --tag');
2209
+ // Validate tag/branch for GitHub URLs
2210
+ if (!opts.tag && !opts.branch) {
2211
+ console.error('❌ When using a GitHub URL you must pass either --tag or --branch');
1682
2212
  process.exit(1);
1683
2213
  }
2214
+ if (opts.tag && opts.branch) {
2215
+ console.error('❌ Cannot specify both --tag and --branch');
2216
+ process.exit(1);
2217
+ }
2218
+
2219
+ let gitRef = opts.tag || opts.branch;
2220
+
2221
+ // Normalize tag: add 'v' prefix if not present for tags
2222
+ if (opts.tag && !gitRef.startsWith('v')) {
2223
+ gitRef = `v${gitRef}`;
2224
+ console.log(`ℹ️ Auto-prepending "v" to tag: ${gitRef}`);
2225
+ }
2226
+
1684
2227
  const u = new URL(root);
1685
2228
  const parts = u.pathname.replace(/\.git$/, '').split('/').filter(Boolean);
1686
2229
  if (parts.length < 2) {
@@ -1689,24 +2232,42 @@ automation
1689
2232
  }
1690
2233
  const [owner, repo, ...sub] = parts;
1691
2234
  const repoUrl = `https://${u.host}/${owner}/${repo}.git`;
1692
- const ref = opts.tag;
1693
2235
 
1694
- console.log(`⏳ Verifying ${repoUrl}@${ref}…`);
2236
+ // Auto-prepend "operator/" for tags in redpanda-operator repository
2237
+ if (opts.tag && owner === 'redpanda-data' && repo === 'redpanda-operator') {
2238
+ if (!gitRef.startsWith('operator/')) {
2239
+ gitRef = `operator/${gitRef}`;
2240
+ console.log(`ℹ️ Auto-prepending "operator/" to tag: ${gitRef}`);
2241
+ }
2242
+ }
2243
+
2244
+ console.log(`⏳ Verifying ${repoUrl}@${gitRef}…`);
1695
2245
  const ok =
1696
2246
  spawnSync(
1697
2247
  'git',
1698
- ['ls-remote', '--exit-code', repoUrl, `refs/heads/${ref}`, `refs/tags/${ref}`],
2248
+ ['ls-remote', '--exit-code', repoUrl, `refs/heads/${gitRef}`, `refs/tags/${gitRef}`],
1699
2249
  { stdio: 'ignore' }
1700
2250
  ).status === 0;
1701
2251
  if (!ok) {
1702
- console.error(`❌ ${ref} not found on ${repoUrl}`);
2252
+ console.error(`❌ ${gitRef} not found on ${repoUrl}`);
1703
2253
  process.exit(1);
1704
2254
  }
1705
2255
 
2256
+ const { getAuthenticatedGitHubUrl, hasGitHubToken } = require('../cli-utils/github-token');
2257
+
1706
2258
  tmpClone = fs.mkdtempSync(path.join(os.tmpdir(), 'helm-'));
1707
- console.log(`⏳ Cloning ${repoUrl}@${ref} → ${tmpClone}`);
2259
+
2260
+ // Use token if available for GitHub repos
2261
+ let cloneUrl = repoUrl;
2262
+ if (hasGitHubToken() && repoUrl.includes('github.com')) {
2263
+ cloneUrl = getAuthenticatedGitHubUrl(repoUrl);
2264
+ console.log(`⏳ Cloning ${repoUrl}@${gitRef} → ${tmpClone} (authenticated)`);
2265
+ } else {
2266
+ console.log(`⏳ Cloning ${repoUrl}@${gitRef} → ${tmpClone}`);
2267
+ }
2268
+
1708
2269
  if (
1709
- spawnSync('git', ['clone', '--depth', '1', '--branch', ref, repoUrl, tmpClone], {
2270
+ spawnSync('git', ['clone', '--depth', '1', '--branch', gitRef, cloneUrl, tmpClone], {
1710
2271
  stdio: 'inherit',
1711
2272
  }).status !== 0
1712
2273
  ) {
@@ -1766,6 +2327,8 @@ automation
1766
2327
  const xrefRe = /https:\/\/docs\.redpanda\.com[^\s\]\[\)"]+(?:\[[^\]]*\])?/g;
1767
2328
  doc = doc
1768
2329
  .replace(/(\[\d+\])\]\./g, '$1\\].')
2330
+ .replace(/(\[\d+\])\]\]/g, '$1\\]\\]')
2331
+ .replace(/^=== +(https?:\/\/[^\[]*)\[([^\]]*)\]/gm, '=== link:++$1++[$2]')
1769
2332
  .replace(/^== # (.*)$/gm, '= $1')
1770
2333
  .replace(/^== description: (.*)$/gm, ':description: $1')
1771
2334
  .replace(xrefRe, (match) => {
@@ -1799,6 +2362,50 @@ automation
1799
2362
  /**
1800
2363
  * Generate Markdown table of cloud regions and tiers from master-data.yaml
1801
2364
  */
2365
+ /**
2366
+ * generate cloud-regions
2367
+ *
2368
+ * @description
2369
+ * Generates a formatted table of Redpanda Cloud regions, tiers, and availability information
2370
+ * by fetching data from the private cloudv2-infra repository. Reads a YAML configuration file
2371
+ * that contains master data for cloud infrastructure, parses region and tier information, and
2372
+ * generates either Markdown or AsciiDoc tables for documentation. Supports custom templates
2373
+ * and dry-run mode for previewing output.
2374
+ *
2375
+ * @why
2376
+ * Cloud region data changes frequently as new regions are added and tier availability evolves.
2377
+ * The cloudv2-infra repository contains the source of truth for cloud infrastructure. Manual
2378
+ * documentation becomes outdated quickly. This automation fetches the latest data directly from
2379
+ * the infrastructure repository, ensuring documentation always reflects current cloud offerings.
2380
+ * Weekly or triggered updates keep docs in sync with cloud expansion.
2381
+ *
2382
+ * @example
2383
+ * # Basic: Generate Markdown table
2384
+ * export GITHUB_TOKEN=ghp_xxx
2385
+ * npx doc-tools generate cloud-regions
2386
+ *
2387
+ * # Generate AsciiDoc format
2388
+ * export GITHUB_TOKEN=ghp_xxx
2389
+ * npx doc-tools generate cloud-regions --format adoc
2390
+ *
2391
+ * # Preview without writing file
2392
+ * export GITHUB_TOKEN=ghp_xxx
2393
+ * npx doc-tools generate cloud-regions --dry-run
2394
+ *
2395
+ * # Use custom output file
2396
+ * export GITHUB_TOKEN=ghp_xxx
2397
+ * npx doc-tools generate cloud-regions \\
2398
+ * --output custom/path/regions.md
2399
+ *
2400
+ * # Use different branch for testing
2401
+ * export GITHUB_TOKEN=ghp_xxx
2402
+ * npx doc-tools generate cloud-regions --ref staging
2403
+ *
2404
+ * @requirements
2405
+ * - GitHub token with access to redpanda-data/cloudv2-infra repository
2406
+ * - Token must be set via GITHUB_TOKEN, GH_TOKEN, or REDPANDA_GITHUB_TOKEN environment variable
2407
+ * - Internet connection to access GitHub API
2408
+ */
1802
2409
  automation
1803
2410
  .command('cloud-regions')
1804
2411
  .description('Generate Markdown table of cloud regions and tiers from GitHub YAML file')
@@ -1812,11 +2419,12 @@ automation
1812
2419
  .option('--dry-run', 'Print output to stdout instead of writing file')
1813
2420
  .action(async (options) => {
1814
2421
  const { generateCloudRegions } = require('../tools/cloud-regions/generate-cloud-regions.js');
2422
+ const { getGitHubToken } = require('../cli-utils/github-token');
1815
2423
 
1816
2424
  try {
1817
- const token = process.env.GITHUB_TOKEN || process.env.GH_TOKEN || process.env.REDPANDA_GITHUB_TOKEN;
2425
+ const token = getGitHubToken();
1818
2426
  if (!token) {
1819
- throw new Error('GITHUB_TOKEN, GH_TOKEN, or REDPANDA_GITHUB_TOKEN environment variable is required to fetch from private cloudv2-infra repo.');
2427
+ throw new Error('GitHub token is required to fetch from private cloudv2-infra repo. Set GITHUB_TOKEN, GH_TOKEN, or REDPANDA_GITHUB_TOKEN.');
1820
2428
  }
1821
2429
  const fmt = (options.format || 'md').toLowerCase();
1822
2430
  let templatePath = undefined;
@@ -1853,10 +2461,60 @@ automation
1853
2461
  }
1854
2462
  });
1855
2463
 
2464
+ /**
2465
+ * generate crd-spec
2466
+ *
2467
+ * @description
2468
+ * Generates Kubernetes Custom Resource Definition (CRD) reference documentation by parsing
2469
+ * Go type definitions from the Redpanda Operator repository. Uses the crd-ref-docs tool to
2470
+ * extract API field definitions, types, descriptions, and validation rules from Go struct tags
2471
+ * and comments, then generates comprehensive AsciiDoc documentation. Supports both local Go
2472
+ * source directories and GitHub URLs for operator versions.
2473
+ *
2474
+ * When to use --tag vs --branch:
2475
+ * - Use --tag for released content (GA or beta releases). Tags reference specific release points.
2476
+ * - Use --branch for in-progress content (unreleased features). Branches track ongoing development.
2477
+ *
2478
+ * @why
2479
+ * Kubernetes CRDs define complex APIs for deploying and managing Redpanda. The API schema
2480
+ * is defined in Go code with hundreds of fields across nested structures. Manual documentation
2481
+ * is error-prone and becomes outdated as the API evolves. This automation uses specialized
2482
+ * tooling (crd-ref-docs) to extract API documentation directly from Go source code, ensuring
2483
+ * accuracy and completeness. It captures field types, validation rules, and descriptions that
2484
+ * are essential for users configuring Redpanda in Kubernetes.
2485
+ *
2486
+ * @example
2487
+ * # Generate CRD docs for specific operator tag
2488
+ * npx doc-tools generate crd-spec --tag operator/v2.2.6-25.3.1
2489
+ *
2490
+ * # Version without prefix (auto-prepends operator/)
2491
+ * npx doc-tools generate crd-spec --tag v25.1.2
2492
+ *
2493
+ * # Generate from release branch
2494
+ * npx doc-tools generate crd-spec --branch release/v2.2.x
2495
+ *
2496
+ * # Generate from main branch
2497
+ * npx doc-tools generate crd-spec --branch main
2498
+ *
2499
+ * # Generate from any custom branch
2500
+ * npx doc-tools generate crd-spec --branch dev
2501
+ *
2502
+ * # Use custom templates and output location
2503
+ * npx doc-tools generate crd-spec \\
2504
+ * --tag operator/v2.2.6-25.3.1 \\
2505
+ * --templates-dir custom/templates \\
2506
+ * --output modules/reference/pages/operator-crd.adoc
2507
+ *
2508
+ * @requirements
2509
+ * - For GitHub URLs: Git and internet connection
2510
+ * - crd-ref-docs tool (automatically installed if missing)
2511
+ * - Go toolchain for running crd-ref-docs
2512
+ */
1856
2513
  automation
1857
2514
  .command('crd-spec')
1858
- .description('Generate Asciidoc documentation for Kubernetes CRD references')
1859
- .requiredOption('-t, --tag <operatorTag>', 'Operator release tag or branch, such as operator/v25.1.2')
2515
+ .description('Generate Asciidoc documentation for Kubernetes CRD references. Requires either --tag or --branch to be specified.')
2516
+ .option('-t, --tag <operatorTag>', 'Operator release tag for GA/beta content (for example operator/v2.2.6-25.3.1 or v25.1.2). Auto-prepends "operator/" if not present.')
2517
+ .option('-b, --branch <branch>', 'Branch name for in-progress content (for example release/v2.2.x, main, dev)')
1860
2518
  .option(
1861
2519
  '-s, --source-path <src>',
1862
2520
  'CRD Go types dir or GitHub URL',
@@ -1868,15 +2526,35 @@ automation
1868
2526
  .action(async (opts) => {
1869
2527
  verifyCrdDependencies();
1870
2528
 
1871
- // Fetch upstream config
2529
+ // Validate that either --tag or --branch is provided (but not both)
2530
+ if (!opts.tag && !opts.branch) {
2531
+ console.error('❌ Error: Either --tag or --branch must be specified');
2532
+ process.exit(1);
2533
+ }
2534
+ if (opts.tag && opts.branch) {
2535
+ console.error('❌ Error: Cannot specify both --tag and --branch');
2536
+ process.exit(1);
2537
+ }
2538
+
2539
+ // Determine the git ref to use
2540
+ let configRef;
2541
+ if (opts.branch) {
2542
+ // Branch - use as-is
2543
+ configRef = opts.branch;
2544
+ } else {
2545
+ // Tag - auto-prepend operator/ if needed
2546
+ configRef = opts.tag.startsWith('operator/') ? opts.tag : `operator/${opts.tag}`;
2547
+ }
2548
+
1872
2549
  const configTmp = fs.mkdtempSync(path.join(os.tmpdir(), 'crd-config-'));
1873
- console.log(`⏳ Fetching crd-ref-docs-config.yaml from redpanda-operator@main…`);
2550
+ console.log(`⏳ Fetching crd-ref-docs-config.yaml from redpanda-operator@${configRef}…`);
1874
2551
  await fetchFromGithub(
1875
2552
  'redpanda-data',
1876
2553
  'redpanda-operator',
1877
2554
  'operator/crd-ref-docs-config.yaml',
1878
2555
  configTmp,
1879
- 'crd-ref-docs-config.yaml'
2556
+ 'crd-ref-docs-config.yaml',
2557
+ configRef
1880
2558
  );
1881
2559
  const configPath = path.join(configTmp, 'crd-ref-docs-config.yaml');
1882
2560
 
@@ -1892,7 +2570,7 @@ automation
1892
2570
  console.warn('⚠️ Not inside redpanda-data/docs; skipping branch suggestion.');
1893
2571
  } else {
1894
2572
  try {
1895
- docsBranch = await determineDocsBranch(opts.tag);
2573
+ docsBranch = await determineDocsBranch(configRef);
1896
2574
  console.log(`✅ Detected docs repo; you should commit to branch '${docsBranch}'.`);
1897
2575
  } catch (err) {
1898
2576
  console.error(`❌ Unable to determine docs branch: ${err.message}`);
@@ -1919,19 +2597,30 @@ automation
1919
2597
  const [owner, repo, ...subpathParts] = parts;
1920
2598
  const repoUrl = `https://${u.host}/${owner}/${repo}`;
1921
2599
  const subpath = subpathParts.join('/');
1922
- console.log(`⏳ Verifying "${opts.tag}" in ${repoUrl}…`);
2600
+ console.log(`⏳ Verifying "${configRef}" in ${repoUrl}…`);
1923
2601
  const ok =
1924
- spawnSync('git', ['ls-remote', '--exit-code', repoUrl, `refs/tags/${opts.tag}`, `refs/heads/${opts.tag}`], {
2602
+ spawnSync('git', ['ls-remote', '--exit-code', repoUrl, `refs/tags/${configRef}`, `refs/heads/${configRef}`], {
1925
2603
  stdio: 'ignore',
1926
2604
  }).status === 0;
1927
2605
  if (!ok) {
1928
- console.error(`❌ Tag or branch "${opts.tag}" not found on ${repoUrl}`);
2606
+ console.error(`❌ Tag or branch "${configRef}" not found on ${repoUrl}`);
1929
2607
  process.exit(1);
1930
2608
  }
2609
+ const { getAuthenticatedGitHubUrl, hasGitHubToken } = require('../cli-utils/github-token');
2610
+
1931
2611
  tmpSrc = fs.mkdtempSync(path.join(os.tmpdir(), 'crd-src-'));
1932
- console.log(`⏳ Cloning ${repoUrl}@${opts.tag} → ${tmpSrc}`);
2612
+
2613
+ // Use token if available for GitHub repos
2614
+ let cloneUrl = repoUrl;
2615
+ if (hasGitHubToken() && repoUrl.includes('github.com')) {
2616
+ cloneUrl = getAuthenticatedGitHubUrl(repoUrl);
2617
+ console.log(`⏳ Cloning ${repoUrl}@${configRef} → ${tmpSrc} (authenticated)`);
2618
+ } else {
2619
+ console.log(`⏳ Cloning ${repoUrl}@${configRef} → ${tmpSrc}`);
2620
+ }
2621
+
1933
2622
  if (
1934
- spawnSync('git', ['clone', '--depth', '1', '--branch', opts.tag, repoUrl, tmpSrc], {
2623
+ spawnSync('git', ['clone', '--depth', '1', '--branch', configRef, cloneUrl, tmpSrc], {
1935
2624
  stdio: 'inherit',
1936
2625
  }).status !== 0
1937
2626
  ) {
@@ -2005,10 +2694,65 @@ automation
2005
2694
  }
2006
2695
  });
2007
2696
 
2697
+ /**
2698
+ * generate bundle-openapi
2699
+ *
2700
+ * @description
2701
+ * Bundles Redpanda's OpenAPI specification fragments into complete, usable OpenAPI 3.1 documents
2702
+ * for both Admin API and Connect API. Clones the Redpanda repository at a specified version,
2703
+ * collects OpenAPI fragments that are distributed throughout the codebase (alongside endpoint
2704
+ * implementations), uses Buf and Redocly CLI to bundle and validate the specifications, and
2705
+ * generates separate complete OpenAPI files for each API surface. The resulting specifications
2706
+ * can be used for API documentation, client SDK generation, or API testing tools.
2707
+ *
2708
+ * @why
2709
+ * Redpanda's API documentation is defined as OpenAPI fragments alongside the C++ implementation
2710
+ * code. This keeps API docs close to code and ensures they stay in sync, but it means the
2711
+ * specification is fragmented across hundreds of files. Users need complete OpenAPI specifications
2712
+ * for tooling (Swagger UI, Postman, client generators). This automation collects all fragments,
2713
+ * bundles them into valid OpenAPI 3.1 documents, and validates the result. It's the only way
2714
+ * to produce accurate, complete API specifications that match a specific Redpanda version.
2715
+ *
2716
+ * @example
2717
+ * # Bundle both Admin and Connect APIs
2718
+ * npx doc-tools generate bundle-openapi \\
2719
+ * --tag v25.3.1 \\
2720
+ * --surface both
2721
+ *
2722
+ * # Bundle only Admin API
2723
+ * npx doc-tools generate bundle-openapi \\
2724
+ * --tag v25.3.1 \\
2725
+ * --surface admin
2726
+ *
2727
+ * # Use custom output paths
2728
+ * npx doc-tools generate bundle-openapi \\
2729
+ * --tag v25.3.1 \\
2730
+ * --surface both \\
2731
+ * --out-admin api/admin-api.yaml \\
2732
+ * --out-connect api/connect-api.yaml
2733
+ *
2734
+ * # Use major version for Admin API version field
2735
+ * npx doc-tools generate bundle-openapi \\
2736
+ * --tag v25.3.1 \\
2737
+ * --surface admin \\
2738
+ * --use-admin-major-version
2739
+ *
2740
+ * # Full workflow: generate API specs for new release
2741
+ * VERSION=$(npx doc-tools get-redpanda-version)
2742
+ * npx doc-tools generate bundle-openapi --tag $VERSION --surface both
2743
+ *
2744
+ * @requirements
2745
+ * - Git to clone Redpanda repository
2746
+ * - Buf tool (automatically installed via npm)
2747
+ * - Redocly CLI or vacuum for OpenAPI bundling (automatically detected)
2748
+ * - Internet connection to clone repository
2749
+ * - Sufficient disk space for repository clone (~2GB)
2750
+ */
2008
2751
  automation
2009
2752
  .command('bundle-openapi')
2010
- .description('Bundle Redpanda OpenAPI fragments for admin and connect APIs into complete OpenAPI 3.1 documents')
2011
- .requiredOption('-t, --tag <tag>', 'Branch or tag to clone from the repository (for example v24.3.2 or 24.3.2 or dev)')
2753
+ .description('Bundle Redpanda OpenAPI fragments for admin and connect APIs into complete OpenAPI 3.1 documents. Requires either --tag or --branch to be specified.')
2754
+ .option('-t, --tag <tag>', 'Git tag for released content (for example, v24.3.2 or 24.3.2)')
2755
+ .option('-b, --branch <branch>', 'Branch name for in-progress content (for example, dev, main)')
2012
2756
  .option('--repo <url>', 'Repository URL', 'https://github.com/redpanda-data/redpanda.git')
2013
2757
  .addOption(new Option('-s, --surface <surface>', 'Which API surface(s) to bundle').choices(['admin', 'connect', 'both']).makeOptionMandatory())
2014
2758
  .option('--out-admin <path>', 'Output path for admin API', 'admin/redpanda-admin-api.yaml')
@@ -2017,6 +2761,18 @@ automation
2017
2761
  .option('--use-admin-major-version', 'Use admin major version for info.version instead of git tag', false)
2018
2762
  .option('--quiet', 'Suppress logs', false)
2019
2763
  .action(async (options) => {
2764
+ // Validate that either tag or branch is provided (but not both)
2765
+ if (!options.tag && !options.branch) {
2766
+ console.error('❌ Error: Either --tag or --branch must be specified');
2767
+ process.exit(1);
2768
+ }
2769
+ if (options.tag && options.branch) {
2770
+ console.error('❌ Error: Cannot specify both --tag and --branch');
2771
+ process.exit(1);
2772
+ }
2773
+
2774
+ // Determine the git ref to use
2775
+ const gitRef = options.tag || options.branch;
2020
2776
  // Verify dependencies
2021
2777
  requireCmd('git', 'Install Git: https://git-scm.com/downloads');
2022
2778
  requireCmd('buf', 'buf should be automatically available after npm install');
@@ -2032,7 +2788,7 @@ automation
2032
2788
  try {
2033
2789
  const { bundleOpenAPI } = require('../tools/bundle-openapi.js');
2034
2790
  await bundleOpenAPI({
2035
- tag: options.tag,
2791
+ tag: gitRef,
2036
2792
  repo: options.repo,
2037
2793
  surface: options.surface,
2038
2794
  outAdmin: options.outAdmin,
@@ -2047,5 +2803,249 @@ automation
2047
2803
  }
2048
2804
  });
2049
2805
 
2806
+ /**
2807
+ * Validate MCP configuration
2808
+ *
2809
+ * Validates that all prompts and resources are properly configured and accessible.
2810
+ * Checks for:
2811
+ * - Valid frontmatter in all prompt files
2812
+ * - Accessible resource files
2813
+ * - Proper metadata and descriptions
2814
+ * - No naming conflicts
2815
+ *
2816
+ * @example
2817
+ * npx doc-tools validate-mcp
2818
+ */
2819
+ programCli
2820
+ .command('validate-mcp')
2821
+ .description('Validate MCP server configuration (prompts, resources, metadata)')
2822
+ .action(() => {
2823
+ const {
2824
+ PromptCache,
2825
+ loadAllPrompts
2826
+ } = require('./mcp-tools/prompt-discovery');
2827
+ const {
2828
+ validateMcpConfiguration,
2829
+ formatValidationResults
2830
+ } = require('./mcp-tools/mcp-validation');
2831
+
2832
+ const baseDir = findRepoRoot();
2833
+ const promptCache = new PromptCache();
2834
+
2835
+ // Resources configuration
2836
+ const resources = [
2837
+ {
2838
+ uri: 'redpanda://style-guide',
2839
+ name: 'Redpanda Documentation Style Guide',
2840
+ description: 'Complete style guide based on Google Developer Documentation Style Guide with Redpanda-specific guidelines',
2841
+ mimeType: 'text/markdown',
2842
+ version: '1.0.0',
2843
+ lastUpdated: '2025-12-11'
2844
+ }
2845
+ ];
2846
+
2847
+ const resourceMap = {
2848
+ 'redpanda://style-guide': { file: 'style-guide.md', mimeType: 'text/markdown' }
2849
+ };
2850
+
2851
+ try {
2852
+ // Load prompts
2853
+ console.log('Loading prompts...');
2854
+ const prompts = loadAllPrompts(baseDir, promptCache);
2855
+ console.log(`Found ${prompts.length} prompts`);
2856
+
2857
+ // Validate configuration
2858
+ console.log('\nValidating configuration...');
2859
+ const validation = validateMcpConfiguration({
2860
+ resources,
2861
+ resourceMap,
2862
+ prompts,
2863
+ baseDir
2864
+ });
2865
+
2866
+ // Format and display results
2867
+ const output = formatValidationResults(validation, { resources, prompts });
2868
+ console.log('\n' + output);
2869
+
2870
+ if (!validation.valid) {
2871
+ process.exit(1);
2872
+ }
2873
+ } catch (err) {
2874
+ console.error(`❌ Validation failed: ${err.message}`);
2875
+ process.exit(1);
2876
+ }
2877
+ });
2878
+
2879
+ /**
2880
+ * Preview a prompt with arguments
2881
+ *
2882
+ * Loads a prompt and shows how it will appear when used with specified arguments.
2883
+ * Useful for testing prompts before using them in Claude Code.
2884
+ *
2885
+ * @example
2886
+ * npx doc-tools preview-prompt review-for-style --content "Sample content"
2887
+ * npx doc-tools preview-prompt write-new-guide --topic "Deploy Redpanda"
2888
+ */
2889
+ programCli
2890
+ .command('preview-prompt')
2891
+ .description('Preview a prompt with arguments to see the final output')
2892
+ .argument('<prompt-name>', 'Name of the prompt to preview')
2893
+ .option('--content <text>', 'Content argument (for review/check prompts)')
2894
+ .option('--topic <text>', 'Topic argument (for write prompts)')
2895
+ .option('--audience <text>', 'Audience argument (for write prompts)')
2896
+ .action((promptName, options) => {
2897
+ const {
2898
+ PromptCache,
2899
+ loadAllPrompts,
2900
+ buildPromptWithArguments
2901
+ } = require('./mcp-tools/prompt-discovery');
2902
+
2903
+ const baseDir = findRepoRoot();
2904
+ const promptCache = new PromptCache();
2905
+
2906
+ try {
2907
+ // Load prompts
2908
+ loadAllPrompts(baseDir, promptCache);
2909
+
2910
+ // Get the requested prompt
2911
+ const prompt = promptCache.get(promptName);
2912
+ if (!prompt) {
2913
+ console.error(`❌ Prompt not found: ${promptName}`);
2914
+ console.error(`\nAvailable prompts: ${promptCache.getNames().join(', ')}`);
2915
+ process.exit(1);
2916
+ }
2917
+
2918
+ // Build arguments object from options
2919
+ const args = {};
2920
+ if (options.content) args.content = options.content;
2921
+ if (options.topic) args.topic = options.topic;
2922
+ if (options.audience) args.audience = options.audience;
2923
+
2924
+ // Build final prompt text
2925
+ const promptText = buildPromptWithArguments(prompt, args);
2926
+
2927
+ // Display preview
2928
+ console.log('='.repeat(70));
2929
+ console.log(`PROMPT PREVIEW: ${promptName}`);
2930
+ console.log('='.repeat(70));
2931
+ console.log(`Description: ${prompt.description}`);
2932
+ console.log(`Version: ${prompt.version}`);
2933
+ if (prompt.arguments.length > 0) {
2934
+ console.log(`Arguments: ${prompt.arguments.map(a => a.name).join(', ')}`);
2935
+ }
2936
+ console.log('='.repeat(70));
2937
+ console.log('\n' + promptText);
2938
+ console.log('\n' + '='.repeat(70));
2939
+ } catch (err) {
2940
+ console.error(`❌ Preview failed: ${err.message}`);
2941
+ process.exit(1);
2942
+ }
2943
+ });
2944
+
2945
+ /**
2946
+ * Show MCP server version and statistics
2947
+ *
2948
+ * Displays version information for the MCP server, including:
2949
+ * - Server version
2950
+ * - Available prompts and their versions
2951
+ * - Available resources and their versions
2952
+ * - Usage statistics (if available)
2953
+ *
2954
+ * @example
2955
+ * npx doc-tools mcp-version
2956
+ * npx doc-tools mcp-version --stats # Show usage statistics if available
2957
+ */
2958
+ programCli
2959
+ .command('mcp-version')
2960
+ .description('Show MCP server version and configuration information')
2961
+ .option('--stats', 'Show usage statistics if available', false)
2962
+ .action((options) => {
2963
+ const packageJson = require('../package.json');
2964
+ const {
2965
+ PromptCache,
2966
+ loadAllPrompts
2967
+ } = require('./mcp-tools/prompt-discovery');
2968
+
2969
+ const baseDir = findRepoRoot();
2970
+ const promptCache = new PromptCache();
2971
+
2972
+ try {
2973
+ // Load prompts
2974
+ const prompts = loadAllPrompts(baseDir, promptCache);
2975
+
2976
+ // Resources configuration
2977
+ const resources = [
2978
+ {
2979
+ uri: 'redpanda://style-guide',
2980
+ name: 'Redpanda Documentation Style Guide',
2981
+ version: '1.0.0',
2982
+ lastUpdated: '2025-12-11'
2983
+ }
2984
+ ];
2985
+
2986
+ // Display version information
2987
+ console.log('Redpanda Doc Tools MCP Server');
2988
+ console.log('='.repeat(60));
2989
+ console.log(`Server version: ${packageJson.version}`);
2990
+ console.log(`Base directory: ${baseDir}`);
2991
+ console.log('');
2992
+
2993
+ // Prompts
2994
+ console.log(`Prompts (${prompts.length} available):`);
2995
+ prompts.forEach(prompt => {
2996
+ const args = prompt.arguments.length > 0
2997
+ ? ` [${prompt.arguments.map(a => a.name).join(', ')}]`
2998
+ : '';
2999
+ console.log(` - ${prompt.name} (v${prompt.version})${args}`);
3000
+ console.log(` ${prompt.description}`);
3001
+ });
3002
+ console.log('');
3003
+
3004
+ // Resources
3005
+ console.log(`Resources (${resources.length} available):`);
3006
+ resources.forEach(resource => {
3007
+ console.log(` - ${resource.uri}`);
3008
+ console.log(` ${resource.name} (v${resource.version})`);
3009
+ console.log(` Last updated: ${resource.lastUpdated}`);
3010
+ });
3011
+ console.log('');
3012
+
3013
+ // Check for usage statistics
3014
+ if (options.stats) {
3015
+ const statsPath = path.join(baseDir, 'mcp-usage-stats.json');
3016
+ if (fs.existsSync(statsPath)) {
3017
+ console.log('Usage Statistics:');
3018
+ console.log('='.repeat(60));
3019
+ const stats = JSON.parse(fs.readFileSync(statsPath, 'utf8'));
3020
+ console.log(`Exported at: ${stats.exportedAt}`);
3021
+ console.log(`Total prompt calls: ${stats.totalPromptCalls}`);
3022
+ console.log(`Total resource calls: ${stats.totalResourceCalls}`);
3023
+ console.log(`Total tool calls: ${stats.totalToolCalls}`);
3024
+
3025
+ if (stats.totalPromptCalls > 0) {
3026
+ console.log('\nMost used prompts:');
3027
+ Object.entries(stats.prompts)
3028
+ .sort((a, b) => b[1] - a[1])
3029
+ .slice(0, 5)
3030
+ .forEach(([name, count]) => {
3031
+ console.log(` ${name}: ${count} calls`);
3032
+ });
3033
+ }
3034
+ } else {
3035
+ console.log('No usage statistics available yet.');
3036
+ console.log('Statistics are exported when the MCP server shuts down.');
3037
+ }
3038
+ }
3039
+
3040
+ console.log('');
3041
+ console.log('For more information, see:');
3042
+ console.log(' mcp/WRITER_EXTENSION_GUIDE.adoc');
3043
+ console.log(' mcp/AI_CONSISTENCY_ARCHITECTURE.adoc');
3044
+ } catch (err) {
3045
+ console.error(`❌ Failed to retrieve version information: ${err.message}`);
3046
+ process.exit(1);
3047
+ }
3048
+ });
3049
+
2050
3050
  programCli.addCommand(automation);
2051
3051
  programCli.parse(process.argv);