@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.
- package/README.adoc +33 -1064
- package/bin/doc-tools-mcp.js +720 -0
- package/bin/doc-tools.js +1050 -50
- package/bin/mcp-tools/antora.js +153 -0
- package/bin/mcp-tools/cache.js +89 -0
- package/bin/mcp-tools/cloud-regions.js +127 -0
- package/bin/mcp-tools/content-review.js +196 -0
- package/bin/mcp-tools/crd-docs.js +153 -0
- package/bin/mcp-tools/frontmatter.js +138 -0
- package/bin/mcp-tools/generated-docs-review.js +887 -0
- package/bin/mcp-tools/helm-docs.js +152 -0
- package/bin/mcp-tools/index.js +245 -0
- package/bin/mcp-tools/job-queue.js +468 -0
- package/bin/mcp-tools/mcp-validation.js +266 -0
- package/bin/mcp-tools/metrics-docs.js +146 -0
- package/bin/mcp-tools/openapi.js +174 -0
- package/bin/mcp-tools/prompt-discovery.js +283 -0
- package/bin/mcp-tools/property-docs.js +157 -0
- package/bin/mcp-tools/rpcn-docs.js +113 -0
- package/bin/mcp-tools/rpk-docs.js +141 -0
- package/bin/mcp-tools/telemetry.js +211 -0
- package/bin/mcp-tools/utils.js +131 -0
- package/bin/mcp-tools/versions.js +168 -0
- package/cli-utils/convert-doc-links.js +1 -1
- package/cli-utils/github-token.js +58 -0
- package/cli-utils/self-managed-docs-branch.js +2 -2
- package/cli-utils/setup-mcp.js +313 -0
- package/docker-compose/25.1/transactions.md +1 -1
- package/docker-compose/transactions.md +1 -1
- package/extensions/DEVELOPMENT.adoc +464 -0
- package/extensions/README.adoc +124 -0
- package/extensions/REFERENCE.adoc +768 -0
- package/extensions/USER_GUIDE.adoc +339 -0
- package/extensions/generate-rp-connect-info.js +3 -4
- package/extensions/version-fetcher/get-latest-console-version.js +38 -27
- package/extensions/version-fetcher/get-latest-redpanda-helm-version-from-operator.js +1 -1
- package/extensions/version-fetcher/get-latest-redpanda-version.js +65 -54
- package/extensions/version-fetcher/retry-util.js +88 -0
- package/extensions/version-fetcher/set-latest-version.js +6 -3
- package/macros/DEVELOPMENT.adoc +377 -0
- package/macros/README.adoc +105 -0
- package/macros/REFERENCE.adoc +222 -0
- package/macros/USER_GUIDE.adoc +220 -0
- package/macros/rp-connect-components.js +6 -6
- package/package.json +12 -3
- package/tools/bundle-openapi.js +20 -10
- package/tools/cloud-regions/generate-cloud-regions.js +1 -1
- package/tools/fetch-from-github.js +18 -4
- package/tools/gen-rpk-ascii.py +3 -1
- package/tools/generate-cli-docs.js +325 -0
- package/tools/get-console-version.js +4 -2
- package/tools/get-redpanda-version.js +4 -2
- package/tools/metrics/metrics.py +19 -7
- package/tools/property-extractor/Makefile +7 -1
- package/tools/property-extractor/cloud_config.py +4 -4
- package/tools/property-extractor/constant_resolver.py +11 -11
- package/tools/property-extractor/property_extractor.py +18 -16
- package/tools/property-extractor/topic_property_extractor.py +2 -2
- package/tools/property-extractor/transformers.py +7 -7
- package/tools/property-extractor/type_definition_extractor.py +4 -4
- package/tools/redpanda-connect/README.adoc +1 -1
- 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
|
+
};
|