skrypt-ai 0.7.0 → 0.8.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 (110) hide show
  1. package/dist/auth/index.js +3 -3
  2. package/dist/cli.js +1 -1
  3. package/dist/commands/cron.js +0 -4
  4. package/dist/commands/generate/index.d.ts +3 -0
  5. package/dist/commands/generate/index.js +393 -0
  6. package/dist/commands/generate/scan.d.ts +41 -0
  7. package/dist/commands/generate/scan.js +256 -0
  8. package/dist/commands/generate/verify.d.ts +14 -0
  9. package/dist/commands/generate/verify.js +122 -0
  10. package/dist/commands/generate/write.d.ts +25 -0
  11. package/dist/commands/generate/write.js +120 -0
  12. package/dist/commands/import.js +4 -1
  13. package/dist/commands/llms-txt.js +6 -4
  14. package/dist/config/loader.d.ts +0 -1
  15. package/dist/config/loader.js +1 -1
  16. package/dist/generator/agents-md.d.ts +25 -0
  17. package/dist/generator/agents-md.js +122 -0
  18. package/dist/generator/index.d.ts +2 -0
  19. package/dist/generator/index.js +2 -0
  20. package/dist/generator/mdx-serializer.d.ts +11 -0
  21. package/dist/generator/mdx-serializer.js +135 -0
  22. package/dist/generator/organizer.d.ts +1 -16
  23. package/dist/generator/organizer.js +0 -38
  24. package/dist/generator/writer.js +5 -4
  25. package/dist/llm/proxy-client.d.ts +32 -0
  26. package/dist/llm/proxy-client.js +103 -0
  27. package/dist/scanner/csharp.d.ts +0 -4
  28. package/dist/scanner/csharp.js +9 -49
  29. package/dist/scanner/go.d.ts +0 -3
  30. package/dist/scanner/go.js +8 -35
  31. package/dist/scanner/java.d.ts +0 -4
  32. package/dist/scanner/java.js +9 -49
  33. package/dist/scanner/kotlin.d.ts +0 -3
  34. package/dist/scanner/kotlin.js +6 -33
  35. package/dist/scanner/php.d.ts +0 -10
  36. package/dist/scanner/php.js +11 -55
  37. package/dist/scanner/ruby.d.ts +0 -3
  38. package/dist/scanner/ruby.js +8 -38
  39. package/dist/scanner/rust.d.ts +0 -3
  40. package/dist/scanner/rust.js +10 -37
  41. package/dist/scanner/swift.d.ts +0 -3
  42. package/dist/scanner/swift.js +8 -35
  43. package/dist/scanner/utils.d.ts +41 -0
  44. package/dist/scanner/utils.js +97 -0
  45. package/dist/template/docs.json +5 -2
  46. package/dist/template/next.config.mjs +31 -0
  47. package/dist/template/package.json +5 -3
  48. package/dist/template/src/app/layout.tsx +13 -13
  49. package/dist/template/src/app/llms-full.md/route.ts +29 -0
  50. package/dist/template/src/app/llms.txt/route.ts +29 -0
  51. package/dist/template/src/app/md/[...slug]/route.ts +174 -0
  52. package/dist/template/src/app/reference/route.ts +22 -18
  53. package/dist/template/src/app/sitemap.ts +1 -1
  54. package/dist/template/src/components/ai-chat-impl.tsx +206 -0
  55. package/dist/template/src/components/ai-chat.tsx +20 -193
  56. package/dist/template/src/components/mdx/index.tsx +27 -4
  57. package/dist/template/src/lib/fonts.ts +135 -0
  58. package/dist/template/src/middleware.ts +101 -0
  59. package/dist/template/src/styles/globals.css +28 -20
  60. package/dist/utils/files.d.ts +0 -8
  61. package/dist/utils/files.js +0 -33
  62. package/package.json +1 -1
  63. package/dist/autofix/autofix.test.d.ts +0 -1
  64. package/dist/autofix/autofix.test.js +0 -487
  65. package/dist/commands/generate.d.ts +0 -9
  66. package/dist/commands/generate.js +0 -739
  67. package/dist/generator/generator.test.d.ts +0 -1
  68. package/dist/generator/generator.test.js +0 -259
  69. package/dist/generator/writer.test.d.ts +0 -1
  70. package/dist/generator/writer.test.js +0 -411
  71. package/dist/llm/llm.manual-test.d.ts +0 -1
  72. package/dist/llm/llm.manual-test.js +0 -112
  73. package/dist/llm/llm.mock-test.d.ts +0 -4
  74. package/dist/llm/llm.mock-test.js +0 -79
  75. package/dist/plugins/index.d.ts +0 -47
  76. package/dist/plugins/index.js +0 -181
  77. package/dist/scanner/content-type.test.d.ts +0 -1
  78. package/dist/scanner/content-type.test.js +0 -231
  79. package/dist/scanner/integration.test.d.ts +0 -4
  80. package/dist/scanner/integration.test.js +0 -180
  81. package/dist/scanner/scanner.test.d.ts +0 -1
  82. package/dist/scanner/scanner.test.js +0 -210
  83. package/dist/scanner/typescript.manual-test.d.ts +0 -1
  84. package/dist/scanner/typescript.manual-test.js +0 -112
  85. package/dist/template/src/app/docs/auth/page.mdx +0 -589
  86. package/dist/template/src/app/docs/autofix/page.mdx +0 -624
  87. package/dist/template/src/app/docs/cli/page.mdx +0 -217
  88. package/dist/template/src/app/docs/config/page.mdx +0 -428
  89. package/dist/template/src/app/docs/configuration/page.mdx +0 -86
  90. package/dist/template/src/app/docs/deployment/page.mdx +0 -112
  91. package/dist/template/src/app/docs/generator/generator.md +0 -504
  92. package/dist/template/src/app/docs/generator/organizer.md +0 -779
  93. package/dist/template/src/app/docs/generator/page.mdx +0 -613
  94. package/dist/template/src/app/docs/github/page.mdx +0 -502
  95. package/dist/template/src/app/docs/llm/anthropic-client.md +0 -549
  96. package/dist/template/src/app/docs/llm/index.md +0 -471
  97. package/dist/template/src/app/docs/llm/page.mdx +0 -428
  98. package/dist/template/src/app/docs/plugins/page.mdx +0 -1793
  99. package/dist/template/src/app/docs/pro/page.mdx +0 -121
  100. package/dist/template/src/app/docs/quickstart/page.mdx +0 -93
  101. package/dist/template/src/app/docs/scanner/content-type.md +0 -599
  102. package/dist/template/src/app/docs/scanner/index.md +0 -212
  103. package/dist/template/src/app/docs/scanner/page.mdx +0 -307
  104. package/dist/template/src/app/docs/scanner/python.md +0 -469
  105. package/dist/template/src/app/docs/scanner/python_parser.md +0 -1056
  106. package/dist/template/src/app/docs/scanner/rust.md +0 -325
  107. package/dist/template/src/app/docs/scanner/typescript.md +0 -201
  108. package/dist/template/src/app/icon.tsx +0 -29
  109. package/dist/utils/validation.d.ts +0 -1
  110. package/dist/utils/validation.js +0 -12
@@ -0,0 +1,122 @@
1
+ /**
2
+ * AGENTS.md / CLAUDE.md generator
3
+ *
4
+ * Generates context files that coding agents (Claude Code, Cursor, GitHub Copilot,
5
+ * OpenCode) load on every turn. Vercel research: 8KB AGENTS.md = 100% pass rate
6
+ * vs 79% for skills-based approaches.
7
+ */
8
+ const MAX_SIZE_BYTES = 8 * 1024; // 8KB — Vercel's optimal size
9
+ /**
10
+ * Generate AGENTS.md content for a project.
11
+ * Designed to be loaded by coding agents on every turn for instant docs access.
12
+ */
13
+ export function generateAgentsMd(options) {
14
+ const { projectName, docsPath, baseUrl, pages, hasLlmsTxt } = options;
15
+ let out = `# ${projectName} — Documentation Guide\n\n`;
16
+ out += `When answering questions about **${projectName}**, consult these docs first.\n\n`;
17
+ // Access instructions
18
+ out += `## How to Access Docs\n\n`;
19
+ if (baseUrl) {
20
+ out += `- **Browse:** ${baseUrl}\n`;
21
+ if (hasLlmsTxt) {
22
+ out += `- **Agent index:** ${baseUrl}/llms.txt (structured summary)\n`;
23
+ out += `- **Full content:** ${baseUrl}/llms-full.md (all docs in one file)\n`;
24
+ }
25
+ out += `- **Markdown variant:** Append \`.md\` to any docs URL, or use \`Accept: text/markdown\` header\n`;
26
+ }
27
+ out += `- **Local files:** \`${docsPath}/\` (MDX/Markdown source)\n`;
28
+ out += `\n`;
29
+ // Identify entry points
30
+ const quickstarts = pages.filter(p => isEntryPoint(p, 'quickstart'));
31
+ const apiRefs = pages.filter(p => isEntryPoint(p, 'api'));
32
+ const guides = pages.filter(p => isEntryPoint(p, 'guide'));
33
+ if (quickstarts.length > 0 || apiRefs.length > 0) {
34
+ out += `## Key Entry Points\n\n`;
35
+ if (quickstarts.length > 0) {
36
+ out += `**Getting Started:**\n`;
37
+ for (const p of quickstarts.slice(0, 3)) {
38
+ out += formatPageLine(p, baseUrl);
39
+ }
40
+ }
41
+ if (apiRefs.length > 0) {
42
+ out += `**API Reference:**\n`;
43
+ for (const p of apiRefs.slice(0, 5)) {
44
+ out += formatPageLine(p, baseUrl);
45
+ }
46
+ }
47
+ if (guides.length > 0) {
48
+ out += `**Guides:**\n`;
49
+ for (const p of guides.slice(0, 5)) {
50
+ out += formatPageLine(p, baseUrl);
51
+ }
52
+ }
53
+ out += `\n`;
54
+ }
55
+ // Page index — compact, one line per page
56
+ out += `## Documentation Index\n\n`;
57
+ // Group by category if available
58
+ const byCategory = groupByCategory(pages);
59
+ for (const [category, categoryPages] of byCategory) {
60
+ if (category !== '_default') {
61
+ out += `### ${category}\n\n`;
62
+ }
63
+ for (const page of categoryPages) {
64
+ out += formatPageLine(page, baseUrl);
65
+ // Check size — stop adding pages if we're near the limit
66
+ if (Buffer.byteLength(out, 'utf-8') > MAX_SIZE_BYTES - 200) {
67
+ const remaining = pages.length - categoryPages.indexOf(page) - 1;
68
+ if (remaining > 0) {
69
+ out += `\n*...and ${remaining} more pages. See ${hasLlmsTxt ? 'llms.txt' : docsPath} for the complete index.*\n`;
70
+ }
71
+ return finalize(out);
72
+ }
73
+ }
74
+ out += `\n`;
75
+ }
76
+ return finalize(out);
77
+ }
78
+ function finalize(content) {
79
+ const out = content.trimEnd() + '\n';
80
+ // Final size check — truncate page index if over limit
81
+ if (Buffer.byteLength(out, 'utf-8') > MAX_SIZE_BYTES) {
82
+ // Find the last complete line that fits
83
+ const lines = out.split('\n');
84
+ let truncated = '';
85
+ for (const line of lines) {
86
+ const next = truncated + line + '\n';
87
+ if (Buffer.byteLength(next, 'utf-8') > MAX_SIZE_BYTES - 100) {
88
+ truncated += '\n*Index truncated to fit 8KB limit. See llms.txt for complete listing.*\n';
89
+ break;
90
+ }
91
+ truncated = next;
92
+ }
93
+ return truncated;
94
+ }
95
+ return out;
96
+ }
97
+ function formatPageLine(page, baseUrl) {
98
+ const ref = baseUrl ? `${baseUrl}/docs/${page.path}` : page.path;
99
+ const desc = page.description ? ` — ${page.description}` : '';
100
+ return `- [${page.title}](${ref})${desc}\n`;
101
+ }
102
+ function isEntryPoint(page, type) {
103
+ const lower = `${page.title} ${page.path} ${page.category || ''}`.toLowerCase();
104
+ switch (type) {
105
+ case 'quickstart':
106
+ return /quickstart|getting.started|introduction|overview|setup|install/i.test(lower);
107
+ case 'api':
108
+ return /api.ref|reference|endpoint|api$/i.test(lower);
109
+ case 'guide':
110
+ return /guide|tutorial|how.to|walkthrough/i.test(lower);
111
+ }
112
+ }
113
+ function groupByCategory(pages) {
114
+ const groups = new Map();
115
+ for (const page of pages) {
116
+ const cat = page.category || '_default';
117
+ if (!groups.has(cat))
118
+ groups.set(cat, []);
119
+ groups.get(cat).push(page);
120
+ }
121
+ return groups;
122
+ }
@@ -2,3 +2,5 @@ export * from './types.js';
2
2
  export * from './generator.js';
3
3
  export * from './writer.js';
4
4
  export * from './organizer.js';
5
+ export * from './mdx-serializer.js';
6
+ export * from './agents-md.js';
@@ -2,3 +2,5 @@ export * from './types.js';
2
2
  export * from './generator.js';
3
3
  export * from './writer.js';
4
4
  export * from './organizer.js';
5
+ export * from './mdx-serializer.js';
6
+ export * from './agents-md.js';
@@ -0,0 +1,11 @@
1
+ /**
2
+ * MDX-to-Markdown serializer
3
+ *
4
+ * Converts MDX content with JSX components into clean markdown
5
+ * for agent consumption (llms-full.md, content negotiation, AGENTS.md).
6
+ */
7
+ /**
8
+ * Strip MDX/JSX components and convert to clean markdown.
9
+ * Handles CodeGroup, Callout, Card, Tabs, Steps, Accordion, Screenshot, etc.
10
+ */
11
+ export declare function serializeMdxToMarkdown(mdx: string): string;
@@ -0,0 +1,135 @@
1
+ /**
2
+ * MDX-to-Markdown serializer
3
+ *
4
+ * Converts MDX content with JSX components into clean markdown
5
+ * for agent consumption (llms-full.md, content negotiation, AGENTS.md).
6
+ */
7
+ /** Extract a named attribute value from a JSX attribute string. */
8
+ function attr(attrs, name) {
9
+ const re = new RegExp(`${name}=["']([^"']+)["']`);
10
+ const m = attrs.match(re);
11
+ return m ? m[1] : undefined;
12
+ }
13
+ /**
14
+ * Strip MDX/JSX components and convert to clean markdown.
15
+ * Handles CodeGroup, Callout, Card, Tabs, Steps, Accordion, Screenshot, etc.
16
+ */
17
+ export function serializeMdxToMarkdown(mdx) {
18
+ let out = mdx;
19
+ // Remove import statements
20
+ out = out.replace(/^import\s+.*$/gm, '');
21
+ // Remove export statements (but not export default with content)
22
+ out = out.replace(/^export\s+(?!default\s).*$/gm, '');
23
+ // --- Component conversions (order matters) ---
24
+ // CodeGroup → flatten to labeled code blocks
25
+ out = out.replace(/<CodeGroup>\s*([\s\S]*?)\s*<\/CodeGroup>/g, (_match, inner) => {
26
+ return inner.trim();
27
+ });
28
+ // Callout variants: <Callout type="warning">, <Warning>, <Info>, <Note>, <Tip>, <Success>, <Error>
29
+ const calloutTypes = ['Callout', 'Info', 'Warning', 'Success', 'Error', 'Tip', 'Note'];
30
+ for (const tag of calloutTypes) {
31
+ // With type attribute: <Callout type="warning">
32
+ const typedRegex = new RegExp(`<${tag}\\s+type=["']([^"']+)["'][^>]*>\\s*([\\s\\S]*?)\\s*<\\/${tag}>`, 'g');
33
+ out = out.replace(typedRegex, (_m, type, content) => {
34
+ const label = type.charAt(0).toUpperCase() + type.slice(1);
35
+ return formatBlockquote(label, content.trim());
36
+ });
37
+ // Without type attribute: <Warning>content</Warning>
38
+ const simpleRegex = new RegExp(`<${tag}(?:\\s[^>]*)?>\\s*([\\s\\S]*?)\\s*<\\/${tag}>`, 'g');
39
+ out = out.replace(simpleRegex, (_m, content) => {
40
+ const label = tag === 'Callout' ? 'Note' : tag;
41
+ return formatBlockquote(label, content.trim());
42
+ });
43
+ }
44
+ // CardGroup → unwrap (just keep children)
45
+ out = out.replace(/<CardGroup[^>]*>\s*/g, '');
46
+ out = out.replace(/\s*<\/CardGroup>/g, '');
47
+ // Card with attributes + children (attribute-order-independent)
48
+ out = out.replace(/<Card\s+([^>]+?)>\s*([\s\S]*?)\s*<\/Card>/g, (_m, attrs, desc) => {
49
+ const title = attr(attrs, 'title');
50
+ const href = attr(attrs, 'href');
51
+ const description = desc.trim().replace(/<[^>]+>/g, '');
52
+ if (title && href) {
53
+ return description
54
+ ? `- **[${title}](${href})** — ${description}`
55
+ : `- **[${title}](${href})**`;
56
+ }
57
+ if (title) {
58
+ return description ? `- **${title}** — ${description}` : `- **${title}**`;
59
+ }
60
+ return description || '';
61
+ });
62
+ // Tabs/TabList/TabPanel → headings + content
63
+ out = out.replace(/<TabList>\s*([\s\S]*?)\s*<\/TabList>/g, '');
64
+ out = out.replace(/<Tab\s+label=["']([^"']+)["'][^>]*>\s*([\s\S]*?)\s*<\/Tab>/g, (_m, label, content) => `#### ${label}\n\n${content.trim()}\n`);
65
+ out = out.replace(/<TabPanel[^>]*>\s*([\s\S]*?)\s*<\/TabPanel>/g, (_m, content) => content.trim());
66
+ out = out.replace(/<Tabs[^>]*>\s*/g, '');
67
+ out = out.replace(/\s*<\/Tabs>/g, '');
68
+ // Steps/Step → numbered list
69
+ out = out.replace(/<Steps>\s*([\s\S]*?)\s*<\/Steps>/g, (_match, inner) => {
70
+ let stepNum = 0;
71
+ return inner.replace(/<Step\s*(?:title=["']([^"']+)["'][^>]*)?>?\s*([\s\S]*?)\s*<\/Step>/g, (_m, title, content) => {
72
+ stepNum++;
73
+ const heading = title ? `**${title}**` : '';
74
+ return `${stepNum}. ${heading}\n ${content.trim()}\n`;
75
+ });
76
+ });
77
+ // Accordion → bold title + content
78
+ out = out.replace(/<AccordionGroup[^>]*>\s*/g, '');
79
+ out = out.replace(/\s*<\/AccordionGroup>/g, '');
80
+ out = out.replace(/<Accordion\s+title=["']([^"']+)["'][^>]*>\s*([\s\S]*?)\s*<\/Accordion>/g, (_m, title, content) => `**${title}**\n\n${content.trim()}\n`);
81
+ // Screenshot → image (attribute-order-independent)
82
+ out = out.replace(/<Screenshot\s+([^>]*?)\s*\/?>/g, (_m, attrs) => {
83
+ const src = attr(attrs, 'src');
84
+ const alt = attr(attrs, 'alt');
85
+ return src ? `![${alt || 'Screenshot'}](${src})` : '';
86
+ });
87
+ // Also handle <Screenshot>...</Screenshot> (non-self-closing)
88
+ out = out.replace(/<Screenshot\s+([^>]*?)>[\s\S]*?<\/Screenshot>/g, (_m, attrs) => {
89
+ const src = attr(attrs, 'src');
90
+ return src ? `![Screenshot](${src})` : '';
91
+ });
92
+ // Frame → unwrap
93
+ out = out.replace(/<Frame[^>]*>\s*/g, '');
94
+ out = out.replace(/\s*<\/Frame>/g, '');
95
+ // DarkImage → image (use light image src)
96
+ out = out.replace(/<DarkImage\s+(?:[^>]*?src=["']([^"']+)["'][^>]*?)\s*\/?>/g, (_m, src) => `![Image](${src})`);
97
+ // CodePlayground / PythonPlayground / GoPlayground → extract code
98
+ for (const tag of ['CodePlayground', 'PythonPlayground', 'GoPlayground']) {
99
+ const regex = new RegExp(`<${tag}[^>]*>\\s*([\\s\\S]*?)\\s*<\\/${tag}>`, 'g');
100
+ out = out.replace(regex, (_m, content) => content.trim());
101
+ }
102
+ // ParamTable / Schema → pass through content
103
+ out = out.replace(/<ParamTable[^>]*>\s*([\s\S]*?)\s*<\/ParamTable>/g, (_m, c) => c.trim());
104
+ out = out.replace(/<Schema[^>]*>\s*([\s\S]*?)\s*<\/Schema>/g, (_m, c) => c.trim());
105
+ // API badges → inline text
106
+ out = out.replace(/<MethodBadge\s+method=["']([^"']+)["'][^>]*\s*\/?>/g, '`$1`');
107
+ out = out.replace(/<StatusBadge\s+code=["']([^"']+)["'][^>]*\s*\/?>/g, '`$1`');
108
+ out = out.replace(/<Endpoint\s+method=["']([^"']+)["']\s+path=["']([^"']+)["'][^>]*\s*\/?>/g, '`$1 $2`');
109
+ // LinkPreview → markdown link
110
+ out = out.replace(/<LinkPreview\s+(?:[^>]*?url=["']([^"']+)["'][^>]*?)(?:\s+title=["']([^"']+)["'])?[^>]*\s*\/?>/g, (_m, url, title) => `[${title || url}](${url})`);
111
+ // Tooltip → just the text
112
+ out = out.replace(/<Tooltip\s+[^>]*>\s*([\s\S]*?)\s*<\/Tooltip>/g, (_m, c) => c.trim());
113
+ // Changelog components → headings
114
+ out = out.replace(/<Changelog>\s*/g, '');
115
+ out = out.replace(/\s*<\/Changelog>/g, '');
116
+ out = out.replace(/<ChangelogEntry\s+(?:[^>]*?date=["']([^"']+)["'][^>]*?)>\s*([\s\S]*?)\s*<\/ChangelogEntry>/g, (_m, date, content) => `### ${date}\n\n${content.trim()}\n`);
117
+ out = out.replace(/<Change\s+type=["']([^"']+)["'][^>]*>\s*([\s\S]*?)\s*<\/Change>/g, (_m, type, content) => `- **${type}:** ${content.trim()}`);
118
+ // Mermaid → keep as code block
119
+ out = out.replace(/<Mermaid\s+chart=["'`]([^"'`]+)["'`][^>]*\s*\/?>/g, (_m, chart) => '```mermaid\n' + chart.trim() + '\n```');
120
+ // Catch-all: remove any remaining self-closing JSX tags
121
+ out = out.replace(/<[A-Z][A-Za-z]*\s[^>]*\/>/g, '');
122
+ // Catch-all: unwrap any remaining JSX component tags (keep children)
123
+ out = out.replace(/<[A-Z][A-Za-z]*(?:\s[^>]*)?>/g, '');
124
+ out = out.replace(/<\/[A-Z][A-Za-z]*>/g, '');
125
+ // Clean up excessive blank lines
126
+ out = out.replace(/\n{4,}/g, '\n\n\n');
127
+ // Trim leading/trailing whitespace
128
+ out = out.trim() + '\n';
129
+ return out;
130
+ }
131
+ function formatBlockquote(label, content) {
132
+ const lines = content.split('\n');
133
+ const quoted = lines.map(line => `> ${line}`).join('\n');
134
+ return `> **${label}:** ${quoted.replace(/^> /, '')}\n`;
135
+ }
@@ -1,4 +1,4 @@
1
- import { GeneratedDoc, Topic, TopicConfig, CrossReference, NavigationItem } from './types.js';
1
+ import { GeneratedDoc, Topic, TopicConfig, CrossReference } from './types.js';
2
2
  /**
3
3
  * Default topic configuration with common patterns
4
4
  */
@@ -15,18 +15,3 @@ export declare function detectCrossReferences(docs: GeneratedDoc[]): CrossRefere
15
15
  * Get cross-references for a specific element
16
16
  */
17
17
  export declare function getCrossRefsForElement(elementName: string, allRefs: CrossReference[]): CrossReference[];
18
- /**
19
- * Build navigation structure from topics
20
- * @internal
21
- */
22
- export declare function buildNavigation(topics: Topic[]): NavigationItem[];
23
- /**
24
- * Generate a sidebar configuration (works with multiple doc platforms)
25
- * @internal
26
- */
27
- export declare function generateSidebarConfig(topics: Topic[]): object;
28
- /**
29
- * Merge user config with defaults
30
- * @internal
31
- */
32
- export declare function mergeTopicConfig(userConfig: Partial<TopicConfig>, defaults?: TopicConfig): TopicConfig;
@@ -1,4 +1,3 @@
1
- import { slugify } from '../utils/files.js';
2
1
  /**
3
2
  * Default topic configuration with common patterns
4
3
  */
@@ -172,32 +171,6 @@ export function detectCrossReferences(docs) {
172
171
  export function getCrossRefsForElement(elementName, allRefs) {
173
172
  return allRefs.filter(r => r.fromElement === elementName);
174
173
  }
175
- /**
176
- * Build navigation structure from topics
177
- * @internal
178
- */
179
- export function buildNavigation(topics) {
180
- return topics.map(topic => ({
181
- title: topic.name,
182
- path: `/${topic.id}`,
183
- children: topic.docs.map(doc => ({
184
- title: doc.element.name,
185
- path: `/${topic.id}#${slugify(doc.element.name)}`
186
- }))
187
- }));
188
- }
189
- /**
190
- * Generate a sidebar configuration (works with multiple doc platforms)
191
- * @internal
192
- */
193
- export function generateSidebarConfig(topics) {
194
- return {
195
- navigation: topics.map(topic => ({
196
- group: topic.name,
197
- pages: topic.docs.map(doc => `${topic.id}/${slugify(doc.element.name)}`)
198
- }))
199
- };
200
- }
201
174
  /**
202
175
  * Convert string to title case
203
176
  */
@@ -206,14 +179,3 @@ function titleCase(str) {
206
179
  .replace(/-/g, ' ')
207
180
  .replace(/\b\w/g, c => c.toUpperCase());
208
181
  }
209
- /**
210
- * Merge user config with defaults
211
- * @internal
212
- */
213
- export function mergeTopicConfig(userConfig, defaults = DEFAULT_TOPIC_CONFIG) {
214
- return {
215
- topics: { ...defaults.topics, ...userConfig.topics },
216
- rules: [...defaults.rules, ...(userConfig.rules || [])],
217
- defaultTopic: userConfig.defaultTopic ?? defaults.defaultTopic
218
- };
219
- }
@@ -4,6 +4,7 @@ import { existsSync } from 'fs';
4
4
  import { formatAsMarkdown } from './generator.js';
5
5
  import { organizeByTopic, detectCrossReferences, getCrossRefsForElement } from './organizer.js';
6
6
  import { slugify } from '../utils/files.js';
7
+ import { serializeMdxToMarkdown } from './mdx-serializer.js';
7
8
  /**
8
9
  * Generate llms.txt file (Answer Engine Optimization)
9
10
  * Format follows https://llmstxt.org convention
@@ -61,14 +62,14 @@ export async function writeLlmsTxt(docs, outputDir, options = {}) {
61
62
  content += `**File:** ${doc.element.filePath}\n\n`;
62
63
  content += `\`\`\`\n${doc.element.signature}\n\`\`\`\n\n`;
63
64
  if (doc.markdown) {
64
- content += doc.markdown.slice(0, 500) + '\n\n';
65
+ content += serializeMdxToMarkdown(doc.markdown.slice(0, 500)) + '\n\n';
65
66
  }
66
67
  if (doc.codeExample) {
67
68
  content += `**Example:**\n\`\`\`${doc.codeLanguage}\n${doc.codeExample.slice(0, 300)}\n\`\`\`\n\n`;
68
69
  }
69
70
  }
70
- // Write both llms.txt and llms-full.md
71
- await writeFile(join(outputDir, 'llms.txt'), content, 'utf-8');
71
+ // Write both llms.txt and llms-full.md — serialize any JSX for clean agent consumption
72
+ await writeFile(join(outputDir, 'llms.txt'), serializeMdxToMarkdown(content), 'utf-8');
72
73
  // Also write a condensed version (just signatures)
73
74
  let condensed = `# ${projectName} API\n\n`;
74
75
  condensed += `> ${description}\n\n`;
@@ -76,7 +77,7 @@ export async function writeLlmsTxt(docs, outputDir, options = {}) {
76
77
  condensed += `## ${doc.element.name}\n`;
77
78
  condensed += `${doc.element.signature}\n\n`;
78
79
  }
79
- await writeFile(join(outputDir, 'llms-full.md'), condensed, 'utf-8');
80
+ await writeFile(join(outputDir, 'llms-full.md'), serializeMdxToMarkdown(condensed), 'utf-8');
80
81
  }
81
82
  /**
82
83
  * Write generated docs to output directory
@@ -0,0 +1,32 @@
1
+ import { LLMClient, CompletionRequest, CompletionResponse } from './types.js';
2
+ /**
3
+ * LLM client that routes requests through Skrypt's proxy API.
4
+ * Used when the user doesn't have their own API key but has a Skrypt account.
5
+ */
6
+ export declare class ProxyClient implements LLMClient {
7
+ provider: "openai";
8
+ private apiKey;
9
+ private sessionId;
10
+ private timeout;
11
+ constructor(config: {
12
+ apiKey: string;
13
+ sessionId: string;
14
+ timeout?: number;
15
+ });
16
+ complete(request: CompletionRequest): Promise<CompletionResponse>;
17
+ isConfigured(): boolean;
18
+ }
19
+ /**
20
+ * Start a proxy generation session.
21
+ * Returns sessionId + remaining generations info.
22
+ */
23
+ export declare function startProxySession(apiKey: string): Promise<{
24
+ sessionId: string;
25
+ plan: 'free' | 'pro';
26
+ remaining: number;
27
+ used: number;
28
+ }>;
29
+ /**
30
+ * Complete a proxy generation session (increments monthly usage counter).
31
+ */
32
+ export declare function completeProxySession(apiKey: string, sessionId: string): Promise<void>;
@@ -0,0 +1,103 @@
1
+ const API_BASE = process.env.SKRYPT_API_URL || 'https://app.skrypt.sh';
2
+ /**
3
+ * LLM client that routes requests through Skrypt's proxy API.
4
+ * Used when the user doesn't have their own API key but has a Skrypt account.
5
+ */
6
+ export class ProxyClient {
7
+ provider = 'openai'; // Proxy decides which model to use
8
+ apiKey;
9
+ sessionId;
10
+ timeout;
11
+ constructor(config) {
12
+ this.apiKey = config.apiKey;
13
+ this.sessionId = config.sessionId;
14
+ this.timeout = config.timeout ?? 120_000;
15
+ }
16
+ async complete(request) {
17
+ const controller = new AbortController();
18
+ const timer = setTimeout(() => controller.abort(), this.timeout);
19
+ try {
20
+ const response = await fetch(`${API_BASE}/api/proxy/generate`, {
21
+ method: 'POST',
22
+ headers: {
23
+ 'Authorization': `Bearer ${this.apiKey}`,
24
+ 'Content-Type': 'application/json',
25
+ },
26
+ body: JSON.stringify({
27
+ sessionId: this.sessionId,
28
+ messages: request.messages,
29
+ temperature: request.temperature ?? 0,
30
+ maxTokens: request.maxTokens ?? 4096,
31
+ }),
32
+ signal: controller.signal,
33
+ });
34
+ if (!response.ok) {
35
+ const error = await response.json().catch(() => ({ error: 'Unknown error' }));
36
+ throw new Error(error.error || `Proxy request failed: ${response.status}`);
37
+ }
38
+ const data = await response.json();
39
+ return {
40
+ content: data.content,
41
+ model: data.model,
42
+ usage: {
43
+ inputTokens: data.usage.inputTokens,
44
+ outputTokens: data.usage.outputTokens,
45
+ totalTokens: data.usage.totalTokens,
46
+ },
47
+ finishReason: data.finishReason || 'stop',
48
+ };
49
+ }
50
+ finally {
51
+ clearTimeout(timer);
52
+ }
53
+ }
54
+ isConfigured() {
55
+ return !!this.apiKey && !!this.sessionId;
56
+ }
57
+ }
58
+ /**
59
+ * Start a proxy generation session.
60
+ * Returns sessionId + remaining generations info.
61
+ */
62
+ export async function startProxySession(apiKey) {
63
+ const response = await fetch(`${API_BASE}/api/proxy/generate/start`, {
64
+ method: 'POST',
65
+ headers: {
66
+ 'Authorization': `Bearer ${apiKey}`,
67
+ 'Content-Type': 'application/json',
68
+ },
69
+ body: JSON.stringify({}),
70
+ });
71
+ if (!response.ok) {
72
+ const error = await response.json().catch(() => ({ error: 'Unknown error' }));
73
+ const errData = error;
74
+ if (response.status === 429) {
75
+ throw new Error(`Monthly generation limit reached (${errData.used}/${errData.limit}). ` +
76
+ `Upgrade to Pro: ${errData.upgrade || 'https://skrypt.sh/pro'}`);
77
+ }
78
+ throw new Error(errData.error || `Failed to start session: ${response.status}`);
79
+ }
80
+ return await response.json();
81
+ }
82
+ /**
83
+ * Complete a proxy generation session (increments monthly usage counter).
84
+ */
85
+ export async function completeProxySession(apiKey, sessionId) {
86
+ try {
87
+ const response = await fetch(`${API_BASE}/api/proxy/generate/complete`, {
88
+ method: 'POST',
89
+ headers: {
90
+ 'Authorization': `Bearer ${apiKey}`,
91
+ 'Content-Type': 'application/json',
92
+ },
93
+ body: JSON.stringify({ sessionId }),
94
+ });
95
+ if (!response.ok) {
96
+ console.warn('Warning: Failed to complete generation session');
97
+ }
98
+ }
99
+ catch {
100
+ // Non-critical — network error, server down, etc.
101
+ console.warn('Warning: Failed to complete generation session');
102
+ }
103
+ }
@@ -12,12 +12,8 @@ export declare class CSharpScanner implements Scanner {
12
12
  private extractMethods;
13
13
  private extractInterfaceMethods;
14
14
  private findClassRanges;
15
- private findMatchingBrace;
16
15
  private findParentClass;
17
16
  private parseCSharpParams;
18
- private splitParams;
19
- private getLineNumber;
20
17
  private getDocComment;
21
18
  private extractGenerics;
22
- private getSourceContext;
23
19
  }