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.
Files changed (88) 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/audit/types.js +1 -0
  11. package/dist/auth/index.js +3 -1
  12. package/dist/cli.js +11 -1
  13. package/dist/commands/audit.d.ts +2 -0
  14. package/dist/commands/audit.js +59 -0
  15. package/dist/commands/config.d.ts +2 -0
  16. package/dist/commands/config.js +73 -0
  17. package/dist/commands/cron.js +4 -0
  18. package/dist/commands/generate.d.ts +7 -0
  19. package/dist/commands/generate.js +528 -234
  20. package/dist/commands/refresh.d.ts +2 -0
  21. package/dist/commands/refresh.js +158 -0
  22. package/dist/commands/review-pr.js +5 -0
  23. package/dist/commands/review.d.ts +2 -0
  24. package/dist/commands/review.js +110 -0
  25. package/dist/commands/test.js +177 -236
  26. package/dist/commands/watch.js +29 -20
  27. package/dist/config/loader.d.ts +6 -1
  28. package/dist/config/loader.js +38 -2
  29. package/dist/config/types.d.ts +7 -0
  30. package/dist/generator/generator.js +2 -1
  31. package/dist/generator/types.d.ts +3 -0
  32. package/dist/generator/writer.js +60 -28
  33. package/dist/github/org-discovery.d.ts +17 -0
  34. package/dist/github/org-discovery.js +93 -0
  35. package/dist/llm/index.d.ts +2 -0
  36. package/dist/llm/index.js +8 -2
  37. package/dist/next-actions/actions.d.ts +2 -0
  38. package/dist/next-actions/actions.js +190 -0
  39. package/dist/next-actions/index.d.ts +6 -0
  40. package/dist/next-actions/index.js +39 -0
  41. package/dist/next-actions/setup.d.ts +2 -0
  42. package/dist/next-actions/setup.js +72 -0
  43. package/dist/next-actions/state.d.ts +7 -0
  44. package/dist/next-actions/state.js +68 -0
  45. package/dist/next-actions/suggest.d.ts +3 -0
  46. package/dist/next-actions/suggest.js +47 -0
  47. package/dist/next-actions/types.d.ts +26 -0
  48. package/dist/next-actions/types.js +1 -0
  49. package/dist/refresh/differ.d.ts +9 -0
  50. package/dist/refresh/differ.js +67 -0
  51. package/dist/refresh/index.d.ts +4 -0
  52. package/dist/refresh/index.js +4 -0
  53. package/dist/refresh/manifest.d.ts +18 -0
  54. package/dist/refresh/manifest.js +71 -0
  55. package/dist/refresh/splicer.d.ts +9 -0
  56. package/dist/refresh/splicer.js +50 -0
  57. package/dist/refresh/types.d.ts +37 -0
  58. package/dist/refresh/types.js +1 -0
  59. package/dist/review/index.d.ts +8 -0
  60. package/dist/review/index.js +94 -0
  61. package/dist/review/parser.d.ts +16 -0
  62. package/dist/review/parser.js +95 -0
  63. package/dist/review/types.d.ts +18 -0
  64. package/dist/review/types.js +1 -0
  65. package/dist/scanner/types.d.ts +2 -0
  66. package/dist/structure/index.d.ts +19 -0
  67. package/dist/structure/index.js +92 -0
  68. package/dist/structure/planner.d.ts +8 -0
  69. package/dist/structure/planner.js +180 -0
  70. package/dist/structure/topology.d.ts +16 -0
  71. package/dist/structure/topology.js +49 -0
  72. package/dist/structure/types.d.ts +26 -0
  73. package/dist/structure/types.js +1 -0
  74. package/dist/testing/comparator.d.ts +7 -0
  75. package/dist/testing/comparator.js +77 -0
  76. package/dist/testing/docker.d.ts +21 -0
  77. package/dist/testing/docker.js +234 -0
  78. package/dist/testing/env.d.ts +16 -0
  79. package/dist/testing/env.js +58 -0
  80. package/dist/testing/extractor.d.ts +9 -0
  81. package/dist/testing/extractor.js +195 -0
  82. package/dist/testing/index.d.ts +6 -0
  83. package/dist/testing/index.js +6 -0
  84. package/dist/testing/runner.d.ts +5 -0
  85. package/dist/testing/runner.js +225 -0
  86. package/dist/testing/types.d.ts +58 -0
  87. package/dist/testing/types.js +1 -0
  88. 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 {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skrypt-ai",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "AI-powered documentation generator with code examples",
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",