skrypt-ai 0.6.0 → 0.7.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/audit/doc-parser.d.ts +5 -0
- package/dist/audit/doc-parser.js +106 -0
- package/dist/audit/index.d.ts +4 -0
- package/dist/audit/index.js +4 -0
- package/dist/audit/matcher.d.ts +6 -0
- package/dist/audit/matcher.js +94 -0
- package/dist/audit/reporter.d.ts +9 -0
- package/dist/audit/reporter.js +106 -0
- package/dist/audit/types.d.ts +37 -0
- package/dist/audit/types.js +1 -0
- package/dist/auth/index.js +3 -1
- package/dist/cli.js +11 -1
- package/dist/commands/audit.d.ts +2 -0
- package/dist/commands/audit.js +59 -0
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.js +73 -0
- package/dist/commands/cron.js +4 -0
- package/dist/commands/generate.d.ts +7 -0
- package/dist/commands/generate.js +528 -234
- package/dist/commands/refresh.d.ts +2 -0
- package/dist/commands/refresh.js +158 -0
- package/dist/commands/review-pr.js +5 -0
- package/dist/commands/review.d.ts +2 -0
- package/dist/commands/review.js +110 -0
- package/dist/commands/test.js +177 -236
- package/dist/commands/watch.js +29 -20
- package/dist/config/loader.d.ts +6 -1
- package/dist/config/loader.js +38 -2
- package/dist/config/types.d.ts +7 -0
- package/dist/generator/generator.js +2 -1
- package/dist/generator/types.d.ts +3 -0
- package/dist/generator/writer.js +60 -28
- package/dist/github/org-discovery.d.ts +17 -0
- package/dist/github/org-discovery.js +93 -0
- package/dist/llm/index.d.ts +2 -0
- package/dist/llm/index.js +8 -2
- package/dist/next-actions/actions.d.ts +2 -0
- package/dist/next-actions/actions.js +190 -0
- package/dist/next-actions/index.d.ts +6 -0
- package/dist/next-actions/index.js +39 -0
- package/dist/next-actions/setup.d.ts +2 -0
- package/dist/next-actions/setup.js +72 -0
- package/dist/next-actions/state.d.ts +7 -0
- package/dist/next-actions/state.js +68 -0
- package/dist/next-actions/suggest.d.ts +3 -0
- package/dist/next-actions/suggest.js +47 -0
- package/dist/next-actions/types.d.ts +26 -0
- package/dist/next-actions/types.js +1 -0
- package/dist/refresh/differ.d.ts +9 -0
- package/dist/refresh/differ.js +67 -0
- package/dist/refresh/index.d.ts +4 -0
- package/dist/refresh/index.js +4 -0
- package/dist/refresh/manifest.d.ts +18 -0
- package/dist/refresh/manifest.js +71 -0
- package/dist/refresh/splicer.d.ts +9 -0
- package/dist/refresh/splicer.js +50 -0
- package/dist/refresh/types.d.ts +37 -0
- package/dist/refresh/types.js +1 -0
- package/dist/review/index.d.ts +8 -0
- package/dist/review/index.js +94 -0
- package/dist/review/parser.d.ts +16 -0
- package/dist/review/parser.js +95 -0
- package/dist/review/types.d.ts +18 -0
- package/dist/review/types.js +1 -0
- package/dist/scanner/types.d.ts +2 -0
- package/dist/structure/index.d.ts +19 -0
- package/dist/structure/index.js +92 -0
- package/dist/structure/planner.d.ts +8 -0
- package/dist/structure/planner.js +180 -0
- package/dist/structure/topology.d.ts +16 -0
- package/dist/structure/topology.js +49 -0
- package/dist/structure/types.d.ts +26 -0
- package/dist/structure/types.js +1 -0
- package/dist/testing/comparator.d.ts +7 -0
- package/dist/testing/comparator.js +77 -0
- package/dist/testing/docker.d.ts +21 -0
- package/dist/testing/docker.js +234 -0
- package/dist/testing/env.d.ts +16 -0
- package/dist/testing/env.js +58 -0
- package/dist/testing/extractor.d.ts +9 -0
- package/dist/testing/extractor.js +195 -0
- package/dist/testing/index.d.ts +6 -0
- package/dist/testing/index.js +6 -0
- package/dist/testing/runner.d.ts +5 -0
- package/dist/testing/runner.js +225 -0
- package/dist/testing/types.d.ts +58 -0
- package/dist/testing/types.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import { writeFileSync, mkdirSync, rmSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { tmpdir } from 'os';
|
|
5
|
+
import { randomUUID } from 'crypto';
|
|
6
|
+
import { compareOutput } from './comparator.js';
|
|
7
|
+
import { buildCleanEnv, checkRequiredEnv } from './env.js';
|
|
8
|
+
/**
|
|
9
|
+
* Run a snippet locally in an isolated temp directory with a clean environment
|
|
10
|
+
*/
|
|
11
|
+
export async function runLocally(snippet, config) {
|
|
12
|
+
const startTime = Date.now();
|
|
13
|
+
// Check for skip directive
|
|
14
|
+
if (snippet.skipReason) {
|
|
15
|
+
return {
|
|
16
|
+
snippet,
|
|
17
|
+
status: 'skip',
|
|
18
|
+
stdout: '',
|
|
19
|
+
stderr: `Skipped: ${snippet.skipReason}`,
|
|
20
|
+
exitCode: 0,
|
|
21
|
+
duration: 0,
|
|
22
|
+
environment: 'local',
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
// Check required env vars
|
|
26
|
+
if (snippet.requiredEnv && snippet.requiredEnv.length > 0) {
|
|
27
|
+
const { ok, missing } = checkRequiredEnv(snippet.requiredEnv, config.envVars);
|
|
28
|
+
if (!ok) {
|
|
29
|
+
return {
|
|
30
|
+
snippet,
|
|
31
|
+
status: 'skip',
|
|
32
|
+
stdout: '',
|
|
33
|
+
stderr: `Missing required env vars: ${missing.join(', ')}`,
|
|
34
|
+
exitCode: 0,
|
|
35
|
+
duration: 0,
|
|
36
|
+
environment: 'local',
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const tempDir = join(tmpdir(), `skrypt-test-${randomUUID()}`);
|
|
41
|
+
mkdirSync(tempDir, { recursive: true });
|
|
42
|
+
try {
|
|
43
|
+
// Install dependencies if needed
|
|
44
|
+
if (config.installDeps && snippet.dependencies && snippet.dependencies.length > 0) {
|
|
45
|
+
const isPython = ['python', 'py'].includes(snippet.language);
|
|
46
|
+
if (isPython) {
|
|
47
|
+
await installPythonDeps(snippet.dependencies, tempDir, config.envVars);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
await installNodeDeps(snippet.dependencies, tempDir, config.envVars);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const { command, args, tempFile } = prepareExecution(snippet, tempDir);
|
|
54
|
+
if (!command) {
|
|
55
|
+
return {
|
|
56
|
+
snippet,
|
|
57
|
+
status: 'fail',
|
|
58
|
+
stdout: '',
|
|
59
|
+
stderr: `Unsupported language: ${snippet.language}`,
|
|
60
|
+
exitCode: 1,
|
|
61
|
+
duration: Date.now() - startTime,
|
|
62
|
+
environment: 'local',
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
writeFileSync(tempFile, snippet.code);
|
|
66
|
+
const cleanEnv = buildCleanEnv(config.envVars);
|
|
67
|
+
// Set TMPDIR to the snippet's temp dir for extra isolation
|
|
68
|
+
cleanEnv.TMPDIR = tempDir;
|
|
69
|
+
const result = await executeWithTimeout(command, args, config.timeout, tempDir, cleanEnv);
|
|
70
|
+
const duration = Date.now() - startTime;
|
|
71
|
+
// Check output if expected
|
|
72
|
+
if (snippet.expectedOutput !== undefined) {
|
|
73
|
+
const comparison = compareOutput(result.stdout, snippet.expectedOutput, snippet.expectedOutputMode || 'exact');
|
|
74
|
+
if (!comparison.match) {
|
|
75
|
+
return {
|
|
76
|
+
snippet,
|
|
77
|
+
status: 'output_mismatch',
|
|
78
|
+
stdout: result.stdout,
|
|
79
|
+
stderr: result.stderr,
|
|
80
|
+
exitCode: result.exitCode,
|
|
81
|
+
duration,
|
|
82
|
+
environment: 'local',
|
|
83
|
+
outputMatch: false,
|
|
84
|
+
diff: comparison.diff,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
snippet,
|
|
89
|
+
status: result.exitCode === 0 ? 'pass' : 'fail',
|
|
90
|
+
stdout: result.stdout,
|
|
91
|
+
stderr: result.stderr,
|
|
92
|
+
exitCode: result.exitCode,
|
|
93
|
+
duration,
|
|
94
|
+
environment: 'local',
|
|
95
|
+
outputMatch: true,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
snippet,
|
|
100
|
+
status: result.timedOut ? 'timeout' : (result.exitCode === 0 ? 'pass' : 'fail'),
|
|
101
|
+
stdout: result.stdout,
|
|
102
|
+
stderr: result.timedOut
|
|
103
|
+
? `Timeout: execution exceeded ${config.timeout}ms`
|
|
104
|
+
: result.stderr,
|
|
105
|
+
exitCode: result.exitCode,
|
|
106
|
+
duration,
|
|
107
|
+
environment: 'local',
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
112
|
+
return {
|
|
113
|
+
snippet,
|
|
114
|
+
status: 'fail',
|
|
115
|
+
stdout: '',
|
|
116
|
+
stderr: message,
|
|
117
|
+
exitCode: 1,
|
|
118
|
+
duration: Date.now() - startTime,
|
|
119
|
+
environment: 'local',
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
finally {
|
|
123
|
+
try {
|
|
124
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// Ignore cleanup errors
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Determine the command and args to execute a snippet
|
|
133
|
+
*/
|
|
134
|
+
function prepareExecution(snippet, tempDir) {
|
|
135
|
+
const isTS = ['typescript', 'ts'].includes(snippet.language);
|
|
136
|
+
const isJS = ['javascript', 'js'].includes(snippet.language);
|
|
137
|
+
const isPy = ['python', 'py'].includes(snippet.language);
|
|
138
|
+
if (isTS) {
|
|
139
|
+
const tempFile = join(tempDir, 'test.ts');
|
|
140
|
+
return { command: 'npx', args: ['tsx', tempFile], tempFile };
|
|
141
|
+
}
|
|
142
|
+
if (isJS) {
|
|
143
|
+
const tempFile = join(tempDir, 'test.js');
|
|
144
|
+
return { command: 'node', args: [tempFile], tempFile };
|
|
145
|
+
}
|
|
146
|
+
if (isPy) {
|
|
147
|
+
const tempFile = join(tempDir, 'test.py');
|
|
148
|
+
return { command: 'python3', args: [tempFile], tempFile };
|
|
149
|
+
}
|
|
150
|
+
return { command: null, args: [], tempFile: join(tempDir, 'test.txt') };
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Install Node.js dependencies in a temp directory
|
|
154
|
+
*/
|
|
155
|
+
async function installNodeDeps(deps, tempDir, envVars) {
|
|
156
|
+
const pkg = { name: 'skrypt-test', private: true, dependencies: {} };
|
|
157
|
+
for (const dep of deps) {
|
|
158
|
+
pkg.dependencies[dep] = '*';
|
|
159
|
+
}
|
|
160
|
+
writeFileSync(join(tempDir, 'package.json'), JSON.stringify(pkg));
|
|
161
|
+
const env = buildCleanEnv(envVars);
|
|
162
|
+
await executeWithTimeout('npm', ['install', '--prefix', tempDir, '--no-audit', '--no-fund'], 30000, tempDir, env);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Install Python dependencies in a temp directory using pip
|
|
166
|
+
*/
|
|
167
|
+
async function installPythonDeps(deps, tempDir, envVars) {
|
|
168
|
+
const env = buildCleanEnv(envVars);
|
|
169
|
+
env.PIP_TARGET = join(tempDir, 'pip_packages');
|
|
170
|
+
env.PYTHONPATH = join(tempDir, 'pip_packages');
|
|
171
|
+
mkdirSync(join(tempDir, 'pip_packages'), { recursive: true });
|
|
172
|
+
await executeWithTimeout('python3', ['-m', 'pip', 'install', '--target', join(tempDir, 'pip_packages'), '--quiet', ...deps], 60000, tempDir, env);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Execute a command with timeout
|
|
176
|
+
*/
|
|
177
|
+
function executeWithTimeout(command, args, timeoutMs, cwd, env) {
|
|
178
|
+
return new Promise((resolve) => {
|
|
179
|
+
const proc = spawn(command, args, {
|
|
180
|
+
cwd,
|
|
181
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
182
|
+
env,
|
|
183
|
+
});
|
|
184
|
+
let stdout = '';
|
|
185
|
+
let stderr = '';
|
|
186
|
+
let timedOut = false;
|
|
187
|
+
const MAX_BUFFER = 1024 * 1024; // 1MB cap to prevent OOM
|
|
188
|
+
const timeout = setTimeout(() => {
|
|
189
|
+
timedOut = true;
|
|
190
|
+
proc.kill('SIGKILL');
|
|
191
|
+
}, timeoutMs);
|
|
192
|
+
proc.stdout?.on('data', (data) => {
|
|
193
|
+
if (stdout.length < MAX_BUFFER) {
|
|
194
|
+
stdout += data.toString();
|
|
195
|
+
if (stdout.length > MAX_BUFFER)
|
|
196
|
+
stdout = stdout.slice(0, MAX_BUFFER);
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
proc.stderr?.on('data', (data) => {
|
|
200
|
+
if (stderr.length < MAX_BUFFER) {
|
|
201
|
+
stderr += data.toString();
|
|
202
|
+
if (stderr.length > MAX_BUFFER)
|
|
203
|
+
stderr = stderr.slice(0, MAX_BUFFER);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
proc.on('close', (code) => {
|
|
207
|
+
clearTimeout(timeout);
|
|
208
|
+
resolve({
|
|
209
|
+
exitCode: timedOut ? 1 : (code ?? 1),
|
|
210
|
+
stdout,
|
|
211
|
+
stderr,
|
|
212
|
+
timedOut,
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
proc.on('error', (err) => {
|
|
216
|
+
clearTimeout(timeout);
|
|
217
|
+
resolve({
|
|
218
|
+
exitCode: 1,
|
|
219
|
+
stdout,
|
|
220
|
+
stderr: err.message,
|
|
221
|
+
timedOut: false,
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A code snippet extracted from a documentation file
|
|
3
|
+
*/
|
|
4
|
+
export interface ExtractedSnippet {
|
|
5
|
+
code: string;
|
|
6
|
+
language: string;
|
|
7
|
+
filePath: string;
|
|
8
|
+
lineNumber: number;
|
|
9
|
+
index: number;
|
|
10
|
+
expectedOutput?: string;
|
|
11
|
+
expectedOutputMode?: 'exact' | 'contains';
|
|
12
|
+
requiredEnv?: string[];
|
|
13
|
+
skipReason?: string;
|
|
14
|
+
dependencies?: string[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Result of running a single snippet
|
|
18
|
+
*/
|
|
19
|
+
export interface TestResult {
|
|
20
|
+
snippet: ExtractedSnippet;
|
|
21
|
+
status: 'pass' | 'fail' | 'skip' | 'timeout' | 'output_mismatch';
|
|
22
|
+
stdout: string;
|
|
23
|
+
stderr: string;
|
|
24
|
+
exitCode: number;
|
|
25
|
+
duration: number;
|
|
26
|
+
environment: string;
|
|
27
|
+
outputMatch?: boolean;
|
|
28
|
+
diff?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Aggregate report for a test run
|
|
32
|
+
*/
|
|
33
|
+
export interface VerificationReport {
|
|
34
|
+
total: number;
|
|
35
|
+
passed: number;
|
|
36
|
+
failed: number;
|
|
37
|
+
skipped: number;
|
|
38
|
+
outputMismatches: number;
|
|
39
|
+
duration: number;
|
|
40
|
+
results: TestResult[];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Configuration for the local runner
|
|
44
|
+
*/
|
|
45
|
+
export interface RunnerConfig {
|
|
46
|
+
timeout: number;
|
|
47
|
+
envVars: Record<string, string>;
|
|
48
|
+
installDeps: boolean;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Configuration for Docker-based multi-environment testing
|
|
52
|
+
*/
|
|
53
|
+
export interface DockerEnvironment {
|
|
54
|
+
name: string;
|
|
55
|
+
image: string;
|
|
56
|
+
command: string;
|
|
57
|
+
languages: string[];
|
|
58
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|