skrypt-ai 0.1.3 → 0.1.4
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/cli.js +1 -1
- package/dist/config/loader.d.ts +0 -1
- package/dist/config/loader.js +0 -3
- package/dist/generator/organizer.js +3 -1
- package/dist/generator/writer.js +6 -2
- package/dist/scanner/index.d.ts +1 -1
- package/dist/scanner/index.js +12 -3
- package/dist/scanner/integration.test.d.ts +4 -0
- package/dist/scanner/integration.test.js +181 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -5,7 +5,7 @@ import { initCommand } from './commands/init.js';
|
|
|
5
5
|
import { watchCommand } from './commands/watch.js';
|
|
6
6
|
import { autofixCommand } from './commands/autofix.js';
|
|
7
7
|
import { reviewPRCommand } from './commands/review-pr.js';
|
|
8
|
-
const VERSION = '0.1.
|
|
8
|
+
const VERSION = '0.1.4';
|
|
9
9
|
async function checkForUpdates() {
|
|
10
10
|
try {
|
|
11
11
|
const res = await fetch('https://registry.npmjs.org/skrypt-ai/latest', {
|
package/dist/config/loader.d.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { Config, LLMProvider } from './types.js';
|
|
|
2
2
|
export declare function findConfigFile(dir: string): string | null;
|
|
3
3
|
export declare function loadConfig(configPath?: string): Config;
|
|
4
4
|
export declare function validateConfig(config: Config): string[];
|
|
5
|
-
export declare function getRequiredEnvKey(provider: LLMProvider): string | null;
|
|
6
5
|
export declare function checkApiKey(provider: LLMProvider): {
|
|
7
6
|
ok: boolean;
|
|
8
7
|
envKey: string | null;
|
package/dist/config/loader.js
CHANGED
|
@@ -69,9 +69,6 @@ export function validateConfig(config) {
|
|
|
69
69
|
}
|
|
70
70
|
return errors;
|
|
71
71
|
}
|
|
72
|
-
export function getRequiredEnvKey(provider) {
|
|
73
|
-
return PROVIDER_ENV_KEYS[provider] || null;
|
|
74
|
-
}
|
|
75
72
|
export function checkApiKey(provider) {
|
|
76
73
|
const envKey = PROVIDER_ENV_KEYS[provider];
|
|
77
74
|
// Ollama doesn't need an API key
|
|
@@ -45,7 +45,9 @@ export function organizeByTopic(docs, config = DEFAULT_TOPIC_CONFIG) {
|
|
|
45
45
|
if (!topicDocs.has(topicId)) {
|
|
46
46
|
topicDocs.set(topicId, []);
|
|
47
47
|
}
|
|
48
|
-
topicDocs.get(topicId)
|
|
48
|
+
const topicDocList = topicDocs.get(topicId);
|
|
49
|
+
if (topicDocList)
|
|
50
|
+
topicDocList.push(doc);
|
|
49
51
|
}
|
|
50
52
|
// Build topic objects
|
|
51
53
|
const topics = [];
|
package/dist/generator/writer.js
CHANGED
|
@@ -18,7 +18,9 @@ export async function writeLlmsTxt(docs, outputDir, options = {}) {
|
|
|
18
18
|
if (!byFile.has(file)) {
|
|
19
19
|
byFile.set(file, []);
|
|
20
20
|
}
|
|
21
|
-
byFile.get(file)
|
|
21
|
+
const fileDocs = byFile.get(file);
|
|
22
|
+
if (fileDocs)
|
|
23
|
+
fileDocs.push(doc);
|
|
22
24
|
}
|
|
23
25
|
// Summary section
|
|
24
26
|
content += `## Overview\n\n`;
|
|
@@ -133,7 +135,9 @@ export function groupDocsByFile(docs) {
|
|
|
133
135
|
if (!byFile.has(file)) {
|
|
134
136
|
byFile.set(file, []);
|
|
135
137
|
}
|
|
136
|
-
byFile.get(file)
|
|
138
|
+
const fileDocs = byFile.get(file);
|
|
139
|
+
if (fileDocs)
|
|
140
|
+
fileDocs.push(doc);
|
|
137
141
|
}
|
|
138
142
|
return Array.from(byFile.entries()).map(([filePath, fileDocs]) => ({
|
|
139
143
|
filePath,
|
package/dist/scanner/index.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export interface ScanAllResult {
|
|
|
12
12
|
errors: string[];
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
|
-
* Scan a directory for all API elements
|
|
15
|
+
* Scan a directory (or single file) for all API elements
|
|
16
16
|
*/
|
|
17
17
|
export declare function scanDirectory(dir: string, options?: ScanOptions): Promise<ScanAllResult>;
|
|
18
18
|
/**
|
package/dist/scanner/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readdir } from 'fs/promises';
|
|
1
|
+
import { readdir, stat } from 'fs/promises';
|
|
2
2
|
import { join, extname } from 'path';
|
|
3
3
|
import { PythonScanner } from './python.js';
|
|
4
4
|
import { TypeScriptScanner } from './typescript.js';
|
|
@@ -89,12 +89,21 @@ async function findFiles(dir, include, exclude) {
|
|
|
89
89
|
return files;
|
|
90
90
|
}
|
|
91
91
|
/**
|
|
92
|
-
* Scan a directory for all API elements
|
|
92
|
+
* Scan a directory (or single file) for all API elements
|
|
93
93
|
*/
|
|
94
94
|
export async function scanDirectory(dir, options = {}) {
|
|
95
95
|
const include = options.include || ['**/*.py', '**/*.ts', '**/*.js', '**/*.go', '**/*.rs'];
|
|
96
96
|
const exclude = options.exclude || ['**/node_modules/**', '**/__pycache__/**', '**/dist/**'];
|
|
97
|
-
|
|
97
|
+
// Check if input is a file or directory
|
|
98
|
+
const stats = await stat(dir);
|
|
99
|
+
let files;
|
|
100
|
+
if (stats.isFile()) {
|
|
101
|
+
// Single file - just use it if it has a scanner
|
|
102
|
+
files = getScannerForFile(dir) ? [dir] : [];
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
files = await findFiles(dir, include, exclude);
|
|
106
|
+
}
|
|
98
107
|
const results = [];
|
|
99
108
|
const allErrors = [];
|
|
100
109
|
let totalElements = 0;
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration tests for the full scanning pipeline
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
5
|
+
import { writeFileSync, mkdirSync, rmSync, existsSync } from 'fs';
|
|
6
|
+
import { join } from 'path';
|
|
7
|
+
import { scanDirectory, scanFile } from './index.js';
|
|
8
|
+
const TEST_DIR = join(process.cwd(), 'test-fixtures');
|
|
9
|
+
describe('Scanner Integration', () => {
|
|
10
|
+
beforeAll(() => {
|
|
11
|
+
// Create test fixtures
|
|
12
|
+
mkdirSync(TEST_DIR, { recursive: true });
|
|
13
|
+
// TypeScript file
|
|
14
|
+
writeFileSync(join(TEST_DIR, 'sample.ts'), `
|
|
15
|
+
/**
|
|
16
|
+
* Add two numbers
|
|
17
|
+
* @param a First number
|
|
18
|
+
* @param b Second number
|
|
19
|
+
*/
|
|
20
|
+
export function add(a: number, b: number): number {
|
|
21
|
+
return a + b;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class Calculator {
|
|
25
|
+
value: number = 0;
|
|
26
|
+
|
|
27
|
+
add(n: number): this {
|
|
28
|
+
this.value += n;
|
|
29
|
+
return this;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
`);
|
|
33
|
+
// Python file
|
|
34
|
+
writeFileSync(join(TEST_DIR, 'utils.py'), `
|
|
35
|
+
"""Utility functions."""
|
|
36
|
+
|
|
37
|
+
def greet(name: str) -> str:
|
|
38
|
+
"""Greet someone by name."""
|
|
39
|
+
return f"Hello, {name}!"
|
|
40
|
+
|
|
41
|
+
class Helper:
|
|
42
|
+
"""Helper class."""
|
|
43
|
+
|
|
44
|
+
def process(self, data: dict) -> dict:
|
|
45
|
+
"""Process data."""
|
|
46
|
+
return data
|
|
47
|
+
`);
|
|
48
|
+
// Go file
|
|
49
|
+
writeFileSync(join(TEST_DIR, 'handlers.go'), `
|
|
50
|
+
package handlers
|
|
51
|
+
|
|
52
|
+
// GetUser retrieves a user
|
|
53
|
+
func GetUser(id string) string {
|
|
54
|
+
return id
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
type Service struct {
|
|
58
|
+
name string
|
|
59
|
+
}
|
|
60
|
+
`);
|
|
61
|
+
// Rust file
|
|
62
|
+
writeFileSync(join(TEST_DIR, 'lib.rs'), `
|
|
63
|
+
/// Configuration struct
|
|
64
|
+
pub struct Config {
|
|
65
|
+
pub name: String,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
impl Config {
|
|
69
|
+
/// Create new config
|
|
70
|
+
pub fn new() -> Self {
|
|
71
|
+
Config { name: String::new() }
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/// Parse config from string
|
|
76
|
+
pub fn parse_config(s: &str) -> Config {
|
|
77
|
+
Config { name: s.to_string() }
|
|
78
|
+
}
|
|
79
|
+
`);
|
|
80
|
+
});
|
|
81
|
+
afterAll(() => {
|
|
82
|
+
// Cleanup test fixtures
|
|
83
|
+
if (existsSync(TEST_DIR)) {
|
|
84
|
+
rmSync(TEST_DIR, { recursive: true });
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
describe('scanDirectory', () => {
|
|
88
|
+
it('scans all supported languages', async () => {
|
|
89
|
+
const result = await scanDirectory(TEST_DIR);
|
|
90
|
+
expect(result.errors).toHaveLength(0);
|
|
91
|
+
expect(result.files.length).toBeGreaterThanOrEqual(4);
|
|
92
|
+
expect(result.totalElements).toBeGreaterThanOrEqual(8);
|
|
93
|
+
// Check we found elements from each language
|
|
94
|
+
const allElements = result.files.flatMap(f => f.elements);
|
|
95
|
+
const languages = new Set(result.files.map(f => f.language));
|
|
96
|
+
expect(languages.has('typescript')).toBe(true);
|
|
97
|
+
expect(languages.has('python')).toBe(true);
|
|
98
|
+
expect(languages.has('go')).toBe(true);
|
|
99
|
+
expect(languages.has('rust')).toBe(true);
|
|
100
|
+
});
|
|
101
|
+
it('respects exclude patterns', async () => {
|
|
102
|
+
const result = await scanDirectory(TEST_DIR, {
|
|
103
|
+
exclude: ['**/utils.py']
|
|
104
|
+
});
|
|
105
|
+
// Should not find the utils.py file
|
|
106
|
+
const fileNames = result.files.map(f => f.filePath);
|
|
107
|
+
const hasPython = fileNames.some(f => f.includes('utils.py'));
|
|
108
|
+
expect(hasPython).toBe(false);
|
|
109
|
+
});
|
|
110
|
+
it('respects include patterns', async () => {
|
|
111
|
+
const result = await scanDirectory(TEST_DIR, {
|
|
112
|
+
include: ['**/*.ts']
|
|
113
|
+
});
|
|
114
|
+
expect(result.files.length).toBe(1);
|
|
115
|
+
expect(result.files[0].language).toBe('typescript');
|
|
116
|
+
});
|
|
117
|
+
it('calls progress callback', async () => {
|
|
118
|
+
const progress = [];
|
|
119
|
+
await scanDirectory(TEST_DIR, {
|
|
120
|
+
onProgress: (current, total) => {
|
|
121
|
+
progress.push({ current, total });
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
expect(progress.length).toBeGreaterThan(0);
|
|
125
|
+
expect(progress[0].current).toBe(1);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
describe('scanFile', () => {
|
|
129
|
+
it('scans a single TypeScript file', async () => {
|
|
130
|
+
const result = await scanFile(join(TEST_DIR, 'sample.ts'));
|
|
131
|
+
expect(result.errors).toHaveLength(0);
|
|
132
|
+
expect(result.language).toBe('typescript');
|
|
133
|
+
expect(result.elements.length).toBeGreaterThanOrEqual(2);
|
|
134
|
+
const functionNames = result.elements.map(e => e.name);
|
|
135
|
+
expect(functionNames).toContain('add');
|
|
136
|
+
expect(functionNames).toContain('Calculator');
|
|
137
|
+
});
|
|
138
|
+
it('scans a single Python file', async () => {
|
|
139
|
+
const result = await scanFile(join(TEST_DIR, 'utils.py'));
|
|
140
|
+
expect(result.errors).toHaveLength(0);
|
|
141
|
+
expect(result.language).toBe('python');
|
|
142
|
+
const names = result.elements.map(e => e.name);
|
|
143
|
+
expect(names).toContain('greet');
|
|
144
|
+
expect(names).toContain('Helper');
|
|
145
|
+
});
|
|
146
|
+
it('returns error for unsupported file type', async () => {
|
|
147
|
+
const result = await scanFile(join(TEST_DIR, 'unknown.xyz'));
|
|
148
|
+
expect(result.errors.length).toBeGreaterThan(0);
|
|
149
|
+
expect(result.elements).toHaveLength(0);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
describe('single file via scanDirectory', () => {
|
|
153
|
+
it('accepts a single file path', async () => {
|
|
154
|
+
const result = await scanDirectory(join(TEST_DIR, 'sample.ts'));
|
|
155
|
+
expect(result.errors).toHaveLength(0);
|
|
156
|
+
expect(result.files.length).toBe(1);
|
|
157
|
+
expect(result.files[0].language).toBe('typescript');
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
describe('element extraction', () => {
|
|
161
|
+
it('extracts function parameters', async () => {
|
|
162
|
+
const result = await scanFile(join(TEST_DIR, 'sample.ts'));
|
|
163
|
+
const addFn = result.elements.find(e => e.name === 'add');
|
|
164
|
+
expect(addFn).toBeDefined();
|
|
165
|
+
expect(addFn?.parameters).toHaveLength(2);
|
|
166
|
+
expect(addFn?.parameters[0].name).toBe('a');
|
|
167
|
+
expect(addFn?.parameters[0].type).toBe('number');
|
|
168
|
+
});
|
|
169
|
+
it('extracts docstrings', async () => {
|
|
170
|
+
const result = await scanFile(join(TEST_DIR, 'sample.ts'));
|
|
171
|
+
const addFn = result.elements.find(e => e.name === 'add');
|
|
172
|
+
expect(addFn?.docstring).toContain('Add two numbers');
|
|
173
|
+
});
|
|
174
|
+
it('extracts class methods', async () => {
|
|
175
|
+
const result = await scanFile(join(TEST_DIR, 'sample.ts'));
|
|
176
|
+
const addMethod = result.elements.find(e => e.name === 'add' && e.kind === 'method');
|
|
177
|
+
expect(addMethod).toBeDefined();
|
|
178
|
+
expect(addMethod?.parentClass).toBe('Calculator');
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
});
|