skrypt-ai 0.5.0 → 0.6.1
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 +114 -103
- 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
package/dist/commands/mcp.js
CHANGED
|
@@ -229,6 +229,12 @@ export const mcpCommand = new Command('mcp')
|
|
|
229
229
|
// Get specific doc
|
|
230
230
|
if (url.pathname.startsWith('/doc/')) {
|
|
231
231
|
const docPath = decodeURIComponent(url.pathname.slice(5));
|
|
232
|
+
// Prevent path traversal
|
|
233
|
+
if (docPath.includes('..') || docPath.startsWith('/')) {
|
|
234
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
235
|
+
res.end(JSON.stringify({ error: 'Invalid path' }));
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
232
238
|
const doc = docs.find(d => d.path === docPath || d.path === docPath + '.mdx' || d.path === docPath + '.md');
|
|
233
239
|
if (doc) {
|
|
234
240
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
package/dist/commands/monitor.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import {
|
|
2
|
+
import { spawnSync } from 'child_process';
|
|
3
3
|
import { existsSync, writeFileSync, readdirSync, statSync, mkdirSync } from 'fs';
|
|
4
4
|
import { resolve, join, dirname } from 'path';
|
|
5
5
|
import { createLLMClient } from '../llm/index.js';
|
|
@@ -275,16 +275,21 @@ export const monitorCommand = new Command('monitor')
|
|
|
275
275
|
console.log('\n=== Creating GitHub PR ===\n');
|
|
276
276
|
try {
|
|
277
277
|
// Check if gh CLI is available
|
|
278
|
-
|
|
279
|
-
// Create branch
|
|
278
|
+
spawnSync('gh', ['--version'], { stdio: 'ignore' });
|
|
279
|
+
// Create branch (use spawnSync to avoid shell injection)
|
|
280
280
|
const branchName = `docs/auto-update-${Date.now()}`;
|
|
281
|
-
|
|
281
|
+
spawnSync('git', ['checkout', '-b', branchName], { cwd: resolvedPath, stdio: 'inherit' });
|
|
282
282
|
// Stage and commit
|
|
283
|
-
|
|
284
|
-
|
|
283
|
+
spawnSync('git', ['add', 'docs/'], { cwd: resolvedPath, stdio: 'inherit' });
|
|
284
|
+
spawnSync('git', ['commit', '-m', 'docs: auto-update documentation for recent changes'], { cwd: resolvedPath, stdio: 'inherit' });
|
|
285
285
|
// Push and create PR
|
|
286
|
-
|
|
287
|
-
const
|
|
286
|
+
spawnSync('git', ['push', '-u', 'origin', branchName], { cwd: resolvedPath, stdio: 'inherit' });
|
|
287
|
+
const changesList = suggestions.map(s => `- ${s.action}: ${s.file}`).join('\n');
|
|
288
|
+
const prBody = `This PR was automatically generated by Skrypt monitor.\n\n## Changes\n${changesList}`;
|
|
289
|
+
const prResult = spawnSync('gh', ['pr', 'create', '--title', 'docs: Auto-update documentation', '--body', prBody], {
|
|
290
|
+
cwd: resolvedPath, encoding: 'utf-8'
|
|
291
|
+
});
|
|
292
|
+
const prUrl = (prResult.stdout || '').trim();
|
|
288
293
|
console.log(`PR created: ${prUrl}`);
|
|
289
294
|
}
|
|
290
295
|
catch (err) {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { resolve } from 'path';
|
|
3
|
+
import { existsSync } from 'fs';
|
|
4
|
+
import { runQA, printQAReport, fixQAIssues, printFixReport } from '../qa/index.js';
|
|
5
|
+
export const qaCommand = new Command('qa')
|
|
6
|
+
.description('Run quality assurance checks on documentation')
|
|
7
|
+
.argument('<dir>', 'Documentation directory to check')
|
|
8
|
+
.option('--strict', 'Fail on warnings (not just errors)')
|
|
9
|
+
.option('--fix', 'Auto-fix safe issues before reporting')
|
|
10
|
+
.action((dir, options) => {
|
|
11
|
+
const targetDir = resolve(dir);
|
|
12
|
+
if (!existsSync(targetDir)) {
|
|
13
|
+
console.error(`Error: Directory not found: ${targetDir}`);
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
console.log('skrypt qa');
|
|
17
|
+
console.log(` target: ${targetDir}`);
|
|
18
|
+
// Auto-fix if requested
|
|
19
|
+
if (options.fix) {
|
|
20
|
+
const fixReport = fixQAIssues(targetDir);
|
|
21
|
+
printFixReport(fixReport);
|
|
22
|
+
}
|
|
23
|
+
const report = runQA(targetDir);
|
|
24
|
+
printQAReport(report);
|
|
25
|
+
// Show check-by-check summary
|
|
26
|
+
console.log('\n Checks:');
|
|
27
|
+
for (const check of report.checks) {
|
|
28
|
+
const icon = check.passed ? '✓' : '✗';
|
|
29
|
+
const count = check.issues.length;
|
|
30
|
+
const label = count > 0 ? ` (${count} issue${count > 1 ? 's' : ''})` : '';
|
|
31
|
+
console.log(` ${icon} ${check.check}${label}`);
|
|
32
|
+
}
|
|
33
|
+
console.log('');
|
|
34
|
+
if (!report.passed) {
|
|
35
|
+
console.log('QA failed — fix errors before deploying.\n');
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
if (options.strict && report.warnings > 0) {
|
|
39
|
+
console.log('QA failed (strict mode) — warnings present.\n');
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
console.log('QA passed.\n');
|
|
43
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import { analyzePRForDocs, postPRComment, postInlineComments } from '../github/pr-comments.js';
|
|
3
|
+
import { requirePro } from '../auth/index.js';
|
|
3
4
|
export const reviewPRCommand = new Command('review-pr')
|
|
4
5
|
.description('Review a GitHub PR for documentation issues')
|
|
5
6
|
.argument('<pr-url>', 'GitHub PR URL (e.g., https://github.com/owner/repo/pull/123)')
|
|
@@ -7,121 +8,131 @@ export const reviewPRCommand = new Command('review-pr')
|
|
|
7
8
|
.option('--dry-run', 'Show issues without posting comments')
|
|
8
9
|
.option('--token <token>', 'GitHub token (or use GITHUB_TOKEN env var)')
|
|
9
10
|
.action(async (prUrl, options) => {
|
|
10
|
-
// Parse PR URL
|
|
11
|
-
const urlMatch = prUrl.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/);
|
|
12
|
-
if (!urlMatch) {
|
|
13
|
-
console.error('Error: Invalid PR URL format');
|
|
14
|
-
console.error('Expected: https://github.com/owner/repo/pull/123');
|
|
15
|
-
process.exit(1);
|
|
16
|
-
}
|
|
17
|
-
const owner = urlMatch[1];
|
|
18
|
-
const repo = urlMatch[2];
|
|
19
|
-
const pullNumber = urlMatch[3];
|
|
20
|
-
if (!owner || !repo || !pullNumber) {
|
|
21
|
-
console.error('Error: Could not parse owner/repo/PR number from URL');
|
|
22
|
-
process.exit(1);
|
|
23
|
-
}
|
|
24
|
-
// Validate owner/repo contain only safe characters (prevent API path injection)
|
|
25
|
-
const safeNamePattern = /^[a-zA-Z0-9._-]+$/;
|
|
26
|
-
if (!safeNamePattern.test(owner) || !safeNamePattern.test(repo)) {
|
|
27
|
-
console.error('Error: Invalid characters in owner/repo name');
|
|
28
|
-
process.exit(1);
|
|
29
|
-
}
|
|
30
|
-
console.log('skrypt review-pr');
|
|
31
|
-
console.log(` repo: ${owner}/${repo}`);
|
|
32
|
-
console.log(` PR: #${pullNumber}`);
|
|
33
|
-
console.log('');
|
|
34
|
-
if (options.token) {
|
|
35
|
-
console.warn(' Warning: Passing tokens via CLI arguments exposes them in shell history.');
|
|
36
|
-
console.warn(' Prefer: GITHUB_TOKEN env var\n');
|
|
37
|
-
}
|
|
38
|
-
const token = options.token || process.env.GITHUB_TOKEN;
|
|
39
|
-
if (!token) {
|
|
40
|
-
console.error('Error: GITHUB_TOKEN environment variable or --token required');
|
|
41
|
-
process.exit(1);
|
|
42
|
-
}
|
|
43
|
-
// Analyze the PR
|
|
44
|
-
console.log('Analyzing PR for documentation issues...');
|
|
45
|
-
const config = {
|
|
46
|
-
owner,
|
|
47
|
-
repo,
|
|
48
|
-
pullNumber: parseInt(pullNumber),
|
|
49
|
-
token,
|
|
50
|
-
};
|
|
51
|
-
let issues;
|
|
52
11
|
try {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
57
|
-
console.error(`Error analyzing PR: ${message}`);
|
|
58
|
-
process.exit(1);
|
|
59
|
-
}
|
|
60
|
-
if (issues.length === 0) {
|
|
61
|
-
console.log('\n✓ No documentation issues found!');
|
|
62
|
-
process.exit(0);
|
|
63
|
-
}
|
|
64
|
-
console.log(`\nFound ${issues.length} issue(s):`);
|
|
65
|
-
console.log('');
|
|
66
|
-
// Group by severity
|
|
67
|
-
const errors = issues.filter(i => i.severity === 'error');
|
|
68
|
-
const warnings = issues.filter(i => i.severity === 'warning');
|
|
69
|
-
const infos = issues.filter(i => i.severity === 'info');
|
|
70
|
-
if (errors.length > 0) {
|
|
71
|
-
console.log(`❌ Errors (${errors.length}):`);
|
|
72
|
-
for (const issue of errors) {
|
|
73
|
-
console.log(` ${issue.file}: ${issue.message}`);
|
|
12
|
+
// Pro feature - requires subscription
|
|
13
|
+
if (!await requirePro('review-pr')) {
|
|
14
|
+
process.exit(1);
|
|
74
15
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
console.
|
|
16
|
+
// Parse PR URL
|
|
17
|
+
const urlMatch = prUrl.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/);
|
|
18
|
+
if (!urlMatch) {
|
|
19
|
+
console.error('Error: Invalid PR URL format');
|
|
20
|
+
console.error('Expected: https://github.com/owner/repo/pull/123');
|
|
21
|
+
process.exit(1);
|
|
80
22
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
console.
|
|
23
|
+
const owner = urlMatch[1];
|
|
24
|
+
const repo = urlMatch[2];
|
|
25
|
+
const pullNumber = urlMatch[3];
|
|
26
|
+
if (!owner || !repo || !pullNumber) {
|
|
27
|
+
console.error('Error: Could not parse owner/repo/PR number from URL');
|
|
28
|
+
process.exit(1);
|
|
86
29
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if (
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
30
|
+
// Validate owner/repo contain only safe characters (prevent API path injection)
|
|
31
|
+
const safeNamePattern = /^[a-zA-Z0-9._-]+$/;
|
|
32
|
+
if (!safeNamePattern.test(owner) || !safeNamePattern.test(repo)) {
|
|
33
|
+
console.error('Error: Invalid characters in owner/repo name');
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
console.log('skrypt review-pr');
|
|
37
|
+
console.log(` repo: ${owner}/${repo}`);
|
|
38
|
+
console.log(` PR: #${pullNumber}`);
|
|
39
|
+
console.log('');
|
|
40
|
+
if (options.token) {
|
|
41
|
+
console.warn(' Warning: Passing tokens via CLI arguments exposes them in shell history.');
|
|
42
|
+
console.warn(' Prefer: GITHUB_TOKEN env var\n');
|
|
43
|
+
}
|
|
44
|
+
const token = options.token || process.env.GITHUB_TOKEN;
|
|
45
|
+
if (!token) {
|
|
46
|
+
console.error('Error: GITHUB_TOKEN environment variable or --token required');
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
// Analyze the PR
|
|
50
|
+
console.log('Analyzing PR for documentation issues...');
|
|
51
|
+
const config = {
|
|
52
|
+
owner,
|
|
53
|
+
repo,
|
|
54
|
+
pullNumber: parseInt(pullNumber),
|
|
55
|
+
token,
|
|
56
|
+
};
|
|
57
|
+
let issues;
|
|
58
|
+
try {
|
|
59
|
+
issues = await analyzePRForDocs(config);
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
63
|
+
console.error(`Error analyzing PR: ${message}`);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
if (issues.length === 0) {
|
|
67
|
+
console.log('\n✓ No documentation issues found!');
|
|
68
|
+
process.exit(0);
|
|
69
|
+
}
|
|
70
|
+
console.log(`\nFound ${issues.length} issue(s):`);
|
|
71
|
+
console.log('');
|
|
72
|
+
// Group by severity
|
|
73
|
+
const errors = issues.filter(i => i.severity === 'error');
|
|
74
|
+
const warnings = issues.filter(i => i.severity === 'warning');
|
|
75
|
+
const infos = issues.filter(i => i.severity === 'info');
|
|
76
|
+
if (errors.length > 0) {
|
|
77
|
+
console.log(`❌ Errors (${errors.length}):`);
|
|
78
|
+
for (const issue of errors) {
|
|
79
|
+
console.log(` ${issue.file}: ${issue.message}`);
|
|
106
80
|
}
|
|
107
|
-
|
|
108
|
-
|
|
81
|
+
}
|
|
82
|
+
if (warnings.length > 0) {
|
|
83
|
+
console.log(`⚠️ Warnings (${warnings.length}):`);
|
|
84
|
+
for (const issue of warnings) {
|
|
85
|
+
console.log(` ${issue.file}: ${issue.message}`);
|
|
109
86
|
}
|
|
110
87
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
88
|
+
if (infos.length > 0) {
|
|
89
|
+
console.log(`ℹ️ Suggestions (${infos.length}):`);
|
|
90
|
+
for (const issue of infos) {
|
|
91
|
+
console.log(` ${issue.file}: ${issue.message}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (options.dryRun) {
|
|
95
|
+
console.log('\n[dry run - no comments posted]');
|
|
96
|
+
process.exit(0);
|
|
97
|
+
}
|
|
98
|
+
// Post comments
|
|
99
|
+
console.log('\nPosting comments to PR...');
|
|
100
|
+
if (options.inline) {
|
|
101
|
+
const inlineIssues = issues.filter(i => i.line);
|
|
102
|
+
const generalIssues = issues.filter(i => !i.line);
|
|
103
|
+
if (inlineIssues.length > 0) {
|
|
104
|
+
const inlineResults = await postInlineComments(config, inlineIssues);
|
|
105
|
+
const success = inlineResults.filter(r => r.success).length;
|
|
106
|
+
console.log(` Posted ${success}/${inlineIssues.length} inline comments`);
|
|
107
|
+
}
|
|
108
|
+
if (generalIssues.length > 0) {
|
|
109
|
+
const result = await postPRComment(config, generalIssues);
|
|
110
|
+
if (result.success) {
|
|
111
|
+
console.log(` Posted summary comment`);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
console.error(` Failed to post summary: ${result.error}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
116
117
|
}
|
|
117
118
|
else {
|
|
118
|
-
|
|
119
|
+
const result = await postPRComment(config, issues);
|
|
120
|
+
if (result.success) {
|
|
121
|
+
console.log(`✓ Comment posted successfully`);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
console.error(`✗ Failed to post comment: ${result.error}`);
|
|
125
|
+
process.exit(1);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
console.log('\nDone!');
|
|
129
|
+
// Exit with error code if there are errors
|
|
130
|
+
if (errors.length > 0) {
|
|
119
131
|
process.exit(1);
|
|
120
132
|
}
|
|
121
133
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (errors.length > 0) {
|
|
134
|
+
catch (err) {
|
|
135
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
125
136
|
process.exit(1);
|
|
126
137
|
}
|
|
127
138
|
});
|
package/dist/commands/sdk.js
CHANGED
|
@@ -223,115 +223,116 @@ export const sdkCommand = new Command('sdk')
|
|
|
223
223
|
.option('--model <name>', 'LLM model')
|
|
224
224
|
.option('--format <type>', 'Output format (markdown or json)', 'markdown')
|
|
225
225
|
.action(async (specPath, options) => {
|
|
226
|
-
// Pro feature - requires subscription
|
|
227
|
-
if (!await requirePro('sdk')) {
|
|
228
|
-
process.exit(1);
|
|
229
|
-
}
|
|
230
|
-
const resolvedSpec = resolve(specPath);
|
|
231
|
-
if (!existsSync(resolvedSpec)) {
|
|
232
|
-
console.error(`Error: Spec file not found: ${resolvedSpec}`);
|
|
233
|
-
process.exit(1);
|
|
234
|
-
}
|
|
235
|
-
console.log('skrypt sdk');
|
|
236
|
-
console.log(` spec: ${resolvedSpec}`);
|
|
237
|
-
console.log(` output: ${options.output}`);
|
|
238
|
-
console.log(` languages: ${options.languages}`);
|
|
239
|
-
console.log('');
|
|
240
|
-
// Parse spec
|
|
241
|
-
console.log('Step 1: Parsing OpenAPI spec...');
|
|
242
|
-
const specContent = readFileSync(resolvedSpec, 'utf-8');
|
|
243
|
-
let spec;
|
|
244
226
|
try {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
console.error('Error: Failed to parse spec file:', err);
|
|
249
|
-
process.exit(1);
|
|
250
|
-
}
|
|
251
|
-
const apiTitle = spec.info?.title || basename(specPath, '.json');
|
|
252
|
-
const apiVersion = spec.info?.version || '1.0.0';
|
|
253
|
-
console.log(` API: ${apiTitle} v${apiVersion}`);
|
|
254
|
-
if (!spec.paths || Object.keys(spec.paths).length === 0) {
|
|
255
|
-
console.error('Error: No paths found in spec');
|
|
256
|
-
process.exit(1);
|
|
257
|
-
}
|
|
258
|
-
// Parse languages
|
|
259
|
-
const languages = (options.languages || 'typescript,python,curl')
|
|
260
|
-
.split(',')
|
|
261
|
-
.map(l => l.trim().toLowerCase())
|
|
262
|
-
.filter(l => SUPPORTED_LANGUAGES.includes(l));
|
|
263
|
-
if (languages.length === 0) {
|
|
264
|
-
console.error(`Error: No valid languages. Supported: ${SUPPORTED_LANGUAGES.join(', ')}`);
|
|
265
|
-
process.exit(1);
|
|
266
|
-
}
|
|
267
|
-
console.log(` Generating for: ${languages.join(', ')}`);
|
|
268
|
-
// Check if we should use AI for enhanced samples
|
|
269
|
-
let client = null;
|
|
270
|
-
if (options.provider) {
|
|
271
|
-
const config = loadConfig();
|
|
272
|
-
config.llm.provider = options.provider;
|
|
273
|
-
const { ok } = checkApiKey(config.llm.provider);
|
|
274
|
-
if (ok) {
|
|
275
|
-
client = createLLMClient({
|
|
276
|
-
provider: config.llm.provider,
|
|
277
|
-
model: options.model || config.llm.model
|
|
278
|
-
});
|
|
279
|
-
console.log(` Using AI enhancement: ${config.llm.provider}`);
|
|
227
|
+
// Pro feature - requires subscription
|
|
228
|
+
if (!await requirePro('sdk')) {
|
|
229
|
+
process.exit(1);
|
|
280
230
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
231
|
+
const resolvedSpec = resolve(specPath);
|
|
232
|
+
if (!existsSync(resolvedSpec)) {
|
|
233
|
+
console.error(`Error: Spec file not found: ${resolvedSpec}`);
|
|
234
|
+
process.exit(1);
|
|
235
|
+
}
|
|
236
|
+
console.log('skrypt sdk');
|
|
237
|
+
console.log(` spec: ${resolvedSpec}`);
|
|
238
|
+
console.log(` output: ${options.output}`);
|
|
239
|
+
console.log(` languages: ${options.languages}`);
|
|
240
|
+
console.log('');
|
|
241
|
+
// Parse spec
|
|
242
|
+
console.log('Step 1: Parsing OpenAPI spec...');
|
|
243
|
+
const specContent = readFileSync(resolvedSpec, 'utf-8');
|
|
244
|
+
let spec;
|
|
245
|
+
try {
|
|
246
|
+
spec = await parseOpenAPISpec(specContent);
|
|
247
|
+
}
|
|
248
|
+
catch (err) {
|
|
249
|
+
console.error('Error: Failed to parse spec file:', err);
|
|
250
|
+
process.exit(1);
|
|
251
|
+
}
|
|
252
|
+
const apiTitle = spec.info?.title || basename(specPath, '.json');
|
|
253
|
+
const apiVersion = spec.info?.version || '1.0.0';
|
|
254
|
+
console.log(` API: ${apiTitle} v${apiVersion}`);
|
|
255
|
+
if (!spec.paths || Object.keys(spec.paths).length === 0) {
|
|
256
|
+
console.error('Error: No paths found in spec');
|
|
257
|
+
process.exit(1);
|
|
258
|
+
}
|
|
259
|
+
// Parse languages
|
|
260
|
+
const languages = (options.languages || 'typescript,python,curl')
|
|
261
|
+
.split(',')
|
|
262
|
+
.map(l => l.trim().toLowerCase())
|
|
263
|
+
.filter(l => SUPPORTED_LANGUAGES.includes(l));
|
|
264
|
+
if (languages.length === 0) {
|
|
265
|
+
console.error(`Error: No valid languages. Supported: ${SUPPORTED_LANGUAGES.join(', ')}`);
|
|
266
|
+
process.exit(1);
|
|
267
|
+
}
|
|
268
|
+
console.log(` Generating for: ${languages.join(', ')}`);
|
|
269
|
+
// Check if we should use AI for enhanced samples
|
|
270
|
+
let client = null;
|
|
271
|
+
if (options.provider) {
|
|
272
|
+
const config = loadConfig();
|
|
273
|
+
config.llm.provider = options.provider;
|
|
274
|
+
const { ok } = checkApiKey(config.llm.provider);
|
|
275
|
+
if (ok) {
|
|
276
|
+
client = createLLMClient({
|
|
277
|
+
provider: config.llm.provider,
|
|
278
|
+
model: options.model || config.llm.model
|
|
279
|
+
});
|
|
280
|
+
console.log(` Using AI enhancement: ${config.llm.provider}`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
// Generate samples for each endpoint
|
|
284
|
+
console.log('\nStep 2: Generating code samples...');
|
|
285
|
+
const allSamples = [];
|
|
286
|
+
for (const [path, methods] of Object.entries(spec.paths || {})) {
|
|
287
|
+
for (const [method, details] of Object.entries(methods || {})) {
|
|
288
|
+
if (!['get', 'post', 'put', 'patch', 'delete'].includes(method))
|
|
289
|
+
continue;
|
|
290
|
+
const operationId = details.operationId || `${method}${path.replace(/\//g, '_').replace(/[{}]/g, '')}`;
|
|
291
|
+
const summary = details.summary || details.description || '';
|
|
292
|
+
console.log(` ${method.toUpperCase()} ${path}`);
|
|
293
|
+
const params = (details.parameters || []).map(p => ({
|
|
294
|
+
name: p.name,
|
|
295
|
+
type: p.schema?.type,
|
|
296
|
+
required: p.required
|
|
297
|
+
}));
|
|
298
|
+
const hasBody = !!details.requestBody;
|
|
299
|
+
const bodySchema = details.requestBody?.content?.['application/json']?.schema;
|
|
300
|
+
const samples = [];
|
|
301
|
+
for (const lang of languages) {
|
|
302
|
+
let code;
|
|
303
|
+
if (client) {
|
|
304
|
+
code = await generateAISample({ path, method, operationId, summary, params, hasBody, bodySchema }, lang, client);
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
code = generateBasicSample({
|
|
308
|
+
path,
|
|
309
|
+
method,
|
|
310
|
+
operationId,
|
|
311
|
+
params: params.map(p => p.name),
|
|
312
|
+
hasBody
|
|
313
|
+
}, lang, '');
|
|
314
|
+
}
|
|
315
|
+
samples.push({ language: lang, code });
|
|
313
316
|
}
|
|
314
|
-
|
|
317
|
+
allSamples.push({ path, method, operationId, summary, samples });
|
|
315
318
|
}
|
|
316
|
-
allSamples.push({ path, method, operationId, summary, samples });
|
|
317
319
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
let content = `---
|
|
320
|
+
// Write output
|
|
321
|
+
console.log('\nStep 3: Writing output...');
|
|
322
|
+
const outputDir = resolve(options.output || './docs/sdk');
|
|
323
|
+
mkdirSync(outputDir, { recursive: true });
|
|
324
|
+
if (options.format === 'json') {
|
|
325
|
+
// JSON output
|
|
326
|
+
const jsonPath = join(outputDir, 'sdk-samples.json');
|
|
327
|
+
writeFileSync(jsonPath, JSON.stringify(allSamples, null, 2));
|
|
328
|
+
console.log(` Written: ${jsonPath}`);
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
// Markdown output - one file per endpoint
|
|
332
|
+
for (const endpoint of allSamples) {
|
|
333
|
+
const fileName = `${endpoint.operationId}.mdx`;
|
|
334
|
+
const filePath = join(outputDir, fileName);
|
|
335
|
+
let content = `---
|
|
335
336
|
title: ${endpoint.operationId}
|
|
336
337
|
description: ${endpoint.summary || `${endpoint.method.toUpperCase()} ${endpoint.path}`}
|
|
337
338
|
---
|
|
@@ -345,21 +346,21 @@ ${endpoint.summary}
|
|
|
345
346
|
## Code Examples
|
|
346
347
|
|
|
347
348
|
`;
|
|
348
|
-
|
|
349
|
-
|
|
349
|
+
for (const sample of endpoint.samples) {
|
|
350
|
+
content += `### ${sample.language.charAt(0).toUpperCase() + sample.language.slice(1)}
|
|
350
351
|
|
|
351
352
|
\`\`\`${sample.language}
|
|
352
353
|
${sample.code}
|
|
353
354
|
\`\`\`
|
|
354
355
|
|
|
355
356
|
`;
|
|
357
|
+
}
|
|
358
|
+
writeFileSync(filePath, content);
|
|
359
|
+
console.log(` Written: ${filePath}`);
|
|
356
360
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
// Write index file
|
|
361
|
-
const indexPath = join(outputDir, 'index.mdx');
|
|
362
|
-
let indexContent = `---
|
|
361
|
+
// Write index file
|
|
362
|
+
const indexPath = join(outputDir, 'index.mdx');
|
|
363
|
+
let indexContent = `---
|
|
363
364
|
title: SDK Code Samples
|
|
364
365
|
description: Code samples for ${apiTitle} API
|
|
365
366
|
---
|
|
@@ -371,16 +372,21 @@ Auto-generated code samples for the ${apiTitle} API v${apiVersion}.
|
|
|
371
372
|
## Endpoints
|
|
372
373
|
|
|
373
374
|
`;
|
|
374
|
-
|
|
375
|
-
|
|
375
|
+
for (const endpoint of allSamples) {
|
|
376
|
+
indexContent += `- [${endpoint.operationId}](./${endpoint.operationId}) - \`${endpoint.method.toUpperCase()} ${endpoint.path}\`\n`;
|
|
377
|
+
}
|
|
378
|
+
writeFileSync(indexPath, indexContent);
|
|
379
|
+
console.log(` Written: ${indexPath}`);
|
|
376
380
|
}
|
|
377
|
-
|
|
378
|
-
console.log(
|
|
381
|
+
// Summary
|
|
382
|
+
console.log('\n=== Summary ===');
|
|
383
|
+
console.log(` Endpoints: ${allSamples.length}`);
|
|
384
|
+
console.log(` Languages: ${languages.length}`);
|
|
385
|
+
console.log(` Total samples: ${allSamples.length * languages.length}`);
|
|
386
|
+
console.log(` Output: ${outputDir}`);
|
|
387
|
+
}
|
|
388
|
+
catch (err) {
|
|
389
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
390
|
+
process.exit(1);
|
|
379
391
|
}
|
|
380
|
-
// Summary
|
|
381
|
-
console.log('\n=== Summary ===');
|
|
382
|
-
console.log(` Endpoints: ${allSamples.length}`);
|
|
383
|
-
console.log(` Languages: ${languages.length}`);
|
|
384
|
-
console.log(` Total samples: ${allSamples.length * languages.length}`);
|
|
385
|
-
console.log(` Output: ${outputDir}`);
|
|
386
392
|
});
|