skrypt-ai 0.5.0 → 0.6.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 (120) hide show
  1. package/dist/auth/index.js +8 -1
  2. package/dist/autofix/index.d.ts +0 -4
  3. package/dist/autofix/index.js +0 -21
  4. package/dist/capture/browser.d.ts +11 -0
  5. package/dist/capture/browser.js +173 -0
  6. package/dist/capture/diff.d.ts +23 -0
  7. package/dist/capture/diff.js +52 -0
  8. package/dist/capture/index.d.ts +23 -0
  9. package/dist/capture/index.js +210 -0
  10. package/dist/capture/naming.d.ts +17 -0
  11. package/dist/capture/naming.js +45 -0
  12. package/dist/capture/parser.d.ts +15 -0
  13. package/dist/capture/parser.js +80 -0
  14. package/dist/capture/types.d.ts +57 -0
  15. package/dist/capture/types.js +1 -0
  16. package/dist/cli.js +4 -0
  17. package/dist/commands/autofix.js +136 -120
  18. package/dist/commands/cron.js +58 -47
  19. package/dist/commands/deploy.js +123 -102
  20. package/dist/commands/generate.js +88 -6
  21. package/dist/commands/heal.d.ts +10 -0
  22. package/dist/commands/heal.js +201 -0
  23. package/dist/commands/i18n.js +146 -111
  24. package/dist/commands/lint.js +50 -44
  25. package/dist/commands/llms-txt.js +59 -49
  26. package/dist/commands/login.js +61 -43
  27. package/dist/commands/mcp.js +6 -0
  28. package/dist/commands/monitor.js +13 -8
  29. package/dist/commands/qa.d.ts +2 -0
  30. package/dist/commands/qa.js +43 -0
  31. package/dist/commands/review-pr.js +114 -103
  32. package/dist/commands/sdk.js +128 -122
  33. package/dist/commands/security.js +86 -80
  34. package/dist/commands/test.js +91 -92
  35. package/dist/commands/version.js +104 -75
  36. package/dist/commands/watch.js +130 -114
  37. package/dist/config/types.js +2 -2
  38. package/dist/context-hub/index.d.ts +23 -0
  39. package/dist/context-hub/index.js +179 -0
  40. package/dist/context-hub/mappings.d.ts +8 -0
  41. package/dist/context-hub/mappings.js +55 -0
  42. package/dist/context-hub/types.d.ts +33 -0
  43. package/dist/context-hub/types.js +1 -0
  44. package/dist/generator/generator.js +39 -6
  45. package/dist/generator/types.d.ts +7 -0
  46. package/dist/generator/writer.d.ts +3 -1
  47. package/dist/generator/writer.js +24 -4
  48. package/dist/llm/anthropic-client.d.ts +1 -0
  49. package/dist/llm/anthropic-client.js +3 -1
  50. package/dist/llm/index.d.ts +6 -4
  51. package/dist/llm/index.js +76 -261
  52. package/dist/llm/openai-client.d.ts +1 -0
  53. package/dist/llm/openai-client.js +7 -2
  54. package/dist/qa/checks.d.ts +10 -0
  55. package/dist/qa/checks.js +492 -0
  56. package/dist/qa/fixes.d.ts +30 -0
  57. package/dist/qa/fixes.js +277 -0
  58. package/dist/qa/index.d.ts +29 -0
  59. package/dist/qa/index.js +187 -0
  60. package/dist/qa/types.d.ts +24 -0
  61. package/dist/qa/types.js +1 -0
  62. package/dist/scanner/csharp.d.ts +23 -0
  63. package/dist/scanner/csharp.js +421 -0
  64. package/dist/scanner/index.js +16 -2
  65. package/dist/scanner/java.d.ts +39 -0
  66. package/dist/scanner/java.js +318 -0
  67. package/dist/scanner/kotlin.d.ts +23 -0
  68. package/dist/scanner/kotlin.js +389 -0
  69. package/dist/scanner/php.d.ts +57 -0
  70. package/dist/scanner/php.js +351 -0
  71. package/dist/scanner/ruby.d.ts +36 -0
  72. package/dist/scanner/ruby.js +431 -0
  73. package/dist/scanner/swift.d.ts +25 -0
  74. package/dist/scanner/swift.js +392 -0
  75. package/dist/scanner/types.d.ts +1 -1
  76. package/dist/template/content/docs/_navigation.json +46 -0
  77. package/dist/template/content/docs/_sidebars.json +684 -0
  78. package/dist/template/content/docs/core.md +4544 -0
  79. package/dist/template/content/docs/index.mdx +89 -0
  80. package/dist/template/content/docs/integrations.md +1158 -0
  81. package/dist/template/content/docs/llms-full.md +403 -0
  82. package/dist/template/content/docs/llms.txt +4588 -0
  83. package/dist/template/content/docs/other.md +10379 -0
  84. package/dist/template/content/docs/tools.md +746 -0
  85. package/dist/template/content/docs/types.md +531 -0
  86. package/dist/template/docs.json +13 -11
  87. package/dist/template/mdx-components.tsx +27 -2
  88. package/dist/template/package.json +6 -0
  89. package/dist/template/public/search-index.json +1 -1
  90. package/dist/template/scripts/build-search-index.mjs +84 -6
  91. package/dist/template/src/app/api/chat/route.ts +83 -128
  92. package/dist/template/src/app/docs/[...slug]/page.tsx +75 -20
  93. package/dist/template/src/app/docs/llms-full.md +151 -4
  94. package/dist/template/src/app/docs/llms.txt +2464 -847
  95. package/dist/template/src/app/docs/page.mdx +48 -38
  96. package/dist/template/src/app/layout.tsx +3 -1
  97. package/dist/template/src/app/page.tsx +22 -8
  98. package/dist/template/src/components/ai-chat.tsx +73 -64
  99. package/dist/template/src/components/breadcrumbs.tsx +21 -23
  100. package/dist/template/src/components/copy-button.tsx +13 -9
  101. package/dist/template/src/components/copy-page-button.tsx +54 -0
  102. package/dist/template/src/components/docs-layout.tsx +37 -25
  103. package/dist/template/src/components/header.tsx +51 -10
  104. package/dist/template/src/components/mdx/card.tsx +17 -3
  105. package/dist/template/src/components/mdx/code-block.tsx +13 -9
  106. package/dist/template/src/components/mdx/code-group.tsx +13 -8
  107. package/dist/template/src/components/mdx/heading.tsx +15 -2
  108. package/dist/template/src/components/mdx/highlighted-code.tsx +13 -8
  109. package/dist/template/src/components/mdx/index.tsx +2 -0
  110. package/dist/template/src/components/mdx/mermaid.tsx +110 -0
  111. package/dist/template/src/components/mdx/screenshot.tsx +150 -0
  112. package/dist/template/src/components/scroll-to-hash.tsx +48 -0
  113. package/dist/template/src/components/sidebar.tsx +12 -18
  114. package/dist/template/src/components/table-of-contents.tsx +9 -0
  115. package/dist/template/src/lib/highlight.ts +3 -88
  116. package/dist/template/src/lib/navigation.ts +159 -0
  117. package/dist/template/src/styles/globals.css +17 -6
  118. package/dist/utils/validation.d.ts +0 -3
  119. package/dist/utils/validation.js +0 -26
  120. package/package.json +3 -2
@@ -1,9 +1,10 @@
1
1
  import { generateDocumentation } from '../llm/index.js';
2
+ import { matchImportsToContext } from '../context-hub/index.js';
2
3
  /**
3
4
  * Build enhanced context for LLM from API element
4
5
  */
5
- function buildElementContext(element) {
6
- return {
6
+ function buildElementContext(element, externalContext, projectContext) {
7
+ const ctx = {
7
8
  kind: element.kind,
8
9
  name: element.name,
9
10
  signature: element.signature,
@@ -14,8 +15,13 @@ function buildElementContext(element) {
14
15
  imports: element.imports,
15
16
  packageName: element.packageName,
16
17
  sourceContext: element.sourceContext,
17
- filePath: element.filePath
18
+ filePath: element.filePath,
19
+ projectContext
18
20
  };
21
+ if (externalContext && element.imports?.length) {
22
+ ctx.externalDocs = matchImportsToContext(element.imports, externalContext);
23
+ }
24
+ return ctx;
19
25
  }
20
26
  /**
21
27
  * Detect code language from file path
@@ -31,6 +37,18 @@ function detectLanguageFromFile(filePath) {
31
37
  return 'go';
32
38
  if (filePath.endsWith('.rs'))
33
39
  return 'rust';
40
+ if (filePath.endsWith('.java'))
41
+ return 'java';
42
+ if (filePath.endsWith('.cs'))
43
+ return 'csharp';
44
+ if (filePath.endsWith('.php'))
45
+ return 'php';
46
+ if (filePath.endsWith('.kt') || filePath.endsWith('.kts'))
47
+ return 'kotlin';
48
+ if (filePath.endsWith('.swift'))
49
+ return 'swift';
50
+ if (filePath.endsWith('.rb'))
51
+ return 'ruby';
34
52
  return 'typescript';
35
53
  }
36
54
  /**
@@ -49,7 +67,7 @@ export async function generateForElement(element, client, options, onProgress) {
49
67
  const useMultiLang = options.multiLanguage ?? false;
50
68
  report('generating');
51
69
  try {
52
- const elementContext = buildElementContext(element);
70
+ const elementContext = buildElementContext(element, options.externalContext, options.projectContext);
53
71
  const result = await generateDocumentation(client, elementContext, { multiLanguage: useMultiLang });
54
72
  report('done');
55
73
  return {
@@ -58,7 +76,8 @@ export async function generateForElement(element, client, options, onProgress) {
58
76
  codeExample: result.codeExample,
59
77
  codeLanguage,
60
78
  typescriptExample: result.typescriptExample,
61
- pythonExample: result.pythonExample
79
+ pythonExample: result.pythonExample,
80
+ usage: result.usage
62
81
  };
63
82
  }
64
83
  catch (err) {
@@ -77,6 +96,8 @@ export async function generateForElement(element, client, options, onProgress) {
77
96
  */
78
97
  export async function generateForElements(elements, client, options) {
79
98
  const results = [];
99
+ let totalTokens = 0;
100
+ const MAX_TOKENS = options.maxTokens ?? 1_000_000; // default 1M tokens (~$10-20)
80
101
  for (let i = 0; i < elements.length; i++) {
81
102
  const element = elements[i];
82
103
  if (!element)
@@ -89,6 +110,14 @@ export async function generateForElements(elements, client, options) {
89
110
  });
90
111
  });
91
112
  results.push(doc);
113
+ if (doc.usage) {
114
+ totalTokens += (doc.usage.inputTokens ?? 0) + (doc.usage.outputTokens ?? 0);
115
+ }
116
+ if (totalTokens > MAX_TOKENS) {
117
+ console.warn(`\nToken budget exceeded (${totalTokens.toLocaleString()} tokens used). Stopping generation.`);
118
+ console.warn('Use --max-tokens to increase the limit.');
119
+ break;
120
+ }
92
121
  }
93
122
  return results;
94
123
  }
@@ -137,7 +166,11 @@ function formatDocSection(doc, headingLevel = '###') {
137
166
  }
138
167
  else if (doc.codeExample) {
139
168
  section += '**Example:**\n\n';
140
- const ext = doc.codeLanguage === 'typescript' ? 'ts' : doc.codeLanguage === 'python' ? 'py' : 'js';
169
+ const extMap = {
170
+ typescript: 'ts', javascript: 'js', python: 'py', go: 'go', rust: 'rs',
171
+ java: 'java', csharp: 'cs', php: 'php', kotlin: 'kt', swift: 'swift', ruby: 'rb'
172
+ };
173
+ const ext = extMap[doc.codeLanguage] ?? 'js';
141
174
  section += `\`\`\`${doc.codeLanguage} example.${ext}\n${doc.codeExample}\n\`\`\`\n\n`;
142
175
  }
143
176
  return section;
@@ -10,6 +10,10 @@ export interface GeneratedDoc {
10
10
  error?: string;
11
11
  typescriptExample?: string;
12
12
  pythonExample?: string;
13
+ usage?: {
14
+ inputTokens: number;
15
+ outputTokens: number;
16
+ };
13
17
  }
14
18
  /**
15
19
  * Progress callback for generation
@@ -26,6 +30,9 @@ export interface GenerationProgress {
26
30
  export interface GenerationOptions {
27
31
  onProgress?: (progress: GenerationProgress) => void;
28
32
  multiLanguage?: boolean;
33
+ externalContext?: Map<string, string>;
34
+ maxTokens?: number;
35
+ projectContext?: string;
29
36
  }
30
37
  /**
31
38
  * Result of generating docs for a file
@@ -10,7 +10,9 @@ export declare function writeLlmsTxt(docs: GeneratedDoc[], outputDir: string, op
10
10
  /**
11
11
  * Write generated docs to output directory
12
12
  */
13
- export declare function writeDocsToDirectory(results: FileGenerationResult[], outputDir: string, sourceDir: string): Promise<{
13
+ export declare function writeDocsToDirectory(results: FileGenerationResult[], outputDir: string, sourceDir: string, options?: {
14
+ force?: boolean;
15
+ }): Promise<{
14
16
  filesWritten: number;
15
17
  totalDocs: number;
16
18
  }>;
@@ -1,5 +1,6 @@
1
- import { mkdir, writeFile } from 'fs/promises';
1
+ import { mkdir, writeFile, readdir } from 'fs/promises';
2
2
  import { dirname, join, basename, relative, resolve, sep } from 'path';
3
+ import { existsSync } from 'fs';
3
4
  import { formatAsMarkdown } from './generator.js';
4
5
  import { organizeByTopic, detectCrossReferences, getCrossRefsForElement } from './organizer.js';
5
6
  import { slugify } from '../utils/files.js';
@@ -71,9 +72,22 @@ export async function writeLlmsTxt(docs, outputDir, options = {}) {
71
72
  /**
72
73
  * Write generated docs to output directory
73
74
  */
74
- export async function writeDocsToDirectory(results, outputDir, sourceDir) {
75
+ export async function writeDocsToDirectory(results, outputDir, sourceDir, options) {
75
76
  let filesWritten = 0;
76
77
  let totalDocs = 0;
78
+ // Check for existing doc files before overwriting
79
+ if (existsSync(outputDir)) {
80
+ try {
81
+ const entries = await readdir(outputDir);
82
+ const existing = entries.filter(f => f.endsWith('.md') || f.endsWith('.mdx'));
83
+ if (existing.length > 0 && !options?.force) {
84
+ console.warn(`Warning: Output directory already contains ${existing.length} doc files. Use --force to overwrite.`);
85
+ }
86
+ }
87
+ catch {
88
+ // Directory read failed, proceed anyway
89
+ }
90
+ }
77
91
  // Create output directory
78
92
  await mkdir(outputDir, { recursive: true });
79
93
  for (const result of results) {
@@ -194,8 +208,14 @@ description: "${safeDesc}"
194
208
  for (const doc of topic.docs) {
195
209
  const anchor = slugify(doc.element.name);
196
210
  const icon = doc.element.kind === 'class' ? 'cube' : doc.element.kind === 'method' ? 'code' : 'function';
197
- content += ` <Card title="${doc.element.name}" icon="${icon}" href="#${anchor}">\n`;
198
- content += ` ${doc.element.kind}\n`;
211
+ // Use short description (first ~5 words) or fall back to kind
212
+ const rawDocstring = doc.element.docstring?.replace(/^@\w+\s+\S+\s*/, '')?.trim();
213
+ const desc = rawDocstring
214
+ ? rawDocstring.split(/\s+/).slice(0, 5).join(' ').replace(/[.,:;]$/, '')
215
+ : doc.element.kind;
216
+ const safeName = doc.element.name.replace(/"/g, '&quot;');
217
+ content += ` <Card title="${safeName}" icon="${icon}" href="#${anchor}">\n`;
218
+ content += ` ${desc}\n`;
199
219
  content += ` </Card>\n`;
200
220
  }
201
221
  content += '</CardGroup>\n\n';
@@ -8,6 +8,7 @@ export declare class AnthropicClient implements LLMClient {
8
8
  private client;
9
9
  private model;
10
10
  private maxRetries;
11
+ private apiKey;
11
12
  constructor(config: LLMClientConfig);
12
13
  isConfigured(): boolean;
13
14
  complete(request: CompletionRequest): Promise<CompletionResponse>;
@@ -7,9 +7,11 @@ export class AnthropicClient {
7
7
  client;
8
8
  model;
9
9
  maxRetries;
10
+ apiKey;
10
11
  constructor(config) {
11
12
  this.model = config.model;
12
13
  this.maxRetries = config.maxRetries ?? 3;
14
+ this.apiKey = config.apiKey || '';
13
15
  this.client = new Anthropic({
14
16
  apiKey: config.apiKey,
15
17
  timeout: config.timeout ?? 60000,
@@ -17,7 +19,7 @@ export class AnthropicClient {
17
19
  });
18
20
  }
19
21
  isConfigured() {
20
- return true; // Anthropic SDK handles validation
22
+ return this.apiKey !== '' && this.apiKey !== 'not-set';
21
23
  }
22
24
  async complete(request) {
23
25
  const model = request.model || this.model;
@@ -31,6 +31,8 @@ export interface ElementContext {
31
31
  packageName?: string;
32
32
  sourceContext?: string;
33
33
  filePath?: string;
34
+ externalDocs?: string;
35
+ projectContext?: string;
34
36
  }
35
37
  /**
36
38
  * Generated documentation result
@@ -40,6 +42,10 @@ export interface GeneratedDocResult {
40
42
  codeExample: string;
41
43
  pythonExample?: string;
42
44
  typescriptExample?: string;
45
+ usage?: {
46
+ inputTokens: number;
47
+ outputTokens: number;
48
+ };
43
49
  }
44
50
  /**
45
51
  * Helper to generate documentation for a code element
@@ -47,7 +53,3 @@ export interface GeneratedDocResult {
47
53
  export declare function generateDocumentation(client: LLMClient, element: ElementContext, options?: {
48
54
  multiLanguage?: boolean;
49
55
  }): Promise<GeneratedDocResult>;
50
- /**
51
- * Helper to fix a broken code sample with smart strategies
52
- */
53
- export declare function fixCodeSample(client: LLMClient, code: string, error: string, context: string, iteration?: number): Promise<string>;
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>;