@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/bin/doc-tools-mcp.js +84 -131
- package/bin/doc-tools.js +105 -192
- package/bin/mcp-tools/generated-docs-review.js +11 -158
- package/bin/mcp-tools/index.js +0 -4
- package/bin/mcp-tools/mcp-validation.js +44 -149
- package/cli-utils/github-token.js +6 -2
- package/mcp/COSTS.adoc +7 -17
- package/mcp/DEVELOPMENT.adoc +2 -175
- package/mcp/README.adoc +27 -61
- package/mcp/WRITER_EXTENSION_GUIDE.adoc +128 -738
- package/package.json +1 -1
- package/tools/redpanda-connect/rpcn-connector-docs-handler.js +60 -60
- package/bin/mcp-tools/content-review.js +0 -196
- package/bin/mcp-tools/frontmatter.js +0 -138
- package/bin/mcp-tools/prompt-discovery.js +0 -283
- package/mcp/prompts/README.adoc +0 -183
- package/mcp/prompts/property-docs-guide.md +0 -283
- package/mcp/prompts/review-for-style.md +0 -128
- package/mcp/prompts/rpcn-connector-docs-guide.md +0 -126
- package/mcp/prompts/write-new-guide.md +0 -222
- package/mcp/team-standards/style-guide.md +0 -321
- package/mcp/templates/README.adoc +0 -212
- package/mcp/templates/prompt-review-template.md +0 -80
- package/mcp/templates/prompt-write-template.md +0 -110
- package/mcp/templates/resource-template.md +0 -76
package/package.json
CHANGED
|
@@ -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
|
|
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
|
|
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:
|
|
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:
|
|
1252
|
+
missingFromCloudDocs.push({ type, name, path: fullPath })
|
|
1215
1253
|
} else {
|
|
1216
|
-
// Non-404 error
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
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(`
|
|
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(`
|
|
1270
|
+
console.log(` - ${type}/${name} - Status ${status}: ${message}`)
|
|
1271
1271
|
})
|
|
1272
|
-
console.log(`
|
|
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(`
|
|
1274
|
+
console.log(` INFO: Additionally, ${missingFromCloudDocs.length} connector(s) confirmed missing from cloud-docs:`)
|
|
1275
1275
|
missingFromCloudDocs.forEach(({ type, name }) => {
|
|
1276
|
-
console.log(`
|
|
1276
|
+
console.log(` - ${type}/${name}`)
|
|
1277
1277
|
})
|
|
1278
1278
|
}
|
|
1279
1279
|
} else if (missingFromCloudDocs.length > 0) {
|
|
1280
|
-
console.log(`
|
|
1280
|
+
console.log(` WARNING: Found ${missingFromCloudDocs.length} cloud-supported connector(s) missing from cloud-docs:`)
|
|
1281
1281
|
missingFromCloudDocs.forEach(({ type, name }) => {
|
|
1282
|
-
console.log(`
|
|
1282
|
+
console.log(` - ${type}/${name}`)
|
|
1283
1283
|
})
|
|
1284
|
-
console.log(`
|
|
1284
|
+
console.log(` INFO: These connectors need pages added to https://github.com/redpanda-data/cloud-docs`)
|
|
1285
1285
|
} else {
|
|
1286
|
-
console.log(`
|
|
1286
|
+
console.log(` All cloud-supported connectors have pages in cloud-docs`)
|
|
1287
1287
|
}
|
|
1288
1288
|
} catch (error) {
|
|
1289
|
-
console.log(`
|
|
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
|
-
};
|