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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/README.adoc +33 -1064
  2. package/bin/doc-tools-mcp.js +720 -0
  3. package/bin/doc-tools.js +1050 -50
  4. package/bin/mcp-tools/antora.js +153 -0
  5. package/bin/mcp-tools/cache.js +89 -0
  6. package/bin/mcp-tools/cloud-regions.js +127 -0
  7. package/bin/mcp-tools/content-review.js +196 -0
  8. package/bin/mcp-tools/crd-docs.js +153 -0
  9. package/bin/mcp-tools/frontmatter.js +138 -0
  10. package/bin/mcp-tools/generated-docs-review.js +887 -0
  11. package/bin/mcp-tools/helm-docs.js +152 -0
  12. package/bin/mcp-tools/index.js +245 -0
  13. package/bin/mcp-tools/job-queue.js +468 -0
  14. package/bin/mcp-tools/mcp-validation.js +266 -0
  15. package/bin/mcp-tools/metrics-docs.js +146 -0
  16. package/bin/mcp-tools/openapi.js +174 -0
  17. package/bin/mcp-tools/prompt-discovery.js +283 -0
  18. package/bin/mcp-tools/property-docs.js +157 -0
  19. package/bin/mcp-tools/rpcn-docs.js +113 -0
  20. package/bin/mcp-tools/rpk-docs.js +141 -0
  21. package/bin/mcp-tools/telemetry.js +211 -0
  22. package/bin/mcp-tools/utils.js +131 -0
  23. package/bin/mcp-tools/versions.js +168 -0
  24. package/cli-utils/convert-doc-links.js +1 -1
  25. package/cli-utils/github-token.js +58 -0
  26. package/cli-utils/self-managed-docs-branch.js +2 -2
  27. package/cli-utils/setup-mcp.js +313 -0
  28. package/docker-compose/25.1/transactions.md +1 -1
  29. package/docker-compose/transactions.md +1 -1
  30. package/extensions/DEVELOPMENT.adoc +464 -0
  31. package/extensions/README.adoc +124 -0
  32. package/extensions/REFERENCE.adoc +768 -0
  33. package/extensions/USER_GUIDE.adoc +339 -0
  34. package/extensions/generate-rp-connect-info.js +3 -4
  35. package/extensions/version-fetcher/get-latest-console-version.js +38 -27
  36. package/extensions/version-fetcher/get-latest-redpanda-version.js +65 -54
  37. package/extensions/version-fetcher/retry-util.js +88 -0
  38. package/extensions/version-fetcher/set-latest-version.js +6 -3
  39. package/macros/DEVELOPMENT.adoc +377 -0
  40. package/macros/README.adoc +105 -0
  41. package/macros/REFERENCE.adoc +222 -0
  42. package/macros/USER_GUIDE.adoc +220 -0
  43. package/macros/rp-connect-components.js +6 -6
  44. package/mcp/CLI_INTERFACE.adoc +384 -0
  45. package/mcp/COSTS.adoc +167 -0
  46. package/mcp/DEVELOPMENT.adoc +726 -0
  47. package/mcp/README.adoc +172 -0
  48. package/mcp/USER_GUIDE.adoc +1392 -0
  49. package/mcp/WRITER_EXTENSION_GUIDE.adoc +814 -0
  50. package/mcp/prompts/README.adoc +183 -0
  51. package/mcp/prompts/property-docs-guide.md +283 -0
  52. package/mcp/prompts/review-for-style.md +128 -0
  53. package/mcp/prompts/rpcn-connector-docs-guide.md +126 -0
  54. package/mcp/prompts/write-new-guide.md +222 -0
  55. package/mcp/team-standards/style-guide.md +321 -0
  56. package/mcp/templates/README.adoc +212 -0
  57. package/mcp/templates/prompt-review-template.md +80 -0
  58. package/mcp/templates/prompt-write-template.md +110 -0
  59. package/mcp/templates/resource-template.md +76 -0
  60. package/package.json +16 -5
  61. package/tools/bundle-openapi.js +20 -10
  62. package/tools/cloud-regions/generate-cloud-regions.js +1 -1
  63. package/tools/fetch-from-github.js +18 -4
  64. package/tools/gen-rpk-ascii.py +3 -1
  65. package/tools/generate-cli-docs.js +325 -0
  66. package/tools/get-console-version.js +4 -2
  67. package/tools/get-redpanda-version.js +4 -2
  68. package/tools/metrics/metrics.py +19 -7
  69. package/tools/property-extractor/Makefile +7 -1
  70. package/tools/property-extractor/cloud_config.py +4 -4
  71. package/tools/property-extractor/constant_resolver.py +11 -11
  72. package/tools/property-extractor/property_extractor.py +18 -16
  73. package/tools/property-extractor/topic_property_extractor.py +2 -2
  74. package/tools/property-extractor/transformers.py +7 -7
  75. package/tools/property-extractor/type_definition_extractor.py +4 -4
  76. package/tools/redpanda-connect/README.adoc +1 -1
  77. package/tools/redpanda-connect/generate-rpcn-connector-docs.js +5 -3
@@ -0,0 +1,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
+ };