@redpanda-data/docs-extensions-and-macros 4.13.5 → 4.14.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redpanda-data/docs-extensions-and-macros",
3
- "version": "4.13.5",
3
+ "version": "4.14.0",
4
4
  "description": "Antora extensions and macros developed for Redpanda documentation.",
5
5
  "keywords": [
6
6
  "antora",
@@ -1172,12 +1172,40 @@ async function handleRpcnConnectorDocs (options) {
1172
1172
  const missingFromCloudDocs = []
1173
1173
  const cloudDocsErrors = []
1174
1174
  if (cloudSupportedSet.size > 0 && options.checkCloudDocs !== false) {
1175
- console.log('\n ℹ️ Checking cloud-docs repository for missing connector pages...')
1175
+ console.log('\n INFO: Checking cloud-docs repository for missing connector pages...')
1176
1176
 
1177
1177
  // Use shared Octokit instance
1178
1178
  const octokit = require('../../cli-utils/octokit-client')
1179
1179
 
1180
1180
  try {
1181
+ // Optimization: Fetch entire directory tree in 1 API call instead of 471 individual calls
1182
+ console.log(' Fetching cloud-docs directory tree (1 API call)...')
1183
+
1184
+ let existingFiles = new Set()
1185
+
1186
+ try {
1187
+ // Get the tree for the components directory
1188
+ const { data: tree } = await octokit.git.getTree({
1189
+ owner: 'redpanda-data',
1190
+ repo: 'cloud-docs',
1191
+ tree_sha: 'main:modules/develop/pages/connect/components',
1192
+ recursive: true
1193
+ })
1194
+
1195
+ // Build a set of existing file paths for O(1) lookup
1196
+ tree.tree.forEach(item => {
1197
+ if (item.type === 'blob' && item.path.endsWith('.adoc')) {
1198
+ existingFiles.add(item.path)
1199
+ }
1200
+ })
1201
+
1202
+ console.log(` Loaded ${existingFiles.size} existing connector pages from cloud-docs`)
1203
+ } catch (treeError) {
1204
+ console.log(` WARNING: Could not fetch tree (${treeError.status}), falling back to individual checks`)
1205
+ // If tree API fails, fall back to individual checks (old behavior)
1206
+ existingFiles = null
1207
+ }
1208
+
1181
1209
  // Check each cloud-supported connector
1182
1210
  // Filter to only check actual connector/component types that need individual pages
1183
1211
  const connectorTypes = ['inputs', 'outputs', 'processors', 'caches', 'buffers', 'scanners', 'metrics', 'tracers']
@@ -1198,95 +1226,67 @@ async function handleRpcnConnectorDocs (options) {
1198
1226
  }
1199
1227
  }
1200
1228
 
1201
- const cloudDocsPath = `modules/develop/pages/connect/components/${type}/${name}.adoc`
1229
+ const relativePath = `${type}/${name}.adoc`
1230
+ const fullPath = `modules/develop/pages/connect/components/${relativePath}`
1231
+
1232
+ // Fast path: Check against tree if we have it
1233
+ if (existingFiles !== null) {
1234
+ if (!existingFiles.has(relativePath)) {
1235
+ missingFromCloudDocs.push({ type, name, path: fullPath })
1236
+ }
1237
+ continue
1238
+ }
1202
1239
 
1240
+ // Fallback path: Individual API calls (only if tree fetch failed)
1203
1241
  try {
1204
1242
  await octokit.repos.getContent({
1205
1243
  owner: 'redpanda-data',
1206
1244
  repo: 'cloud-docs',
1207
- path: cloudDocsPath,
1245
+ path: fullPath,
1208
1246
  ref: 'main'
1209
1247
  })
1210
1248
  // File exists, no action needed
1211
1249
  } catch (error) {
1212
1250
  if (error.status === 404) {
1213
1251
  // File doesn't exist in cloud-docs
1214
- missingFromCloudDocs.push({ type, name, path: cloudDocsPath })
1252
+ missingFromCloudDocs.push({ type, name, path: fullPath })
1215
1253
  } else {
1216
- // Non-404 error (auth, rate-limit, network, etc.)
1217
- // Try fallback: check raw URL without authentication
1218
- const rawUrl = `https://raw.githubusercontent.com/redpanda-data/cloud-docs/main/${cloudDocsPath}`
1219
- try {
1220
- const https = require('https')
1221
- const { URL } = require('url')
1222
- const parsedUrl = new URL(rawUrl)
1223
-
1224
- await new Promise((resolve, reject) => {
1225
- const req = https.request({
1226
- hostname: parsedUrl.hostname,
1227
- path: parsedUrl.pathname,
1228
- method: 'HEAD',
1229
- timeout: 5000
1230
- }, (res) => {
1231
- if (res.statusCode === 200) {
1232
- resolve() // File exists
1233
- } else if (res.statusCode === 404) {
1234
- reject(new Error('404'))
1235
- } else {
1236
- reject(new Error(`Status ${res.statusCode}`))
1237
- }
1238
- })
1239
- req.on('error', reject)
1240
- req.on('timeout', () => {
1241
- req.destroy()
1242
- reject(new Error('Timeout'))
1243
- })
1244
- req.end()
1245
- })
1246
- // Fallback succeeded - file exists, no action needed
1247
- } catch (fallbackError) {
1248
- if (fallbackError.message === '404') {
1249
- // Confirmed missing via fallback
1250
- missingFromCloudDocs.push({ type, name, path: cloudDocsPath })
1251
- } else {
1252
- // Both API and fallback failed
1253
- cloudDocsErrors.push({
1254
- type,
1255
- name,
1256
- path: cloudDocsPath,
1257
- status: error.status || 'unknown',
1258
- message: `API: ${error.message}; Fallback: ${fallbackError.message}`
1259
- })
1260
- }
1261
- }
1254
+ // Non-404 error - record as error
1255
+ cloudDocsErrors.push({
1256
+ type,
1257
+ name,
1258
+ path: fullPath,
1259
+ status: error.status || 'unknown',
1260
+ message: error.message
1261
+ })
1262
1262
  }
1263
1263
  }
1264
1264
  }
1265
1265
 
1266
1266
  // Report results
1267
1267
  if (cloudDocsErrors.length > 0) {
1268
- console.log(` ⚠️ Encountered ${cloudDocsErrors.length} error(s) while checking cloud-docs (check inconclusive):`)
1268
+ console.log(` WARNING: Encountered ${cloudDocsErrors.length} error(s) while checking cloud-docs (check inconclusive):`)
1269
1269
  cloudDocsErrors.forEach(({ type, name, status, message }) => {
1270
- console.log(` ${type}/${name} - Status ${status}: ${message}`)
1270
+ console.log(` - ${type}/${name} - Status ${status}: ${message}`)
1271
1271
  })
1272
- console.log(` ℹ️ Please resolve these errors (e.g., check GITHUB_TOKEN or VBOT_GITHUB_API_TOKEN, API rate limits, network connectivity)`)
1272
+ console.log(` INFO: Please resolve these errors (e.g., check GITHUB_TOKEN or VBOT_GITHUB_API_TOKEN, API rate limits, network connectivity)`)
1273
1273
  if (missingFromCloudDocs.length > 0) {
1274
- console.log(` ℹ️ Additionally, ${missingFromCloudDocs.length} connector(s) confirmed missing from cloud-docs:`)
1274
+ console.log(` INFO: Additionally, ${missingFromCloudDocs.length} connector(s) confirmed missing from cloud-docs:`)
1275
1275
  missingFromCloudDocs.forEach(({ type, name }) => {
1276
- console.log(` ${type}/${name}`)
1276
+ console.log(` - ${type}/${name}`)
1277
1277
  })
1278
1278
  }
1279
1279
  } else if (missingFromCloudDocs.length > 0) {
1280
- console.log(` ⚠️ Found ${missingFromCloudDocs.length} cloud-supported connector(s) missing from cloud-docs:`)
1280
+ console.log(` WARNING: Found ${missingFromCloudDocs.length} cloud-supported connector(s) missing from cloud-docs:`)
1281
1281
  missingFromCloudDocs.forEach(({ type, name }) => {
1282
- console.log(` ${type}/${name}`)
1282
+ console.log(` - ${type}/${name}`)
1283
1283
  })
1284
- console.log(` ℹ️ These connectors need pages added to https://github.com/redpanda-data/cloud-docs`)
1284
+ console.log(` INFO: These connectors need pages added to https://github.com/redpanda-data/cloud-docs`)
1285
1285
  } else {
1286
- console.log(` All cloud-supported connectors have pages in cloud-docs`)
1286
+ console.log(` All cloud-supported connectors have pages in cloud-docs`)
1287
1287
  }
1288
1288
  } catch (error) {
1289
- console.log(` ⚠️ Could not check cloud-docs: ${error.message}`)
1289
+ console.log(` WARNING: Could not check cloud-docs: ${error.message}`)
1290
1290
  }
1291
1291
  }
1292
1292
 
@@ -1,196 +0,0 @@
1
- /**
2
- * Content Review Tool
3
- *
4
- * Provides comprehensive content review with automatic style guide context.
5
- * This tool automatically fetches the style guide and provides review instructions,
6
- * so the LLM has everything needed in a single call.
7
- *
8
- * OPTIMIZATION: Caches style guide and review instructions.
9
- * - Style guide cached for 1 hour (rarely changes)
10
- * - Review instructions cached permanently (static)
11
- */
12
-
13
- const fs = require('fs');
14
- const path = require('path');
15
- const cache = require('./cache');
16
-
17
- /**
18
- * Review content with automatic style guide context
19
- * @param {Object} args - Tool arguments
20
- * @param {string} args.content - Content to review
21
- * @param {string} [args.focus] - What to focus on (comprehensive, style, terminology, clarity)
22
- * @param {string} [baseDir] - Base directory for finding resources
23
- * @returns {Object} Review context including style guide
24
- */
25
- function reviewContent(args, baseDir) {
26
- if (!args || !args.content) {
27
- return {
28
- success: false,
29
- error: 'Missing required parameter: content'
30
- };
31
- }
32
-
33
- const focus = args.focus || 'comprehensive';
34
-
35
- // Validate focus parameter
36
- const validFocus = ['comprehensive', 'style', 'terminology', 'clarity'];
37
- if (!validFocus.includes(focus)) {
38
- return {
39
- success: false,
40
- error: `Invalid focus parameter. Must be one of: ${validFocus.join(', ')}`
41
- };
42
- }
43
-
44
- // Find base directory (either provided or from repo root)
45
- const actualBaseDir = baseDir || path.join(__dirname, '../..');
46
-
47
- // Load style guide resource (with caching)
48
- const styleGuidePath = path.join(actualBaseDir, 'mcp', 'team-standards', 'style-guide.md');
49
- const cacheKey = 'style-guide-content';
50
-
51
- let styleGuide = cache.get(cacheKey);
52
-
53
- if (!styleGuide) {
54
- if (!fs.existsSync(styleGuidePath)) {
55
- return {
56
- success: false,
57
- error: `Style guide not found at: ${styleGuidePath}`,
58
- suggestion: 'Ensure the MCP server is running from the correct directory'
59
- };
60
- }
61
-
62
- try {
63
- styleGuide = fs.readFileSync(styleGuidePath, 'utf8');
64
- cache.set(cacheKey, styleGuide, 60 * 60 * 1000); // Cache for 1 hour
65
- } catch (err) {
66
- return {
67
- success: false,
68
- error: `Failed to read style guide: ${err.message}`
69
- };
70
- }
71
- }
72
-
73
- // Build review instructions based on focus (cached permanently as they're static)
74
- const instructionsCacheKey = `review-instructions:${focus}`;
75
- let instructions = cache.get(instructionsCacheKey);
76
-
77
- if (!instructions) {
78
- instructions = buildReviewInstructions(focus);
79
- cache.set(instructionsCacheKey, instructions, 24 * 60 * 60 * 1000); // Cache for 24 hours
80
- }
81
-
82
- // Return the bundled context
83
- return {
84
- success: true,
85
- message: 'Style guide loaded. Please review the content according to the instructions below.',
86
- styleGuide,
87
- instructions,
88
- content: args.content,
89
- focus,
90
- // Provide formatted output that LLM can easily parse
91
- reviewContext: formatReviewContext(styleGuide, instructions, args.content, focus)
92
- };
93
- }
94
-
95
- /**
96
- * Build review instructions based on focus area
97
- * @param {string} focus - What to focus on
98
- * @returns {string} Review instructions
99
- */
100
- function buildReviewInstructions(focus) {
101
- const baseInstructions = `
102
- # Content Review Instructions
103
-
104
- You are reviewing documentation for the Redpanda documentation team.
105
-
106
- The style guide has been automatically loaded for your reference.
107
-
108
- ## Your task
109
-
110
- Review the provided content and provide detailed, actionable feedback.
111
- `;
112
-
113
- const focusInstructions = {
114
- comprehensive: `
115
- Review all aspects of the content:
116
-
117
- 1. **Style violations** - Check against the style guide (capitalization, formatting, structure)
118
- 2. **Terminology issues** - Verify correct usage of approved terms
119
- 3. **Voice and tone** - Ensure consistent, appropriate tone
120
- 4. **Clarity and readability** - Identify confusing or unclear sections
121
- 5. **Technical accuracy** - Flag any technical issues (if detectable)
122
- 6. **Formatting** - Check AsciiDoc formatting, code blocks, lists
123
- 7. **Accessibility** - Verify heading hierarchy, alt text, link text
124
-
125
- Provide specific line numbers or sections for each issue found.
126
- `,
127
- style: `
128
- Focus on style guide compliance:
129
-
130
- 1. **Formatting violations** - Capitalization, punctuation, spacing
131
- 2. **Structural issues** - Heading hierarchy, section organization
132
- 3. **Voice and tone** - Too formal, too casual, or inconsistent
133
- 4. **Style guide rules** - Any violations of documented standards
134
-
135
- Reference specific style guide sections when identifying issues.
136
- `,
137
- terminology: `
138
- Focus on terminology:
139
-
140
- 1. **Incorrect terms** - Wrong capitalization, spelling, or usage
141
- 2. **Deprecated terms** - Outdated terms that should be replaced
142
- 3. **Inconsistent usage** - Same concept referred to differently
143
- 4. **Missing approved terms** - Concepts that should use glossary terms
144
-
145
- Check against the terminology section of the style guide.
146
- `,
147
- clarity: `
148
- Focus on clarity and readability:
149
-
150
- 1. **Complex sentences** - Sentences that are too long or convoluted
151
- 2. **Unclear explanations** - Technical concepts that need more context
152
- 3. **Poor structure** - Content organization issues
153
- 4. **Missing context** - Assumptions about reader knowledge
154
- 5. **Confusing language** - Jargon without explanation
155
-
156
- Suggest specific improvements for each issue.
157
- `
158
- };
159
-
160
- return baseInstructions + (focusInstructions[focus] || focusInstructions.comprehensive);
161
- }
162
-
163
- /**
164
- * Format the complete review context for the LLM
165
- * @param {string} styleGuide - Style guide content
166
- * @param {string} instructions - Review instructions
167
- * @param {string} content - Content to review
168
- * @param {string} focus - Focus area
169
- * @returns {string} Formatted review context
170
- */
171
- function formatReviewContext(styleGuide, instructions, content, focus) {
172
- return `${instructions}
173
-
174
- ---
175
-
176
- # Style Guide Reference
177
-
178
- ${styleGuide}
179
-
180
- ---
181
-
182
- # Content to Review
183
-
184
- ${content}
185
-
186
- ---
187
-
188
- **Focus Area**: ${focus}
189
-
190
- Please provide your review above, organized by issue type with specific line/section references.
191
- `;
192
- }
193
-
194
- module.exports = {
195
- reviewContent
196
- };
@@ -1,138 +0,0 @@
1
- /**
2
- * Frontmatter Parser for MCP Prompts
3
- *
4
- * Parses YAML frontmatter from markdown files to extract metadata.
5
- * Supports validation against JSON schemas.
6
- */
7
-
8
- const yaml = require('js-yaml');
9
-
10
- /**
11
- * Parse frontmatter from markdown content
12
- * @param {string} content - Markdown content with optional frontmatter
13
- * @returns {{ metadata: Object, content: string }} Parsed frontmatter and remaining content
14
- */
15
- function parseFrontmatter(content) {
16
- const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
17
- const match = content.match(frontmatterRegex);
18
-
19
- if (!match) {
20
- // No frontmatter found - return empty metadata and full content
21
- return {
22
- metadata: {},
23
- content: content
24
- };
25
- }
26
-
27
- try {
28
- const metadata = yaml.load(match[1]) || {};
29
- const remainingContent = match[2];
30
-
31
- return {
32
- metadata,
33
- content: remainingContent
34
- };
35
- } catch (err) {
36
- throw new Error(`Failed to parse YAML frontmatter: ${err.message}`);
37
- }
38
- }
39
-
40
- /**
41
- * JSON Schema for prompt frontmatter
42
- */
43
- const promptFrontmatterSchema = {
44
- type: 'object',
45
- properties: {
46
- description: {
47
- type: 'string',
48
- minLength: 10,
49
- description: 'Description of what this prompt does'
50
- },
51
- version: {
52
- type: 'string',
53
- pattern: '^\\d+\\.\\d+\\.\\d+$',
54
- description: 'Semantic version (for example, 1.0.0)'
55
- },
56
- arguments: {
57
- type: 'array',
58
- description: 'Arguments this prompt accepts',
59
- items: {
60
- type: 'object',
61
- required: ['name', 'description', 'required'],
62
- properties: {
63
- name: {
64
- type: 'string',
65
- pattern: '^[a-z_][a-z0-9_]*$',
66
- description: 'Argument name (lowercase, underscores allowed)'
67
- },
68
- description: {
69
- type: 'string',
70
- minLength: 5,
71
- description: 'What this argument is for'
72
- },
73
- required: {
74
- type: 'boolean',
75
- description: 'Whether this argument is required'
76
- }
77
- },
78
- additionalProperties: false
79
- }
80
- },
81
- argumentFormat: {
82
- type: 'string',
83
- enum: ['content-append', 'structured'],
84
- description: 'How to format arguments when building the prompt'
85
- }
86
- },
87
- additionalProperties: false
88
- };
89
-
90
- /**
91
- * Validate frontmatter against schema
92
- * @param {Object} metadata - Parsed frontmatter metadata
93
- * @param {string} filename - File name (for error messages)
94
- * @param {Object} schema - JSON schema to validate against
95
- * @throws {Error} If validation fails
96
- */
97
- function validateFrontmatter(metadata, filename, schema = promptFrontmatterSchema) {
98
- const Ajv = require('ajv');
99
- const ajv = new Ajv({ allErrors: true });
100
-
101
- const valid = ajv.validate(schema, metadata);
102
-
103
- if (!valid) {
104
- const errors = ajv.errors.map(err => {
105
- const path = err.instancePath || 'root';
106
- return ` - ${path}: ${err.message}`;
107
- }).join('\n');
108
-
109
- throw new Error(`Invalid frontmatter in ${filename}:\n${errors}`);
110
- }
111
- }
112
-
113
- /**
114
- * Parse and validate prompt file
115
- * @param {string} content - File content
116
- * @param {string} filename - File name
117
- * @returns {{ metadata: Object, content: string }} Validated metadata and content
118
- */
119
- function parsePromptFile(content, filename) {
120
- const { metadata, content: promptContent } = parseFrontmatter(content);
121
-
122
- // If metadata exists, validate it
123
- if (Object.keys(metadata).length > 0) {
124
- validateFrontmatter(metadata, filename);
125
- }
126
-
127
- return {
128
- metadata,
129
- content: promptContent
130
- };
131
- }
132
-
133
- module.exports = {
134
- parseFrontmatter,
135
- parsePromptFile,
136
- validateFrontmatter,
137
- promptFrontmatterSchema
138
- };