skrypt-ai 0.4.2 → 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.d.ts +13 -3
- package/dist/auth/index.js +101 -9
- package/dist/auth/keychain.d.ts +5 -0
- package/dist/auth/keychain.js +82 -0
- package/dist/auth/notices.d.ts +3 -0
- package/dist/auth/notices.js +42 -0
- package/dist/autofix/index.d.ts +0 -4
- package/dist/autofix/index.js +10 -24
- 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 +20 -3
- 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 +125 -7
- 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/import.d.ts +2 -0
- package/dist/commands/import.js +157 -0
- package/dist/commands/init.js +19 -7
- package/dist/commands/lint.js +50 -44
- package/dist/commands/llms-txt.js +59 -49
- package/dist/commands/login.js +63 -34
- 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 -92
- package/dist/commands/sdk.js +128 -122
- package/dist/commands/security.d.ts +2 -0
- package/dist/commands/security.js +109 -0
- 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 +36 -7
- package/dist/importers/confluence.d.ts +5 -0
- package/dist/importers/confluence.js +137 -0
- package/dist/importers/detect.d.ts +20 -0
- package/dist/importers/detect.js +121 -0
- package/dist/importers/docusaurus.d.ts +5 -0
- package/dist/importers/docusaurus.js +279 -0
- package/dist/importers/gitbook.d.ts +5 -0
- package/dist/importers/gitbook.js +189 -0
- package/dist/importers/github.d.ts +8 -0
- package/dist/importers/github.js +99 -0
- package/dist/importers/index.d.ts +15 -0
- package/dist/importers/index.js +30 -0
- package/dist/importers/markdown.d.ts +6 -0
- package/dist/importers/markdown.js +105 -0
- package/dist/importers/mintlify.d.ts +5 -0
- package/dist/importers/mintlify.js +172 -0
- package/dist/importers/notion.d.ts +5 -0
- package/dist/importers/notion.js +174 -0
- package/dist/importers/readme.d.ts +5 -0
- package/dist/importers/readme.js +184 -0
- package/dist/importers/transform.d.ts +90 -0
- package/dist/importers/transform.js +457 -0
- package/dist/importers/types.d.ts +37 -0
- package/dist/importers/types.js +1 -0
- 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/plugins/index.js +7 -0
- 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 +53 -26
- 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/python.js +17 -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 +149 -13
- 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/lib/search-types.ts +4 -1
- package/dist/template/src/lib/search.ts +30 -7
- package/dist/template/src/styles/globals.css +17 -6
- package/dist/utils/files.d.ts +9 -1
- package/dist/utils/files.js +59 -10
- package/dist/utils/validation.d.ts +0 -3
- package/dist/utils/validation.js +0 -26
- package/package.json +5 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import { existsSync, readFileSync, writeFileSync, readdirSync, statSync } from 'fs';
|
|
3
|
-
import { resolve, join, extname } from 'path';
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync, readdirSync, statSync, mkdirSync } from 'fs';
|
|
3
|
+
import { resolve, join, extname, dirname } from 'path';
|
|
4
4
|
function extractFrontmatter(content) {
|
|
5
5
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
6
6
|
if (!match)
|
|
@@ -136,54 +136,64 @@ export const llmsTxtCommand = new Command('llms-txt')
|
|
|
136
136
|
.option('--title <title>', 'Documentation title', 'Documentation')
|
|
137
137
|
.option('--description <desc>', 'Documentation description', 'API and usage documentation')
|
|
138
138
|
.action(async (docsPath, options) => {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
139
|
+
try {
|
|
140
|
+
const resolvedDocs = resolve(docsPath);
|
|
141
|
+
const outputDir = resolve(options.output || './public');
|
|
142
|
+
console.log('skrypt llms-txt');
|
|
143
|
+
console.log(` docs: ${resolvedDocs}`);
|
|
144
|
+
console.log(` output: ${outputDir}`);
|
|
145
|
+
console.log(` base-url: ${options.baseUrl}`);
|
|
146
|
+
console.log('');
|
|
147
|
+
// Check if docs directory exists
|
|
148
|
+
if (!existsSync(resolvedDocs)) {
|
|
149
|
+
console.error(`Error: Documentation directory not found: ${resolvedDocs}`);
|
|
150
|
+
process.exit(1);
|
|
151
|
+
}
|
|
152
|
+
// Scan documentation
|
|
153
|
+
console.log('Step 1: Scanning documentation...');
|
|
154
|
+
const docs = walkDocs(resolvedDocs);
|
|
155
|
+
if (docs.length === 0) {
|
|
156
|
+
console.error('Error: No documentation files found');
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
console.log(` Found ${docs.length} documentation files`);
|
|
160
|
+
// Ensure output directory exists
|
|
161
|
+
mkdirSync(outputDir, { recursive: true });
|
|
162
|
+
// Generate llms.txt (summary)
|
|
163
|
+
console.log('\nStep 2: Generating llms.txt...');
|
|
164
|
+
const llmsTxt = generateLLMsTxt(docs, {
|
|
165
|
+
title: options.title || 'Documentation',
|
|
166
|
+
description: options.description || 'API and usage documentation',
|
|
167
|
+
baseUrl: options.baseUrl || 'https://docs.example.com'
|
|
168
|
+
});
|
|
169
|
+
const llmsTxtPath = join(outputDir, 'llms.txt');
|
|
170
|
+
mkdirSync(dirname(llmsTxtPath), { recursive: true });
|
|
171
|
+
writeFileSync(llmsTxtPath, llmsTxt);
|
|
172
|
+
console.log(` Written: ${llmsTxtPath}`);
|
|
173
|
+
// Generate llms-full.txt (complete content)
|
|
174
|
+
console.log('\nStep 3: Generating llms-full.txt...');
|
|
175
|
+
const llmsFullTxt = generateLLMsFullTxt(docs, {
|
|
176
|
+
title: options.title || 'Documentation',
|
|
177
|
+
description: options.description || 'API and usage documentation',
|
|
178
|
+
baseUrl: options.baseUrl || 'https://docs.example.com'
|
|
179
|
+
});
|
|
180
|
+
const llmsFullTxtPath = join(outputDir, 'llms-full.txt');
|
|
181
|
+
mkdirSync(dirname(llmsFullTxtPath), { recursive: true });
|
|
182
|
+
writeFileSync(llmsFullTxtPath, llmsFullTxt);
|
|
183
|
+
console.log(` Written: ${llmsFullTxtPath}`);
|
|
184
|
+
// Summary
|
|
185
|
+
console.log('\n=== Summary ===');
|
|
186
|
+
console.log(` Documentation files: ${docs.length}`);
|
|
187
|
+
console.log(` llms.txt: ${(llmsTxt.length / 1024).toFixed(1)} KB`);
|
|
188
|
+
console.log(` llms-full.txt: ${(llmsFullTxt.length / 1024).toFixed(1)} KB`);
|
|
189
|
+
console.log('');
|
|
190
|
+
console.log('Add these to your robots.txt:');
|
|
191
|
+
console.log(' Sitemap: /llms.txt');
|
|
192
|
+
console.log('');
|
|
193
|
+
console.log('AI agents (GPTBot, ClaudeBot, etc.) will automatically discover your docs!');
|
|
150
194
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
const docs = walkDocs(resolvedDocs);
|
|
154
|
-
if (docs.length === 0) {
|
|
155
|
-
console.error('Error: No documentation files found');
|
|
195
|
+
catch (err) {
|
|
196
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
156
197
|
process.exit(1);
|
|
157
198
|
}
|
|
158
|
-
console.log(` Found ${docs.length} documentation files`);
|
|
159
|
-
// Generate llms.txt (summary)
|
|
160
|
-
console.log('\nStep 2: Generating llms.txt...');
|
|
161
|
-
const llmsTxt = generateLLMsTxt(docs, {
|
|
162
|
-
title: options.title || 'Documentation',
|
|
163
|
-
description: options.description || 'API and usage documentation',
|
|
164
|
-
baseUrl: options.baseUrl || 'https://docs.example.com'
|
|
165
|
-
});
|
|
166
|
-
const llmsTxtPath = join(outputDir, 'llms.txt');
|
|
167
|
-
writeFileSync(llmsTxtPath, llmsTxt);
|
|
168
|
-
console.log(` Written: ${llmsTxtPath}`);
|
|
169
|
-
// Generate llms-full.txt (complete content)
|
|
170
|
-
console.log('\nStep 3: Generating llms-full.txt...');
|
|
171
|
-
const llmsFullTxt = generateLLMsFullTxt(docs, {
|
|
172
|
-
title: options.title || 'Documentation',
|
|
173
|
-
description: options.description || 'API and usage documentation',
|
|
174
|
-
baseUrl: options.baseUrl || 'https://docs.example.com'
|
|
175
|
-
});
|
|
176
|
-
const llmsFullTxtPath = join(outputDir, 'llms-full.txt');
|
|
177
|
-
writeFileSync(llmsFullTxtPath, llmsFullTxt);
|
|
178
|
-
console.log(` Written: ${llmsFullTxtPath}`);
|
|
179
|
-
// Summary
|
|
180
|
-
console.log('\n=== Summary ===');
|
|
181
|
-
console.log(` Documentation files: ${docs.length}`);
|
|
182
|
-
console.log(` llms.txt: ${(llmsTxt.length / 1024).toFixed(1)} KB`);
|
|
183
|
-
console.log(` llms-full.txt: ${(llmsFullTxt.length / 1024).toFixed(1)} KB`);
|
|
184
|
-
console.log('');
|
|
185
|
-
console.log('Add these to your robots.txt:');
|
|
186
|
-
console.log(' Sitemap: /llms.txt');
|
|
187
|
-
console.log('');
|
|
188
|
-
console.log('AI agents (GPTBot, ClaudeBot, etc.) will automatically discover your docs!');
|
|
189
199
|
});
|
package/dist/commands/login.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import { saveAuthConfig,
|
|
2
|
+
import { saveAuthConfig, getAuthConfigAsync, clearAuth, checkPlan, getKeyStorageMethod } from '../auth/index.js';
|
|
3
|
+
import { getKeychainPlatformName } from '../auth/keychain.js';
|
|
3
4
|
import * as readline from 'readline';
|
|
4
5
|
function prompt(question) {
|
|
5
6
|
const rl = readline.createInterface({
|
|
@@ -17,49 +18,77 @@ export const loginCommand = new Command('login')
|
|
|
17
18
|
.description('Login to Skrypt Pro')
|
|
18
19
|
.option('--key <api-key>', 'API key (or set SKRYPT_API_KEY env var)')
|
|
19
20
|
.action(async (options) => {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
21
|
+
try {
|
|
22
|
+
console.log('skrypt login\n');
|
|
23
|
+
let apiKey = options.key || process.env.SKRYPT_API_KEY;
|
|
24
|
+
if (!apiKey) {
|
|
25
|
+
console.log(' Get your API key at: https://skrypt.sh/dashboard/settings\n');
|
|
26
|
+
apiKey = await prompt(' Enter API key: ');
|
|
27
|
+
}
|
|
28
|
+
if (!apiKey) {
|
|
29
|
+
console.error(' Error: No API key provided');
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
console.log('\n Verifying...');
|
|
33
|
+
const result = await checkPlan(apiKey);
|
|
34
|
+
if (!result.valid) {
|
|
35
|
+
console.error(`\n Error: ${result.error || 'Invalid API key'}`);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
await saveAuthConfig({
|
|
39
|
+
apiKey,
|
|
40
|
+
email: result.email,
|
|
41
|
+
plan: result.plan,
|
|
42
|
+
expiresAt: new Date(Date.now() + 60 * 60 * 1000).toISOString()
|
|
43
|
+
});
|
|
44
|
+
console.log(`\n ✓ Logged in as ${result.email}`);
|
|
45
|
+
console.log(` Plan: ${result.plan.toUpperCase()}\n`);
|
|
46
|
+
// Trust signals
|
|
47
|
+
const storageMethod = await getKeyStorageMethod();
|
|
48
|
+
if (storageMethod === 'keychain') {
|
|
49
|
+
console.log(` 🔒 API key stored in ${getKeychainPlatformName()}`);
|
|
50
|
+
}
|
|
51
|
+
else if (storageMethod === 'file') {
|
|
52
|
+
console.log(' 🔒 API key stored in ~/.skrypt/auth.json (0600 permissions)');
|
|
53
|
+
}
|
|
54
|
+
console.log(' Your key is hashed (SHA-256) on our servers — never stored in plaintext');
|
|
55
|
+
console.log('');
|
|
56
|
+
if (result.plan === 'free') {
|
|
57
|
+
console.log(' Upgrade to Pro: https://skrypt.sh/pro\n');
|
|
58
|
+
}
|
|
29
59
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if (!result.valid) {
|
|
33
|
-
console.error(`\n Error: ${result.error || 'Invalid API key'}`);
|
|
60
|
+
catch (err) {
|
|
61
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
34
62
|
process.exit(1);
|
|
35
63
|
}
|
|
36
|
-
saveAuthConfig({
|
|
37
|
-
apiKey,
|
|
38
|
-
email: result.email,
|
|
39
|
-
plan: result.plan,
|
|
40
|
-
expiresAt: new Date(Date.now() + 60 * 60 * 1000).toISOString()
|
|
41
|
-
});
|
|
42
|
-
console.log(`\n ✓ Logged in as ${result.email}`);
|
|
43
|
-
console.log(` Plan: ${result.plan.toUpperCase()}\n`);
|
|
44
|
-
if (result.plan === 'free') {
|
|
45
|
-
console.log(' Upgrade to Pro: https://skrypt.sh/pro\n');
|
|
46
|
-
}
|
|
47
64
|
});
|
|
48
65
|
export const logoutCommand = new Command('logout')
|
|
49
66
|
.description('Logout from Skrypt')
|
|
50
67
|
.action(async () => {
|
|
51
|
-
|
|
52
|
-
|
|
68
|
+
try {
|
|
69
|
+
await clearAuth();
|
|
70
|
+
console.log(' Logged out successfully\n');
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
53
76
|
});
|
|
54
77
|
export const whoamiCommand = new Command('whoami')
|
|
55
78
|
.description('Show current login status')
|
|
56
79
|
.action(async () => {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
80
|
+
try {
|
|
81
|
+
const config = await getAuthConfigAsync();
|
|
82
|
+
if (!config.apiKey) {
|
|
83
|
+
console.log(' Not logged in');
|
|
84
|
+
console.log(' Run: skrypt login\n');
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
console.log(` Email: ${config.email || 'Unknown'}`);
|
|
88
|
+
console.log(` Plan: ${(config.plan || 'free').toUpperCase()}\n`);
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
92
|
+
process.exit(1);
|
|
62
93
|
}
|
|
63
|
-
console.log(` Email: ${config.email || 'Unknown'}`);
|
|
64
|
-
console.log(` Plan: ${(config.plan || 'free').toUpperCase()}\n`);
|
|
65
94
|
});
|
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
|
+
});
|
|
@@ -7,111 +7,127 @@ export const reviewPRCommand = new Command('review-pr')
|
|
|
7
7
|
.option('--dry-run', 'Show issues without posting comments')
|
|
8
8
|
.option('--token <token>', 'GitHub token (or use GITHUB_TOKEN env var)')
|
|
9
9
|
.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
|
-
console.log('skrypt review-pr');
|
|
25
|
-
console.log(` repo: ${owner}/${repo}`);
|
|
26
|
-
console.log(` PR: #${pullNumber}`);
|
|
27
|
-
console.log('');
|
|
28
|
-
const token = options.token || process.env.GITHUB_TOKEN;
|
|
29
|
-
if (!token) {
|
|
30
|
-
console.error('Error: GITHUB_TOKEN environment variable or --token required');
|
|
31
|
-
process.exit(1);
|
|
32
|
-
}
|
|
33
|
-
// Analyze the PR
|
|
34
|
-
console.log('Analyzing PR for documentation issues...');
|
|
35
|
-
const config = {
|
|
36
|
-
owner,
|
|
37
|
-
repo,
|
|
38
|
-
pullNumber: parseInt(pullNumber),
|
|
39
|
-
token,
|
|
40
|
-
};
|
|
41
|
-
let issues;
|
|
42
10
|
try {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
if (issues.length === 0) {
|
|
51
|
-
console.log('\n✓ No documentation issues found!');
|
|
52
|
-
process.exit(0);
|
|
53
|
-
}
|
|
54
|
-
console.log(`\nFound ${issues.length} issue(s):`);
|
|
55
|
-
console.log('');
|
|
56
|
-
// Group by severity
|
|
57
|
-
const errors = issues.filter(i => i.severity === 'error');
|
|
58
|
-
const warnings = issues.filter(i => i.severity === 'warning');
|
|
59
|
-
const infos = issues.filter(i => i.severity === 'info');
|
|
60
|
-
if (errors.length > 0) {
|
|
61
|
-
console.log(`❌ Errors (${errors.length}):`);
|
|
62
|
-
for (const issue of errors) {
|
|
63
|
-
console.log(` ${issue.file}: ${issue.message}`);
|
|
11
|
+
// Parse PR URL
|
|
12
|
+
const urlMatch = prUrl.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/);
|
|
13
|
+
if (!urlMatch) {
|
|
14
|
+
console.error('Error: Invalid PR URL format');
|
|
15
|
+
console.error('Expected: https://github.com/owner/repo/pull/123');
|
|
16
|
+
process.exit(1);
|
|
64
17
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
console.
|
|
18
|
+
const owner = urlMatch[1];
|
|
19
|
+
const repo = urlMatch[2];
|
|
20
|
+
const pullNumber = urlMatch[3];
|
|
21
|
+
if (!owner || !repo || !pullNumber) {
|
|
22
|
+
console.error('Error: Could not parse owner/repo/PR number from URL');
|
|
23
|
+
process.exit(1);
|
|
70
24
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
25
|
+
// Validate owner/repo contain only safe characters (prevent API path injection)
|
|
26
|
+
const safeNamePattern = /^[a-zA-Z0-9._-]+$/;
|
|
27
|
+
if (!safeNamePattern.test(owner) || !safeNamePattern.test(repo)) {
|
|
28
|
+
console.error('Error: Invalid characters in owner/repo name');
|
|
29
|
+
process.exit(1);
|
|
76
30
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
console.log(
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if (options.inline) {
|
|
85
|
-
const inlineIssues = issues.filter(i => i.line);
|
|
86
|
-
const generalIssues = issues.filter(i => !i.line);
|
|
87
|
-
if (inlineIssues.length > 0) {
|
|
88
|
-
const inlineResults = await postInlineComments(config, inlineIssues);
|
|
89
|
-
const success = inlineResults.filter(r => r.success).length;
|
|
90
|
-
console.log(` Posted ${success}/${inlineIssues.length} inline comments`);
|
|
31
|
+
console.log('skrypt review-pr');
|
|
32
|
+
console.log(` repo: ${owner}/${repo}`);
|
|
33
|
+
console.log(` PR: #${pullNumber}`);
|
|
34
|
+
console.log('');
|
|
35
|
+
if (options.token) {
|
|
36
|
+
console.warn(' Warning: Passing tokens via CLI arguments exposes them in shell history.');
|
|
37
|
+
console.warn(' Prefer: GITHUB_TOKEN env var\n');
|
|
91
38
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
39
|
+
const token = options.token || process.env.GITHUB_TOKEN;
|
|
40
|
+
if (!token) {
|
|
41
|
+
console.error('Error: GITHUB_TOKEN environment variable or --token required');
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
// Analyze the PR
|
|
45
|
+
console.log('Analyzing PR for documentation issues...');
|
|
46
|
+
const config = {
|
|
47
|
+
owner,
|
|
48
|
+
repo,
|
|
49
|
+
pullNumber: parseInt(pullNumber),
|
|
50
|
+
token,
|
|
51
|
+
};
|
|
52
|
+
let issues;
|
|
53
|
+
try {
|
|
54
|
+
issues = await analyzePRForDocs(config);
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
58
|
+
console.error(`Error analyzing PR: ${message}`);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
if (issues.length === 0) {
|
|
62
|
+
console.log('\n✓ No documentation issues found!');
|
|
63
|
+
process.exit(0);
|
|
64
|
+
}
|
|
65
|
+
console.log(`\nFound ${issues.length} issue(s):`);
|
|
66
|
+
console.log('');
|
|
67
|
+
// Group by severity
|
|
68
|
+
const errors = issues.filter(i => i.severity === 'error');
|
|
69
|
+
const warnings = issues.filter(i => i.severity === 'warning');
|
|
70
|
+
const infos = issues.filter(i => i.severity === 'info');
|
|
71
|
+
if (errors.length > 0) {
|
|
72
|
+
console.log(`❌ Errors (${errors.length}):`);
|
|
73
|
+
for (const issue of errors) {
|
|
74
|
+
console.log(` ${issue.file}: ${issue.message}`);
|
|
96
75
|
}
|
|
97
|
-
|
|
98
|
-
|
|
76
|
+
}
|
|
77
|
+
if (warnings.length > 0) {
|
|
78
|
+
console.log(`⚠️ Warnings (${warnings.length}):`);
|
|
79
|
+
for (const issue of warnings) {
|
|
80
|
+
console.log(` ${issue.file}: ${issue.message}`);
|
|
99
81
|
}
|
|
100
82
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
83
|
+
if (infos.length > 0) {
|
|
84
|
+
console.log(`ℹ️ Suggestions (${infos.length}):`);
|
|
85
|
+
for (const issue of infos) {
|
|
86
|
+
console.log(` ${issue.file}: ${issue.message}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (options.dryRun) {
|
|
90
|
+
console.log('\n[dry run - no comments posted]');
|
|
91
|
+
process.exit(0);
|
|
92
|
+
}
|
|
93
|
+
// Post comments
|
|
94
|
+
console.log('\nPosting comments to PR...');
|
|
95
|
+
if (options.inline) {
|
|
96
|
+
const inlineIssues = issues.filter(i => i.line);
|
|
97
|
+
const generalIssues = issues.filter(i => !i.line);
|
|
98
|
+
if (inlineIssues.length > 0) {
|
|
99
|
+
const inlineResults = await postInlineComments(config, inlineIssues);
|
|
100
|
+
const success = inlineResults.filter(r => r.success).length;
|
|
101
|
+
console.log(` Posted ${success}/${inlineIssues.length} inline comments`);
|
|
102
|
+
}
|
|
103
|
+
if (generalIssues.length > 0) {
|
|
104
|
+
const result = await postPRComment(config, generalIssues);
|
|
105
|
+
if (result.success) {
|
|
106
|
+
console.log(` Posted summary comment`);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
console.error(` Failed to post summary: ${result.error}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
106
112
|
}
|
|
107
113
|
else {
|
|
108
|
-
|
|
114
|
+
const result = await postPRComment(config, issues);
|
|
115
|
+
if (result.success) {
|
|
116
|
+
console.log(`✓ Comment posted successfully`);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
console.error(`✗ Failed to post comment: ${result.error}`);
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
console.log('\nDone!');
|
|
124
|
+
// Exit with error code if there are errors
|
|
125
|
+
if (errors.length > 0) {
|
|
109
126
|
process.exit(1);
|
|
110
127
|
}
|
|
111
128
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
if (errors.length > 0) {
|
|
129
|
+
catch (err) {
|
|
130
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
115
131
|
process.exit(1);
|
|
116
132
|
}
|
|
117
133
|
});
|