@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,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Tools - Antora Structure
|
|
3
|
+
*
|
|
4
|
+
* OPTIMIZATION: This tool uses caching and should use Haiku model.
|
|
5
|
+
* - Caches structure for 1 hour (rarely changes)
|
|
6
|
+
* - No complex reasoning required (just directory scanning)
|
|
7
|
+
* - Recommended model: haiku
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const yaml = require('js-yaml');
|
|
13
|
+
const { MAX_RECURSION_DEPTH, DEFAULT_SKIP_DIRS, PLAYBOOK_NAMES } = require('./utils');
|
|
14
|
+
const cache = require('./cache');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Get Antora structure information for the current repository
|
|
18
|
+
* @param {string|{root: string, detected: boolean, type: string|null}} repoRoot - Repository root path or info object
|
|
19
|
+
* @param {string[]} [skipDirs=DEFAULT_SKIP_DIRS] - Directories to skip during search
|
|
20
|
+
* @param {Object} [options] - Additional options
|
|
21
|
+
* @param {boolean} [options.skipCache] - Skip cache and force fresh scan
|
|
22
|
+
* @returns {Object} Antora structure information
|
|
23
|
+
*/
|
|
24
|
+
function getAntoraStructure(repoRoot, skipDirs = DEFAULT_SKIP_DIRS, options = {}) {
|
|
25
|
+
const rootPath = typeof repoRoot === 'string' ? repoRoot : repoRoot.root;
|
|
26
|
+
const repoInfo = typeof repoRoot === 'object' ? repoRoot : { root: repoRoot, detected: true, type: null };
|
|
27
|
+
|
|
28
|
+
// Check cache first (1 hour TTL)
|
|
29
|
+
const cacheKey = `antora-structure:${rootPath}`;
|
|
30
|
+
if (!options.skipCache) {
|
|
31
|
+
const cached = cache.get(cacheKey);
|
|
32
|
+
if (cached) {
|
|
33
|
+
return {
|
|
34
|
+
...cached,
|
|
35
|
+
_cached: true,
|
|
36
|
+
_cacheHit: true
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const playbookPath = PLAYBOOK_NAMES
|
|
42
|
+
.map(name => path.join(rootPath, name))
|
|
43
|
+
.find(p => fs.existsSync(p));
|
|
44
|
+
|
|
45
|
+
let playbookContent = null;
|
|
46
|
+
if (playbookPath) {
|
|
47
|
+
try {
|
|
48
|
+
playbookContent = yaml.load(fs.readFileSync(playbookPath, 'utf8'));
|
|
49
|
+
} catch (err) {
|
|
50
|
+
console.error(`Warning: Failed to parse playbook at ${playbookPath}: ${err.message}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const antoraYmls = [];
|
|
55
|
+
const findAntoraYmls = (dir, depth = 0, visited = new Set()) => {
|
|
56
|
+
if (depth > MAX_RECURSION_DEPTH || !fs.existsSync(dir)) return;
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const realPath = fs.realpathSync(dir);
|
|
60
|
+
if (visited.has(realPath)) return;
|
|
61
|
+
visited.add(realPath);
|
|
62
|
+
|
|
63
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
64
|
+
for (const entry of entries) {
|
|
65
|
+
if (skipDirs.includes(entry.name)) continue;
|
|
66
|
+
|
|
67
|
+
const fullPath = path.join(dir, entry.name);
|
|
68
|
+
if (entry.isDirectory()) {
|
|
69
|
+
findAntoraYmls(fullPath, depth + 1, visited);
|
|
70
|
+
} else if (entry.name === 'antora.yml') {
|
|
71
|
+
antoraYmls.push(fullPath);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
} catch (err) {
|
|
75
|
+
console.error(`Warning: Failed to read directory ${dir}: ${err.message}`);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
findAntoraYmls(rootPath);
|
|
79
|
+
|
|
80
|
+
const components = antoraYmls.map(ymlPath => {
|
|
81
|
+
try {
|
|
82
|
+
const content = yaml.load(fs.readFileSync(ymlPath, 'utf8'));
|
|
83
|
+
const componentDir = path.dirname(ymlPath);
|
|
84
|
+
const modulesDir = path.join(componentDir, 'modules');
|
|
85
|
+
const modules = fs.existsSync(modulesDir)
|
|
86
|
+
? fs.readdirSync(modulesDir).filter(m => {
|
|
87
|
+
const stat = fs.statSync(path.join(modulesDir, m));
|
|
88
|
+
return stat.isDirectory();
|
|
89
|
+
})
|
|
90
|
+
: [];
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
name: content.name,
|
|
94
|
+
version: content.version,
|
|
95
|
+
title: content.title,
|
|
96
|
+
path: componentDir,
|
|
97
|
+
modules: modules.map(moduleName => {
|
|
98
|
+
const modulePath = path.join(modulesDir, moduleName);
|
|
99
|
+
return {
|
|
100
|
+
name: moduleName,
|
|
101
|
+
path: modulePath,
|
|
102
|
+
pages: fs.existsSync(path.join(modulePath, 'pages')),
|
|
103
|
+
partials: fs.existsSync(path.join(modulePath, 'partials')),
|
|
104
|
+
examples: fs.existsSync(path.join(modulePath, 'examples')),
|
|
105
|
+
attachments: fs.existsSync(path.join(modulePath, 'attachments')),
|
|
106
|
+
images: fs.existsSync(path.join(modulePath, 'images')),
|
|
107
|
+
};
|
|
108
|
+
}),
|
|
109
|
+
};
|
|
110
|
+
} catch (err) {
|
|
111
|
+
return { error: `Failed to parse ${ymlPath}: ${err.message}` };
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const result = {
|
|
116
|
+
repoRoot: rootPath,
|
|
117
|
+
repoInfo,
|
|
118
|
+
playbook: playbookContent,
|
|
119
|
+
playbookPath,
|
|
120
|
+
components,
|
|
121
|
+
hasDocTools: (() => {
|
|
122
|
+
// Check if we're in the source repo (docs-extensions-and-macros)
|
|
123
|
+
if (fs.existsSync(path.join(rootPath, 'bin', 'doc-tools.js'))) {
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Check if doc-tools is available via npx or as installed dependency
|
|
128
|
+
try {
|
|
129
|
+
const { execSync } = require('child_process');
|
|
130
|
+
execSync('npx doc-tools --version', {
|
|
131
|
+
stdio: 'ignore',
|
|
132
|
+
timeout: 5000,
|
|
133
|
+
cwd: rootPath
|
|
134
|
+
});
|
|
135
|
+
return true;
|
|
136
|
+
} catch {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
})(),
|
|
140
|
+
// Metadata for cost optimization
|
|
141
|
+
_modelRecommendation: 'haiku',
|
|
142
|
+
_reasoning: 'Simple directory scanning, no complex reasoning required'
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// Cache for 1 hour
|
|
146
|
+
cache.set(cacheKey, result, 60 * 60 * 1000);
|
|
147
|
+
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
module.exports = {
|
|
152
|
+
getAntoraStructure
|
|
153
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple in-memory cache for MCP tools
|
|
3
|
+
*
|
|
4
|
+
* Reduces costs by caching expensive operations like:
|
|
5
|
+
* - Style guide loading
|
|
6
|
+
* - Antora structure scanning
|
|
7
|
+
* - Static content that rarely changes
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const cache = new Map();
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Get a cached value
|
|
14
|
+
* @param {string} key - Cache key
|
|
15
|
+
* @returns {any|null} Cached value or null if not found/expired
|
|
16
|
+
*/
|
|
17
|
+
function get(key) {
|
|
18
|
+
const entry = cache.get(key);
|
|
19
|
+
if (!entry) return null;
|
|
20
|
+
|
|
21
|
+
// Check if expired
|
|
22
|
+
if (entry.expiresAt && Date.now() > entry.expiresAt) {
|
|
23
|
+
cache.delete(key);
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return entry.value;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Set a cached value
|
|
32
|
+
* @param {string} key - Cache key
|
|
33
|
+
* @param {any} value - Value to cache
|
|
34
|
+
* @param {number} ttl - Time to live in milliseconds (default: 1 hour)
|
|
35
|
+
*/
|
|
36
|
+
function set(key, value, ttl = 60 * 60 * 1000) {
|
|
37
|
+
cache.set(key, {
|
|
38
|
+
value,
|
|
39
|
+
expiresAt: ttl ? Date.now() + ttl : null
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Clear all cached values
|
|
45
|
+
*/
|
|
46
|
+
function clear() {
|
|
47
|
+
cache.clear();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Clear expired entries
|
|
52
|
+
*/
|
|
53
|
+
function prune() {
|
|
54
|
+
const now = Date.now();
|
|
55
|
+
for (const [key, entry] of cache.entries()) {
|
|
56
|
+
if (entry.expiresAt && now > entry.expiresAt) {
|
|
57
|
+
cache.delete(key);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get or compute a value (cache aside pattern)
|
|
64
|
+
* @param {string} key - Cache key
|
|
65
|
+
* @param {Function} compute - Function to compute value if not cached
|
|
66
|
+
* @param {number} ttl - Time to live in milliseconds
|
|
67
|
+
* @returns {Promise<any>} Cached or computed value
|
|
68
|
+
*/
|
|
69
|
+
async function getOrCompute(key, compute, ttl = 60 * 60 * 1000) {
|
|
70
|
+
const cached = get(key);
|
|
71
|
+
if (cached !== null) {
|
|
72
|
+
return cached;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const value = await compute();
|
|
76
|
+
set(key, value, ttl);
|
|
77
|
+
return value;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Prune expired entries every 5 minutes
|
|
81
|
+
setInterval(prune, 5 * 60 * 1000);
|
|
82
|
+
|
|
83
|
+
module.exports = {
|
|
84
|
+
get,
|
|
85
|
+
set,
|
|
86
|
+
clear,
|
|
87
|
+
prune,
|
|
88
|
+
getOrCompute
|
|
89
|
+
};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Tools - Cloud Regions Table Generation
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const { spawnSync } = require('child_process');
|
|
6
|
+
const { findRepoRoot, getDocToolsCommand, MAX_EXEC_BUFFER_SIZE, DEFAULT_COMMAND_TIMEOUT } = require('./utils');
|
|
7
|
+
const { getAntoraStructure } = require('./antora');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Generate cloud regions table documentation
|
|
11
|
+
* @param {Object} args - Arguments
|
|
12
|
+
* @param {string} [args.output] - Output file path (relative to repo root)
|
|
13
|
+
* @param {string} [args.format] - Output format: 'md' or 'adoc'
|
|
14
|
+
* @param {string} [args.owner] - GitHub repository owner
|
|
15
|
+
* @param {string} [args.repo] - GitHub repository name
|
|
16
|
+
* @param {string} [args.path] - Path to YAML file in repository
|
|
17
|
+
* @param {string} [args.ref] - Git reference (branch, tag, or commit SHA)
|
|
18
|
+
* @param {string} [args.template] - Path to custom Handlebars template
|
|
19
|
+
* @param {boolean} [args.dry_run] - Print output to stdout instead of writing file
|
|
20
|
+
* @returns {Object} Generation results
|
|
21
|
+
*/
|
|
22
|
+
function generateCloudRegions(args = {}) {
|
|
23
|
+
const repoRoot = findRepoRoot();
|
|
24
|
+
const structure = getAntoraStructure(repoRoot);
|
|
25
|
+
|
|
26
|
+
if (!structure.hasDocTools) {
|
|
27
|
+
return {
|
|
28
|
+
success: false,
|
|
29
|
+
error: 'doc-tools not found in this repository',
|
|
30
|
+
suggestion: 'Navigate to the docs-extensions-and-macros repository'
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
// Get doc-tools command (handles both local and installed)
|
|
36
|
+
const docTools = getDocToolsCommand(repoRoot);
|
|
37
|
+
|
|
38
|
+
// Build command arguments array
|
|
39
|
+
const baseArgs = ['generate', 'cloud-regions'];
|
|
40
|
+
|
|
41
|
+
if (args.output) {
|
|
42
|
+
baseArgs.push('--output');
|
|
43
|
+
baseArgs.push(args.output);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (args.format) {
|
|
47
|
+
baseArgs.push('--format');
|
|
48
|
+
baseArgs.push(args.format);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (args.owner) {
|
|
52
|
+
baseArgs.push('--owner');
|
|
53
|
+
baseArgs.push(args.owner);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (args.repo) {
|
|
57
|
+
baseArgs.push('--repo');
|
|
58
|
+
baseArgs.push(args.repo);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (args.path) {
|
|
62
|
+
baseArgs.push('--path');
|
|
63
|
+
baseArgs.push(args.path);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (args.ref) {
|
|
67
|
+
baseArgs.push('--ref');
|
|
68
|
+
baseArgs.push(args.ref);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (args.template) {
|
|
72
|
+
baseArgs.push('--template');
|
|
73
|
+
baseArgs.push(args.template);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (args.dry_run) {
|
|
77
|
+
baseArgs.push('--dry-run');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const result = spawnSync(docTools.program, docTools.getArgs(baseArgs), {
|
|
81
|
+
cwd: repoRoot.root,
|
|
82
|
+
encoding: 'utf8',
|
|
83
|
+
stdio: 'pipe',
|
|
84
|
+
maxBuffer: MAX_EXEC_BUFFER_SIZE,
|
|
85
|
+
timeout: DEFAULT_COMMAND_TIMEOUT
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Check for spawn errors
|
|
89
|
+
if (result.error) {
|
|
90
|
+
throw new Error(`Failed to execute command: ${result.error.message}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Check for non-zero exit codes
|
|
94
|
+
if (result.status !== 0) {
|
|
95
|
+
const errorMsg = result.stderr || `Command failed with exit code ${result.status}`;
|
|
96
|
+
throw new Error(errorMsg);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const output = result.stdout;
|
|
100
|
+
|
|
101
|
+
// Parse output to extract information
|
|
102
|
+
const regionCountMatch = output.match(/(\d+) regions?/i);
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
success: true,
|
|
106
|
+
format: args.format || 'md',
|
|
107
|
+
ref: args.ref || 'integration',
|
|
108
|
+
regions_documented: regionCountMatch ? parseInt(regionCountMatch[1]) : null,
|
|
109
|
+
files_generated: args.dry_run ? [] : [args.output || 'cloud-controlplane/x-topics/cloud-regions.md'],
|
|
110
|
+
output: output.trim(),
|
|
111
|
+
summary: `Generated cloud regions table from GitHub YAML`
|
|
112
|
+
};
|
|
113
|
+
} catch (err) {
|
|
114
|
+
return {
|
|
115
|
+
success: false,
|
|
116
|
+
error: err.message,
|
|
117
|
+
stdout: err.stdout || '',
|
|
118
|
+
stderr: err.stderr || '',
|
|
119
|
+
exitCode: err.status,
|
|
120
|
+
suggestion: 'Check that you have access to the GitHub repository and the YAML file exists'
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
module.exports = {
|
|
126
|
+
generateCloudRegions
|
|
127
|
+
};
|
|
@@ -0,0 +1,196 @@
|
|
|
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
|
+
};
|