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.
- package/dist/auth/index.js +3 -3
- package/dist/cli.js +1 -1
- package/dist/commands/cron.js +0 -4
- package/dist/commands/generate/index.d.ts +3 -0
- package/dist/commands/generate/index.js +393 -0
- package/dist/commands/generate/scan.d.ts +41 -0
- package/dist/commands/generate/scan.js +256 -0
- package/dist/commands/generate/verify.d.ts +14 -0
- package/dist/commands/generate/verify.js +122 -0
- package/dist/commands/generate/write.d.ts +25 -0
- package/dist/commands/generate/write.js +120 -0
- package/dist/commands/import.js +4 -1
- package/dist/commands/llms-txt.js +6 -4
- package/dist/config/loader.d.ts +0 -1
- package/dist/config/loader.js +1 -1
- package/dist/generator/agents-md.d.ts +25 -0
- package/dist/generator/agents-md.js +122 -0
- package/dist/generator/index.d.ts +2 -0
- package/dist/generator/index.js +2 -0
- package/dist/generator/mdx-serializer.d.ts +11 -0
- package/dist/generator/mdx-serializer.js +135 -0
- package/dist/generator/organizer.d.ts +1 -16
- package/dist/generator/organizer.js +0 -38
- package/dist/generator/writer.js +5 -4
- package/dist/llm/proxy-client.d.ts +32 -0
- package/dist/llm/proxy-client.js +103 -0
- package/dist/scanner/csharp.d.ts +0 -4
- package/dist/scanner/csharp.js +9 -49
- package/dist/scanner/go.d.ts +0 -3
- package/dist/scanner/go.js +8 -35
- package/dist/scanner/java.d.ts +0 -4
- package/dist/scanner/java.js +9 -49
- package/dist/scanner/kotlin.d.ts +0 -3
- package/dist/scanner/kotlin.js +6 -33
- package/dist/scanner/php.d.ts +0 -10
- package/dist/scanner/php.js +11 -55
- package/dist/scanner/ruby.d.ts +0 -3
- package/dist/scanner/ruby.js +8 -38
- package/dist/scanner/rust.d.ts +0 -3
- package/dist/scanner/rust.js +10 -37
- package/dist/scanner/swift.d.ts +0 -3
- package/dist/scanner/swift.js +8 -35
- package/dist/scanner/utils.d.ts +41 -0
- package/dist/scanner/utils.js +97 -0
- package/dist/template/docs.json +5 -2
- package/dist/template/next.config.mjs +31 -0
- package/dist/template/package.json +5 -3
- package/dist/template/src/app/layout.tsx +13 -13
- package/dist/template/src/app/llms-full.md/route.ts +29 -0
- package/dist/template/src/app/llms.txt/route.ts +29 -0
- package/dist/template/src/app/md/[...slug]/route.ts +174 -0
- package/dist/template/src/app/reference/route.ts +22 -18
- package/dist/template/src/app/sitemap.ts +1 -1
- package/dist/template/src/components/ai-chat-impl.tsx +206 -0
- package/dist/template/src/components/ai-chat.tsx +20 -193
- package/dist/template/src/components/mdx/index.tsx +27 -4
- package/dist/template/src/lib/fonts.ts +135 -0
- package/dist/template/src/middleware.ts +101 -0
- package/dist/template/src/styles/globals.css +28 -20
- package/dist/utils/files.d.ts +0 -8
- package/dist/utils/files.js +0 -33
- package/package.json +1 -1
- package/dist/autofix/autofix.test.d.ts +0 -1
- package/dist/autofix/autofix.test.js +0 -487
- package/dist/commands/generate.d.ts +0 -9
- package/dist/commands/generate.js +0 -739
- package/dist/generator/generator.test.d.ts +0 -1
- package/dist/generator/generator.test.js +0 -259
- package/dist/generator/writer.test.d.ts +0 -1
- package/dist/generator/writer.test.js +0 -411
- package/dist/llm/llm.manual-test.d.ts +0 -1
- package/dist/llm/llm.manual-test.js +0 -112
- package/dist/llm/llm.mock-test.d.ts +0 -4
- package/dist/llm/llm.mock-test.js +0 -79
- package/dist/plugins/index.d.ts +0 -47
- package/dist/plugins/index.js +0 -181
- package/dist/scanner/content-type.test.d.ts +0 -1
- package/dist/scanner/content-type.test.js +0 -231
- package/dist/scanner/integration.test.d.ts +0 -4
- package/dist/scanner/integration.test.js +0 -180
- package/dist/scanner/scanner.test.d.ts +0 -1
- package/dist/scanner/scanner.test.js +0 -210
- package/dist/scanner/typescript.manual-test.d.ts +0 -1
- package/dist/scanner/typescript.manual-test.js +0 -112
- package/dist/template/src/app/docs/auth/page.mdx +0 -589
- package/dist/template/src/app/docs/autofix/page.mdx +0 -624
- package/dist/template/src/app/docs/cli/page.mdx +0 -217
- package/dist/template/src/app/docs/config/page.mdx +0 -428
- package/dist/template/src/app/docs/configuration/page.mdx +0 -86
- package/dist/template/src/app/docs/deployment/page.mdx +0 -112
- package/dist/template/src/app/docs/generator/generator.md +0 -504
- package/dist/template/src/app/docs/generator/organizer.md +0 -779
- package/dist/template/src/app/docs/generator/page.mdx +0 -613
- package/dist/template/src/app/docs/github/page.mdx +0 -502
- package/dist/template/src/app/docs/llm/anthropic-client.md +0 -549
- package/dist/template/src/app/docs/llm/index.md +0 -471
- package/dist/template/src/app/docs/llm/page.mdx +0 -428
- package/dist/template/src/app/docs/plugins/page.mdx +0 -1793
- package/dist/template/src/app/docs/pro/page.mdx +0 -121
- package/dist/template/src/app/docs/quickstart/page.mdx +0 -93
- package/dist/template/src/app/docs/scanner/content-type.md +0 -599
- package/dist/template/src/app/docs/scanner/index.md +0 -212
- package/dist/template/src/app/docs/scanner/page.mdx +0 -307
- package/dist/template/src/app/docs/scanner/python.md +0 -469
- package/dist/template/src/app/docs/scanner/python_parser.md +0 -1056
- package/dist/template/src/app/docs/scanner/rust.md +0 -325
- package/dist/template/src/app/docs/scanner/typescript.md +0 -201
- package/dist/template/src/app/icon.tsx +0 -29
- package/dist/utils/validation.d.ts +0 -1
- 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
|
+
}
|
package/dist/generator/index.js
CHANGED
|
@@ -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 ? `` : '';
|
|
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 ? `` : '';
|
|
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) => ``);
|
|
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
|
|
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
|
-
}
|
package/dist/generator/writer.js
CHANGED
|
@@ -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
|
+
}
|
package/dist/scanner/csharp.d.ts
CHANGED
|
@@ -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
|
}
|