skrypt-ai 0.5.0 → 0.6.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 +8 -1
- package/dist/autofix/index.d.ts +0 -4
- package/dist/autofix/index.js +0 -21
- package/dist/capture/browser.d.ts +11 -0
- package/dist/capture/browser.js +173 -0
- package/dist/capture/diff.d.ts +23 -0
- package/dist/capture/diff.js +52 -0
- package/dist/capture/index.d.ts +23 -0
- package/dist/capture/index.js +210 -0
- package/dist/capture/naming.d.ts +17 -0
- package/dist/capture/naming.js +45 -0
- package/dist/capture/parser.d.ts +15 -0
- package/dist/capture/parser.js +80 -0
- package/dist/capture/types.d.ts +57 -0
- package/dist/capture/types.js +1 -0
- package/dist/cli.js +4 -0
- package/dist/commands/autofix.js +136 -120
- package/dist/commands/cron.js +58 -47
- package/dist/commands/deploy.js +123 -102
- package/dist/commands/generate.js +88 -6
- package/dist/commands/heal.d.ts +10 -0
- package/dist/commands/heal.js +201 -0
- package/dist/commands/i18n.js +146 -111
- package/dist/commands/lint.js +50 -44
- package/dist/commands/llms-txt.js +59 -49
- package/dist/commands/login.js +61 -43
- package/dist/commands/mcp.js +6 -0
- package/dist/commands/monitor.js +13 -8
- package/dist/commands/qa.d.ts +2 -0
- package/dist/commands/qa.js +43 -0
- package/dist/commands/review-pr.js +108 -102
- package/dist/commands/sdk.js +128 -122
- package/dist/commands/security.js +86 -80
- package/dist/commands/test.js +91 -92
- package/dist/commands/version.js +104 -75
- package/dist/commands/watch.js +130 -114
- package/dist/config/types.js +2 -2
- package/dist/context-hub/index.d.ts +23 -0
- package/dist/context-hub/index.js +179 -0
- package/dist/context-hub/mappings.d.ts +8 -0
- package/dist/context-hub/mappings.js +55 -0
- package/dist/context-hub/types.d.ts +33 -0
- package/dist/context-hub/types.js +1 -0
- package/dist/generator/generator.js +39 -6
- package/dist/generator/types.d.ts +7 -0
- package/dist/generator/writer.d.ts +3 -1
- package/dist/generator/writer.js +24 -4
- package/dist/llm/anthropic-client.d.ts +1 -0
- package/dist/llm/anthropic-client.js +3 -1
- package/dist/llm/index.d.ts +6 -4
- package/dist/llm/index.js +76 -261
- package/dist/llm/openai-client.d.ts +1 -0
- package/dist/llm/openai-client.js +7 -2
- package/dist/qa/checks.d.ts +10 -0
- package/dist/qa/checks.js +492 -0
- package/dist/qa/fixes.d.ts +30 -0
- package/dist/qa/fixes.js +277 -0
- package/dist/qa/index.d.ts +29 -0
- package/dist/qa/index.js +187 -0
- package/dist/qa/types.d.ts +24 -0
- package/dist/qa/types.js +1 -0
- package/dist/scanner/csharp.d.ts +23 -0
- package/dist/scanner/csharp.js +421 -0
- package/dist/scanner/index.js +16 -2
- package/dist/scanner/java.d.ts +39 -0
- package/dist/scanner/java.js +318 -0
- package/dist/scanner/kotlin.d.ts +23 -0
- package/dist/scanner/kotlin.js +389 -0
- package/dist/scanner/php.d.ts +57 -0
- package/dist/scanner/php.js +351 -0
- package/dist/scanner/ruby.d.ts +36 -0
- package/dist/scanner/ruby.js +431 -0
- package/dist/scanner/swift.d.ts +25 -0
- package/dist/scanner/swift.js +392 -0
- package/dist/scanner/types.d.ts +1 -1
- package/dist/template/content/docs/_navigation.json +46 -0
- package/dist/template/content/docs/_sidebars.json +684 -0
- package/dist/template/content/docs/core.md +4544 -0
- package/dist/template/content/docs/index.mdx +89 -0
- package/dist/template/content/docs/integrations.md +1158 -0
- package/dist/template/content/docs/llms-full.md +403 -0
- package/dist/template/content/docs/llms.txt +4588 -0
- package/dist/template/content/docs/other.md +10379 -0
- package/dist/template/content/docs/tools.md +746 -0
- package/dist/template/content/docs/types.md +531 -0
- package/dist/template/docs.json +13 -11
- package/dist/template/mdx-components.tsx +27 -2
- package/dist/template/package.json +6 -0
- package/dist/template/public/search-index.json +1 -1
- package/dist/template/scripts/build-search-index.mjs +84 -6
- package/dist/template/src/app/api/chat/route.ts +83 -128
- package/dist/template/src/app/docs/[...slug]/page.tsx +75 -20
- package/dist/template/src/app/docs/llms-full.md +151 -4
- package/dist/template/src/app/docs/llms.txt +2464 -847
- package/dist/template/src/app/docs/page.mdx +48 -38
- package/dist/template/src/app/layout.tsx +3 -1
- package/dist/template/src/app/page.tsx +22 -8
- package/dist/template/src/components/ai-chat.tsx +73 -64
- package/dist/template/src/components/breadcrumbs.tsx +21 -23
- package/dist/template/src/components/copy-button.tsx +13 -9
- package/dist/template/src/components/copy-page-button.tsx +54 -0
- package/dist/template/src/components/docs-layout.tsx +37 -25
- package/dist/template/src/components/header.tsx +51 -10
- package/dist/template/src/components/mdx/card.tsx +17 -3
- package/dist/template/src/components/mdx/code-block.tsx +13 -9
- package/dist/template/src/components/mdx/code-group.tsx +13 -8
- package/dist/template/src/components/mdx/heading.tsx +15 -2
- package/dist/template/src/components/mdx/highlighted-code.tsx +13 -8
- package/dist/template/src/components/mdx/index.tsx +2 -0
- package/dist/template/src/components/mdx/mermaid.tsx +110 -0
- package/dist/template/src/components/mdx/screenshot.tsx +150 -0
- package/dist/template/src/components/scroll-to-hash.tsx +48 -0
- package/dist/template/src/components/sidebar.tsx +12 -18
- package/dist/template/src/components/table-of-contents.tsx +9 -0
- package/dist/template/src/lib/highlight.ts +3 -88
- package/dist/template/src/lib/navigation.ts +159 -0
- package/dist/template/src/styles/globals.css +17 -6
- package/dist/utils/validation.d.ts +0 -3
- package/dist/utils/validation.js +0 -26
- package/package.json +3 -2
|
@@ -7,97 +7,103 @@ import { keychainAvailable, getKeychainPlatformName } from '../auth/keychain.js'
|
|
|
7
7
|
export const securityCommand = new Command('security')
|
|
8
8
|
.description('Show security and key storage details')
|
|
9
9
|
.action(async () => {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
else if (method === 'keychain') {
|
|
21
|
-
console.log(` ✓ API key stored in ${getKeychainPlatformName()}`);
|
|
22
|
-
console.log(' Hardware-backed encryption (Secure Enclave on macOS)');
|
|
23
|
-
}
|
|
24
|
-
else if (method === 'file') {
|
|
25
|
-
console.log(` ● API key stored in ${authFile}`);
|
|
26
|
-
console.log(' File permissions: 0600 (owner read/write only)');
|
|
27
|
-
if (!hasKeychain) {
|
|
28
|
-
console.log(' \x1b[33mTip: Install @napi-rs/keyring for OS keychain storage\x1b[0m');
|
|
10
|
+
try {
|
|
11
|
+
console.log('skrypt security\n');
|
|
12
|
+
const configDir = join(homedir(), '.skrypt');
|
|
13
|
+
const authFile = join(configDir, 'auth.json');
|
|
14
|
+
// 1. Key Storage
|
|
15
|
+
console.log(' \x1b[1mKey Storage\x1b[0m');
|
|
16
|
+
const method = await getKeyStorageMethod();
|
|
17
|
+
const hasKeychain = await keychainAvailable();
|
|
18
|
+
if (method === 'env') {
|
|
19
|
+
console.log(' ✓ Using SKRYPT_API_KEY environment variable');
|
|
29
20
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
ANTHROPIC_API_KEY: !!process.env.ANTHROPIC_API_KEY,
|
|
41
|
-
GOOGLE_API_KEY: !!process.env.GOOGLE_API_KEY,
|
|
42
|
-
DEEPSEEK_API_KEY: !!process.env.DEEPSEEK_API_KEY,
|
|
43
|
-
};
|
|
44
|
-
const hasBYOK = Object.values(envKeys).some(Boolean);
|
|
45
|
-
if (hasBYOK) {
|
|
46
|
-
const providers = Object.entries(envKeys)
|
|
47
|
-
.filter(([, v]) => v)
|
|
48
|
-
.map(([k]) => k.replace('_API_KEY', '').toLowerCase());
|
|
49
|
-
console.log(` ✓ BYOK mode: LLM calls go directly to ${providers.join(', ')}`);
|
|
50
|
-
console.log(' Your keys never touch Skrypt servers');
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
const config = await getAuthConfigAsync();
|
|
54
|
-
if (config.apiKey) {
|
|
55
|
-
console.log(' ● Proxy mode: LLM calls routed through Skrypt API');
|
|
56
|
-
console.log(' Your Skrypt key authenticates requests');
|
|
21
|
+
else if (method === 'keychain') {
|
|
22
|
+
console.log(` ✓ API key stored in ${getKeychainPlatformName()}`);
|
|
23
|
+
console.log(' Hardware-backed encryption (Secure Enclave on macOS)');
|
|
24
|
+
}
|
|
25
|
+
else if (method === 'file') {
|
|
26
|
+
console.log(` ● API key stored in ${authFile}`);
|
|
27
|
+
console.log(' File permissions: 0600 (owner read/write only)');
|
|
28
|
+
if (!hasKeychain) {
|
|
29
|
+
console.log(' \x1b[33mTip: Install @napi-rs/keyring for OS keychain storage\x1b[0m');
|
|
30
|
+
}
|
|
57
31
|
}
|
|
58
32
|
else {
|
|
59
|
-
console.log(' ○ No
|
|
33
|
+
console.log(' ○ No API key configured');
|
|
34
|
+
console.log(' Run: skrypt login');
|
|
60
35
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
console.log(`
|
|
36
|
+
console.log('');
|
|
37
|
+
// 2. Data Flow
|
|
38
|
+
console.log(' \x1b[1mData Flow\x1b[0m');
|
|
39
|
+
const envKeys = {
|
|
40
|
+
OPENAI_API_KEY: !!process.env.OPENAI_API_KEY,
|
|
41
|
+
ANTHROPIC_API_KEY: !!process.env.ANTHROPIC_API_KEY,
|
|
42
|
+
GOOGLE_API_KEY: !!process.env.GOOGLE_API_KEY,
|
|
43
|
+
DEEPSEEK_API_KEY: !!process.env.DEEPSEEK_API_KEY,
|
|
44
|
+
};
|
|
45
|
+
const hasBYOK = Object.values(envKeys).some(Boolean);
|
|
46
|
+
if (hasBYOK) {
|
|
47
|
+
const providers = Object.entries(envKeys)
|
|
48
|
+
.filter(([, v]) => v)
|
|
49
|
+
.map(([k]) => k.replace('_API_KEY', '').toLowerCase());
|
|
50
|
+
console.log(` ✓ BYOK mode: LLM calls go directly to ${providers.join(', ')}`);
|
|
51
|
+
console.log(' Your keys never touch Skrypt servers');
|
|
76
52
|
}
|
|
77
|
-
|
|
78
|
-
|
|
53
|
+
else {
|
|
54
|
+
const config = await getAuthConfigAsync();
|
|
55
|
+
if (config.apiKey) {
|
|
56
|
+
console.log(' ● Proxy mode: LLM calls routed through Skrypt API');
|
|
57
|
+
console.log(' Your Skrypt key authenticates requests');
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
console.log(' ○ No provider keys or Skrypt key configured');
|
|
61
|
+
}
|
|
79
62
|
}
|
|
80
|
-
|
|
63
|
+
console.log('');
|
|
64
|
+
// 3. Server-Side Security
|
|
65
|
+
console.log(' \x1b[1mServer-Side Security\x1b[0m');
|
|
66
|
+
console.log(' • API keys hashed with SHA-256 — never stored in plaintext');
|
|
67
|
+
console.log(' • Encrypted at rest with AES-256 via AWS KMS');
|
|
68
|
+
console.log(' • TLS 1.3 for all API communication');
|
|
69
|
+
console.log('');
|
|
70
|
+
// 4. Permissions
|
|
71
|
+
console.log(' \x1b[1mPermissions\x1b[0m');
|
|
72
|
+
if (existsSync(configDir)) {
|
|
81
73
|
try {
|
|
82
|
-
const
|
|
83
|
-
const
|
|
84
|
-
console.log(` ~/.skrypt/
|
|
74
|
+
const dirStat = statSync(configDir);
|
|
75
|
+
const dirMode = (dirStat.mode & 0o777).toString(8);
|
|
76
|
+
console.log(` ~/.skrypt/ ${dirMode} (${dirMode === '700' ? '✓' : '⚠ expected 700'})`);
|
|
85
77
|
}
|
|
86
78
|
catch {
|
|
87
|
-
console.log(' ~/.skrypt/
|
|
79
|
+
console.log(' ~/.skrypt/ unable to read');
|
|
88
80
|
}
|
|
81
|
+
if (existsSync(authFile)) {
|
|
82
|
+
try {
|
|
83
|
+
const fileStat = statSync(authFile);
|
|
84
|
+
const fileMode = (fileStat.mode & 0o777).toString(8);
|
|
85
|
+
console.log(` ~/.skrypt/auth.json ${fileMode} (${fileMode === '600' ? '✓' : '⚠ expected 600'})`);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
console.log(' ~/.skrypt/auth.json unable to read');
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
console.log(' ~/.skrypt/ does not exist yet');
|
|
89
94
|
}
|
|
95
|
+
console.log('');
|
|
96
|
+
// 5. Environment
|
|
97
|
+
console.log(' \x1b[1mEnvironment\x1b[0m');
|
|
98
|
+
console.log(` Platform: ${process.platform}`);
|
|
99
|
+
console.log(` OS keychain: ${hasKeychain ? '✓ available' : '✗ not available'}`);
|
|
100
|
+
console.log(` SKRYPT_API_KEY: ${process.env.SKRYPT_API_KEY ? '✓ set' : '○ not set'}`);
|
|
101
|
+
console.log(` OPENAI_API_KEY: ${process.env.OPENAI_API_KEY ? '✓ set' : '○ not set'}`);
|
|
102
|
+
console.log(` ANTHROPIC_API_KEY: ${process.env.ANTHROPIC_API_KEY ? '✓ set' : '○ not set'}`);
|
|
103
|
+
console.log('');
|
|
90
104
|
}
|
|
91
|
-
|
|
92
|
-
console.
|
|
105
|
+
catch (err) {
|
|
106
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
107
|
+
process.exit(1);
|
|
93
108
|
}
|
|
94
|
-
console.log('');
|
|
95
|
-
// 5. Environment
|
|
96
|
-
console.log(' \x1b[1mEnvironment\x1b[0m');
|
|
97
|
-
console.log(` Platform: ${process.platform}`);
|
|
98
|
-
console.log(` OS keychain: ${hasKeychain ? '✓ available' : '✗ not available'}`);
|
|
99
|
-
console.log(` SKRYPT_API_KEY: ${process.env.SKRYPT_API_KEY ? '✓ set' : '○ not set'}`);
|
|
100
|
-
console.log(` OPENAI_API_KEY: ${process.env.OPENAI_API_KEY ? '✓ set' : '○ not set'}`);
|
|
101
|
-
console.log(` ANTHROPIC_API_KEY: ${process.env.ANTHROPIC_API_KEY ? '✓ set' : '○ not set'}`);
|
|
102
|
-
console.log('');
|
|
103
109
|
});
|
package/dist/commands/test.js
CHANGED
|
@@ -2,7 +2,7 @@ import { Command } from 'commander';
|
|
|
2
2
|
import { existsSync, readFileSync, readdirSync, statSync } from 'fs';
|
|
3
3
|
import { resolve, join, extname, relative } from 'path';
|
|
4
4
|
import { spawn } from 'child_process';
|
|
5
|
-
import { writeFileSync,
|
|
5
|
+
import { writeFileSync, mkdirSync, rmSync } from 'fs';
|
|
6
6
|
import { tmpdir } from 'os';
|
|
7
7
|
import { randomUUID } from 'crypto';
|
|
8
8
|
import { requirePro } from '../auth/index.js';
|
|
@@ -123,17 +123,10 @@ async function runCodeBlock(block, timeoutMs) {
|
|
|
123
123
|
finally {
|
|
124
124
|
// Cleanup temp directory
|
|
125
125
|
try {
|
|
126
|
-
|
|
127
|
-
for (const file of files) {
|
|
128
|
-
unlinkSync(join(tempDir, file));
|
|
129
|
-
}
|
|
130
|
-
readdirSync(tempDir); // Verify empty
|
|
131
|
-
// Remove the directory
|
|
132
|
-
const { rmdirSync } = await import('fs');
|
|
133
|
-
rmdirSync(tempDir);
|
|
126
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
134
127
|
}
|
|
135
128
|
catch {
|
|
136
|
-
// Ignore cleanup errors
|
|
129
|
+
// Ignore cleanup errors — OS will clean tmpdir
|
|
137
130
|
}
|
|
138
131
|
}
|
|
139
132
|
}
|
|
@@ -241,95 +234,101 @@ export const testCommand = new Command('test')
|
|
|
241
234
|
.option('-t, --timeout <ms>', 'Timeout per code block in milliseconds', '10000')
|
|
242
235
|
.option('-v, --verbose', 'Show detailed output')
|
|
243
236
|
.action(async (path, options) => {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
const targetPath = resolve(options.file || path);
|
|
249
|
-
if (!existsSync(targetPath)) {
|
|
250
|
-
console.error(`Error: Path not found: ${targetPath}`);
|
|
251
|
-
process.exit(1);
|
|
252
|
-
}
|
|
253
|
-
const timeoutMs = parseInt(options.timeout);
|
|
254
|
-
if (isNaN(timeoutMs) || timeoutMs <= 0) {
|
|
255
|
-
console.error(`Error: Invalid timeout value: ${options.timeout}`);
|
|
256
|
-
process.exit(1);
|
|
257
|
-
}
|
|
258
|
-
console.log('skrypt test');
|
|
259
|
-
console.log(` path: ${targetPath}`);
|
|
260
|
-
if (options.language) {
|
|
261
|
-
console.log(` language: ${options.language}`);
|
|
262
|
-
}
|
|
263
|
-
console.log(` timeout: ${timeoutMs}ms`);
|
|
264
|
-
console.log('');
|
|
265
|
-
// Find all doc files
|
|
266
|
-
const files = statSync(targetPath).isDirectory()
|
|
267
|
-
? findDocFiles(targetPath)
|
|
268
|
-
: [targetPath];
|
|
269
|
-
if (files.length === 0) {
|
|
270
|
-
console.log('No .md or .mdx files found.');
|
|
271
|
-
process.exit(0);
|
|
272
|
-
}
|
|
273
|
-
// Extract all code blocks
|
|
274
|
-
const allBlocks = [];
|
|
275
|
-
for (const file of files) {
|
|
276
|
-
const blocks = extractCodeBlocks(file, options.language);
|
|
277
|
-
allBlocks.push(...blocks);
|
|
278
|
-
}
|
|
279
|
-
if (allBlocks.length === 0) {
|
|
280
|
-
console.log('No testable code blocks found.');
|
|
281
|
-
if (options.language) {
|
|
282
|
-
console.log(` (filtered by language: ${options.language})`);
|
|
237
|
+
try {
|
|
238
|
+
// Pro feature - requires subscription
|
|
239
|
+
if (!await requirePro('test')) {
|
|
240
|
+
process.exit(1);
|
|
283
241
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
console.log('');
|
|
289
|
-
console.log('Running tests...\n');
|
|
290
|
-
const results = [];
|
|
291
|
-
const failedBlocks = [];
|
|
292
|
-
const startTime = Date.now();
|
|
293
|
-
for (let i = 0; i < allBlocks.length; i++) {
|
|
294
|
-
const block = allBlocks[i];
|
|
295
|
-
if (!block)
|
|
296
|
-
continue;
|
|
297
|
-
const result = await runCodeBlock(block, timeoutMs);
|
|
298
|
-
results.push(result);
|
|
299
|
-
printResult(result, targetPath, options.verbose || false);
|
|
300
|
-
if (!result.passed) {
|
|
301
|
-
failedBlocks.push(block);
|
|
242
|
+
const targetPath = resolve(options.file || path);
|
|
243
|
+
if (!existsSync(targetPath)) {
|
|
244
|
+
console.error(`Error: Path not found: ${targetPath}`);
|
|
245
|
+
process.exit(1);
|
|
302
246
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
if (options.fix && failedBlocks.length > 0) {
|
|
315
|
-
console.log('');
|
|
316
|
-
console.log('Attempting to auto-fix failing examples...');
|
|
317
|
-
console.log(' Run: skrypt autofix <file> for each failing file');
|
|
247
|
+
const timeoutMs = parseInt(options.timeout);
|
|
248
|
+
if (isNaN(timeoutMs) || timeoutMs <= 0) {
|
|
249
|
+
console.error(`Error: Invalid timeout value: ${options.timeout}`);
|
|
250
|
+
process.exit(1);
|
|
251
|
+
}
|
|
252
|
+
console.log('skrypt test');
|
|
253
|
+
console.log(` path: ${targetPath}`);
|
|
254
|
+
if (options.language) {
|
|
255
|
+
console.log(` language: ${options.language}`);
|
|
256
|
+
}
|
|
257
|
+
console.log(` timeout: ${timeoutMs}ms`);
|
|
318
258
|
console.log('');
|
|
319
|
-
//
|
|
320
|
-
const
|
|
321
|
-
|
|
322
|
-
|
|
259
|
+
// Find all doc files
|
|
260
|
+
const files = statSync(targetPath).isDirectory()
|
|
261
|
+
? findDocFiles(targetPath)
|
|
262
|
+
: [targetPath];
|
|
263
|
+
if (files.length === 0) {
|
|
264
|
+
console.log('No .md or .mdx files found.');
|
|
265
|
+
process.exit(0);
|
|
266
|
+
}
|
|
267
|
+
// Extract all code blocks
|
|
268
|
+
const allBlocks = [];
|
|
269
|
+
for (const file of files) {
|
|
270
|
+
const blocks = extractCodeBlocks(file, options.language);
|
|
271
|
+
allBlocks.push(...blocks);
|
|
272
|
+
}
|
|
273
|
+
if (allBlocks.length === 0) {
|
|
274
|
+
console.log('No testable code blocks found.');
|
|
275
|
+
if (options.language) {
|
|
276
|
+
console.log(` (filtered by language: ${options.language})`);
|
|
277
|
+
}
|
|
278
|
+
console.log(` Supported languages: ${SUPPORTED_LANGUAGES.join(', ')}`);
|
|
279
|
+
process.exit(0);
|
|
323
280
|
}
|
|
281
|
+
console.log(`Found ${allBlocks.length} code block(s) in ${files.length} file(s)`);
|
|
324
282
|
console.log('');
|
|
325
|
-
console.log('
|
|
326
|
-
|
|
327
|
-
|
|
283
|
+
console.log('Running tests...\n');
|
|
284
|
+
const results = [];
|
|
285
|
+
const failedBlocks = [];
|
|
286
|
+
const startTime = Date.now();
|
|
287
|
+
for (let i = 0; i < allBlocks.length; i++) {
|
|
288
|
+
const block = allBlocks[i];
|
|
289
|
+
if (!block)
|
|
290
|
+
continue;
|
|
291
|
+
const result = await runCodeBlock(block, timeoutMs);
|
|
292
|
+
results.push(result);
|
|
293
|
+
printResult(result, targetPath, options.verbose || false);
|
|
294
|
+
if (!result.passed) {
|
|
295
|
+
failedBlocks.push(block);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
const totalDuration = Date.now() - startTime;
|
|
299
|
+
const summary = {
|
|
300
|
+
total: allBlocks.length,
|
|
301
|
+
passed: results.filter(r => r.passed).length,
|
|
302
|
+
failed: results.filter(r => !r.passed).length,
|
|
303
|
+
skipped: 0,
|
|
304
|
+
duration: totalDuration,
|
|
305
|
+
};
|
|
306
|
+
printSummary(summary);
|
|
307
|
+
// Handle --fix flag
|
|
308
|
+
if (options.fix && failedBlocks.length > 0) {
|
|
309
|
+
console.log('');
|
|
310
|
+
console.log('Attempting to auto-fix failing examples...');
|
|
311
|
+
console.log(' Run: skrypt autofix <file> for each failing file');
|
|
312
|
+
console.log('');
|
|
313
|
+
// Get unique files with failures
|
|
314
|
+
const failedFiles = [...new Set(failedBlocks.map(b => b.file))];
|
|
315
|
+
for (const file of failedFiles) {
|
|
316
|
+
console.log(` → ${relative(targetPath, file)}`);
|
|
317
|
+
}
|
|
318
|
+
console.log('');
|
|
319
|
+
console.log('To auto-fix, run:');
|
|
320
|
+
for (const file of failedFiles) {
|
|
321
|
+
console.log(` skrypt autofix "${file}"`);
|
|
322
|
+
}
|
|
328
323
|
}
|
|
324
|
+
// Exit with error code if tests failed
|
|
325
|
+
if (summary.failed > 0) {
|
|
326
|
+
process.exit(1);
|
|
327
|
+
}
|
|
328
|
+
console.log('\nAll tests passed!');
|
|
329
329
|
}
|
|
330
|
-
|
|
331
|
-
|
|
330
|
+
catch (err) {
|
|
331
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
332
332
|
process.exit(1);
|
|
333
333
|
}
|
|
334
|
-
console.log('\nAll tests passed!');
|
|
335
334
|
});
|
package/dist/commands/version.js
CHANGED
|
@@ -5,7 +5,12 @@ const CONFIG_FILE = 'skrypt.versions.json';
|
|
|
5
5
|
function loadVersionConfig(docsPath) {
|
|
6
6
|
const configPath = join(docsPath, CONFIG_FILE);
|
|
7
7
|
if (existsSync(configPath)) {
|
|
8
|
-
|
|
8
|
+
try {
|
|
9
|
+
return JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return { versions: [], current: '' };
|
|
13
|
+
}
|
|
9
14
|
}
|
|
10
15
|
return { versions: [], current: '' };
|
|
11
16
|
}
|
|
@@ -20,16 +25,22 @@ versionCommand
|
|
|
20
25
|
.description('List all versions')
|
|
21
26
|
.argument('[docs-path]', 'Documentation directory', './docs')
|
|
22
27
|
.action((docsPath) => {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
try {
|
|
29
|
+
const config = loadVersionConfig(resolve(docsPath));
|
|
30
|
+
if (config.versions.length === 0) {
|
|
31
|
+
console.log('No versions configured.');
|
|
32
|
+
console.log('Run: skrypt version create <version>');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
console.log('Versions:');
|
|
36
|
+
for (const v of config.versions) {
|
|
37
|
+
const marker = v === config.current ? ' (current)' : '';
|
|
38
|
+
console.log(` ${v}${marker}`);
|
|
39
|
+
}
|
|
28
40
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
console.log(` ${v}${marker}`);
|
|
41
|
+
catch (err) {
|
|
42
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
43
|
+
process.exit(1);
|
|
33
44
|
}
|
|
34
45
|
});
|
|
35
46
|
versionCommand
|
|
@@ -39,49 +50,55 @@ versionCommand
|
|
|
39
50
|
.argument('[docs-path]', 'Documentation directory', './docs')
|
|
40
51
|
.option('--from <version>', 'Copy from existing version instead of current')
|
|
41
52
|
.action((version, docsPath, options) => {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
const versionDir = join(resolvedPath, 'versions', version);
|
|
49
|
-
if (existsSync(versionDir)) {
|
|
50
|
-
console.error(`Directory already exists: ${versionDir}`);
|
|
51
|
-
process.exit(1);
|
|
52
|
-
}
|
|
53
|
-
// Determine source
|
|
54
|
-
let sourceDir;
|
|
55
|
-
if (options.from) {
|
|
56
|
-
sourceDir = join(resolvedPath, 'versions', options.from);
|
|
57
|
-
if (!existsSync(sourceDir)) {
|
|
58
|
-
console.error(`Source version not found: ${options.from}`);
|
|
53
|
+
try {
|
|
54
|
+
const resolvedPath = resolve(docsPath);
|
|
55
|
+
const config = loadVersionConfig(resolvedPath);
|
|
56
|
+
if (config.versions.includes(version)) {
|
|
57
|
+
console.error(`Version ${version} already exists.`);
|
|
59
58
|
process.exit(1);
|
|
60
59
|
}
|
|
60
|
+
const versionDir = join(resolvedPath, 'versions', version);
|
|
61
|
+
if (existsSync(versionDir)) {
|
|
62
|
+
console.error(`Directory already exists: ${versionDir}`);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
// Determine source
|
|
66
|
+
let sourceDir;
|
|
67
|
+
if (options.from) {
|
|
68
|
+
sourceDir = join(resolvedPath, 'versions', options.from);
|
|
69
|
+
if (!existsSync(sourceDir)) {
|
|
70
|
+
console.error(`Source version not found: ${options.from}`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
// Copy current docs (excluding versions folder)
|
|
76
|
+
sourceDir = resolvedPath;
|
|
77
|
+
}
|
|
78
|
+
// Create version directory
|
|
79
|
+
mkdirSync(versionDir, { recursive: true });
|
|
80
|
+
// Copy files
|
|
81
|
+
const entries = readdirSync(sourceDir, { withFileTypes: true });
|
|
82
|
+
for (const entry of entries) {
|
|
83
|
+
if (entry.name === 'versions' || entry.name === CONFIG_FILE)
|
|
84
|
+
continue;
|
|
85
|
+
const srcPath = join(sourceDir, entry.name);
|
|
86
|
+
const destPath = join(versionDir, entry.name);
|
|
87
|
+
cpSync(srcPath, destPath, { recursive: true });
|
|
88
|
+
}
|
|
89
|
+
// Update config
|
|
90
|
+
config.versions.push(version);
|
|
91
|
+
if (!config.current) {
|
|
92
|
+
config.current = version;
|
|
93
|
+
}
|
|
94
|
+
saveVersionConfig(resolvedPath, config);
|
|
95
|
+
console.log(`Created version: ${version}`);
|
|
96
|
+
console.log(` Location: ${versionDir}`);
|
|
61
97
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
// Create version directory
|
|
67
|
-
mkdirSync(versionDir, { recursive: true });
|
|
68
|
-
// Copy files
|
|
69
|
-
const entries = readdirSync(sourceDir, { withFileTypes: true });
|
|
70
|
-
for (const entry of entries) {
|
|
71
|
-
if (entry.name === 'versions' || entry.name === CONFIG_FILE)
|
|
72
|
-
continue;
|
|
73
|
-
const srcPath = join(sourceDir, entry.name);
|
|
74
|
-
const destPath = join(versionDir, entry.name);
|
|
75
|
-
cpSync(srcPath, destPath, { recursive: true });
|
|
76
|
-
}
|
|
77
|
-
// Update config
|
|
78
|
-
config.versions.push(version);
|
|
79
|
-
if (!config.current) {
|
|
80
|
-
config.current = version;
|
|
98
|
+
catch (err) {
|
|
99
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
100
|
+
process.exit(1);
|
|
81
101
|
}
|
|
82
|
-
saveVersionConfig(resolvedPath, config);
|
|
83
|
-
console.log(`Created version: ${version}`);
|
|
84
|
-
console.log(` Location: ${versionDir}`);
|
|
85
102
|
});
|
|
86
103
|
versionCommand
|
|
87
104
|
.command('set-current')
|
|
@@ -89,16 +106,22 @@ versionCommand
|
|
|
89
106
|
.argument('<version>', 'Version to set as current')
|
|
90
107
|
.argument('[docs-path]', 'Documentation directory', './docs')
|
|
91
108
|
.action((version, docsPath) => {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
109
|
+
try {
|
|
110
|
+
const resolvedPath = resolve(docsPath);
|
|
111
|
+
const config = loadVersionConfig(resolvedPath);
|
|
112
|
+
if (!config.versions.includes(version)) {
|
|
113
|
+
console.error(`Version ${version} not found.`);
|
|
114
|
+
console.log('Available versions:', config.versions.join(', '));
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
config.current = version;
|
|
118
|
+
saveVersionConfig(resolvedPath, config);
|
|
119
|
+
console.log(`Set current version to: ${version}`);
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
97
123
|
process.exit(1);
|
|
98
124
|
}
|
|
99
|
-
config.current = version;
|
|
100
|
-
saveVersionConfig(resolvedPath, config);
|
|
101
|
-
console.log(`Set current version to: ${version}`);
|
|
102
125
|
});
|
|
103
126
|
versionCommand
|
|
104
127
|
.command('delete')
|
|
@@ -107,25 +130,31 @@ versionCommand
|
|
|
107
130
|
.argument('[docs-path]', 'Documentation directory', './docs')
|
|
108
131
|
.option('--force', 'Skip confirmation')
|
|
109
132
|
.action((version, docsPath, options) => {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
133
|
+
try {
|
|
134
|
+
const resolvedPath = resolve(docsPath);
|
|
135
|
+
const config = loadVersionConfig(resolvedPath);
|
|
136
|
+
if (!config.versions.includes(version)) {
|
|
137
|
+
console.error(`Version ${version} not found.`);
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
if (version === config.current && !options.force) {
|
|
141
|
+
console.error(`Cannot delete current version. Use --force or set a different current version first.`);
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
const versionDir = join(resolvedPath, 'versions', version);
|
|
145
|
+
// Remove from config
|
|
146
|
+
config.versions = config.versions.filter(v => v !== version);
|
|
147
|
+
if (config.current === version) {
|
|
148
|
+
config.current = config.versions[0] || '';
|
|
149
|
+
}
|
|
150
|
+
saveVersionConfig(resolvedPath, config);
|
|
151
|
+
// Note: Not actually deleting files for safety
|
|
152
|
+
console.log(`Removed version ${version} from config.`);
|
|
153
|
+
console.log(`Files still exist at: ${versionDir}`);
|
|
154
|
+
console.log(`To fully delete, run: rm -rf "${versionDir}"`);
|
|
115
155
|
}
|
|
116
|
-
|
|
117
|
-
console.error(`
|
|
156
|
+
catch (err) {
|
|
157
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
118
158
|
process.exit(1);
|
|
119
159
|
}
|
|
120
|
-
const versionDir = join(resolvedPath, 'versions', version);
|
|
121
|
-
// Remove from config
|
|
122
|
-
config.versions = config.versions.filter(v => v !== version);
|
|
123
|
-
if (config.current === version) {
|
|
124
|
-
config.current = config.versions[0] || '';
|
|
125
|
-
}
|
|
126
|
-
saveVersionConfig(resolvedPath, config);
|
|
127
|
-
// Note: Not actually deleting files for safety
|
|
128
|
-
console.log(`Removed version ${version} from config.`);
|
|
129
|
-
console.log(`Files still exist at: ${versionDir}`);
|
|
130
|
-
console.log(`To fully delete, run: rm -rf "${versionDir}"`);
|
|
131
160
|
});
|