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
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import { createLLMClient, generateDocumentation, fixCodeSample } from './index.js';
|
|
2
|
-
import { PROVIDER_ENV_KEYS } from '../config/types.js';
|
|
3
|
-
async function runTests() {
|
|
4
|
-
console.log('=== LLM Client Tests ===\n');
|
|
5
|
-
// Find an available provider
|
|
6
|
-
const providers = ['deepseek', 'openai', 'anthropic', 'google', 'openrouter', 'ollama'];
|
|
7
|
-
let availableProvider = null;
|
|
8
|
-
let availableModel = null;
|
|
9
|
-
for (const provider of providers) {
|
|
10
|
-
const envKey = PROVIDER_ENV_KEYS[provider];
|
|
11
|
-
if (!envKey || process.env[envKey]) {
|
|
12
|
-
availableProvider = provider;
|
|
13
|
-
availableModel = getTestModel(provider);
|
|
14
|
-
break;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
if (!availableProvider) {
|
|
18
|
-
console.log('No LLM provider available. Set one of these env vars:');
|
|
19
|
-
console.log(' DEEPSEEK_API_KEY, OPENAI_API_KEY, ANTHROPIC_API_KEY,');
|
|
20
|
-
console.log(' GOOGLE_API_KEY, OPENROUTER_API_KEY, or run Ollama locally');
|
|
21
|
-
process.exit(0);
|
|
22
|
-
}
|
|
23
|
-
console.log(`Using provider: ${availableProvider} (${availableModel})`);
|
|
24
|
-
console.log('');
|
|
25
|
-
// Test 1: Create client
|
|
26
|
-
console.log('Test 1: Create LLM client');
|
|
27
|
-
const client = createLLMClient({
|
|
28
|
-
provider: availableProvider,
|
|
29
|
-
model: availableModel
|
|
30
|
-
});
|
|
31
|
-
console.log(` provider: ${client.provider}`);
|
|
32
|
-
console.log(' ✓ client created\n');
|
|
33
|
-
// Test 2: Simple completion
|
|
34
|
-
console.log('Test 2: Simple completion');
|
|
35
|
-
try {
|
|
36
|
-
const response = await client.complete({
|
|
37
|
-
messages: [
|
|
38
|
-
{ role: 'user', content: 'Say "hello" and nothing else.' }
|
|
39
|
-
],
|
|
40
|
-
maxTokens: 10
|
|
41
|
-
});
|
|
42
|
-
console.log(` response: "${response.content.trim()}"`);
|
|
43
|
-
console.log(` model: ${response.model}`);
|
|
44
|
-
console.log(` tokens: ${response.usage.totalTokens}`);
|
|
45
|
-
console.log(' ✓ completion works\n');
|
|
46
|
-
}
|
|
47
|
-
catch (err) {
|
|
48
|
-
console.log(` ✗ completion failed: ${err}`);
|
|
49
|
-
process.exit(1);
|
|
50
|
-
}
|
|
51
|
-
// Test 3: Generate documentation
|
|
52
|
-
console.log('Test 3: Generate documentation');
|
|
53
|
-
try {
|
|
54
|
-
const result = await generateDocumentation(client, {
|
|
55
|
-
kind: 'function',
|
|
56
|
-
name: 'add',
|
|
57
|
-
signature: 'def add(a: int, b: int) -> int',
|
|
58
|
-
parameters: [
|
|
59
|
-
{ name: 'a', type: 'int' },
|
|
60
|
-
{ name: 'b', type: 'int' }
|
|
61
|
-
],
|
|
62
|
-
returnType: 'int',
|
|
63
|
-
docstring: 'Add two numbers together.'
|
|
64
|
-
});
|
|
65
|
-
console.log(` markdown length: ${result.markdown.length} chars`);
|
|
66
|
-
console.log(` code example length: ${result.codeExample.length} chars`);
|
|
67
|
-
if (result.codeExample.includes('add(')) {
|
|
68
|
-
console.log(' ✓ code example calls the function\n');
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
console.log(` code example:\n${result.codeExample}`);
|
|
72
|
-
console.log(' ⚠ code example might not call the function\n');
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
catch (err) {
|
|
76
|
-
console.log(` ✗ doc generation failed: ${err}`);
|
|
77
|
-
process.exit(1);
|
|
78
|
-
}
|
|
79
|
-
// Test 4: Fix broken code
|
|
80
|
-
console.log('Test 4: Fix broken code');
|
|
81
|
-
try {
|
|
82
|
-
const fixed = await fixCodeSample(client, 'print(add(1, 2)', // Missing closing paren
|
|
83
|
-
'SyntaxError: unexpected EOF while parsing', 'This is a code example for the add function');
|
|
84
|
-
console.log(` fixed code: ${fixed}`);
|
|
85
|
-
if (fixed.includes(')')) {
|
|
86
|
-
console.log(' ✓ code was fixed\n');
|
|
87
|
-
}
|
|
88
|
-
else {
|
|
89
|
-
console.log(' ⚠ fix might be incomplete\n');
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
catch (err) {
|
|
93
|
-
console.log(` ✗ code fix failed: ${err}`);
|
|
94
|
-
process.exit(1);
|
|
95
|
-
}
|
|
96
|
-
console.log('✓ All LLM client tests passed');
|
|
97
|
-
}
|
|
98
|
-
function getTestModel(provider) {
|
|
99
|
-
switch (provider) {
|
|
100
|
-
case 'deepseek': return 'deepseek-chat';
|
|
101
|
-
case 'openai': return 'gpt-4o-mini';
|
|
102
|
-
case 'anthropic': return 'claude-sonnet-4-5-20241022';
|
|
103
|
-
case 'google': return 'gemini-2.0-flash';
|
|
104
|
-
case 'ollama': return 'llama3.2:1b'; // Small model for testing
|
|
105
|
-
case 'openrouter': return 'openai/gpt-4o-mini';
|
|
106
|
-
default: return 'gpt-4o-mini';
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
runTests().catch(err => {
|
|
110
|
-
console.error('Test failed:', err);
|
|
111
|
-
process.exit(1);
|
|
112
|
-
});
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Mock tests for LLM client - tests structure without API calls
|
|
3
|
-
*/
|
|
4
|
-
import { createLLMClient, PROVIDER_BASE_URLS } from './index.js';
|
|
5
|
-
function runMockTests() {
|
|
6
|
-
console.log('=== LLM Client Mock Tests ===\n');
|
|
7
|
-
// Test 1: Client creation for each provider
|
|
8
|
-
console.log('Test 1: Create client for each provider');
|
|
9
|
-
const providers = ['deepseek', 'openai', 'anthropic', 'google', 'ollama', 'openrouter'];
|
|
10
|
-
for (const provider of providers) {
|
|
11
|
-
try {
|
|
12
|
-
// Create client to verify no errors thrown
|
|
13
|
-
createLLMClient({
|
|
14
|
-
provider,
|
|
15
|
-
model: 'test-model'
|
|
16
|
-
});
|
|
17
|
-
console.log(` ✓ ${provider}: client created`);
|
|
18
|
-
}
|
|
19
|
-
catch (err) {
|
|
20
|
-
console.log(` ✗ ${provider}: ${err}`);
|
|
21
|
-
process.exit(1);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
console.log('');
|
|
25
|
-
// Test 2: Verify base URLs
|
|
26
|
-
console.log('Test 2: Verify provider base URLs');
|
|
27
|
-
const expectedUrls = {
|
|
28
|
-
deepseek: 'https://api.deepseek.com/v1',
|
|
29
|
-
openai: 'https://api.openai.com/v1',
|
|
30
|
-
anthropic: 'https://api.anthropic.com',
|
|
31
|
-
google: 'https://generativelanguage.googleapis.com/v1beta/openai',
|
|
32
|
-
ollama: 'http://localhost:11434/v1',
|
|
33
|
-
openrouter: 'https://openrouter.ai/api/v1'
|
|
34
|
-
};
|
|
35
|
-
for (const [provider, expectedUrl] of Object.entries(expectedUrls)) {
|
|
36
|
-
const actualUrl = PROVIDER_BASE_URLS[provider];
|
|
37
|
-
if (actualUrl === expectedUrl) {
|
|
38
|
-
console.log(` ✓ ${provider}: ${actualUrl}`);
|
|
39
|
-
}
|
|
40
|
-
else {
|
|
41
|
-
console.log(` ✗ ${provider}: expected ${expectedUrl}, got ${actualUrl}`);
|
|
42
|
-
process.exit(1);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
console.log('');
|
|
46
|
-
// Test 3: Anthropic vs OpenAI-compatible client selection
|
|
47
|
-
console.log('Test 3: Client type selection');
|
|
48
|
-
const anthropicClient = createLLMClient({ provider: 'anthropic', model: 'claude-3' });
|
|
49
|
-
const openaiClient = createLLMClient({ provider: 'openai', model: 'gpt-4' });
|
|
50
|
-
const deepseekClient = createLLMClient({ provider: 'deepseek', model: 'deepseek-v3' });
|
|
51
|
-
// Check they're different implementations for anthropic vs others
|
|
52
|
-
const anthropicType = anthropicClient.constructor.name;
|
|
53
|
-
const openaiType = openaiClient.constructor.name;
|
|
54
|
-
const deepseekType = deepseekClient.constructor.name;
|
|
55
|
-
console.log(` anthropic client: ${anthropicType}`);
|
|
56
|
-
console.log(` openai client: ${openaiType}`);
|
|
57
|
-
console.log(` deepseek client: ${deepseekType}`);
|
|
58
|
-
if (anthropicType !== openaiType) {
|
|
59
|
-
console.log(' ✓ anthropic uses different client than openai');
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
console.log(' ✗ anthropic should use different client');
|
|
63
|
-
process.exit(1);
|
|
64
|
-
}
|
|
65
|
-
if (openaiType === deepseekType) {
|
|
66
|
-
console.log(' ✓ deepseek uses same client as openai (OpenAI-compatible)');
|
|
67
|
-
}
|
|
68
|
-
else {
|
|
69
|
-
console.log(' ✗ deepseek should use same client as openai');
|
|
70
|
-
process.exit(1);
|
|
71
|
-
}
|
|
72
|
-
console.log('');
|
|
73
|
-
console.log('✓ All mock tests passed\n');
|
|
74
|
-
console.log('To run real API tests, set an API key and run:');
|
|
75
|
-
console.log(' DEEPSEEK_API_KEY=your-key npx tsx src/llm/llm.test.ts');
|
|
76
|
-
console.log(' ANTHROPIC_API_KEY=your-key npx tsx src/llm/llm.test.ts');
|
|
77
|
-
console.log(' Or start Ollama locally');
|
|
78
|
-
}
|
|
79
|
-
runMockTests();
|
package/dist/plugins/index.d.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
export interface SkryptPlugin {
|
|
2
|
-
name: string;
|
|
3
|
-
version?: string;
|
|
4
|
-
onInit?: (context: PluginContext) => Promise<void> | void;
|
|
5
|
-
onBeforeScan?: (context: PluginContext) => Promise<void> | void;
|
|
6
|
-
onAfterScan?: (context: PluginContext, elements: unknown[]) => Promise<unknown[]> | unknown[];
|
|
7
|
-
onBeforeGenerate?: (context: PluginContext, elements: unknown[]) => Promise<unknown[]> | unknown[];
|
|
8
|
-
onAfterGenerate?: (context: PluginContext, docs: unknown[]) => Promise<unknown[]> | unknown[];
|
|
9
|
-
onBeforeWrite?: (context: PluginContext, docs: unknown[]) => Promise<unknown[]> | unknown[];
|
|
10
|
-
onAfterWrite?: (context: PluginContext) => Promise<void> | void;
|
|
11
|
-
transformContent?: (content: string, filePath: string) => Promise<string> | string;
|
|
12
|
-
transformElement?: (element: unknown) => Promise<unknown> | unknown;
|
|
13
|
-
}
|
|
14
|
-
export interface PluginContext {
|
|
15
|
-
sourcePath: string;
|
|
16
|
-
outputPath: string;
|
|
17
|
-
config: Record<string, unknown>;
|
|
18
|
-
logger: {
|
|
19
|
-
info: (msg: string) => void;
|
|
20
|
-
warn: (msg: string) => void;
|
|
21
|
-
error: (msg: string) => void;
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
export declare class PluginManager {
|
|
25
|
-
private plugins;
|
|
26
|
-
private context;
|
|
27
|
-
constructor(sourcePath: string, outputPath: string, config?: Record<string, unknown>);
|
|
28
|
-
loadPlugins(configPath?: string): Promise<void>;
|
|
29
|
-
private findConfigFile;
|
|
30
|
-
private loadPluginByName;
|
|
31
|
-
register(plugin: SkryptPlugin): void;
|
|
32
|
-
runHook<T>(hook: keyof SkryptPlugin, ...args: unknown[]): Promise<T | undefined>;
|
|
33
|
-
onInit(): Promise<void>;
|
|
34
|
-
onBeforeScan(): Promise<void>;
|
|
35
|
-
onAfterScan<T>(elements: T[]): Promise<T[]>;
|
|
36
|
-
onBeforeGenerate<T>(elements: T[]): Promise<T[]>;
|
|
37
|
-
onAfterGenerate<T>(docs: T[]): Promise<T[]>;
|
|
38
|
-
onBeforeWrite<T>(docs: T[]): Promise<T[]>;
|
|
39
|
-
onAfterWrite(): Promise<void>;
|
|
40
|
-
transformContent(content: string, filePath: string): Promise<string>;
|
|
41
|
-
}
|
|
42
|
-
export declare const builtinPlugins: {
|
|
43
|
-
timestamp: SkryptPlugin;
|
|
44
|
-
editOnGithub: (repoUrl: string, branch?: string) => SkryptPlugin;
|
|
45
|
-
toc: SkryptPlugin;
|
|
46
|
-
};
|
|
47
|
-
export declare function definePlugin(plugin: SkryptPlugin): SkryptPlugin;
|
package/dist/plugins/index.js
DELETED
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
// TODO: Plugin system is defined but not yet wired into the generate pipeline
|
|
2
|
-
import { existsSync } from 'fs';
|
|
3
|
-
import { resolve } from 'path';
|
|
4
|
-
import { pathToFileURL } from 'url';
|
|
5
|
-
export class PluginManager {
|
|
6
|
-
plugins = [];
|
|
7
|
-
context;
|
|
8
|
-
constructor(sourcePath, outputPath, config = {}) {
|
|
9
|
-
this.context = {
|
|
10
|
-
sourcePath,
|
|
11
|
-
outputPath,
|
|
12
|
-
config,
|
|
13
|
-
logger: {
|
|
14
|
-
info: (msg) => console.log(`[plugin] ${msg}`),
|
|
15
|
-
warn: (msg) => console.warn(`[plugin] ${msg}`),
|
|
16
|
-
error: (msg) => console.error(`[plugin] ${msg}`),
|
|
17
|
-
},
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
async loadPlugins(configPath) {
|
|
21
|
-
// Load from skrypt.config.js or skrypt.config.ts
|
|
22
|
-
const configFile = configPath || this.findConfigFile();
|
|
23
|
-
if (!configFile)
|
|
24
|
-
return;
|
|
25
|
-
try {
|
|
26
|
-
console.log(` Loading config: ${configFile}`);
|
|
27
|
-
console.warn(' ⚠ Plugin configs execute code — only load trusted files');
|
|
28
|
-
const configUrl = pathToFileURL(configFile).href;
|
|
29
|
-
const module = await import(configUrl);
|
|
30
|
-
const config = module.default || module;
|
|
31
|
-
if (config.plugins && Array.isArray(config.plugins)) {
|
|
32
|
-
for (const plugin of config.plugins) {
|
|
33
|
-
if (typeof plugin === 'string') {
|
|
34
|
-
// Load plugin by name
|
|
35
|
-
await this.loadPluginByName(plugin);
|
|
36
|
-
}
|
|
37
|
-
else if (typeof plugin === 'object' && plugin.name) {
|
|
38
|
-
// Plugin object
|
|
39
|
-
this.register(plugin);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
catch (err) {
|
|
45
|
-
console.error(`Failed to load config: ${err}`);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
findConfigFile() {
|
|
49
|
-
const candidates = [
|
|
50
|
-
'skrypt.config.js',
|
|
51
|
-
'skrypt.config.mjs',
|
|
52
|
-
'skrypt.config.ts',
|
|
53
|
-
];
|
|
54
|
-
for (const name of candidates) {
|
|
55
|
-
const path = resolve(process.cwd(), name);
|
|
56
|
-
if (existsSync(path))
|
|
57
|
-
return path;
|
|
58
|
-
}
|
|
59
|
-
return null;
|
|
60
|
-
}
|
|
61
|
-
async loadPluginByName(name) {
|
|
62
|
-
// Validate plugin name — must be a valid npm package name, no path traversal
|
|
63
|
-
if (!/^(@[a-zA-Z0-9._-]+\/)?[a-zA-Z0-9._-]+$/.test(name)) {
|
|
64
|
-
console.warn(`Skipping plugin with invalid name: ${name}`);
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
// Try to load from node_modules
|
|
68
|
-
try {
|
|
69
|
-
const module = await import(name);
|
|
70
|
-
const plugin = module.default || module;
|
|
71
|
-
if (plugin.name) {
|
|
72
|
-
this.register(plugin);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
catch {
|
|
76
|
-
console.warn(`Failed to load plugin: ${name}`);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
register(plugin) {
|
|
80
|
-
this.plugins.push(plugin);
|
|
81
|
-
console.log(`Loaded plugin: ${plugin.name}${plugin.version ? ` v${plugin.version}` : ''}`);
|
|
82
|
-
}
|
|
83
|
-
async runHook(hook, ...args) {
|
|
84
|
-
let result = args[0];
|
|
85
|
-
for (const plugin of this.plugins) {
|
|
86
|
-
const fn = plugin[hook];
|
|
87
|
-
if (typeof fn === 'function') {
|
|
88
|
-
try {
|
|
89
|
-
const hookResult = await fn.call(plugin, this.context, ...args);
|
|
90
|
-
if (hookResult !== undefined) {
|
|
91
|
-
result = hookResult;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
catch (err) {
|
|
95
|
-
console.error(`Plugin ${plugin.name} error in ${hook}: ${err}`);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
return result;
|
|
100
|
-
}
|
|
101
|
-
async onInit() {
|
|
102
|
-
await this.runHook('onInit');
|
|
103
|
-
}
|
|
104
|
-
async onBeforeScan() {
|
|
105
|
-
await this.runHook('onBeforeScan');
|
|
106
|
-
}
|
|
107
|
-
async onAfterScan(elements) {
|
|
108
|
-
return (await this.runHook('onAfterScan', elements)) || elements;
|
|
109
|
-
}
|
|
110
|
-
async onBeforeGenerate(elements) {
|
|
111
|
-
return (await this.runHook('onBeforeGenerate', elements)) || elements;
|
|
112
|
-
}
|
|
113
|
-
async onAfterGenerate(docs) {
|
|
114
|
-
return (await this.runHook('onAfterGenerate', docs)) || docs;
|
|
115
|
-
}
|
|
116
|
-
async onBeforeWrite(docs) {
|
|
117
|
-
return (await this.runHook('onBeforeWrite', docs)) || docs;
|
|
118
|
-
}
|
|
119
|
-
async onAfterWrite() {
|
|
120
|
-
await this.runHook('onAfterWrite');
|
|
121
|
-
}
|
|
122
|
-
async transformContent(content, filePath) {
|
|
123
|
-
let result = content;
|
|
124
|
-
for (const plugin of this.plugins) {
|
|
125
|
-
if (typeof plugin.transformContent === 'function') {
|
|
126
|
-
try {
|
|
127
|
-
result = await plugin.transformContent(result, filePath);
|
|
128
|
-
}
|
|
129
|
-
catch (err) {
|
|
130
|
-
console.error(`Plugin ${plugin.name} error in transformContent: ${err}`);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
return result;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
// Built-in plugins
|
|
138
|
-
export const builtinPlugins = {
|
|
139
|
-
// Add timestamp to generated files
|
|
140
|
-
timestamp: {
|
|
141
|
-
name: 'timestamp',
|
|
142
|
-
transformContent: (content) => {
|
|
143
|
-
const timestamp = new Date().toISOString();
|
|
144
|
-
return content.replace(/^(---\n)/, `$1generated_at: "${timestamp}"\n`);
|
|
145
|
-
},
|
|
146
|
-
},
|
|
147
|
-
// Add "Edit on GitHub" links
|
|
148
|
-
editOnGithub: (repoUrl, branch = 'main') => ({
|
|
149
|
-
name: 'edit-on-github',
|
|
150
|
-
transformContent: (content, filePath) => {
|
|
151
|
-
const editUrl = `${repoUrl}/edit/${branch}/${filePath}`;
|
|
152
|
-
return content.replace(/^(---\n[\s\S]*?---\n)/, `$1\n<EditOnGithub url="${editUrl}" />\n`);
|
|
153
|
-
},
|
|
154
|
-
}),
|
|
155
|
-
// Auto-generate table of contents
|
|
156
|
-
toc: {
|
|
157
|
-
name: 'toc',
|
|
158
|
-
transformContent: (content) => {
|
|
159
|
-
const headings = [];
|
|
160
|
-
const lines = content.split('\n');
|
|
161
|
-
for (const line of lines) {
|
|
162
|
-
const match = line.match(/^(#{2,4})\s+(.+)$/);
|
|
163
|
-
if (match) {
|
|
164
|
-
const level = match[1].length;
|
|
165
|
-
const text = match[2];
|
|
166
|
-
const anchor = text.toLowerCase().replace(/[^\w]+/g, '-');
|
|
167
|
-
headings.push({ level, text, anchor });
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
if (headings.length < 2)
|
|
171
|
-
return content;
|
|
172
|
-
const toc = headings
|
|
173
|
-
.map(h => `${' '.repeat(h.level - 2)}- [${h.text}](#${h.anchor})`)
|
|
174
|
-
.join('\n');
|
|
175
|
-
return content.replace(/^(---\n[\s\S]*?---\n)/, `$1\n## Table of Contents\n\n${toc}\n\n`);
|
|
176
|
-
},
|
|
177
|
-
},
|
|
178
|
-
};
|
|
179
|
-
export function definePlugin(plugin) {
|
|
180
|
-
return plugin;
|
|
181
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { classifyElement, classifyElements, getRecommendedStructure, getPromptForContentType } from './content-type.js';
|
|
3
|
-
function createMockElement(overrides = {}) {
|
|
4
|
-
return {
|
|
5
|
-
kind: 'function',
|
|
6
|
-
name: 'testFunction',
|
|
7
|
-
signature: 'function testFunction()',
|
|
8
|
-
parameters: [],
|
|
9
|
-
filePath: '/src/test.ts',
|
|
10
|
-
lineNumber: 1,
|
|
11
|
-
isExported: true,
|
|
12
|
-
isPublic: true,
|
|
13
|
-
...overrides,
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
describe('Content Type Detection', () => {
|
|
17
|
-
describe('classifyElement', () => {
|
|
18
|
-
it('should classify API elements with multiple parameters', () => {
|
|
19
|
-
const element = createMockElement({
|
|
20
|
-
name: 'fetchData',
|
|
21
|
-
parameters: [
|
|
22
|
-
{ name: 'url', type: 'string' },
|
|
23
|
-
{ name: 'options', type: 'RequestOptions' },
|
|
24
|
-
{ name: 'timeout', type: 'number' },
|
|
25
|
-
],
|
|
26
|
-
});
|
|
27
|
-
const result = classifyElement(element);
|
|
28
|
-
expect(result.type).toBe('api');
|
|
29
|
-
expect(result.confidence).toBeGreaterThan(0);
|
|
30
|
-
});
|
|
31
|
-
it('should classify handler/endpoint patterns as API', () => {
|
|
32
|
-
const element = createMockElement({
|
|
33
|
-
name: 'userHandler',
|
|
34
|
-
kind: 'method',
|
|
35
|
-
parentClass: 'UserController',
|
|
36
|
-
filePath: '/src/handlers/user.ts',
|
|
37
|
-
parameters: [
|
|
38
|
-
{ name: 'req', type: 'Request' },
|
|
39
|
-
{ name: 'res', type: 'Response' },
|
|
40
|
-
],
|
|
41
|
-
});
|
|
42
|
-
const result = classifyElement(element);
|
|
43
|
-
expect(result.type).toBe('api');
|
|
44
|
-
});
|
|
45
|
-
it('should classify HTTP methods as API', () => {
|
|
46
|
-
const element = createMockElement({
|
|
47
|
-
signature: 'async function handleGET(request: Request): Promise<Response>',
|
|
48
|
-
docstring: 'Handles GET requests for user data',
|
|
49
|
-
});
|
|
50
|
-
const result = classifyElement(element);
|
|
51
|
-
expect(result.type).toBe('api');
|
|
52
|
-
});
|
|
53
|
-
it('should classify config/setup functions as guide', () => {
|
|
54
|
-
const element = createMockElement({
|
|
55
|
-
name: 'configureDatabase',
|
|
56
|
-
});
|
|
57
|
-
const result = classifyElement(element);
|
|
58
|
-
expect(result.type).toBe('guide');
|
|
59
|
-
});
|
|
60
|
-
it('should classify init functions as guide', () => {
|
|
61
|
-
const element = createMockElement({
|
|
62
|
-
name: 'initializeApp',
|
|
63
|
-
});
|
|
64
|
-
const result = classifyElement(element);
|
|
65
|
-
expect(result.type).toBe('guide');
|
|
66
|
-
});
|
|
67
|
-
it('should classify example files as tutorial', () => {
|
|
68
|
-
const element = createMockElement({
|
|
69
|
-
name: 'main',
|
|
70
|
-
filePath: '/examples/quickstart.ts',
|
|
71
|
-
});
|
|
72
|
-
const result = classifyElement(element);
|
|
73
|
-
expect(result.type).toBe('tutorial');
|
|
74
|
-
});
|
|
75
|
-
it('should classify demo functions as tutorial', () => {
|
|
76
|
-
const element = createMockElement({
|
|
77
|
-
name: 'demoFeature',
|
|
78
|
-
});
|
|
79
|
-
const result = classifyElement(element);
|
|
80
|
-
expect(result.type).toBe('tutorial');
|
|
81
|
-
});
|
|
82
|
-
it('should classify simple functions with few params as tutorial', () => {
|
|
83
|
-
const element = createMockElement({
|
|
84
|
-
name: 'simpleFunction',
|
|
85
|
-
kind: 'function',
|
|
86
|
-
parameters: [{ name: 'input', type: 'string' }],
|
|
87
|
-
});
|
|
88
|
-
const result = classifyElement(element);
|
|
89
|
-
// Simple functions default to tutorial
|
|
90
|
-
expect(result.type).toBe('tutorial');
|
|
91
|
-
});
|
|
92
|
-
it('should classify async API patterns correctly', () => {
|
|
93
|
-
const element = createMockElement({
|
|
94
|
-
signature: 'async function createUser(data: UserData): Promise<User>',
|
|
95
|
-
parameters: [
|
|
96
|
-
{ name: 'data', type: 'UserData' },
|
|
97
|
-
{ name: 'options', type: 'CreateOptions' },
|
|
98
|
-
{ name: 'validate', type: 'boolean' },
|
|
99
|
-
],
|
|
100
|
-
});
|
|
101
|
-
const result = classifyElement(element);
|
|
102
|
-
expect(result.type).toBe('api');
|
|
103
|
-
});
|
|
104
|
-
it('should return tutorial for simple functions with no indicators', () => {
|
|
105
|
-
// Simple functions with few params default to tutorial
|
|
106
|
-
const element = createMockElement({
|
|
107
|
-
name: 'x',
|
|
108
|
-
kind: 'function',
|
|
109
|
-
parameters: [],
|
|
110
|
-
docstring: undefined,
|
|
111
|
-
filePath: '/src/utils.ts',
|
|
112
|
-
});
|
|
113
|
-
const result = classifyElement(element);
|
|
114
|
-
// Gets tutorial because simple function with <=1 params
|
|
115
|
-
expect(result.type).toBe('tutorial');
|
|
116
|
-
});
|
|
117
|
-
it('should return overview for class elements with no indicators', () => {
|
|
118
|
-
// Classes without other indicators get overview
|
|
119
|
-
const element = createMockElement({
|
|
120
|
-
name: 'X',
|
|
121
|
-
kind: 'class',
|
|
122
|
-
parameters: [],
|
|
123
|
-
docstring: undefined,
|
|
124
|
-
filePath: '/src/utils.ts',
|
|
125
|
-
});
|
|
126
|
-
const result = classifyElement(element);
|
|
127
|
-
expect(result.type).toBe('overview');
|
|
128
|
-
});
|
|
129
|
-
it('should include reasons for classification', () => {
|
|
130
|
-
const element = createMockElement({
|
|
131
|
-
name: 'handler',
|
|
132
|
-
filePath: '/src/handlers/api.ts',
|
|
133
|
-
});
|
|
134
|
-
const result = classifyElement(element);
|
|
135
|
-
expect(result.reasons.length).toBeGreaterThan(0);
|
|
136
|
-
});
|
|
137
|
-
});
|
|
138
|
-
describe('classifyElements', () => {
|
|
139
|
-
it('should group elements by content type', () => {
|
|
140
|
-
const elements = [
|
|
141
|
-
createMockElement({
|
|
142
|
-
name: 'fetchUsers',
|
|
143
|
-
kind: 'method',
|
|
144
|
-
parentClass: 'UserService',
|
|
145
|
-
parameters: [
|
|
146
|
-
{ name: 'page', type: 'number' },
|
|
147
|
-
{ name: 'limit', type: 'number' },
|
|
148
|
-
{ name: 'filter', type: 'Filter' },
|
|
149
|
-
],
|
|
150
|
-
}),
|
|
151
|
-
createMockElement({
|
|
152
|
-
name: 'configureAuth',
|
|
153
|
-
kind: 'function',
|
|
154
|
-
parameters: [
|
|
155
|
-
{ name: 'options', type: 'AuthOptions' },
|
|
156
|
-
{ name: 'provider', type: 'Provider' },
|
|
157
|
-
],
|
|
158
|
-
}),
|
|
159
|
-
createMockElement({
|
|
160
|
-
name: 'exampleUsage',
|
|
161
|
-
filePath: '/examples/demo.ts',
|
|
162
|
-
}),
|
|
163
|
-
];
|
|
164
|
-
const result = classifyElements(elements);
|
|
165
|
-
expect(result.get('api')?.length).toBeGreaterThanOrEqual(1);
|
|
166
|
-
// configureAuth should be guide (config pattern)
|
|
167
|
-
expect(result.get('guide')?.length).toBeGreaterThanOrEqual(1);
|
|
168
|
-
expect(result.get('tutorial')?.length).toBeGreaterThanOrEqual(1);
|
|
169
|
-
});
|
|
170
|
-
it('should handle empty elements array', () => {
|
|
171
|
-
const result = classifyElements([]);
|
|
172
|
-
expect(result.get('api')).toHaveLength(0);
|
|
173
|
-
expect(result.get('guide')).toHaveLength(0);
|
|
174
|
-
expect(result.get('tutorial')).toHaveLength(0);
|
|
175
|
-
expect(result.get('overview')).toHaveLength(0);
|
|
176
|
-
});
|
|
177
|
-
});
|
|
178
|
-
describe('getRecommendedStructure', () => {
|
|
179
|
-
it('should return sections grouped by type', () => {
|
|
180
|
-
const elements = [
|
|
181
|
-
createMockElement({
|
|
182
|
-
name: 'createUser',
|
|
183
|
-
parameters: [
|
|
184
|
-
{ name: 'data', type: 'UserData' },
|
|
185
|
-
{ name: 'options', type: 'Options' },
|
|
186
|
-
{ name: 'callback', type: 'Function' },
|
|
187
|
-
],
|
|
188
|
-
}),
|
|
189
|
-
createMockElement({
|
|
190
|
-
name: 'setupDatabase',
|
|
191
|
-
}),
|
|
192
|
-
];
|
|
193
|
-
const result = getRecommendedStructure(elements);
|
|
194
|
-
expect(result.sections.length).toBeGreaterThan(0);
|
|
195
|
-
expect(result.stats.api + result.stats.guide + result.stats.tutorial + result.stats.overview).toBe(elements.length);
|
|
196
|
-
});
|
|
197
|
-
it('should provide stats for each type', () => {
|
|
198
|
-
const elements = [
|
|
199
|
-
createMockElement({ name: 'api1', parameters: [{ name: 'a' }, { name: 'b' }, { name: 'c' }] }),
|
|
200
|
-
createMockElement({ name: 'api2', parameters: [{ name: 'x' }, { name: 'y' }, { name: 'z' }] }),
|
|
201
|
-
createMockElement({ name: 'config1' }),
|
|
202
|
-
];
|
|
203
|
-
const result = getRecommendedStructure(elements);
|
|
204
|
-
expect(typeof result.stats.api).toBe('number');
|
|
205
|
-
expect(typeof result.stats.guide).toBe('number');
|
|
206
|
-
expect(typeof result.stats.tutorial).toBe('number');
|
|
207
|
-
expect(typeof result.stats.overview).toBe('number');
|
|
208
|
-
});
|
|
209
|
-
});
|
|
210
|
-
describe('getPromptForContentType', () => {
|
|
211
|
-
it('should return appropriate prompt for api type', () => {
|
|
212
|
-
const prompt = getPromptForContentType('api');
|
|
213
|
-
expect(prompt).toContain('API reference');
|
|
214
|
-
expect(prompt).toContain('parameter');
|
|
215
|
-
});
|
|
216
|
-
it('should return appropriate prompt for guide type', () => {
|
|
217
|
-
const prompt = getPromptForContentType('guide');
|
|
218
|
-
expect(prompt).toContain('guide');
|
|
219
|
-
expect(prompt).toContain('setup');
|
|
220
|
-
});
|
|
221
|
-
it('should return appropriate prompt for tutorial type', () => {
|
|
222
|
-
const prompt = getPromptForContentType('tutorial');
|
|
223
|
-
expect(prompt).toContain('tutorial');
|
|
224
|
-
expect(prompt).toContain('beginner');
|
|
225
|
-
});
|
|
226
|
-
it('should return appropriate prompt for overview type', () => {
|
|
227
|
-
const prompt = getPromptForContentType('overview');
|
|
228
|
-
expect(prompt).toContain('overview');
|
|
229
|
-
});
|
|
230
|
-
});
|
|
231
|
-
});
|