skrypt-ai 0.6.1 → 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 (180) hide show
  1. package/dist/audit/doc-parser.d.ts +5 -0
  2. package/dist/audit/doc-parser.js +106 -0
  3. package/dist/audit/index.d.ts +4 -0
  4. package/dist/audit/index.js +4 -0
  5. package/dist/audit/matcher.d.ts +6 -0
  6. package/dist/audit/matcher.js +94 -0
  7. package/dist/audit/reporter.d.ts +9 -0
  8. package/dist/audit/reporter.js +106 -0
  9. package/dist/audit/types.d.ts +37 -0
  10. package/dist/auth/index.js +6 -4
  11. package/dist/cli.js +12 -2
  12. package/dist/commands/audit.d.ts +2 -0
  13. package/dist/commands/audit.js +59 -0
  14. package/dist/commands/config.d.ts +2 -0
  15. package/dist/commands/config.js +73 -0
  16. package/dist/commands/{generate.d.ts → generate/index.d.ts} +1 -0
  17. package/dist/commands/generate/index.js +393 -0
  18. package/dist/commands/generate/scan.d.ts +41 -0
  19. package/dist/commands/generate/scan.js +256 -0
  20. package/dist/commands/generate/verify.d.ts +14 -0
  21. package/dist/commands/generate/verify.js +122 -0
  22. package/dist/commands/generate/write.d.ts +25 -0
  23. package/dist/commands/generate/write.js +120 -0
  24. package/dist/commands/import.js +4 -1
  25. package/dist/commands/llms-txt.js +6 -4
  26. package/dist/commands/refresh.d.ts +2 -0
  27. package/dist/commands/refresh.js +158 -0
  28. package/dist/commands/review.d.ts +2 -0
  29. package/dist/commands/review.js +110 -0
  30. package/dist/commands/test.js +177 -236
  31. package/dist/commands/watch.js +29 -20
  32. package/dist/config/loader.d.ts +6 -2
  33. package/dist/config/loader.js +39 -3
  34. package/dist/config/types.d.ts +7 -0
  35. package/dist/generator/agents-md.d.ts +25 -0
  36. package/dist/generator/agents-md.js +122 -0
  37. package/dist/generator/generator.js +2 -1
  38. package/dist/generator/index.d.ts +2 -0
  39. package/dist/generator/index.js +2 -0
  40. package/dist/generator/mdx-serializer.d.ts +11 -0
  41. package/dist/generator/mdx-serializer.js +135 -0
  42. package/dist/generator/organizer.d.ts +1 -16
  43. package/dist/generator/organizer.js +0 -38
  44. package/dist/generator/types.d.ts +3 -0
  45. package/dist/generator/writer.js +65 -32
  46. package/dist/github/org-discovery.d.ts +17 -0
  47. package/dist/github/org-discovery.js +93 -0
  48. package/dist/llm/index.d.ts +2 -0
  49. package/dist/llm/index.js +8 -2
  50. package/dist/llm/proxy-client.d.ts +32 -0
  51. package/dist/llm/proxy-client.js +103 -0
  52. package/dist/next-actions/actions.d.ts +2 -0
  53. package/dist/next-actions/actions.js +190 -0
  54. package/dist/next-actions/index.d.ts +6 -0
  55. package/dist/next-actions/index.js +39 -0
  56. package/dist/next-actions/setup.d.ts +2 -0
  57. package/dist/next-actions/setup.js +72 -0
  58. package/dist/next-actions/state.d.ts +7 -0
  59. package/dist/next-actions/state.js +68 -0
  60. package/dist/next-actions/suggest.d.ts +3 -0
  61. package/dist/next-actions/suggest.js +47 -0
  62. package/dist/next-actions/types.d.ts +26 -0
  63. package/dist/refresh/differ.d.ts +9 -0
  64. package/dist/refresh/differ.js +67 -0
  65. package/dist/refresh/index.d.ts +4 -0
  66. package/dist/refresh/index.js +4 -0
  67. package/dist/refresh/manifest.d.ts +18 -0
  68. package/dist/refresh/manifest.js +71 -0
  69. package/dist/refresh/splicer.d.ts +9 -0
  70. package/dist/refresh/splicer.js +50 -0
  71. package/dist/refresh/types.d.ts +37 -0
  72. package/dist/review/index.d.ts +8 -0
  73. package/dist/review/index.js +94 -0
  74. package/dist/review/parser.d.ts +16 -0
  75. package/dist/review/parser.js +95 -0
  76. package/dist/review/types.d.ts +18 -0
  77. package/dist/scanner/csharp.d.ts +0 -4
  78. package/dist/scanner/csharp.js +9 -49
  79. package/dist/scanner/go.d.ts +0 -3
  80. package/dist/scanner/go.js +8 -35
  81. package/dist/scanner/java.d.ts +0 -4
  82. package/dist/scanner/java.js +9 -49
  83. package/dist/scanner/kotlin.d.ts +0 -3
  84. package/dist/scanner/kotlin.js +6 -33
  85. package/dist/scanner/php.d.ts +0 -10
  86. package/dist/scanner/php.js +11 -55
  87. package/dist/scanner/ruby.d.ts +0 -3
  88. package/dist/scanner/ruby.js +8 -38
  89. package/dist/scanner/rust.d.ts +0 -3
  90. package/dist/scanner/rust.js +10 -37
  91. package/dist/scanner/swift.d.ts +0 -3
  92. package/dist/scanner/swift.js +8 -35
  93. package/dist/scanner/types.d.ts +2 -0
  94. package/dist/scanner/utils.d.ts +41 -0
  95. package/dist/scanner/utils.js +97 -0
  96. package/dist/structure/index.d.ts +19 -0
  97. package/dist/structure/index.js +92 -0
  98. package/dist/structure/planner.d.ts +8 -0
  99. package/dist/structure/planner.js +180 -0
  100. package/dist/structure/topology.d.ts +16 -0
  101. package/dist/structure/topology.js +49 -0
  102. package/dist/structure/types.d.ts +26 -0
  103. package/dist/template/docs.json +5 -2
  104. package/dist/template/next.config.mjs +31 -0
  105. package/dist/template/package.json +5 -3
  106. package/dist/template/src/app/layout.tsx +13 -13
  107. package/dist/template/src/app/llms-full.md/route.ts +29 -0
  108. package/dist/template/src/app/llms.txt/route.ts +29 -0
  109. package/dist/template/src/app/md/[...slug]/route.ts +174 -0
  110. package/dist/template/src/app/reference/route.ts +22 -18
  111. package/dist/template/src/app/sitemap.ts +1 -1
  112. package/dist/template/src/components/ai-chat-impl.tsx +206 -0
  113. package/dist/template/src/components/ai-chat.tsx +20 -193
  114. package/dist/template/src/components/mdx/index.tsx +27 -4
  115. package/dist/template/src/lib/fonts.ts +135 -0
  116. package/dist/template/src/middleware.ts +101 -0
  117. package/dist/template/src/styles/globals.css +28 -20
  118. package/dist/testing/comparator.d.ts +7 -0
  119. package/dist/testing/comparator.js +77 -0
  120. package/dist/testing/docker.d.ts +21 -0
  121. package/dist/testing/docker.js +234 -0
  122. package/dist/testing/env.d.ts +16 -0
  123. package/dist/testing/env.js +58 -0
  124. package/dist/testing/extractor.d.ts +9 -0
  125. package/dist/testing/extractor.js +195 -0
  126. package/dist/testing/index.d.ts +6 -0
  127. package/dist/testing/index.js +6 -0
  128. package/dist/testing/runner.d.ts +5 -0
  129. package/dist/testing/runner.js +225 -0
  130. package/dist/testing/types.d.ts +58 -0
  131. package/dist/utils/files.d.ts +0 -8
  132. package/dist/utils/files.js +0 -33
  133. package/package.json +1 -1
  134. package/dist/autofix/autofix.test.js +0 -487
  135. package/dist/commands/generate.js +0 -445
  136. package/dist/generator/generator.test.js +0 -259
  137. package/dist/generator/writer.test.js +0 -411
  138. package/dist/llm/llm.manual-test.js +0 -112
  139. package/dist/llm/llm.mock-test.d.ts +0 -4
  140. package/dist/llm/llm.mock-test.js +0 -79
  141. package/dist/plugins/index.d.ts +0 -47
  142. package/dist/plugins/index.js +0 -181
  143. package/dist/scanner/content-type.test.js +0 -231
  144. package/dist/scanner/integration.test.d.ts +0 -4
  145. package/dist/scanner/integration.test.js +0 -180
  146. package/dist/scanner/scanner.test.js +0 -210
  147. package/dist/scanner/typescript.manual-test.d.ts +0 -1
  148. package/dist/scanner/typescript.manual-test.js +0 -112
  149. package/dist/template/src/app/docs/auth/page.mdx +0 -589
  150. package/dist/template/src/app/docs/autofix/page.mdx +0 -624
  151. package/dist/template/src/app/docs/cli/page.mdx +0 -217
  152. package/dist/template/src/app/docs/config/page.mdx +0 -428
  153. package/dist/template/src/app/docs/configuration/page.mdx +0 -86
  154. package/dist/template/src/app/docs/deployment/page.mdx +0 -112
  155. package/dist/template/src/app/docs/generator/generator.md +0 -504
  156. package/dist/template/src/app/docs/generator/organizer.md +0 -779
  157. package/dist/template/src/app/docs/generator/page.mdx +0 -613
  158. package/dist/template/src/app/docs/github/page.mdx +0 -502
  159. package/dist/template/src/app/docs/llm/anthropic-client.md +0 -549
  160. package/dist/template/src/app/docs/llm/index.md +0 -471
  161. package/dist/template/src/app/docs/llm/page.mdx +0 -428
  162. package/dist/template/src/app/docs/plugins/page.mdx +0 -1793
  163. package/dist/template/src/app/docs/pro/page.mdx +0 -121
  164. package/dist/template/src/app/docs/quickstart/page.mdx +0 -93
  165. package/dist/template/src/app/docs/scanner/content-type.md +0 -599
  166. package/dist/template/src/app/docs/scanner/index.md +0 -212
  167. package/dist/template/src/app/docs/scanner/page.mdx +0 -307
  168. package/dist/template/src/app/docs/scanner/python.md +0 -469
  169. package/dist/template/src/app/docs/scanner/python_parser.md +0 -1056
  170. package/dist/template/src/app/docs/scanner/rust.md +0 -325
  171. package/dist/template/src/app/docs/scanner/typescript.md +0 -201
  172. package/dist/template/src/app/icon.tsx +0 -29
  173. package/dist/utils/validation.d.ts +0 -1
  174. package/dist/utils/validation.js +0 -12
  175. /package/dist/{autofix/autofix.test.d.ts → audit/types.js} +0 -0
  176. /package/dist/{generator/generator.test.d.ts → next-actions/types.js} +0 -0
  177. /package/dist/{generator/writer.test.d.ts → refresh/types.js} +0 -0
  178. /package/dist/{llm/llm.manual-test.d.ts → review/types.js} +0 -0
  179. /package/dist/{scanner/content-type.test.d.ts → structure/types.js} +0 -0
  180. /package/dist/{scanner/scanner.test.d.ts → testing/types.js} +0 -0
@@ -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,4 +0,0 @@
1
- /**
2
- * Mock tests for LLM client - tests structure without API calls
3
- */
4
- export {};
@@ -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();
@@ -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;
@@ -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,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
- });
@@ -1,4 +0,0 @@
1
- /**
2
- * Integration tests for the full scanning pipeline
3
- */
4
- export {};