skrypt-ai 0.1.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 (125) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +200 -0
  3. package/dist/autofix/index.d.ts +46 -0
  4. package/dist/autofix/index.js +240 -0
  5. package/dist/cli.d.ts +2 -0
  6. package/dist/cli.js +40 -0
  7. package/dist/commands/autofix.d.ts +2 -0
  8. package/dist/commands/autofix.js +143 -0
  9. package/dist/commands/generate.d.ts +2 -0
  10. package/dist/commands/generate.js +320 -0
  11. package/dist/commands/init.d.ts +2 -0
  12. package/dist/commands/init.js +56 -0
  13. package/dist/commands/review-pr.d.ts +2 -0
  14. package/dist/commands/review-pr.js +117 -0
  15. package/dist/commands/watch.d.ts +2 -0
  16. package/dist/commands/watch.js +142 -0
  17. package/dist/config/index.d.ts +2 -0
  18. package/dist/config/index.js +2 -0
  19. package/dist/config/loader.d.ts +9 -0
  20. package/dist/config/loader.js +82 -0
  21. package/dist/config/types.d.ts +24 -0
  22. package/dist/config/types.js +34 -0
  23. package/dist/generator/generator.d.ts +15 -0
  24. package/dist/generator/generator.js +144 -0
  25. package/dist/generator/index.d.ts +4 -0
  26. package/dist/generator/index.js +4 -0
  27. package/dist/generator/organizer.d.ts +29 -0
  28. package/dist/generator/organizer.js +222 -0
  29. package/dist/generator/types.d.ts +83 -0
  30. package/dist/generator/types.js +1 -0
  31. package/dist/generator/writer.d.ts +28 -0
  32. package/dist/generator/writer.js +320 -0
  33. package/dist/github/pr-comments.d.ts +40 -0
  34. package/dist/github/pr-comments.js +308 -0
  35. package/dist/llm/anthropic-client.d.ts +16 -0
  36. package/dist/llm/anthropic-client.js +92 -0
  37. package/dist/llm/index.d.ts +53 -0
  38. package/dist/llm/index.js +400 -0
  39. package/dist/llm/llm.manual-test.d.ts +1 -0
  40. package/dist/llm/llm.manual-test.js +112 -0
  41. package/dist/llm/llm.mock-test.d.ts +4 -0
  42. package/dist/llm/llm.mock-test.js +79 -0
  43. package/dist/llm/openai-client.d.ts +17 -0
  44. package/dist/llm/openai-client.js +90 -0
  45. package/dist/llm/types.d.ts +60 -0
  46. package/dist/llm/types.js +20 -0
  47. package/dist/scanner/content-type.d.ts +39 -0
  48. package/dist/scanner/content-type.js +194 -0
  49. package/dist/scanner/content-type.test.d.ts +1 -0
  50. package/dist/scanner/content-type.test.js +231 -0
  51. package/dist/scanner/go.d.ts +20 -0
  52. package/dist/scanner/go.js +269 -0
  53. package/dist/scanner/index.d.ts +21 -0
  54. package/dist/scanner/index.js +137 -0
  55. package/dist/scanner/python.d.ts +6 -0
  56. package/dist/scanner/python.js +57 -0
  57. package/dist/scanner/python_parser.py +230 -0
  58. package/dist/scanner/rust.d.ts +23 -0
  59. package/dist/scanner/rust.js +304 -0
  60. package/dist/scanner/scanner.test.d.ts +1 -0
  61. package/dist/scanner/scanner.test.js +210 -0
  62. package/dist/scanner/types.d.ts +50 -0
  63. package/dist/scanner/types.js +1 -0
  64. package/dist/scanner/typescript.d.ts +34 -0
  65. package/dist/scanner/typescript.js +327 -0
  66. package/dist/scanner/typescript.manual-test.d.ts +1 -0
  67. package/dist/scanner/typescript.manual-test.js +112 -0
  68. package/dist/template/docs.json +32 -0
  69. package/dist/template/mdx-components.tsx +62 -0
  70. package/dist/template/next-env.d.ts +6 -0
  71. package/dist/template/next.config.mjs +17 -0
  72. package/dist/template/package.json +39 -0
  73. package/dist/template/postcss.config.mjs +5 -0
  74. package/dist/template/public/search-index.json +1 -0
  75. package/dist/template/scripts/build-search-index.mjs +120 -0
  76. package/dist/template/src/app/api/mock/[...path]/route.ts +224 -0
  77. package/dist/template/src/app/api/openapi/route.ts +48 -0
  78. package/dist/template/src/app/api/rate-limit/route.ts +84 -0
  79. package/dist/template/src/app/docs/[...slug]/page.tsx +81 -0
  80. package/dist/template/src/app/docs/layout.tsx +9 -0
  81. package/dist/template/src/app/docs/page.mdx +67 -0
  82. package/dist/template/src/app/error.tsx +63 -0
  83. package/dist/template/src/app/layout.tsx +71 -0
  84. package/dist/template/src/app/page.tsx +18 -0
  85. package/dist/template/src/app/reference/route.ts +36 -0
  86. package/dist/template/src/app/robots.ts +14 -0
  87. package/dist/template/src/app/sitemap.ts +64 -0
  88. package/dist/template/src/components/breadcrumbs.tsx +41 -0
  89. package/dist/template/src/components/copy-button.tsx +29 -0
  90. package/dist/template/src/components/docs-layout.tsx +35 -0
  91. package/dist/template/src/components/edit-link.tsx +39 -0
  92. package/dist/template/src/components/feedback.tsx +52 -0
  93. package/dist/template/src/components/header.tsx +66 -0
  94. package/dist/template/src/components/mdx/accordion.tsx +48 -0
  95. package/dist/template/src/components/mdx/api-badge.tsx +57 -0
  96. package/dist/template/src/components/mdx/callout.tsx +111 -0
  97. package/dist/template/src/components/mdx/card.tsx +62 -0
  98. package/dist/template/src/components/mdx/changelog.tsx +57 -0
  99. package/dist/template/src/components/mdx/code-block.tsx +42 -0
  100. package/dist/template/src/components/mdx/code-group.tsx +125 -0
  101. package/dist/template/src/components/mdx/code-playground.tsx +322 -0
  102. package/dist/template/src/components/mdx/go-playground.tsx +235 -0
  103. package/dist/template/src/components/mdx/heading.tsx +37 -0
  104. package/dist/template/src/components/mdx/highlighted-code.tsx +89 -0
  105. package/dist/template/src/components/mdx/index.tsx +15 -0
  106. package/dist/template/src/components/mdx/param-table.tsx +71 -0
  107. package/dist/template/src/components/mdx/python-playground.tsx +293 -0
  108. package/dist/template/src/components/mdx/steps.tsx +43 -0
  109. package/dist/template/src/components/mdx/tabs.tsx +81 -0
  110. package/dist/template/src/components/rate-limit-display.tsx +183 -0
  111. package/dist/template/src/components/search-dialog.tsx +178 -0
  112. package/dist/template/src/components/sidebar.tsx +129 -0
  113. package/dist/template/src/components/syntax-theme-selector.tsx +50 -0
  114. package/dist/template/src/components/table-of-contents.tsx +84 -0
  115. package/dist/template/src/components/theme-toggle.tsx +46 -0
  116. package/dist/template/src/components/version-selector.tsx +61 -0
  117. package/dist/template/src/contexts/syntax-theme.tsx +52 -0
  118. package/dist/template/src/lib/highlight.ts +83 -0
  119. package/dist/template/src/lib/search-types.ts +37 -0
  120. package/dist/template/src/lib/search.ts +125 -0
  121. package/dist/template/src/lib/utils.ts +6 -0
  122. package/dist/template/src/styles/globals.css +152 -0
  123. package/dist/template/tsconfig.json +25 -0
  124. package/dist/template/tsconfig.tsbuildinfo +1 -0
  125. package/package.json +72 -0
@@ -0,0 +1,82 @@
1
+ import { readFileSync, existsSync } from 'fs';
2
+ import { join } from 'path';
3
+ import yaml from 'js-yaml';
4
+ import { DEFAULT_CONFIG, PROVIDER_ENV_KEYS } from './types.js';
5
+ const VALID_PROVIDERS = ['deepseek', 'openai', 'anthropic', 'google', 'ollama', 'openrouter'];
6
+ const CONFIG_FILENAMES = ['.skrypt.yaml', '.skrypt.yml', 'skrypt.yaml', 'skrypt.yml'];
7
+ export function findConfigFile(dir) {
8
+ for (const filename of CONFIG_FILENAMES) {
9
+ const filepath = join(dir, filename);
10
+ if (existsSync(filepath)) {
11
+ return filepath;
12
+ }
13
+ }
14
+ return null;
15
+ }
16
+ export function loadConfig(configPath) {
17
+ // If explicit path provided, use it
18
+ if (configPath) {
19
+ if (!existsSync(configPath)) {
20
+ throw new Error(`Config file not found: ${configPath}`);
21
+ }
22
+ return parseConfigFile(configPath);
23
+ }
24
+ // Otherwise look for config in current directory
25
+ const foundPath = findConfigFile(process.cwd());
26
+ if (foundPath) {
27
+ return parseConfigFile(foundPath);
28
+ }
29
+ // No config file, use defaults
30
+ return DEFAULT_CONFIG;
31
+ }
32
+ function parseConfigFile(filepath) {
33
+ const content = readFileSync(filepath, 'utf-8');
34
+ const parsed = yaml.load(content);
35
+ // Merge with defaults
36
+ return mergeConfig(DEFAULT_CONFIG, parsed);
37
+ }
38
+ function mergeConfig(defaults, overrides) {
39
+ return {
40
+ version: overrides.version ?? defaults.version,
41
+ source: {
42
+ ...defaults.source,
43
+ ...overrides.source
44
+ },
45
+ output: {
46
+ ...defaults.output,
47
+ ...overrides.output
48
+ },
49
+ llm: {
50
+ ...defaults.llm,
51
+ ...overrides.llm
52
+ },
53
+ license: overrides.license ?? defaults.license
54
+ };
55
+ }
56
+ export function validateConfig(config) {
57
+ const errors = [];
58
+ if (config.version !== 1) {
59
+ errors.push(`Unsupported config version: ${config.version}`);
60
+ }
61
+ if (!config.source.path) {
62
+ errors.push('source.path is required');
63
+ }
64
+ if (!config.output.path) {
65
+ errors.push('output.path is required');
66
+ }
67
+ if (!VALID_PROVIDERS.includes(config.llm.provider)) {
68
+ errors.push(`Invalid LLM provider: ${config.llm.provider}. Valid: ${VALID_PROVIDERS.join(', ')}`);
69
+ }
70
+ return errors;
71
+ }
72
+ export function getRequiredEnvKey(provider) {
73
+ return PROVIDER_ENV_KEYS[provider] || null;
74
+ }
75
+ export function checkApiKey(provider) {
76
+ const envKey = PROVIDER_ENV_KEYS[provider];
77
+ // Ollama doesn't need an API key
78
+ if (!envKey) {
79
+ return { ok: true, envKey: null };
80
+ }
81
+ return { ok: !!process.env[envKey], envKey };
82
+ }
@@ -0,0 +1,24 @@
1
+ export interface SourceConfig {
2
+ path: string;
3
+ include?: string[];
4
+ exclude?: string[];
5
+ }
6
+ export type LLMProvider = 'deepseek' | 'openai' | 'anthropic' | 'google' | 'ollama' | 'openrouter';
7
+ export interface LLMConfig {
8
+ provider: LLMProvider;
9
+ model: string;
10
+ baseUrl?: string;
11
+ }
12
+ export declare const PROVIDER_ENV_KEYS: Record<LLMProvider, string>;
13
+ export declare const DEFAULT_MODELS: Record<LLMProvider, string>;
14
+ export interface Config {
15
+ version: number;
16
+ source: SourceConfig;
17
+ output: {
18
+ path: string;
19
+ format: 'markdown' | 'mdx';
20
+ };
21
+ llm: LLMConfig;
22
+ license?: string;
23
+ }
24
+ export declare const DEFAULT_CONFIG: Config;
@@ -0,0 +1,34 @@
1
+ // Provider -> env var mapping
2
+ export const PROVIDER_ENV_KEYS = {
3
+ deepseek: 'DEEPSEEK_API_KEY',
4
+ openai: 'OPENAI_API_KEY',
5
+ anthropic: 'ANTHROPIC_API_KEY',
6
+ google: 'GOOGLE_API_KEY',
7
+ ollama: '', // No key needed for local
8
+ openrouter: 'OPENROUTER_API_KEY'
9
+ };
10
+ // Default models per provider
11
+ export const DEFAULT_MODELS = {
12
+ deepseek: 'deepseek-v3',
13
+ openai: 'gpt-4.1',
14
+ anthropic: 'claude-sonnet-4-6',
15
+ google: 'gemini-2.5-flash',
16
+ ollama: 'qwen2.5-coder:32b',
17
+ openrouter: 'deepseek/deepseek-v3'
18
+ };
19
+ export const DEFAULT_CONFIG = {
20
+ version: 1,
21
+ source: {
22
+ path: './src',
23
+ include: ['**/*.py', '**/*.js', '**/*.ts', '**/*.go', '**/*.rs'],
24
+ exclude: ['**/node_modules/**', '**/__pycache__/**', '**/dist/**', '**/target/**']
25
+ },
26
+ output: {
27
+ path: './docs',
28
+ format: 'markdown'
29
+ },
30
+ llm: {
31
+ provider: 'deepseek',
32
+ model: 'deepseek-v3'
33
+ }
34
+ };
@@ -0,0 +1,15 @@
1
+ import { APIElement } from '../scanner/types.js';
2
+ import { LLMClient } from '../llm/index.js';
3
+ import { GeneratedDoc, GenerationOptions, GenerationProgress } from './types.js';
4
+ /**
5
+ * Generate documentation for an API element (no testing)
6
+ */
7
+ export declare function generateForElement(element: APIElement, client: LLMClient, options: GenerationOptions, onProgress?: (progress: GenerationProgress) => void): Promise<GeneratedDoc>;
8
+ /**
9
+ * Generate documentation for multiple elements
10
+ */
11
+ export declare function generateForElements(elements: APIElement[], client: LLMClient, options: GenerationOptions): Promise<GeneratedDoc[]>;
12
+ /**
13
+ * Format generated docs as markdown file content
14
+ */
15
+ export declare function formatAsMarkdown(docs: GeneratedDoc[], title: string): string;
@@ -0,0 +1,144 @@
1
+ import { generateDocumentation } from '../llm/index.js';
2
+ /**
3
+ * Build enhanced context for LLM from API element
4
+ */
5
+ function buildElementContext(element) {
6
+ return {
7
+ kind: element.kind,
8
+ name: element.name,
9
+ signature: element.signature,
10
+ parameters: element.parameters,
11
+ returnType: element.returnType,
12
+ docstring: element.docstring,
13
+ parentClass: element.parentClass,
14
+ imports: element.imports,
15
+ packageName: element.packageName,
16
+ sourceContext: element.sourceContext,
17
+ filePath: element.filePath
18
+ };
19
+ }
20
+ /**
21
+ * Detect code language from file path
22
+ */
23
+ function detectLanguageFromFile(filePath) {
24
+ if (filePath.endsWith('.py'))
25
+ return 'python';
26
+ if (filePath.endsWith('.ts') || filePath.endsWith('.tsx'))
27
+ return 'typescript';
28
+ if (filePath.endsWith('.js') || filePath.endsWith('.jsx'))
29
+ return 'javascript';
30
+ if (filePath.endsWith('.go'))
31
+ return 'go';
32
+ if (filePath.endsWith('.rs'))
33
+ return 'rust';
34
+ return 'typescript';
35
+ }
36
+ /**
37
+ * Generate documentation for an API element (no testing)
38
+ */
39
+ export async function generateForElement(element, client, options, onProgress) {
40
+ const report = (status) => {
41
+ onProgress?.({
42
+ current: 0,
43
+ total: 0,
44
+ element: element.name,
45
+ status
46
+ });
47
+ };
48
+ const codeLanguage = detectLanguageFromFile(element.filePath);
49
+ const useMultiLang = options.multiLanguage ?? false;
50
+ report('generating');
51
+ try {
52
+ const elementContext = buildElementContext(element);
53
+ const result = await generateDocumentation(client, elementContext, { multiLanguage: useMultiLang });
54
+ report('done');
55
+ return {
56
+ element,
57
+ markdown: result.markdown,
58
+ codeExample: result.codeExample,
59
+ codeLanguage,
60
+ typescriptExample: result.typescriptExample,
61
+ pythonExample: result.pythonExample
62
+ };
63
+ }
64
+ catch (err) {
65
+ report('failed');
66
+ return {
67
+ element,
68
+ markdown: '',
69
+ codeExample: '',
70
+ codeLanguage,
71
+ error: `Failed to generate: ${err}`
72
+ };
73
+ }
74
+ }
75
+ /**
76
+ * Generate documentation for multiple elements
77
+ */
78
+ export async function generateForElements(elements, client, options) {
79
+ const results = [];
80
+ for (let i = 0; i < elements.length; i++) {
81
+ const element = elements[i];
82
+ if (!element)
83
+ continue;
84
+ const doc = await generateForElement(element, client, options, (progress) => {
85
+ options.onProgress?.({
86
+ ...progress,
87
+ current: i + 1,
88
+ total: elements.length
89
+ });
90
+ });
91
+ results.push(doc);
92
+ }
93
+ return results;
94
+ }
95
+ /**
96
+ * Format generated docs as markdown file content
97
+ */
98
+ export function formatAsMarkdown(docs, title) {
99
+ let content = `# ${title}\n\n`;
100
+ const functions = docs.filter(d => d.element.kind === 'function');
101
+ const classes = docs.filter(d => d.element.kind === 'class');
102
+ const methods = docs.filter(d => d.element.kind === 'method');
103
+ if (functions.length > 0) {
104
+ content += '## Functions\n\n';
105
+ for (const doc of functions) {
106
+ content += formatDocSection(doc);
107
+ }
108
+ }
109
+ if (classes.length > 0) {
110
+ content += '## Classes\n\n';
111
+ for (const doc of classes) {
112
+ content += formatDocSection(doc);
113
+ const classMethods = methods.filter(m => m.element.parentClass === doc.element.name);
114
+ if (classMethods.length > 0) {
115
+ content += '### Methods\n\n';
116
+ for (const method of classMethods) {
117
+ content += formatDocSection(method, '####');
118
+ }
119
+ }
120
+ }
121
+ }
122
+ return content;
123
+ }
124
+ function formatDocSection(doc, headingLevel = '###') {
125
+ let section = `${headingLevel} \`${doc.element.name}\`\n\n`;
126
+ section += `\`\`\`${doc.codeLanguage}\n${doc.element.signature}\n\`\`\`\n\n`;
127
+ if (doc.markdown) {
128
+ section += doc.markdown + '\n\n';
129
+ }
130
+ const hasMultiLang = doc.typescriptExample && doc.pythonExample;
131
+ if (hasMultiLang) {
132
+ section += '**Examples:**\n\n';
133
+ section += '<CodeGroup>\n\n';
134
+ section += `\`\`\`typescript example.ts\n${doc.typescriptExample}\n\`\`\`\n\n`;
135
+ section += `\`\`\`python example.py\n${doc.pythonExample}\n\`\`\`\n\n`;
136
+ section += '</CodeGroup>\n\n';
137
+ }
138
+ else if (doc.codeExample) {
139
+ section += '**Example:**\n\n';
140
+ const ext = doc.codeLanguage === 'typescript' ? 'ts' : doc.codeLanguage === 'python' ? 'py' : 'js';
141
+ section += `\`\`\`${doc.codeLanguage} example.${ext}\n${doc.codeExample}\n\`\`\`\n\n`;
142
+ }
143
+ return section;
144
+ }
@@ -0,0 +1,4 @@
1
+ export * from './types.js';
2
+ export * from './generator.js';
3
+ export * from './writer.js';
4
+ export * from './organizer.js';
@@ -0,0 +1,4 @@
1
+ export * from './types.js';
2
+ export * from './generator.js';
3
+ export * from './writer.js';
4
+ export * from './organizer.js';
@@ -0,0 +1,29 @@
1
+ import { GeneratedDoc, Topic, TopicConfig, CrossReference, NavigationItem } from './types.js';
2
+ /**
3
+ * Default topic configuration with common patterns
4
+ */
5
+ export declare const DEFAULT_TOPIC_CONFIG: TopicConfig;
6
+ /**
7
+ * Organize generated docs into topics
8
+ */
9
+ export declare function organizeByTopic(docs: GeneratedDoc[], config?: TopicConfig): Topic[];
10
+ /**
11
+ * Detect cross-references between elements
12
+ */
13
+ export declare function detectCrossReferences(docs: GeneratedDoc[]): CrossReference[];
14
+ /**
15
+ * Get cross-references for a specific element
16
+ */
17
+ export declare function getCrossRefsForElement(elementName: string, allRefs: CrossReference[]): CrossReference[];
18
+ /**
19
+ * Build navigation structure from topics
20
+ */
21
+ export declare function buildNavigation(topics: Topic[]): NavigationItem[];
22
+ /**
23
+ * Generate a sidebar configuration (works with multiple doc platforms)
24
+ */
25
+ export declare function generateSidebarConfig(topics: Topic[]): object;
26
+ /**
27
+ * Merge user config with defaults
28
+ */
29
+ export declare function mergeTopicConfig(userConfig: Partial<TopicConfig>, defaults?: TopicConfig): TopicConfig;
@@ -0,0 +1,222 @@
1
+ /**
2
+ * Default topic configuration with common patterns
3
+ */
4
+ export const DEFAULT_TOPIC_CONFIG = {
5
+ topics: {
6
+ 'getting-started': { name: 'Getting Started', description: 'Quick start and setup guides', order: 0 },
7
+ 'core': { name: 'Core API', description: 'Core functionality and main exports', order: 1 },
8
+ 'middleware': { name: 'Middleware', description: 'Request/response middleware and handlers', order: 2 },
9
+ 'memory': { name: 'Memory & Storage', description: 'Memory management and persistence', order: 3 },
10
+ 'tools': { name: 'Tools & Utilities', description: 'Helper functions and utilities', order: 4 },
11
+ 'types': { name: 'Types & Interfaces', description: 'Type definitions and interfaces', order: 5 },
12
+ 'integrations': { name: 'Integrations', description: 'Third-party integrations', order: 6 },
13
+ 'advanced': { name: 'Advanced', description: 'Advanced usage and configuration', order: 7 },
14
+ 'other': { name: 'Other', description: 'Miscellaneous utilities', order: 99 }
15
+ },
16
+ rules: [
17
+ // File path patterns
18
+ { topic: 'middleware', patterns: ['**/middleware*', '**/handler*'] },
19
+ { topic: 'memory', patterns: ['**/memory*', '**/store*', '**/persist*', '**/cache*'] },
20
+ { topic: 'tools', patterns: ['**/tool*', '**/util*', '**/helper*', '**/shared*'] },
21
+ { topic: 'types', patterns: ['**/types*', '**/interface*', '**/schema*'] },
22
+ { topic: 'core', patterns: ['**/index*', '**/main*', '**/core*', '**/client*'] },
23
+ { topic: 'integrations', patterns: ['**/openai*', '**/anthropic*', '**/vercel*', '**/mastra*'] },
24
+ // Function name patterns
25
+ { topic: 'middleware', namePatterns: ['^create.*Handler$', '^.*Middleware$', '^wrap.*$'] },
26
+ { topic: 'memory', namePatterns: ['^save.*Memory', '^get.*Memory', '^.*Memory$', '^store.*$', '^persist.*$'] },
27
+ { topic: 'tools', namePatterns: ['^create.*Tool$', '^.*Tool$', '^use.*$'] },
28
+ // Element kinds
29
+ { topic: 'types', kinds: ['interface', 'type'] }
30
+ ],
31
+ defaultTopic: 'other'
32
+ };
33
+ /**
34
+ * Organize generated docs into topics
35
+ */
36
+ export function organizeByTopic(docs, config = DEFAULT_TOPIC_CONFIG) {
37
+ const topicDocs = new Map();
38
+ // Initialize all topics
39
+ for (const topicId of Object.keys(config.topics)) {
40
+ topicDocs.set(topicId, []);
41
+ }
42
+ // Categorize each doc
43
+ for (const doc of docs) {
44
+ const topicId = inferTopic(doc, config);
45
+ if (!topicDocs.has(topicId)) {
46
+ topicDocs.set(topicId, []);
47
+ }
48
+ topicDocs.get(topicId).push(doc);
49
+ }
50
+ // Build topic objects
51
+ const topics = [];
52
+ for (const [topicId, topicDocList] of topicDocs.entries()) {
53
+ if (topicDocList.length === 0)
54
+ continue;
55
+ const topicConfig = config.topics[topicId] || { name: titleCase(topicId), order: 50 };
56
+ topics.push({
57
+ id: topicId,
58
+ name: topicConfig.name,
59
+ description: topicConfig.description,
60
+ order: topicConfig.order ?? 50,
61
+ docs: sortDocsWithinTopic(topicDocList)
62
+ });
63
+ }
64
+ // Sort topics by order
65
+ return topics.sort((a, b) => a.order - b.order);
66
+ }
67
+ /**
68
+ * Infer the topic for a doc based on rules
69
+ */
70
+ function inferTopic(doc, config) {
71
+ const { element } = doc;
72
+ for (const rule of config.rules) {
73
+ // Check file path patterns
74
+ if (rule.patterns) {
75
+ for (const pattern of rule.patterns) {
76
+ if (matchGlobPattern(element.filePath, pattern)) {
77
+ return rule.topic;
78
+ }
79
+ }
80
+ }
81
+ // Check name patterns
82
+ if (rule.namePatterns) {
83
+ for (const pattern of rule.namePatterns) {
84
+ if (new RegExp(pattern, 'i').test(element.name)) {
85
+ return rule.topic;
86
+ }
87
+ }
88
+ }
89
+ // Check element kinds
90
+ if (rule.kinds && rule.kinds.includes(element.kind)) {
91
+ return rule.topic;
92
+ }
93
+ }
94
+ return config.defaultTopic || 'other';
95
+ }
96
+ /**
97
+ * Simple glob pattern matching
98
+ */
99
+ function matchGlobPattern(path, pattern) {
100
+ // Convert glob to regex
101
+ const regex = pattern
102
+ .replace(/\*\*/g, '___DOUBLESTAR___')
103
+ .replace(/\*/g, '[^/]*')
104
+ .replace(/___DOUBLESTAR___/g, '.*')
105
+ .replace(/\?/g, '.');
106
+ return new RegExp(regex, 'i').test(path);
107
+ }
108
+ /**
109
+ * Sort docs within a topic by importance
110
+ */
111
+ function sortDocsWithinTopic(docs) {
112
+ return docs.sort((a, b) => {
113
+ // Classes first, then functions, then methods
114
+ const kindOrder = { class: 0, function: 1, method: 2 };
115
+ const aKind = kindOrder[a.element.kind] ?? 3;
116
+ const bKind = kindOrder[b.element.kind] ?? 3;
117
+ if (aKind !== bKind)
118
+ return aKind - bKind;
119
+ // Then by name
120
+ return a.element.name.localeCompare(b.element.name);
121
+ });
122
+ }
123
+ /**
124
+ * Detect cross-references between elements
125
+ */
126
+ export function detectCrossReferences(docs) {
127
+ const refs = [];
128
+ const elementNames = new Set(docs.map(d => d.element.name));
129
+ for (const doc of docs) {
130
+ const { element } = doc;
131
+ // Check source context for references to other elements
132
+ const sourceContext = element.sourceContext || '';
133
+ const signature = element.signature || '';
134
+ for (const otherName of elementNames) {
135
+ if (otherName === element.name)
136
+ continue;
137
+ // Check if this element uses another element
138
+ if (sourceContext.includes(otherName) || signature.includes(otherName)) {
139
+ refs.push({
140
+ fromElement: element.name,
141
+ toElement: otherName,
142
+ relationship: 'uses'
143
+ });
144
+ }
145
+ }
146
+ // Methods reference their parent class
147
+ if (element.parentClass && elementNames.has(element.parentClass)) {
148
+ refs.push({
149
+ fromElement: element.name,
150
+ toElement: element.parentClass,
151
+ relationship: 'related'
152
+ });
153
+ }
154
+ }
155
+ // Add reverse references (used-by)
156
+ const usesRefs = refs.filter(r => r.relationship === 'uses');
157
+ for (const ref of usesRefs) {
158
+ refs.push({
159
+ fromElement: ref.toElement,
160
+ toElement: ref.fromElement,
161
+ relationship: 'used-by'
162
+ });
163
+ }
164
+ return refs;
165
+ }
166
+ /**
167
+ * Get cross-references for a specific element
168
+ */
169
+ export function getCrossRefsForElement(elementName, allRefs) {
170
+ return allRefs.filter(r => r.fromElement === elementName);
171
+ }
172
+ /**
173
+ * Build navigation structure from topics
174
+ */
175
+ export function buildNavigation(topics) {
176
+ return topics.map(topic => ({
177
+ title: topic.name,
178
+ path: `/${topic.id}`,
179
+ children: topic.docs.map(doc => ({
180
+ title: doc.element.name,
181
+ path: `/${topic.id}#${slugify(doc.element.name)}`
182
+ }))
183
+ }));
184
+ }
185
+ /**
186
+ * Generate a sidebar configuration (works with multiple doc platforms)
187
+ */
188
+ export function generateSidebarConfig(topics) {
189
+ return {
190
+ navigation: topics.map(topic => ({
191
+ group: topic.name,
192
+ pages: topic.docs.map(doc => `${topic.id}/${slugify(doc.element.name)}`)
193
+ }))
194
+ };
195
+ }
196
+ /**
197
+ * Convert string to title case
198
+ */
199
+ function titleCase(str) {
200
+ return str
201
+ .replace(/-/g, ' ')
202
+ .replace(/\b\w/g, c => c.toUpperCase());
203
+ }
204
+ /**
205
+ * Convert string to URL-safe slug
206
+ */
207
+ function slugify(str) {
208
+ return str
209
+ .toLowerCase()
210
+ .replace(/[^a-z0-9]+/g, '-')
211
+ .replace(/^-|-$/g, '');
212
+ }
213
+ /**
214
+ * Merge user config with defaults
215
+ */
216
+ export function mergeTopicConfig(userConfig, defaults = DEFAULT_TOPIC_CONFIG) {
217
+ return {
218
+ topics: { ...defaults.topics, ...userConfig.topics },
219
+ rules: [...defaults.rules, ...(userConfig.rules || [])],
220
+ defaultTopic: userConfig.defaultTopic ?? defaults.defaultTopic
221
+ };
222
+ }
@@ -0,0 +1,83 @@
1
+ import { APIElement } from '../scanner/types.js';
2
+ /**
3
+ * Generated documentation for an API element
4
+ */
5
+ export interface GeneratedDoc {
6
+ element: APIElement;
7
+ markdown: string;
8
+ codeExample: string;
9
+ codeLanguage: string;
10
+ error?: string;
11
+ typescriptExample?: string;
12
+ pythonExample?: string;
13
+ }
14
+ /**
15
+ * Progress callback for generation
16
+ */
17
+ export interface GenerationProgress {
18
+ current: number;
19
+ total: number;
20
+ element: string;
21
+ status: 'generating' | 'done' | 'failed';
22
+ }
23
+ /**
24
+ * Options for generation
25
+ */
26
+ export interface GenerationOptions {
27
+ onProgress?: (progress: GenerationProgress) => void;
28
+ multiLanguage?: boolean;
29
+ }
30
+ /**
31
+ * Result of generating docs for a file
32
+ */
33
+ export interface FileGenerationResult {
34
+ filePath: string;
35
+ docs: GeneratedDoc[];
36
+ }
37
+ /**
38
+ * A topic groups related API elements by concept
39
+ */
40
+ export interface Topic {
41
+ id: string;
42
+ name: string;
43
+ description?: string;
44
+ order: number;
45
+ docs: GeneratedDoc[];
46
+ }
47
+ /**
48
+ * Topic mapping rule for auto-categorization
49
+ */
50
+ export interface TopicRule {
51
+ topic: string;
52
+ patterns?: string[];
53
+ namePatterns?: string[];
54
+ kinds?: string[];
55
+ }
56
+ /**
57
+ * Topic configuration
58
+ */
59
+ export interface TopicConfig {
60
+ topics: Record<string, {
61
+ name: string;
62
+ description?: string;
63
+ order?: number;
64
+ }>;
65
+ rules: TopicRule[];
66
+ defaultTopic?: string;
67
+ }
68
+ /**
69
+ * Navigation structure for generated docs
70
+ */
71
+ export interface NavigationItem {
72
+ title: string;
73
+ path: string;
74
+ children?: NavigationItem[];
75
+ }
76
+ /**
77
+ * Cross-reference between elements
78
+ */
79
+ export interface CrossReference {
80
+ fromElement: string;
81
+ toElement: string;
82
+ relationship: 'uses' | 'used-by' | 'see-also' | 'related';
83
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,28 @@
1
+ import { GeneratedDoc, FileGenerationResult, Topic } from './types.js';
2
+ /**
3
+ * Generate llms.txt file (Answer Engine Optimization)
4
+ * Format follows https://llmstxt.org convention
5
+ */
6
+ export declare function writeLlmsTxt(docs: GeneratedDoc[], outputDir: string, options?: {
7
+ projectName?: string;
8
+ description?: string;
9
+ }): Promise<void>;
10
+ /**
11
+ * Write generated docs to output directory
12
+ */
13
+ export declare function writeDocsToDirectory(results: FileGenerationResult[], outputDir: string, sourceDir: string): Promise<{
14
+ filesWritten: number;
15
+ totalDocs: number;
16
+ }>;
17
+ /**
18
+ * Group generated docs by file
19
+ */
20
+ export declare function groupDocsByFile(docs: GeneratedDoc[]): FileGenerationResult[];
21
+ /**
22
+ * Write docs organized by topic
23
+ */
24
+ export declare function writeDocsByTopic(docs: GeneratedDoc[], outputDir: string): Promise<{
25
+ filesWritten: number;
26
+ totalDocs: number;
27
+ topics: Topic[];
28
+ }>;