skrypt-ai 0.4.2 → 0.6.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.
Files changed (159) hide show
  1. package/dist/auth/index.d.ts +13 -3
  2. package/dist/auth/index.js +101 -9
  3. package/dist/auth/keychain.d.ts +5 -0
  4. package/dist/auth/keychain.js +82 -0
  5. package/dist/auth/notices.d.ts +3 -0
  6. package/dist/auth/notices.js +42 -0
  7. package/dist/autofix/index.d.ts +0 -4
  8. package/dist/autofix/index.js +10 -24
  9. package/dist/capture/browser.d.ts +11 -0
  10. package/dist/capture/browser.js +173 -0
  11. package/dist/capture/diff.d.ts +23 -0
  12. package/dist/capture/diff.js +52 -0
  13. package/dist/capture/index.d.ts +23 -0
  14. package/dist/capture/index.js +210 -0
  15. package/dist/capture/naming.d.ts +17 -0
  16. package/dist/capture/naming.js +45 -0
  17. package/dist/capture/parser.d.ts +15 -0
  18. package/dist/capture/parser.js +80 -0
  19. package/dist/capture/types.d.ts +57 -0
  20. package/dist/capture/types.js +1 -0
  21. package/dist/cli.js +20 -3
  22. package/dist/commands/autofix.js +136 -120
  23. package/dist/commands/cron.js +58 -47
  24. package/dist/commands/deploy.js +123 -102
  25. package/dist/commands/generate.js +125 -7
  26. package/dist/commands/heal.d.ts +10 -0
  27. package/dist/commands/heal.js +201 -0
  28. package/dist/commands/i18n.js +146 -111
  29. package/dist/commands/import.d.ts +2 -0
  30. package/dist/commands/import.js +157 -0
  31. package/dist/commands/init.js +19 -7
  32. package/dist/commands/lint.js +50 -44
  33. package/dist/commands/llms-txt.js +59 -49
  34. package/dist/commands/login.js +63 -34
  35. package/dist/commands/mcp.js +6 -0
  36. package/dist/commands/monitor.js +13 -8
  37. package/dist/commands/qa.d.ts +2 -0
  38. package/dist/commands/qa.js +43 -0
  39. package/dist/commands/review-pr.js +108 -92
  40. package/dist/commands/sdk.js +128 -122
  41. package/dist/commands/security.d.ts +2 -0
  42. package/dist/commands/security.js +109 -0
  43. package/dist/commands/test.js +91 -92
  44. package/dist/commands/version.js +104 -75
  45. package/dist/commands/watch.js +130 -114
  46. package/dist/config/types.js +2 -2
  47. package/dist/context-hub/index.d.ts +23 -0
  48. package/dist/context-hub/index.js +179 -0
  49. package/dist/context-hub/mappings.d.ts +8 -0
  50. package/dist/context-hub/mappings.js +55 -0
  51. package/dist/context-hub/types.d.ts +33 -0
  52. package/dist/context-hub/types.js +1 -0
  53. package/dist/generator/generator.js +39 -6
  54. package/dist/generator/types.d.ts +7 -0
  55. package/dist/generator/writer.d.ts +3 -1
  56. package/dist/generator/writer.js +36 -7
  57. package/dist/importers/confluence.d.ts +5 -0
  58. package/dist/importers/confluence.js +137 -0
  59. package/dist/importers/detect.d.ts +20 -0
  60. package/dist/importers/detect.js +121 -0
  61. package/dist/importers/docusaurus.d.ts +5 -0
  62. package/dist/importers/docusaurus.js +279 -0
  63. package/dist/importers/gitbook.d.ts +5 -0
  64. package/dist/importers/gitbook.js +189 -0
  65. package/dist/importers/github.d.ts +8 -0
  66. package/dist/importers/github.js +99 -0
  67. package/dist/importers/index.d.ts +15 -0
  68. package/dist/importers/index.js +30 -0
  69. package/dist/importers/markdown.d.ts +6 -0
  70. package/dist/importers/markdown.js +105 -0
  71. package/dist/importers/mintlify.d.ts +5 -0
  72. package/dist/importers/mintlify.js +172 -0
  73. package/dist/importers/notion.d.ts +5 -0
  74. package/dist/importers/notion.js +174 -0
  75. package/dist/importers/readme.d.ts +5 -0
  76. package/dist/importers/readme.js +184 -0
  77. package/dist/importers/transform.d.ts +90 -0
  78. package/dist/importers/transform.js +457 -0
  79. package/dist/importers/types.d.ts +37 -0
  80. package/dist/importers/types.js +1 -0
  81. package/dist/llm/anthropic-client.d.ts +1 -0
  82. package/dist/llm/anthropic-client.js +3 -1
  83. package/dist/llm/index.d.ts +6 -4
  84. package/dist/llm/index.js +76 -261
  85. package/dist/llm/openai-client.d.ts +1 -0
  86. package/dist/llm/openai-client.js +7 -2
  87. package/dist/plugins/index.js +7 -0
  88. package/dist/qa/checks.d.ts +10 -0
  89. package/dist/qa/checks.js +492 -0
  90. package/dist/qa/fixes.d.ts +30 -0
  91. package/dist/qa/fixes.js +277 -0
  92. package/dist/qa/index.d.ts +29 -0
  93. package/dist/qa/index.js +187 -0
  94. package/dist/qa/types.d.ts +24 -0
  95. package/dist/qa/types.js +1 -0
  96. package/dist/scanner/csharp.d.ts +23 -0
  97. package/dist/scanner/csharp.js +421 -0
  98. package/dist/scanner/index.js +53 -26
  99. package/dist/scanner/java.d.ts +39 -0
  100. package/dist/scanner/java.js +318 -0
  101. package/dist/scanner/kotlin.d.ts +23 -0
  102. package/dist/scanner/kotlin.js +389 -0
  103. package/dist/scanner/php.d.ts +57 -0
  104. package/dist/scanner/php.js +351 -0
  105. package/dist/scanner/python.js +17 -0
  106. package/dist/scanner/ruby.d.ts +36 -0
  107. package/dist/scanner/ruby.js +431 -0
  108. package/dist/scanner/swift.d.ts +25 -0
  109. package/dist/scanner/swift.js +392 -0
  110. package/dist/scanner/types.d.ts +1 -1
  111. package/dist/template/content/docs/_navigation.json +46 -0
  112. package/dist/template/content/docs/_sidebars.json +684 -0
  113. package/dist/template/content/docs/core.md +4544 -0
  114. package/dist/template/content/docs/index.mdx +89 -0
  115. package/dist/template/content/docs/integrations.md +1158 -0
  116. package/dist/template/content/docs/llms-full.md +403 -0
  117. package/dist/template/content/docs/llms.txt +4588 -0
  118. package/dist/template/content/docs/other.md +10379 -0
  119. package/dist/template/content/docs/tools.md +746 -0
  120. package/dist/template/content/docs/types.md +531 -0
  121. package/dist/template/docs.json +13 -11
  122. package/dist/template/mdx-components.tsx +27 -2
  123. package/dist/template/package.json +6 -0
  124. package/dist/template/public/search-index.json +1 -1
  125. package/dist/template/scripts/build-search-index.mjs +149 -13
  126. package/dist/template/src/app/api/chat/route.ts +83 -128
  127. package/dist/template/src/app/docs/[...slug]/page.tsx +75 -20
  128. package/dist/template/src/app/docs/llms-full.md +151 -4
  129. package/dist/template/src/app/docs/llms.txt +2464 -847
  130. package/dist/template/src/app/docs/page.mdx +48 -38
  131. package/dist/template/src/app/layout.tsx +3 -1
  132. package/dist/template/src/app/page.tsx +22 -8
  133. package/dist/template/src/components/ai-chat.tsx +73 -64
  134. package/dist/template/src/components/breadcrumbs.tsx +21 -23
  135. package/dist/template/src/components/copy-button.tsx +13 -9
  136. package/dist/template/src/components/copy-page-button.tsx +54 -0
  137. package/dist/template/src/components/docs-layout.tsx +37 -25
  138. package/dist/template/src/components/header.tsx +51 -10
  139. package/dist/template/src/components/mdx/card.tsx +17 -3
  140. package/dist/template/src/components/mdx/code-block.tsx +13 -9
  141. package/dist/template/src/components/mdx/code-group.tsx +13 -8
  142. package/dist/template/src/components/mdx/heading.tsx +15 -2
  143. package/dist/template/src/components/mdx/highlighted-code.tsx +13 -8
  144. package/dist/template/src/components/mdx/index.tsx +2 -0
  145. package/dist/template/src/components/mdx/mermaid.tsx +110 -0
  146. package/dist/template/src/components/mdx/screenshot.tsx +150 -0
  147. package/dist/template/src/components/scroll-to-hash.tsx +48 -0
  148. package/dist/template/src/components/sidebar.tsx +12 -18
  149. package/dist/template/src/components/table-of-contents.tsx +9 -0
  150. package/dist/template/src/lib/highlight.ts +3 -88
  151. package/dist/template/src/lib/navigation.ts +159 -0
  152. package/dist/template/src/lib/search-types.ts +4 -1
  153. package/dist/template/src/lib/search.ts +30 -7
  154. package/dist/template/src/styles/globals.css +17 -6
  155. package/dist/utils/files.d.ts +9 -1
  156. package/dist/utils/files.js +59 -10
  157. package/dist/utils/validation.d.ts +0 -3
  158. package/dist/utils/validation.js +0 -26
  159. package/package.json +5 -1
package/dist/llm/index.js CHANGED
@@ -9,6 +9,9 @@ export function createLLMClient(config) {
9
9
  // Get API key from environment
10
10
  const envKey = PROVIDER_ENV_KEYS[config.provider];
11
11
  const apiKey = envKey ? process.env[envKey] || '' : '';
12
+ if (!apiKey) {
13
+ console.warn(`Warning: ${envKey} is not set. LLM calls will fail.`);
14
+ }
12
15
  const clientConfig = {
13
16
  provider: config.provider,
14
17
  model: config.model,
@@ -43,176 +46,102 @@ export async function generateDocumentation(client, element, options) {
43
46
  temperature: 0,
44
47
  maxTokens: useMultiLang ? 4096 : 2048
45
48
  });
46
- return useMultiLang
47
- ? parseMultiLangResponse(response.content)
48
- : parseDocResponse(response.content);
49
+ const usage = response.usage
50
+ ? { inputTokens: response.usage.inputTokens, outputTokens: response.usage.outputTokens }
51
+ : undefined;
52
+ const result = useMultiLang
53
+ ? parseMultiLangResponse(response.content, element.name)
54
+ : parseDocResponse(response.content, element.name);
55
+ return { ...result, usage };
49
56
  }
50
57
  // Phase 2: Multi-language system prompt
51
- const SYSTEM_PROMPT_MULTI_LANG = `You are an expert technical writer creating OUTCOMES-FOCUSED documentation with DUAL-LANGUAGE examples.
52
-
53
- Your goal: Help developers achieve their task FAST. Lead with "Use this to..." not "This function...".
54
-
55
- ## Documentation Rules
56
- 1. Do NOT include a heading with the function/class name - start directly with the use case
57
- 2. Start with the PRIMARY USE CASE - what problem does this solve?
58
- 3. Use a markdown table for parameters (Name | Type | Required | Description)
59
- 4. Clearly document what is returned and when
60
-
61
- ## Code Example Rules (CRITICAL)
62
- Generate TWO self-contained examples: TypeScript AND Python.
63
-
64
- Both examples MUST be:
65
- 1. COMPLETELY SELF-CONTAINED - no external imports from the library
66
- 2. EXECUTABLE standalone - inline all types and mock all functions
67
- 3. Using realistic data (API keys, user IDs, etc.)
68
- 4. Wrapped in try/except (Python) or try/catch (TypeScript)
69
- 5. Showing environment variable usage
70
- 6. Ending with print/console.log showing expected output
58
+ const SYSTEM_PROMPT_MULTI_LANG = `You are an expert technical writer. Write like the best devtool docs (Stripe, Vercel, Shopify): lead with the outcome, explain why this exists, then get technical.
71
59
 
72
- ## TypeScript Example Pattern
73
- \`\`\`typescript
74
- // Inline types
75
- type Config = { apiKey: string }
60
+ ## Writing Rules
76
61
 
77
- // Mock implementation
78
- function createTool(config: Config) {
79
- return { execute: async (q: string) => ({ results: [q] }) }
80
- }
62
+ 1. **Opening sentence**: Start with "Use [name] to [outcome]." Tell the developer what this ENABLES them to do. NOT "This function takes X and returns Y."
81
63
 
82
- // Usage
83
- const tool = createTool({ apiKey: process.env.API_KEY || 'your-key' })
64
+ 2. **When to use this** (1-2 sentences): What scenario would make a developer reach for this? If it's a method on a class, how does it fit into the typical workflow with that class? Connect it to a real task.
84
65
 
85
- async function main() {
86
- try {
87
- const result = await tool.execute('query')
88
- console.log('Result:', result)
89
- // Output: { results: ['query'] }
90
- } catch (error) {
91
- console.error('Failed:', error)
92
- }
93
- }
94
- main()
95
- \`\`\`
66
+ 3. **How it works** (1-3 sentences, only if the mechanism isn't obvious): Briefly explain the behavior. Skip this if the function name + params make it self-evident.
96
67
 
97
- ## Python Example Pattern
98
- \`\`\`python
99
- import os
100
- from dataclasses import dataclass
101
- from typing import Optional, List, Dict, Any
68
+ 4. **Parameters**: Markdown table (Name | Type | Required | Description). Descriptions must explain IMPACT, not restate the type. Write "Maximum retry attempts before the request fails and throws" not "Number of retries." Write "Your Stripe secret key — find it at dashboard.stripe.com/apikeys" not "A string."
102
69
 
103
- # Inline types
104
- @dataclass
105
- class Config:
106
- api_key: str
107
- timeout: int = 30
70
+ 5. **Returns**: What you get back AND what to do with it next. "Returns the created user object. Pass user.id to subsequent API calls to act on behalf of this user."
108
71
 
109
- # Mock implementation
110
- class Tool:
111
- def __init__(self, config: Config):
112
- self.config = config
72
+ 6. **Heads up** (only if non-obvious): 1-2 bullet points about common mistakes or surprising behavior. Omit entirely if there's nothing surprising.
113
73
 
114
- async def execute(self, query: str) -> Dict[str, Any]:
115
- return {"results": [query]}
74
+ Do NOT include a heading with the function/class name — start directly with the opening sentence.
75
+ Do NOT write filler like "This is a powerful utility for..." — be direct.
76
+ Do NOT just restate the function signature in prose — add information the signature doesn't convey.
116
77
 
117
- # Usage
118
- import asyncio
119
-
120
- async def main():
121
- try:
122
- tool = Tool(Config(api_key=os.getenv("API_KEY", "your-key")))
123
- result = await tool.execute("query")
124
- print(f"Result: {result}")
125
- # Output: {'results': ['query']}
126
- except Exception as e:
127
- print(f"Failed: {e}")
78
+ ## Code Examples
79
+ Generate TWO self-contained examples: TypeScript AND Python.
128
80
 
129
- asyncio.run(main())
130
- \`\`\`
81
+ Both MUST be:
82
+ - COMPLETELY SELF-CONTAINED — no imports from the library being documented. Inline types and mock implementations.
83
+ - Using realistic data (real-looking API keys, user IDs, URLs)
84
+ - Wrapped in try/catch (TS) or try/except (Python)
85
+ - Ending with console.log/print showing expected output
86
+ - Short and focused — demonstrate the ONE primary use case, not every parameter
131
87
 
132
88
  ## Output Format
133
89
  ---MARKDOWN---
134
- [Your markdown documentation with parameter table]
90
+ [Documentation following the rules above]
135
91
  ---TYPESCRIPT---
136
- [Self-contained TypeScript example]
92
+ [Self-contained TypeScript example — no markdown fences]
137
93
  ---PYTHON---
138
- [Self-contained Python example]
94
+ [Self-contained Python example — no markdown fences]
139
95
  ---END---`;
140
- const SYSTEM_PROMPT = `You are an expert technical writer creating OUTCOMES-FOCUSED documentation.
96
+ const SYSTEM_PROMPT = `You are an expert technical writer. Write like the best devtool docs (Stripe, Vercel, Shopify): lead with the outcome, explain why this exists, then get technical.
141
97
 
142
- Your goal: Help developers achieve their task FAST. Lead with "Use this to..." not "This function...".
98
+ ## Writing Rules
143
99
 
144
- ## Documentation Rules
145
- 1. Do NOT include a heading with the function/class name - start directly with the use case
146
- 2. Start with the PRIMARY USE CASE - what problem does this solve?
147
- 3. Use a markdown table for parameters (Name | Type | Required | Description)
148
- 4. Clearly document what is returned and when
100
+ 1. **Opening sentence**: Start with "Use [name] to [outcome]." Tell the developer what this ENABLES them to do. NOT "This function takes X and returns Y."
149
101
 
150
- ## Code Example Rules (CRITICAL)
151
- Your code examples MUST be COMPLETELY SELF-CONTAINED and EXECUTABLE:
102
+ 2. **When to use this** (1-2 sentences): What scenario would make a developer reach for this? If it's a method on a class, how does it fit into the typical workflow with that class? Connect it to a real task.
152
103
 
153
- 1. NEVER import from the library being documented (e.g., "@supermemory/tools")
154
- 2. Instead, INLINE any types or simple implementations needed
155
- 3. Use realistic but simple data (real-looking API keys, user IDs, etc.)
156
- 4. ALWAYS wrap async code in try/catch with meaningful error handling
157
- 5. ALWAYS show environment variable usage: process.env.API_KEY || 'your-api-key'
158
- 6. End with a console.log showing expected output
159
- 7. Include a brief comment showing what output to expect
104
+ 3. **How it works** (1-3 sentences, only if the mechanism isn't obvious): Briefly explain the behavior. Skip this if the function name + params make it self-evident.
160
105
 
161
- ## Self-Contained Example Pattern
162
- BAD (imports from library - will fail):
163
- \`\`\`typescript
164
- import { createTool } from '@mylib/tools'
165
- const tool = createTool('key')
166
- \`\`\`
106
+ 4. **Parameters**: Markdown table (Name | Type | Required | Description). Descriptions must explain IMPACT, not restate the type. Write "Maximum retry attempts before the request fails and throws" not "Number of retries." Write "Your Stripe secret key — find it at dashboard.stripe.com/apikeys" not "A string."
167
107
 
168
- GOOD (self-contained - will run):
169
- \`\`\`typescript
170
- // Define the types inline
171
- type ToolConfig = { apiKey: string; timeout?: number }
108
+ 5. **Returns**: What you get back AND what to do with it next. "Returns the created user object. Pass user.id to subsequent API calls to act on behalf of this user."
172
109
 
173
- // Simulate the function behavior
174
- function createTool(config: ToolConfig) {
175
- return {
176
- execute: async (query: string) => {
177
- console.log(\`Searching for: \${query}\`)
178
- return { results: ['result1', 'result2'] }
179
- }
180
- }
181
- }
110
+ 6. **Heads up** (only if non-obvious): 1-2 bullet points about common mistakes or surprising behavior. Omit entirely if there's nothing surprising.
182
111
 
183
- // Usage example
184
- const tool = createTool({
185
- apiKey: process.env.MY_API_KEY || 'your-api-key-here'
186
- })
112
+ Do NOT include a heading with the function/class name — start directly with the opening sentence.
113
+ Do NOT write filler like "This is a powerful utility for..." — be direct.
114
+ Do NOT just restate the function signature in prose — add information the signature doesn't convey.
187
115
 
188
- async function main() {
189
- try {
190
- const result = await tool.execute('my query')
191
- console.log('Results:', result)
192
- // Output: { results: ['result1', 'result2'] }
193
- } catch (error) {
194
- console.error('Failed:', error)
195
- }
196
- }
197
-
198
- main()
199
- \`\`\`
116
+ ## Code Example
117
+ Generate ONE self-contained, executable example:
118
+ - Do NOT import from the library being documented — inline types and mock implementations
119
+ - Use realistic data (real-looking API keys, user IDs, URLs)
120
+ - Wrap async code in try/catch
121
+ - End with console.log showing expected output
122
+ - Short and focused — demonstrate the ONE primary use case
200
123
 
201
- Output format:
124
+ ## Output Format
202
125
  ---MARKDOWN---
203
- [Your markdown documentation with parameter table]
126
+ [Documentation following the rules above]
204
127
  ---CODE---
205
- [Your SELF-CONTAINED, EXECUTABLE code example]
128
+ [Self-contained example no markdown fences]
206
129
  ---END---`;
207
130
  function buildDocPrompt(element, multiLanguage = false) {
208
- let prompt = `Generate documentation for this ${element.kind}:\n\n`;
131
+ let prompt = '';
132
+ // Project context first — gives the LLM the "big picture" for better explanations
133
+ if (element.projectContext) {
134
+ prompt += `**Project context** (use this to explain WHY this ${element.kind} exists and how it fits in):\n`;
135
+ prompt += element.projectContext.slice(0, 1500) + '\n\n';
136
+ }
137
+ prompt += `Generate documentation for this ${element.kind}:\n\n`;
209
138
  prompt += `**Name:** ${element.name}\n`;
210
139
  prompt += `**Signature:** ${element.signature}\n`;
211
140
  if (element.parentClass) {
212
141
  prompt += `**Class:** ${element.parentClass}\n`;
213
142
  }
214
143
  if (element.packageName && element.packageName !== 'unknown') {
215
- prompt += `**Package:** ${element.packageName} (DO NOT import from this - make example self-contained)\n`;
144
+ prompt += `**Package:** ${element.packageName} (DO NOT import from this make example self-contained)\n`;
216
145
  }
217
146
  if (element.parameters.length > 0) {
218
147
  prompt += `\n**Parameters:**\n`;
@@ -232,21 +161,25 @@ function buildDocPrompt(element, multiLanguage = false) {
232
161
  prompt += `\n**Existing docstring:**\n${element.docstring}\n`;
233
162
  }
234
163
  if (element.sourceContext) {
235
- prompt += `\n**Source context (for understanding, not for importing):**\n\`\`\`\n${element.sourceContext}\n\`\`\`\n`;
164
+ prompt += `\n**Source context (understand the code, but do NOT import from it):**\n\`\`\`\n${element.sourceContext}\n\`\`\`\n`;
236
165
  }
237
166
  if (element.imports && element.imports.length > 0) {
238
- prompt += `\n**File imports (shows dependencies - your example should NOT use these):**\n`;
167
+ prompt += `\n**File imports (shows what this code depends on — do NOT use in examples):**\n`;
239
168
  prompt += element.imports.slice(0, 5).join('\n') + '\n';
240
169
  }
241
- if (multiLanguage) {
242
- prompt += `\n**IMPORTANT:** Generate BOTH TypeScript AND Python self-contained examples.`;
170
+ if (element.externalDocs) {
171
+ prompt += '\n**Third-party API reference (use for accurate API details):**\n';
172
+ prompt += element.externalDocs.slice(0, 3000) + '\n';
243
173
  }
244
- else {
245
- prompt += `\n**IMPORTANT:** Generate a SELF-CONTAINED example that runs without any external imports.`;
174
+ if (multiLanguage) {
175
+ prompt += `\nGenerate BOTH TypeScript AND Python self-contained examples.`;
246
176
  }
247
177
  return prompt;
248
178
  }
249
- function parseDocResponse(content) {
179
+ function parseDocResponse(content, elementName) {
180
+ if (!content.includes('---MARKDOWN---')) {
181
+ console.warn(` Warning: LLM response missing expected format for ${elementName ?? 'unknown'}`);
182
+ }
250
183
  // Extract markdown section
251
184
  const markdownMatch = content.match(/---MARKDOWN---\s*([\s\S]*?)\s*---CODE---/);
252
185
  const markdown = markdownMatch?.[1]?.trim() || content;
@@ -257,7 +190,10 @@ function parseDocResponse(content) {
257
190
  codeExample = codeExample.replace(/^```\w*\n?/, '').replace(/\n?```$/, '');
258
191
  return { markdown, codeExample, typescriptExample: codeExample };
259
192
  }
260
- function parseMultiLangResponse(content) {
193
+ function parseMultiLangResponse(content, elementName) {
194
+ if (!content.includes('---MARKDOWN---')) {
195
+ console.warn(` Warning: LLM response missing expected format for ${elementName ?? 'unknown'}`);
196
+ }
261
197
  // Extract markdown section
262
198
  const markdownMatch = content.match(/---MARKDOWN---\s*([\s\S]*?)\s*---TYPESCRIPT---/);
263
199
  const markdown = markdownMatch?.[1]?.trim() || '';
@@ -277,124 +213,3 @@ function parseMultiLangResponse(content) {
277
213
  pythonExample
278
214
  };
279
215
  }
280
- /**
281
- * Helper to fix a broken code sample with smart strategies
282
- */
283
- export async function fixCodeSample(client, code, error, context, iteration = 1) {
284
- // Analyze error type to provide better fix guidance
285
- const fixStrategy = analyzeErrorAndGetStrategy(error, code);
286
- const response = await client.complete({
287
- messages: [
288
- {
289
- role: 'system',
290
- content: FIX_SYSTEM_PROMPT
291
- },
292
- {
293
- role: 'user',
294
- content: `Fix this code example. It must be COMPLETELY SELF-CONTAINED and runnable.
295
-
296
- **Current code:**
297
- \`\`\`
298
- ${code}
299
- \`\`\`
300
-
301
- **Error:**
302
- ${error}
303
-
304
- **Fix strategy:** ${fixStrategy}
305
-
306
- **Context:**
307
- ${context}
308
-
309
- **Iteration ${iteration}:** ${iteration > 2 ? 'SIMPLIFY the example drastically - focus on demonstrating the concept, not real functionality.' : 'Make it self-contained.'}
310
-
311
- Return ONLY the fixed code, no explanations or markdown fences.`
312
- }
313
- ],
314
- temperature: iteration > 3 ? 0.3 : 0, // Add creativity on later iterations
315
- maxTokens: 1024
316
- });
317
- // Clean up any markdown fences
318
- return response.content
319
- .replace(/^```\w*\n?/, '')
320
- .replace(/\n?```$/, '')
321
- .trim();
322
- }
323
- const FIX_SYSTEM_PROMPT = `You are fixing a documentation code example to make it SELF-CONTAINED and RUNNABLE.
324
-
325
- ## Fix Strategies by Error Type
326
-
327
- **Import/Module errors ("Cannot find module", "is not defined"):**
328
- - DO NOT try to fix the import
329
- - INLINE the type definitions
330
- - MOCK the function behavior with a simple implementation
331
- - Show what the function WOULD do conceptually
332
-
333
- **Type errors ("Type X is not assignable"):**
334
- - Define the type inline with \`type X = {...}\`
335
- - Use simpler types if complex ones cause issues
336
-
337
- **Runtime errors ("undefined is not a function", "Cannot read property"):**
338
- - Initialize all variables
339
- - Add null checks
340
- - Use optional chaining (?.)
341
-
342
- **Async errors ("await is only valid in async"):**
343
- - Wrap in async function
344
- - Add proper try/catch
345
- - Call the async function at the end
346
-
347
- ## Example Fix Pattern
348
- If the error is "Cannot find module '@mylib/tools'":
349
-
350
- WRONG (trying to fix import):
351
- \`\`\`
352
- import { tool } from '@mylib/tools-v2' // Still fails
353
- \`\`\`
354
-
355
- RIGHT (inline everything):
356
- \`\`\`
357
- // Define types inline
358
- type ToolResult = { data: string[] }
359
-
360
- // Mock the tool behavior
361
- const tool = {
362
- search: async (query: string): Promise<ToolResult> => {
363
- // Simulated behavior
364
- return { data: ['result1', 'result2'] }
365
- }
366
- }
367
-
368
- // Usage
369
- async function main() {
370
- const result = await tool.search('test')
371
- console.log(result) // { data: ['result1', 'result2'] }
372
- }
373
- main()
374
- \`\`\`
375
-
376
- Return ONLY the fixed code.`;
377
- function analyzeErrorAndGetStrategy(error, _code) {
378
- const errorLower = error.toLowerCase();
379
- if (errorLower.includes('cannot find module') || errorLower.includes('module not found')) {
380
- const importMatch = error.match(/['"]([^'"]+)['"]/)?.[1] || 'the library';
381
- return `IMPORT ERROR: Cannot import from "${importMatch}". Remove ALL imports and inline types/mock functions instead.`;
382
- }
383
- if (errorLower.includes('is not defined') || errorLower.includes('is not a function')) {
384
- const undefinedMatch = error.match(/(\w+) is not (defined|a function)/)?.[1];
385
- return `UNDEFINED: "${undefinedMatch}" is not available. Define it inline or mock it.`;
386
- }
387
- if (errorLower.includes('type') && (errorLower.includes('assignable') || errorLower.includes('missing'))) {
388
- return `TYPE ERROR: Type mismatch. Simplify types or define them inline with correct structure.`;
389
- }
390
- if (errorLower.includes('await') && errorLower.includes('async')) {
391
- return `ASYNC ERROR: Wrap code in async function and call it: async function main() {...} main()`;
392
- }
393
- if (errorLower.includes('timeout') || errorLower.includes('timed out')) {
394
- return `TIMEOUT: Code took too long. Remove any actual API calls or network requests. Use mock data.`;
395
- }
396
- if (errorLower.includes('syntaxerror') || errorLower.includes('unexpected token')) {
397
- return `SYNTAX ERROR: Fix syntax issues. Check for missing brackets, quotes, or semicolons.`;
398
- }
399
- return `GENERAL: Make the code self-contained. Define all types inline, mock all external calls, use try/catch.`;
400
- }
@@ -9,6 +9,7 @@ export declare class OpenAICompatibleClient implements LLMClient {
9
9
  private client;
10
10
  private model;
11
11
  private maxRetries;
12
+ private apiKey;
12
13
  constructor(config: LLMClientConfig);
13
14
  isConfigured(): boolean;
14
15
  complete(request: CompletionRequest): Promise<CompletionResponse>;
@@ -9,14 +9,19 @@ export class OpenAICompatibleClient {
9
9
  client;
10
10
  model;
11
11
  maxRetries;
12
+ apiKey;
12
13
  constructor(config) {
13
14
  this.provider = config.provider;
14
15
  this.model = config.model;
15
16
  this.maxRetries = config.maxRetries ?? 3;
16
17
  const baseURL = config.baseUrl || PROVIDER_BASE_URLS[config.provider];
17
18
  const extraHeaders = PROVIDER_EXTRA_HEADERS[config.provider] || {};
19
+ // Ollama doesn't need a real key; other providers get empty string
20
+ // (SDK validates on first request, not construction)
21
+ const effectiveKey = config.apiKey || (config.provider === 'ollama' ? 'ollama' : 'not-set');
22
+ this.apiKey = effectiveKey;
18
23
  this.client = new OpenAI({
19
- apiKey: config.apiKey || 'ollama', // Ollama doesn't need a real key
24
+ apiKey: effectiveKey,
20
25
  baseURL,
21
26
  timeout: config.timeout ?? 60000,
22
27
  maxRetries: this.maxRetries,
@@ -24,7 +29,7 @@ export class OpenAICompatibleClient {
24
29
  });
25
30
  }
26
31
  isConfigured() {
27
- return true; // OpenAI SDK handles validation
32
+ return this.apiKey !== '' && this.apiKey !== 'not-set';
28
33
  }
29
34
  async complete(request) {
30
35
  const model = request.model || this.model;
@@ -23,6 +23,8 @@ export class PluginManager {
23
23
  if (!configFile)
24
24
  return;
25
25
  try {
26
+ console.log(` Loading config: ${configFile}`);
27
+ console.warn(' ⚠ Plugin configs execute code — only load trusted files');
26
28
  const configUrl = pathToFileURL(configFile).href;
27
29
  const module = await import(configUrl);
28
30
  const config = module.default || module;
@@ -57,6 +59,11 @@ export class PluginManager {
57
59
  return null;
58
60
  }
59
61
  async loadPluginByName(name) {
62
+ // Validate plugin name — must be a valid npm package name, no path traversal
63
+ if (!/^(@[a-zA-Z0-9._-]+\/)?[a-zA-Z0-9._-]+$/.test(name)) {
64
+ console.warn(`Skipping plugin with invalid name: ${name}`);
65
+ return;
66
+ }
60
67
  // Try to load from node_modules
61
68
  try {
62
69
  const module = await import(name);
@@ -0,0 +1,10 @@
1
+ import type { QAIssue } from './types.js';
2
+ export declare function checkFrontmatter(filePath: string, content: string, relPath: string): QAIssue[];
3
+ export declare function checkHeadings(content: string, relPath: string): QAIssue[];
4
+ export declare function checkCodeBlocks(content: string, relPath: string): QAIssue[];
5
+ export declare function checkComponents(content: string, relPath: string): QAIssue[];
6
+ export declare function checkLinks(content: string, relPath: string, _allFiles: Set<string>): QAIssue[];
7
+ export declare function checkSecurity(content: string, relPath: string): QAIssue[];
8
+ export declare function checkContentQuality(content: string, relPath: string): QAIssue[];
9
+ export declare function checkScreenshots(content: string, relPath: string, outputDir: string): QAIssue[];
10
+ export declare function checkMdxSyntax(content: string, relPath: string): QAIssue[];