@redpanda-data/docs-extensions-and-macros 4.12.6 → 4.13.1

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 (77) 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-version.js +65 -54
  37. package/extensions/version-fetcher/retry-util.js +88 -0
  38. package/extensions/version-fetcher/set-latest-version.js +6 -3
  39. package/macros/DEVELOPMENT.adoc +377 -0
  40. package/macros/README.adoc +105 -0
  41. package/macros/REFERENCE.adoc +222 -0
  42. package/macros/USER_GUIDE.adoc +220 -0
  43. package/macros/rp-connect-components.js +6 -6
  44. package/mcp/CLI_INTERFACE.adoc +384 -0
  45. package/mcp/COSTS.adoc +167 -0
  46. package/mcp/DEVELOPMENT.adoc +726 -0
  47. package/mcp/README.adoc +172 -0
  48. package/mcp/USER_GUIDE.adoc +1392 -0
  49. package/mcp/WRITER_EXTENSION_GUIDE.adoc +814 -0
  50. package/mcp/prompts/README.adoc +183 -0
  51. package/mcp/prompts/property-docs-guide.md +283 -0
  52. package/mcp/prompts/review-for-style.md +128 -0
  53. package/mcp/prompts/rpcn-connector-docs-guide.md +126 -0
  54. package/mcp/prompts/write-new-guide.md +222 -0
  55. package/mcp/team-standards/style-guide.md +321 -0
  56. package/mcp/templates/README.adoc +212 -0
  57. package/mcp/templates/prompt-review-template.md +80 -0
  58. package/mcp/templates/prompt-write-template.md +110 -0
  59. package/mcp/templates/resource-template.md +76 -0
  60. package/package.json +16 -5
  61. package/tools/bundle-openapi.js +20 -10
  62. package/tools/cloud-regions/generate-cloud-regions.js +1 -1
  63. package/tools/fetch-from-github.js +18 -4
  64. package/tools/gen-rpk-ascii.py +3 -1
  65. package/tools/generate-cli-docs.js +325 -0
  66. package/tools/get-console-version.js +4 -2
  67. package/tools/get-redpanda-version.js +4 -2
  68. package/tools/metrics/metrics.py +19 -7
  69. package/tools/property-extractor/Makefile +7 -1
  70. package/tools/property-extractor/cloud_config.py +4 -4
  71. package/tools/property-extractor/constant_resolver.py +11 -11
  72. package/tools/property-extractor/property_extractor.py +18 -16
  73. package/tools/property-extractor/topic_property_extractor.py +2 -2
  74. package/tools/property-extractor/transformers.py +7 -7
  75. package/tools/property-extractor/type_definition_extractor.py +4 -4
  76. package/tools/redpanda-connect/README.adoc +1 -1
  77. package/tools/redpanda-connect/generate-rpcn-connector-docs.js +5 -3
@@ -0,0 +1,283 @@
1
+ /**
2
+ * Prompt Discovery and Caching
3
+ *
4
+ * Automatically discovers prompts from the mcp/prompts directory.
5
+ * Caches prompt content and metadata for performance.
6
+ * Supports file watching in development mode.
7
+ */
8
+
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+ const { parsePromptFile, validateFrontmatter } = require('./frontmatter');
12
+ const { validatePromptName } = require('./mcp-validation');
13
+
14
+ /**
15
+ * In-memory prompt cache
16
+ */
17
+ class PromptCache {
18
+ constructor() {
19
+ this.prompts = new Map(); // name -> { metadata, content, filePath }
20
+ this.watchers = [];
21
+ }
22
+
23
+ /**
24
+ * Set prompt in cache
25
+ * @param {string} name - Prompt name
26
+ * @param {Object} data - Prompt data
27
+ */
28
+ set(name, data) {
29
+ this.prompts.set(name, data);
30
+ }
31
+
32
+ /**
33
+ * Get prompt from cache
34
+ * @param {string} name - Prompt name
35
+ * @returns {Object|null} Prompt data or null
36
+ */
37
+ get(name) {
38
+ return this.prompts.get(name) || null;
39
+ }
40
+
41
+ /**
42
+ * Get all prompts
43
+ * @returns {Array} Array of prompt objects
44
+ */
45
+ getAll() {
46
+ return Array.from(this.prompts.values());
47
+ }
48
+
49
+ /**
50
+ * Get all prompt names
51
+ * @returns {Array} Array of prompt names
52
+ */
53
+ getNames() {
54
+ return Array.from(this.prompts.keys());
55
+ }
56
+
57
+ /**
58
+ * Check if prompt exists
59
+ * @param {string} name - Prompt name
60
+ * @returns {boolean}
61
+ */
62
+ has(name) {
63
+ return this.prompts.has(name);
64
+ }
65
+
66
+ /**
67
+ * Clear all prompts
68
+ */
69
+ clear() {
70
+ this.prompts.clear();
71
+ }
72
+
73
+ /**
74
+ * Stop all file watchers
75
+ */
76
+ stopWatching() {
77
+ this.watchers.forEach(watcher => watcher.close());
78
+ this.watchers = [];
79
+ }
80
+
81
+ /**
82
+ * Add a file watcher
83
+ * @param {FSWatcher} watcher - File system watcher
84
+ */
85
+ addWatcher(watcher) {
86
+ this.watchers.push(watcher);
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Default argument formatters for different types
92
+ */
93
+ const argumentFormatters = {
94
+ 'content-append': (args, schema) => {
95
+ if (!args) return '';
96
+
97
+ let result = '\n\n---\n\n';
98
+
99
+ // If there's a 'content' argument, add it specially
100
+ if (args.content) {
101
+ result += `**Content to review:**\n\n${args.content}`;
102
+ return result;
103
+ }
104
+
105
+ // Otherwise, format all arguments
106
+ Object.entries(args).forEach(([key, value]) => {
107
+ const argDef = schema?.find(a => a.name === key);
108
+ const label = argDef?.name || key;
109
+ result += `**${label.charAt(0).toUpperCase() + label.slice(1)}:** ${value}\n`;
110
+ });
111
+
112
+ return result;
113
+ },
114
+
115
+ 'structured': (args, schema) => {
116
+ if (!args) return '';
117
+
118
+ let result = '\n\n---\n\n';
119
+ Object.entries(args).forEach(([key, value]) => {
120
+ result += `**${key}:** ${value}\n`;
121
+ });
122
+
123
+ return result;
124
+ }
125
+ };
126
+
127
+ /**
128
+ * Discover all prompts in the prompts directory
129
+ * @param {string} promptsDir - Path to prompts directory
130
+ * @returns {Array} Array of discovered prompts
131
+ */
132
+ function discoverPrompts(promptsDir) {
133
+ if (!fs.existsSync(promptsDir)) {
134
+ throw new Error(`Prompts directory not found: ${promptsDir}`);
135
+ }
136
+
137
+ const files = fs.readdirSync(promptsDir).filter(f => f.endsWith('.md'));
138
+ const prompts = [];
139
+
140
+ for (const file of files) {
141
+ try {
142
+ const name = path.basename(file, '.md');
143
+ validatePromptName(name); // Ensure safe name
144
+
145
+ const filePath = path.join(promptsDir, file);
146
+ const fileContent = fs.readFileSync(filePath, 'utf8');
147
+
148
+ const { metadata, content } = parsePromptFile(fileContent, file);
149
+
150
+ // Build prompt object
151
+ const prompt = {
152
+ name,
153
+ description: metadata.description || `Prompt: ${name}`,
154
+ version: metadata.version || '1.0.0',
155
+ arguments: metadata.arguments || [],
156
+ argumentFormat: metadata.argumentFormat || 'content-append',
157
+ content,
158
+ filePath,
159
+ _rawMetadata: metadata
160
+ };
161
+
162
+ prompts.push(prompt);
163
+ } catch (err) {
164
+ console.error(`Error loading prompt ${file}: ${err.message}`);
165
+ // Continue loading other prompts
166
+ }
167
+ }
168
+
169
+ return prompts;
170
+ }
171
+
172
+ /**
173
+ * Load all prompts into cache
174
+ * @param {string} baseDir - Base directory (repo root)
175
+ * @param {PromptCache} cache - Prompt cache instance
176
+ * @returns {Array} Loaded prompts
177
+ */
178
+ function loadAllPrompts(baseDir, cache) {
179
+ const promptsDir = path.join(baseDir, 'mcp', 'prompts');
180
+ const prompts = discoverPrompts(promptsDir);
181
+
182
+ // Clear and reload cache
183
+ cache.clear();
184
+
185
+ prompts.forEach(prompt => {
186
+ cache.set(prompt.name, prompt);
187
+ });
188
+
189
+ return prompts;
190
+ }
191
+
192
+ /**
193
+ * Watch prompts directory for changes (development mode)
194
+ * @param {string} baseDir - Base directory
195
+ * @param {PromptCache} cache - Prompt cache instance
196
+ * @param {Function} onChange - Callback when prompts change
197
+ */
198
+ function watchPrompts(baseDir, cache, onChange) {
199
+ const promptsDir = path.join(baseDir, 'mcp', 'prompts');
200
+
201
+ if (!fs.existsSync(promptsDir)) {
202
+ console.error(`Cannot watch prompts directory (not found): ${promptsDir}`);
203
+ return;
204
+ }
205
+
206
+ const watcher = fs.watch(promptsDir, (eventType, filename) => {
207
+ if (!filename || !filename.endsWith('.md')) {
208
+ return;
209
+ }
210
+
211
+ console.error(`Prompt file changed: ${filename} (${eventType})`);
212
+ console.error('Reloading all prompts...');
213
+
214
+ try {
215
+ const prompts = loadAllPrompts(baseDir, cache);
216
+ console.error(`Reloaded ${prompts.length} prompts`);
217
+
218
+ if (onChange) {
219
+ onChange(prompts);
220
+ }
221
+ } catch (err) {
222
+ console.error(`Error reloading prompts: ${err.message}`);
223
+ }
224
+ });
225
+
226
+ cache.addWatcher(watcher);
227
+ console.error('File watching enabled for prompts (dev mode)');
228
+ }
229
+
230
+ /**
231
+ * Build prompt text with arguments
232
+ * @param {Object} prompt - Prompt object from cache
233
+ * @param {Object} args - Arguments provided by user
234
+ * @returns {string} Complete prompt text
235
+ */
236
+ function buildPromptWithArguments(prompt, args) {
237
+ let promptText = prompt.content;
238
+
239
+ if (!args || Object.keys(args).length === 0) {
240
+ return promptText;
241
+ }
242
+
243
+ // Use the formatter specified by the prompt
244
+ const formatter = argumentFormatters[prompt.argumentFormat];
245
+ if (!formatter) {
246
+ console.error(
247
+ `Unknown argument format: ${prompt.argumentFormat} for prompt ${prompt.name}`
248
+ );
249
+ return promptText;
250
+ }
251
+
252
+ const formattedArgs = formatter(args, prompt.arguments);
253
+ promptText += formattedArgs;
254
+
255
+ return promptText;
256
+ }
257
+
258
+ /**
259
+ * Convert prompts to MCP protocol format
260
+ * @param {Array} prompts - Discovered prompts
261
+ * @returns {Array} Prompts in MCP format
262
+ */
263
+ function promptsToMcpFormat(prompts) {
264
+ return prompts.map(prompt => ({
265
+ name: prompt.name,
266
+ description: prompt.description,
267
+ arguments: prompt.arguments.map(arg => ({
268
+ name: arg.name,
269
+ description: arg.description,
270
+ required: arg.required
271
+ }))
272
+ }));
273
+ }
274
+
275
+ module.exports = {
276
+ PromptCache,
277
+ discoverPrompts,
278
+ loadAllPrompts,
279
+ watchPrompts,
280
+ buildPromptWithArguments,
281
+ promptsToMcpFormat,
282
+ argumentFormatters
283
+ };
@@ -0,0 +1,157 @@
1
+ /**
2
+ * MCP Tools - Property Documentation Generation
3
+ *
4
+ * OPTIMIZATION: This tool calls CLI, doesn't use LLM directly.
5
+ * - No model recommendation (CLI tool)
6
+ * - Cost comes from doc-tools CLI execution
7
+ */
8
+
9
+ const { spawnSync } = require('child_process');
10
+ const { findRepoRoot, getDocToolsCommand, MAX_EXEC_BUFFER_SIZE, DEFAULT_COMMAND_TIMEOUT } = require('./utils');
11
+ const { getAntoraStructure } = require('./antora');
12
+ const { createJob } = require('./job-queue');
13
+
14
+ /**
15
+ * Generate Redpanda property documentation
16
+ *
17
+ * Use tags for released content (GA or beta), branches for in-progress content.
18
+ * Defaults to branch "dev" if neither tag nor branch is provided.
19
+ *
20
+ * @param {Object} args - Arguments
21
+ * @param {string} [args.tag] - Git tag for released content (for example, "v25.3.1")
22
+ * @param {string} [args.branch] - Branch name for in-progress content (for example, "dev", "main")
23
+ * @param {boolean} [args.generate_partials] - Whether to generate AsciiDoc partials
24
+ * @param {boolean} [args.background] - Run as background job
25
+ * @returns {Object} Generation results
26
+ */
27
+ function generatePropertyDocs(args) {
28
+ const repoRoot = findRepoRoot();
29
+ const structure = getAntoraStructure(repoRoot);
30
+
31
+ if (!structure.hasDocTools) {
32
+ return {
33
+ success: false,
34
+ error: 'doc-tools not found in this repository',
35
+ suggestion: 'Navigate to the docs-extensions-and-macros repository'
36
+ };
37
+ }
38
+
39
+ // Validate that tag and branch are mutually exclusive
40
+ if (args.tag && args.branch) {
41
+ return {
42
+ success: false,
43
+ error: 'Cannot specify both tag and branch',
44
+ suggestion: 'Use either --tag or --branch, not both'
45
+ };
46
+ }
47
+
48
+ // Default to 'dev' branch if neither provided
49
+ const gitRef = args.tag || args.branch || 'dev';
50
+ const refType = args.tag ? 'tag' : 'branch';
51
+
52
+ // Normalize version (add 'v' prefix if tag and not present and not "latest")
53
+ let version = gitRef;
54
+ if (args.tag && version !== 'latest' && !version.startsWith('v')) {
55
+ version = `v${version}`;
56
+ }
57
+
58
+ // Get doc-tools command (handles both local and installed)
59
+ const docTools = getDocToolsCommand(repoRoot);
60
+
61
+ // Build command arguments
62
+ const baseArgs = ['generate', 'property-docs'];
63
+
64
+ if (args.tag) {
65
+ baseArgs.push('--tag');
66
+ baseArgs.push(version);
67
+ } else {
68
+ baseArgs.push('--branch');
69
+ baseArgs.push(version);
70
+ }
71
+
72
+ if (args.generate_partials) {
73
+ baseArgs.push('--generate-partials');
74
+ }
75
+
76
+ // If background mode, create job and return immediately
77
+ if (args.background) {
78
+ const cmdArgs = [docTools.program, ...docTools.getArgs(baseArgs)];
79
+ const jobId = createJob('generate_property_docs', cmdArgs, {
80
+ cwd: repoRoot.root
81
+ });
82
+
83
+ return {
84
+ success: true,
85
+ background: true,
86
+ job_id: jobId,
87
+ message: `Property docs generation started in background. Use get_job_status with job_id: ${jobId} to check progress.`,
88
+ [refType]: version
89
+ };
90
+ }
91
+
92
+ // Otherwise run synchronously
93
+ try {
94
+ const result = spawnSync(docTools.program, docTools.getArgs(baseArgs), {
95
+ cwd: repoRoot.root,
96
+ encoding: 'utf8',
97
+ stdio: 'pipe',
98
+ maxBuffer: MAX_EXEC_BUFFER_SIZE,
99
+ timeout: DEFAULT_COMMAND_TIMEOUT
100
+ });
101
+
102
+ // Check for spawn errors
103
+ if (result.error) {
104
+ const err = new Error(`Failed to execute command: ${result.error.message}`);
105
+ err.stdout = result.stdout || '';
106
+ err.stderr = result.stderr || '';
107
+ err.status = result.status;
108
+ throw err;
109
+ }
110
+
111
+ // Check for non-zero exit codes
112
+ if (result.status !== 0) {
113
+ const errorMsg = result.stderr || `Command failed with exit code ${result.status}`;
114
+ const err = new Error(errorMsg);
115
+ err.stdout = result.stdout || '';
116
+ err.stderr = result.stderr || '';
117
+ err.status = result.status;
118
+ throw err;
119
+ }
120
+
121
+ const output = result.stdout;
122
+
123
+ // Parse output to extract useful information
124
+ const propertyCountMatch = output.match(/(\d+) properties/i);
125
+ const filesGenerated = [];
126
+
127
+ if (args.generate_partials) {
128
+ filesGenerated.push('modules/reference/partials/cluster-properties.adoc');
129
+ filesGenerated.push('modules/reference/partials/broker-properties.adoc');
130
+ filesGenerated.push('modules/reference/partials/tunable-properties.adoc');
131
+ filesGenerated.push('modules/reference/partials/topic-properties.adoc');
132
+ }
133
+ filesGenerated.push('modules/reference/partials/properties.json');
134
+
135
+ return {
136
+ success: true,
137
+ [refType]: version,
138
+ files_generated: filesGenerated,
139
+ property_count: propertyCountMatch ? parseInt(propertyCountMatch[1]) : null,
140
+ output: output.trim(),
141
+ summary: `Generated property documentation for Redpanda ${refType} ${version}`
142
+ };
143
+ } catch (err) {
144
+ return {
145
+ success: false,
146
+ error: err.message,
147
+ stdout: err.stdout || '',
148
+ stderr: err.stderr || '',
149
+ exitCode: err.status,
150
+ suggestion: 'Check that the version exists in the Redpanda repository'
151
+ };
152
+ }
153
+ }
154
+
155
+ module.exports = {
156
+ generatePropertyDocs
157
+ };
@@ -0,0 +1,113 @@
1
+ /**
2
+ * MCP Tools - Redpanda Connect Documentation Generation
3
+ *
4
+ * OPTIMIZATION: This tool calls CLI, doesn't use LLM directly.
5
+ * - Caches connector metadata when possible
6
+ * - Use --data-dir to avoid re-fetching
7
+ * - Consider incremental generation for updates
8
+ * - No model recommendation (CLI tool)
9
+ */
10
+
11
+ const { execSync, spawnSync } = require('child_process');
12
+ const { findRepoRoot, MAX_EXEC_BUFFER_SIZE, DEFAULT_COMMAND_TIMEOUT } = require('./utils');
13
+ const { getAntoraStructure } = require('./antora');
14
+ const cache = require('./cache');
15
+
16
+ /**
17
+ * Generate Redpanda Connect connector documentation
18
+ * @param {Object} args - Arguments
19
+ * @returns {Object} Generation results
20
+ */
21
+ function generateRpConnectDocs(args = {}) {
22
+ const repoRoot = findRepoRoot();
23
+ const structure = getAntoraStructure(repoRoot);
24
+
25
+ if (!structure.hasDocTools) {
26
+ return {
27
+ success: false,
28
+ error: 'doc-tools not found in this repository',
29
+ suggestion: 'Navigate to the docs-extensions-and-macros repository'
30
+ };
31
+ }
32
+
33
+ try {
34
+ // Build command arguments array (no shell interpolation)
35
+ const cmdArgs = ['doc-tools', 'generate', 'rpcn-connector-docs'];
36
+
37
+ // Add flags only when present, each as separate array entries
38
+ if (args.fetch_connectors) cmdArgs.push('--fetch-connectors');
39
+ if (args.draft_missing) cmdArgs.push('--draft-missing');
40
+ if (args.update_whats_new) cmdArgs.push('--update-whats-new');
41
+ if (args.include_bloblang) cmdArgs.push('--include-bloblang');
42
+ if (args.data_dir) {
43
+ cmdArgs.push('--data-dir');
44
+ cmdArgs.push(args.data_dir);
45
+ }
46
+ if (args.old_data) {
47
+ cmdArgs.push('--old-data');
48
+ cmdArgs.push(args.old_data);
49
+ }
50
+ if (args.csv) {
51
+ cmdArgs.push('--csv');
52
+ cmdArgs.push(args.csv);
53
+ }
54
+ if (args.overrides) {
55
+ cmdArgs.push('--overrides');
56
+ cmdArgs.push(args.overrides);
57
+ }
58
+
59
+ const result = spawnSync('npx', cmdArgs, {
60
+ cwd: repoRoot.root,
61
+ encoding: 'utf8',
62
+ stdio: 'pipe',
63
+ maxBuffer: MAX_EXEC_BUFFER_SIZE,
64
+ timeout: DEFAULT_COMMAND_TIMEOUT
65
+ });
66
+
67
+ // Check for spawn errors
68
+ if (result.error) {
69
+ const err = new Error(`Failed to execute command: ${result.error.message}`);
70
+ err.stdout = result.stdout || '';
71
+ err.stderr = result.stderr || '';
72
+ err.status = result.status;
73
+ throw err;
74
+ }
75
+
76
+ // Check for non-zero exit codes
77
+ if (result.status !== 0) {
78
+ const errorMsg = result.stderr || `Command failed with exit code ${result.status}`;
79
+ const err = new Error(errorMsg);
80
+ err.stdout = result.stdout || '';
81
+ err.stderr = result.stderr || '';
82
+ err.status = result.status;
83
+ throw err;
84
+ }
85
+
86
+ const output = result.stdout;
87
+
88
+ const connectorsMatch = output.match(/(\d+) connectors/i);
89
+
90
+ return {
91
+ success: true,
92
+ connectors_documented: connectorsMatch ? parseInt(connectorsMatch[1]) : null,
93
+ files_generated: ['modules/reference/pages/redpanda-connect/components/'],
94
+ output: output.trim(),
95
+ summary: 'Generated Redpanda Connect connector documentation',
96
+ // Cost optimization metadata
97
+ _optimizationNotes: 'This tool calls CLI. To reduce costs: 1) Cache connector data with --data-dir, 2) Use incremental generation when possible, 3) Avoid --fetch-connectors unless needed'
98
+ };
99
+ } catch (err) {
100
+ return {
101
+ success: false,
102
+ error: err.message,
103
+ stdout: err.stdout || '',
104
+ stderr: err.stderr || '',
105
+ exitCode: err.status,
106
+ suggestion: 'Check that you have network access to fetch connector data if using --fetch-connectors'
107
+ };
108
+ }
109
+ }
110
+
111
+ module.exports = {
112
+ generateRpConnectDocs
113
+ };
@@ -0,0 +1,141 @@
1
+ /**
2
+ * MCP Tools - RPK Documentation Generation
3
+ *
4
+ * OPTIMIZATION: This tool calls CLI, doesn't use LLM directly.
5
+ * - No model recommendation (CLI tool)
6
+ * - Cost comes from doc-tools CLI execution
7
+ */
8
+
9
+ const { spawnSync } = require('child_process');
10
+ const { findRepoRoot, getDocToolsCommand, MAX_EXEC_BUFFER_SIZE, DEFAULT_COMMAND_TIMEOUT } = require('./utils');
11
+ const { getAntoraStructure } = require('./antora');
12
+ const { createJob } = require('./job-queue');
13
+
14
+ /**
15
+ * Generate RPK command documentation
16
+ *
17
+ * Use tags for released content (GA or beta), branches for in-progress content.
18
+ * Defaults to branch "dev" if neither tag nor branch is provided.
19
+ *
20
+ * @param {Object} args - Arguments
21
+ * @param {string} [args.tag] - Git tag for released content (for example, "v25.3.1")
22
+ * @param {string} [args.branch] - Branch name for in-progress content (for example, "dev", "main")
23
+ * @param {boolean} [args.background] - Run as background job
24
+ * @returns {Object} Generation results
25
+ */
26
+ function generateRpkDocs(args) {
27
+ const repoRoot = findRepoRoot();
28
+ const structure = getAntoraStructure(repoRoot);
29
+
30
+ if (!structure.hasDocTools) {
31
+ return {
32
+ success: false,
33
+ error: 'doc-tools not found in this repository',
34
+ suggestion: 'Navigate to the docs-extensions-and-macros repository'
35
+ };
36
+ }
37
+
38
+ // Validate that tag and branch are mutually exclusive
39
+ if (args.tag && args.branch) {
40
+ return {
41
+ success: false,
42
+ error: 'Cannot specify both tag and branch',
43
+ suggestion: 'Use either --tag or --branch, not both'
44
+ };
45
+ }
46
+
47
+ // Default to 'dev' branch if neither provided
48
+ const gitRef = args.tag || args.branch || 'dev';
49
+ const refType = args.tag ? 'tag' : 'branch';
50
+
51
+ // Normalize version (add 'v' prefix if not present and not a branch name like 'dev' or 'main')
52
+ let version = gitRef;
53
+ if (args.tag && !version.startsWith('v')) {
54
+ version = `v${version}`;
55
+ }
56
+
57
+ // Get doc-tools command (handles both local and installed)
58
+ const docTools = getDocToolsCommand(repoRoot);
59
+
60
+ // Build command arguments array
61
+ const baseArgs = ['generate', 'rpk-docs'];
62
+
63
+ if (args.tag) {
64
+ baseArgs.push('--tag');
65
+ baseArgs.push(version);
66
+ } else {
67
+ baseArgs.push('--branch');
68
+ baseArgs.push(version);
69
+ }
70
+
71
+ // If background mode, create job and return immediately
72
+ if (args.background) {
73
+ const cmdArgs = [docTools.program, ...docTools.getArgs(baseArgs)];
74
+ const jobId = createJob('generate_rpk_docs', cmdArgs, {
75
+ cwd: repoRoot.root
76
+ });
77
+
78
+ return {
79
+ success: true,
80
+ background: true,
81
+ job_id: jobId,
82
+ message: `RPK docs generation started in background. Use get_job_status with job_id: ${jobId} to check progress.`,
83
+ [refType]: version
84
+ };
85
+ }
86
+
87
+ // Otherwise run synchronously
88
+ try {
89
+ const result = spawnSync(docTools.program, docTools.getArgs(baseArgs), {
90
+ cwd: repoRoot.root,
91
+ encoding: 'utf8',
92
+ stdio: 'pipe',
93
+ maxBuffer: MAX_EXEC_BUFFER_SIZE,
94
+ timeout: DEFAULT_COMMAND_TIMEOUT
95
+ });
96
+
97
+ // Check for spawn errors
98
+ if (result.error) {
99
+ const err = new Error(`Failed to execute command: ${result.error.message}`);
100
+ err.stdout = result.stdout || '';
101
+ err.stderr = result.stderr || '';
102
+ err.status = result.status;
103
+ throw err;
104
+ }
105
+
106
+ // Check for non-zero exit codes
107
+ if (result.status !== 0) {
108
+ const errorMsg = result.stderr || `Command failed with exit code ${result.status}`;
109
+ const err = new Error(errorMsg);
110
+ err.stdout = result.stdout || '';
111
+ err.stderr = result.stderr || '';
112
+ err.status = result.status;
113
+ throw err;
114
+ }
115
+
116
+ const output = result.stdout;
117
+ const commandCountMatch = output.match(/(\d+) commands/i);
118
+
119
+ return {
120
+ success: true,
121
+ [refType]: version,
122
+ files_generated: [`autogenerated/${version}/rpk/*.adoc`],
123
+ commands_documented: commandCountMatch ? parseInt(commandCountMatch[1]) : null,
124
+ output: output.trim(),
125
+ summary: `Generated RPK documentation for Redpanda ${refType} ${version}`
126
+ };
127
+ } catch (err) {
128
+ return {
129
+ success: false,
130
+ error: err.message,
131
+ stdout: err.stdout || '',
132
+ stderr: err.stderr || '',
133
+ exitCode: err.status,
134
+ suggestion: 'Check that the version exists in the Redpanda repository'
135
+ };
136
+ }
137
+ }
138
+
139
+ module.exports = {
140
+ generateRpkDocs
141
+ };