skrypt-ai 0.6.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audit/doc-parser.d.ts +5 -0
- package/dist/audit/doc-parser.js +106 -0
- package/dist/audit/index.d.ts +4 -0
- package/dist/audit/index.js +4 -0
- package/dist/audit/matcher.d.ts +6 -0
- package/dist/audit/matcher.js +94 -0
- package/dist/audit/reporter.d.ts +9 -0
- package/dist/audit/reporter.js +106 -0
- package/dist/audit/types.d.ts +37 -0
- package/dist/auth/index.js +6 -4
- package/dist/cli.js +12 -2
- package/dist/commands/audit.d.ts +2 -0
- package/dist/commands/audit.js +59 -0
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.js +73 -0
- package/dist/commands/{generate.d.ts → generate/index.d.ts} +1 -0
- package/dist/commands/generate/index.js +393 -0
- package/dist/commands/generate/scan.d.ts +41 -0
- package/dist/commands/generate/scan.js +256 -0
- package/dist/commands/generate/verify.d.ts +14 -0
- package/dist/commands/generate/verify.js +122 -0
- package/dist/commands/generate/write.d.ts +25 -0
- package/dist/commands/generate/write.js +120 -0
- package/dist/commands/import.js +4 -1
- package/dist/commands/llms-txt.js +6 -4
- package/dist/commands/refresh.d.ts +2 -0
- package/dist/commands/refresh.js +158 -0
- package/dist/commands/review.d.ts +2 -0
- package/dist/commands/review.js +110 -0
- package/dist/commands/test.js +177 -236
- package/dist/commands/watch.js +29 -20
- package/dist/config/loader.d.ts +6 -2
- package/dist/config/loader.js +39 -3
- package/dist/config/types.d.ts +7 -0
- package/dist/generator/agents-md.d.ts +25 -0
- package/dist/generator/agents-md.js +122 -0
- package/dist/generator/generator.js +2 -1
- package/dist/generator/index.d.ts +2 -0
- package/dist/generator/index.js +2 -0
- package/dist/generator/mdx-serializer.d.ts +11 -0
- package/dist/generator/mdx-serializer.js +135 -0
- package/dist/generator/organizer.d.ts +1 -16
- package/dist/generator/organizer.js +0 -38
- package/dist/generator/types.d.ts +3 -0
- package/dist/generator/writer.js +65 -32
- package/dist/github/org-discovery.d.ts +17 -0
- package/dist/github/org-discovery.js +93 -0
- package/dist/llm/index.d.ts +2 -0
- package/dist/llm/index.js +8 -2
- package/dist/llm/proxy-client.d.ts +32 -0
- package/dist/llm/proxy-client.js +103 -0
- package/dist/next-actions/actions.d.ts +2 -0
- package/dist/next-actions/actions.js +190 -0
- package/dist/next-actions/index.d.ts +6 -0
- package/dist/next-actions/index.js +39 -0
- package/dist/next-actions/setup.d.ts +2 -0
- package/dist/next-actions/setup.js +72 -0
- package/dist/next-actions/state.d.ts +7 -0
- package/dist/next-actions/state.js +68 -0
- package/dist/next-actions/suggest.d.ts +3 -0
- package/dist/next-actions/suggest.js +47 -0
- package/dist/next-actions/types.d.ts +26 -0
- package/dist/refresh/differ.d.ts +9 -0
- package/dist/refresh/differ.js +67 -0
- package/dist/refresh/index.d.ts +4 -0
- package/dist/refresh/index.js +4 -0
- package/dist/refresh/manifest.d.ts +18 -0
- package/dist/refresh/manifest.js +71 -0
- package/dist/refresh/splicer.d.ts +9 -0
- package/dist/refresh/splicer.js +50 -0
- package/dist/refresh/types.d.ts +37 -0
- package/dist/review/index.d.ts +8 -0
- package/dist/review/index.js +94 -0
- package/dist/review/parser.d.ts +16 -0
- package/dist/review/parser.js +95 -0
- package/dist/review/types.d.ts +18 -0
- package/dist/scanner/csharp.d.ts +0 -4
- package/dist/scanner/csharp.js +9 -49
- package/dist/scanner/go.d.ts +0 -3
- package/dist/scanner/go.js +8 -35
- package/dist/scanner/java.d.ts +0 -4
- package/dist/scanner/java.js +9 -49
- package/dist/scanner/kotlin.d.ts +0 -3
- package/dist/scanner/kotlin.js +6 -33
- package/dist/scanner/php.d.ts +0 -10
- package/dist/scanner/php.js +11 -55
- package/dist/scanner/ruby.d.ts +0 -3
- package/dist/scanner/ruby.js +8 -38
- package/dist/scanner/rust.d.ts +0 -3
- package/dist/scanner/rust.js +10 -37
- package/dist/scanner/swift.d.ts +0 -3
- package/dist/scanner/swift.js +8 -35
- package/dist/scanner/types.d.ts +2 -0
- package/dist/scanner/utils.d.ts +41 -0
- package/dist/scanner/utils.js +97 -0
- package/dist/structure/index.d.ts +19 -0
- package/dist/structure/index.js +92 -0
- package/dist/structure/planner.d.ts +8 -0
- package/dist/structure/planner.js +180 -0
- package/dist/structure/topology.d.ts +16 -0
- package/dist/structure/topology.js +49 -0
- package/dist/structure/types.d.ts +26 -0
- package/dist/template/docs.json +5 -2
- package/dist/template/next.config.mjs +31 -0
- package/dist/template/package.json +5 -3
- package/dist/template/src/app/layout.tsx +13 -13
- package/dist/template/src/app/llms-full.md/route.ts +29 -0
- package/dist/template/src/app/llms.txt/route.ts +29 -0
- package/dist/template/src/app/md/[...slug]/route.ts +174 -0
- package/dist/template/src/app/reference/route.ts +22 -18
- package/dist/template/src/app/sitemap.ts +1 -1
- package/dist/template/src/components/ai-chat-impl.tsx +206 -0
- package/dist/template/src/components/ai-chat.tsx +20 -193
- package/dist/template/src/components/mdx/index.tsx +27 -4
- package/dist/template/src/lib/fonts.ts +135 -0
- package/dist/template/src/middleware.ts +101 -0
- package/dist/template/src/styles/globals.css +28 -20
- package/dist/testing/comparator.d.ts +7 -0
- package/dist/testing/comparator.js +77 -0
- package/dist/testing/docker.d.ts +21 -0
- package/dist/testing/docker.js +234 -0
- package/dist/testing/env.d.ts +16 -0
- package/dist/testing/env.js +58 -0
- package/dist/testing/extractor.d.ts +9 -0
- package/dist/testing/extractor.js +195 -0
- package/dist/testing/index.d.ts +6 -0
- package/dist/testing/index.js +6 -0
- package/dist/testing/runner.d.ts +5 -0
- package/dist/testing/runner.js +225 -0
- package/dist/testing/types.d.ts +58 -0
- package/dist/utils/files.d.ts +0 -8
- package/dist/utils/files.js +0 -33
- package/package.json +1 -1
- package/dist/autofix/autofix.test.js +0 -487
- package/dist/commands/generate.js +0 -445
- package/dist/generator/generator.test.js +0 -259
- package/dist/generator/writer.test.js +0 -411
- package/dist/llm/llm.manual-test.js +0 -112
- package/dist/llm/llm.mock-test.d.ts +0 -4
- package/dist/llm/llm.mock-test.js +0 -79
- package/dist/plugins/index.d.ts +0 -47
- package/dist/plugins/index.js +0 -181
- package/dist/scanner/content-type.test.js +0 -231
- package/dist/scanner/integration.test.d.ts +0 -4
- package/dist/scanner/integration.test.js +0 -180
- package/dist/scanner/scanner.test.js +0 -210
- package/dist/scanner/typescript.manual-test.d.ts +0 -1
- package/dist/scanner/typescript.manual-test.js +0 -112
- package/dist/template/src/app/docs/auth/page.mdx +0 -589
- package/dist/template/src/app/docs/autofix/page.mdx +0 -624
- package/dist/template/src/app/docs/cli/page.mdx +0 -217
- package/dist/template/src/app/docs/config/page.mdx +0 -428
- package/dist/template/src/app/docs/configuration/page.mdx +0 -86
- package/dist/template/src/app/docs/deployment/page.mdx +0 -112
- package/dist/template/src/app/docs/generator/generator.md +0 -504
- package/dist/template/src/app/docs/generator/organizer.md +0 -779
- package/dist/template/src/app/docs/generator/page.mdx +0 -613
- package/dist/template/src/app/docs/github/page.mdx +0 -502
- package/dist/template/src/app/docs/llm/anthropic-client.md +0 -549
- package/dist/template/src/app/docs/llm/index.md +0 -471
- package/dist/template/src/app/docs/llm/page.mdx +0 -428
- package/dist/template/src/app/docs/plugins/page.mdx +0 -1793
- package/dist/template/src/app/docs/pro/page.mdx +0 -121
- package/dist/template/src/app/docs/quickstart/page.mdx +0 -93
- package/dist/template/src/app/docs/scanner/content-type.md +0 -599
- package/dist/template/src/app/docs/scanner/index.md +0 -212
- package/dist/template/src/app/docs/scanner/page.mdx +0 -307
- package/dist/template/src/app/docs/scanner/python.md +0 -469
- package/dist/template/src/app/docs/scanner/python_parser.md +0 -1056
- package/dist/template/src/app/docs/scanner/rust.md +0 -325
- package/dist/template/src/app/docs/scanner/typescript.md +0 -201
- package/dist/template/src/app/icon.tsx +0 -29
- package/dist/utils/validation.d.ts +0 -1
- package/dist/utils/validation.js +0 -12
- /package/dist/{autofix/autofix.test.d.ts → audit/types.js} +0 -0
- /package/dist/{generator/generator.test.d.ts → next-actions/types.js} +0 -0
- /package/dist/{generator/writer.test.d.ts → refresh/types.js} +0 -0
- /package/dist/{llm/llm.manual-test.d.ts → review/types.js} +0 -0
- /package/dist/{scanner/content-type.test.d.ts → structure/types.js} +0 -0
- /package/dist/{scanner/scanner.test.d.ts → testing/types.js} +0 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface DiscoveredRepo {
|
|
2
|
+
name: string;
|
|
3
|
+
full_name: string;
|
|
4
|
+
clone_url: string;
|
|
5
|
+
default_branch: string;
|
|
6
|
+
private: boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Discover repositories in a GitHub organization.
|
|
10
|
+
* Returns up to MAX_REPOS repos sorted by most recently pushed.
|
|
11
|
+
*/
|
|
12
|
+
export declare function discoverOrgRepos(org: string, token: string): Promise<DiscoveredRepo[]>;
|
|
13
|
+
/**
|
|
14
|
+
* Shallow clone a repository to a temporary directory.
|
|
15
|
+
* Uses spawnSync with array args to prevent shell injection.
|
|
16
|
+
*/
|
|
17
|
+
export declare function cloneRepoToTemp(repo: DiscoveredRepo, token: string): Promise<string>;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { spawnSync } from 'child_process';
|
|
2
|
+
import { mkdtempSync, rmSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { tmpdir } from 'os';
|
|
5
|
+
const MAX_REPOS = 50;
|
|
6
|
+
/**
|
|
7
|
+
* Discover repositories in a GitHub organization.
|
|
8
|
+
* Returns up to MAX_REPOS repos sorted by most recently pushed.
|
|
9
|
+
*/
|
|
10
|
+
export async function discoverOrgRepos(org, token) {
|
|
11
|
+
const repos = [];
|
|
12
|
+
let page = 1;
|
|
13
|
+
while (repos.length < MAX_REPOS) {
|
|
14
|
+
const url = `https://api.github.com/orgs/${encodeURIComponent(org)}/repos?per_page=100&sort=pushed&page=${page}`;
|
|
15
|
+
const response = await fetch(url, {
|
|
16
|
+
headers: {
|
|
17
|
+
'Authorization': `Bearer ${token}`,
|
|
18
|
+
'Accept': 'application/vnd.github.v3+json',
|
|
19
|
+
'User-Agent': 'Skrypt-CLI',
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
if (!response.ok) {
|
|
23
|
+
if (response.status === 404) {
|
|
24
|
+
throw new Error(`Organization "${org}" not found`);
|
|
25
|
+
}
|
|
26
|
+
if (response.status === 401 || response.status === 403) {
|
|
27
|
+
throw new Error('GitHub token does not have access to this organization');
|
|
28
|
+
}
|
|
29
|
+
throw new Error(`GitHub API error: ${response.status}`);
|
|
30
|
+
}
|
|
31
|
+
const data = await response.json();
|
|
32
|
+
if (data.length === 0)
|
|
33
|
+
break;
|
|
34
|
+
for (const repo of data) {
|
|
35
|
+
if (repos.length >= MAX_REPOS)
|
|
36
|
+
break;
|
|
37
|
+
// Skip archived and forked repos
|
|
38
|
+
if (repo.archived || repo.fork)
|
|
39
|
+
continue;
|
|
40
|
+
repos.push({
|
|
41
|
+
name: repo.name,
|
|
42
|
+
full_name: repo.full_name,
|
|
43
|
+
clone_url: repo.clone_url,
|
|
44
|
+
default_branch: repo.default_branch,
|
|
45
|
+
private: repo.private,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
page++;
|
|
49
|
+
}
|
|
50
|
+
return repos;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Shallow clone a repository to a temporary directory.
|
|
54
|
+
* Uses spawnSync with array args to prevent shell injection.
|
|
55
|
+
*/
|
|
56
|
+
export async function cloneRepoToTemp(repo, token) {
|
|
57
|
+
const tempDir = mkdtempSync(join(tmpdir(), `skrypt-${repo.name}-`));
|
|
58
|
+
// Build authenticated clone URL
|
|
59
|
+
const cloneUrl = repo.clone_url.replace('https://', `https://x-access-token:${token}@`);
|
|
60
|
+
let result;
|
|
61
|
+
try {
|
|
62
|
+
result = spawnSync('git', [
|
|
63
|
+
'clone',
|
|
64
|
+
'--depth', '1',
|
|
65
|
+
'--single-branch',
|
|
66
|
+
'--branch', repo.default_branch,
|
|
67
|
+
cloneUrl,
|
|
68
|
+
tempDir,
|
|
69
|
+
], {
|
|
70
|
+
stdio: 'pipe',
|
|
71
|
+
timeout: 60_000,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
// Clean up temp dir on spawn failure
|
|
76
|
+
try {
|
|
77
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
78
|
+
}
|
|
79
|
+
catch { /* ignore */ }
|
|
80
|
+
throw new Error(`Failed to clone ${repo.full_name}: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
81
|
+
}
|
|
82
|
+
if (result.status !== 0) {
|
|
83
|
+
// Clean up temp dir on clone failure
|
|
84
|
+
try {
|
|
85
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
86
|
+
}
|
|
87
|
+
catch { /* ignore */ }
|
|
88
|
+
// Sanitize stderr to avoid leaking the token
|
|
89
|
+
const stderr = (result.stderr?.toString() || '').replace(/x-access-token:[^@]+@/g, 'x-access-token:***@');
|
|
90
|
+
throw new Error(`Failed to clone ${repo.full_name}: ${stderr}`);
|
|
91
|
+
}
|
|
92
|
+
return tempDir;
|
|
93
|
+
}
|
package/dist/llm/index.d.ts
CHANGED
package/dist/llm/index.js
CHANGED
|
@@ -31,7 +31,10 @@ export function createLLMClient(config) {
|
|
|
31
31
|
*/
|
|
32
32
|
export async function generateDocumentation(client, element, options) {
|
|
33
33
|
const useMultiLang = options?.multiLanguage ?? true;
|
|
34
|
-
|
|
34
|
+
let prompt = buildDocPrompt(element, useMultiLang, options?.verify);
|
|
35
|
+
if (options?.previousError) {
|
|
36
|
+
prompt += `\n\n⚠️ IMPORTANT: The previous code example for this element FAILED verification with the following error:\n\`\`\`\n${options.previousError}\n\`\`\`\nGenerate a DIFFERENT, working code example that avoids this error. Ensure the example is self-contained and runs without external dependencies unless specified.`;
|
|
37
|
+
}
|
|
35
38
|
const response = await client.complete({
|
|
36
39
|
messages: [
|
|
37
40
|
{
|
|
@@ -127,7 +130,7 @@ Generate ONE self-contained, executable example:
|
|
|
127
130
|
---CODE---
|
|
128
131
|
[Self-contained example — no markdown fences]
|
|
129
132
|
---END---`;
|
|
130
|
-
function buildDocPrompt(element, multiLanguage = false) {
|
|
133
|
+
function buildDocPrompt(element, multiLanguage = false, verify = false) {
|
|
131
134
|
let prompt = '';
|
|
132
135
|
// Project context first — gives the LLM the "big picture" for better explanations
|
|
133
136
|
if (element.projectContext) {
|
|
@@ -174,6 +177,9 @@ function buildDocPrompt(element, multiLanguage = false) {
|
|
|
174
177
|
if (multiLanguage) {
|
|
175
178
|
prompt += `\nGenerate BOTH TypeScript AND Python self-contained examples.`;
|
|
176
179
|
}
|
|
180
|
+
if (verify) {
|
|
181
|
+
prompt += `\n\nIMPORTANT: Include \`// Output: <expected>\` comments showing expected console output for every \`console.log\` call (or \`# Output: <expected>\` for Python \`print\` calls). These will be verified by running the code.`;
|
|
182
|
+
}
|
|
177
183
|
return prompt;
|
|
178
184
|
}
|
|
179
185
|
function parseDocResponse(content, elementName) {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { LLMClient, CompletionRequest, CompletionResponse } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* LLM client that routes requests through Skrypt's proxy API.
|
|
4
|
+
* Used when the user doesn't have their own API key but has a Skrypt account.
|
|
5
|
+
*/
|
|
6
|
+
export declare class ProxyClient implements LLMClient {
|
|
7
|
+
provider: "openai";
|
|
8
|
+
private apiKey;
|
|
9
|
+
private sessionId;
|
|
10
|
+
private timeout;
|
|
11
|
+
constructor(config: {
|
|
12
|
+
apiKey: string;
|
|
13
|
+
sessionId: string;
|
|
14
|
+
timeout?: number;
|
|
15
|
+
});
|
|
16
|
+
complete(request: CompletionRequest): Promise<CompletionResponse>;
|
|
17
|
+
isConfigured(): boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Start a proxy generation session.
|
|
21
|
+
* Returns sessionId + remaining generations info.
|
|
22
|
+
*/
|
|
23
|
+
export declare function startProxySession(apiKey: string): Promise<{
|
|
24
|
+
sessionId: string;
|
|
25
|
+
plan: 'free' | 'pro';
|
|
26
|
+
remaining: number;
|
|
27
|
+
used: number;
|
|
28
|
+
}>;
|
|
29
|
+
/**
|
|
30
|
+
* Complete a proxy generation session (increments monthly usage counter).
|
|
31
|
+
*/
|
|
32
|
+
export declare function completeProxySession(apiKey: string, sessionId: string): Promise<void>;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
const API_BASE = process.env.SKRYPT_API_URL || 'https://app.skrypt.sh';
|
|
2
|
+
/**
|
|
3
|
+
* LLM client that routes requests through Skrypt's proxy API.
|
|
4
|
+
* Used when the user doesn't have their own API key but has a Skrypt account.
|
|
5
|
+
*/
|
|
6
|
+
export class ProxyClient {
|
|
7
|
+
provider = 'openai'; // Proxy decides which model to use
|
|
8
|
+
apiKey;
|
|
9
|
+
sessionId;
|
|
10
|
+
timeout;
|
|
11
|
+
constructor(config) {
|
|
12
|
+
this.apiKey = config.apiKey;
|
|
13
|
+
this.sessionId = config.sessionId;
|
|
14
|
+
this.timeout = config.timeout ?? 120_000;
|
|
15
|
+
}
|
|
16
|
+
async complete(request) {
|
|
17
|
+
const controller = new AbortController();
|
|
18
|
+
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
19
|
+
try {
|
|
20
|
+
const response = await fetch(`${API_BASE}/api/proxy/generate`, {
|
|
21
|
+
method: 'POST',
|
|
22
|
+
headers: {
|
|
23
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
24
|
+
'Content-Type': 'application/json',
|
|
25
|
+
},
|
|
26
|
+
body: JSON.stringify({
|
|
27
|
+
sessionId: this.sessionId,
|
|
28
|
+
messages: request.messages,
|
|
29
|
+
temperature: request.temperature ?? 0,
|
|
30
|
+
maxTokens: request.maxTokens ?? 4096,
|
|
31
|
+
}),
|
|
32
|
+
signal: controller.signal,
|
|
33
|
+
});
|
|
34
|
+
if (!response.ok) {
|
|
35
|
+
const error = await response.json().catch(() => ({ error: 'Unknown error' }));
|
|
36
|
+
throw new Error(error.error || `Proxy request failed: ${response.status}`);
|
|
37
|
+
}
|
|
38
|
+
const data = await response.json();
|
|
39
|
+
return {
|
|
40
|
+
content: data.content,
|
|
41
|
+
model: data.model,
|
|
42
|
+
usage: {
|
|
43
|
+
inputTokens: data.usage.inputTokens,
|
|
44
|
+
outputTokens: data.usage.outputTokens,
|
|
45
|
+
totalTokens: data.usage.totalTokens,
|
|
46
|
+
},
|
|
47
|
+
finishReason: data.finishReason || 'stop',
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
clearTimeout(timer);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
isConfigured() {
|
|
55
|
+
return !!this.apiKey && !!this.sessionId;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Start a proxy generation session.
|
|
60
|
+
* Returns sessionId + remaining generations info.
|
|
61
|
+
*/
|
|
62
|
+
export async function startProxySession(apiKey) {
|
|
63
|
+
const response = await fetch(`${API_BASE}/api/proxy/generate/start`, {
|
|
64
|
+
method: 'POST',
|
|
65
|
+
headers: {
|
|
66
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
67
|
+
'Content-Type': 'application/json',
|
|
68
|
+
},
|
|
69
|
+
body: JSON.stringify({}),
|
|
70
|
+
});
|
|
71
|
+
if (!response.ok) {
|
|
72
|
+
const error = await response.json().catch(() => ({ error: 'Unknown error' }));
|
|
73
|
+
const errData = error;
|
|
74
|
+
if (response.status === 429) {
|
|
75
|
+
throw new Error(`Monthly generation limit reached (${errData.used}/${errData.limit}). ` +
|
|
76
|
+
`Upgrade to Pro: ${errData.upgrade || 'https://skrypt.sh/pro'}`);
|
|
77
|
+
}
|
|
78
|
+
throw new Error(errData.error || `Failed to start session: ${response.status}`);
|
|
79
|
+
}
|
|
80
|
+
return await response.json();
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Complete a proxy generation session (increments monthly usage counter).
|
|
84
|
+
*/
|
|
85
|
+
export async function completeProxySession(apiKey, sessionId) {
|
|
86
|
+
try {
|
|
87
|
+
const response = await fetch(`${API_BASE}/api/proxy/generate/complete`, {
|
|
88
|
+
method: 'POST',
|
|
89
|
+
headers: {
|
|
90
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
91
|
+
'Content-Type': 'application/json',
|
|
92
|
+
},
|
|
93
|
+
body: JSON.stringify({ sessionId }),
|
|
94
|
+
});
|
|
95
|
+
if (!response.ok) {
|
|
96
|
+
console.warn('Warning: Failed to complete generation session');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// Non-critical — network error, server down, etc.
|
|
101
|
+
console.warn('Warning: Failed to complete generation session');
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { existsSync } from 'fs';
|
|
2
|
+
function noWorkflowFile() {
|
|
3
|
+
return !existsSync('.github/workflows/skrypt.yml') &&
|
|
4
|
+
!existsSync('.github/workflows/skrypt.yaml');
|
|
5
|
+
}
|
|
6
|
+
export const ACTION_DEFINITIONS = [
|
|
7
|
+
// ── After init ──────────────────────────────────────────────
|
|
8
|
+
{
|
|
9
|
+
id: 'gen-init',
|
|
10
|
+
afterCommands: ['init'],
|
|
11
|
+
category: 'workflow',
|
|
12
|
+
message: 'Generate docs from your source code',
|
|
13
|
+
command: 'skrypt generate ./src -o ./content/docs',
|
|
14
|
+
priority: 100,
|
|
15
|
+
},
|
|
16
|
+
// ── After generate ──────────────────────────────────────────
|
|
17
|
+
{
|
|
18
|
+
id: 'test-gen',
|
|
19
|
+
afterCommands: ['generate'],
|
|
20
|
+
category: 'workflow',
|
|
21
|
+
message: 'Verify code snippets execute correctly',
|
|
22
|
+
command: 'skrypt test ./docs',
|
|
23
|
+
priority: 100,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: 'qa-gen',
|
|
27
|
+
afterCommands: ['generate'],
|
|
28
|
+
category: 'quality',
|
|
29
|
+
message: 'Run quality checks on generated docs',
|
|
30
|
+
command: 'skrypt qa ./docs',
|
|
31
|
+
priority: 80,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: 'audit-gen',
|
|
35
|
+
afterCommands: ['generate'],
|
|
36
|
+
category: 'advanced',
|
|
37
|
+
message: 'Check documentation coverage',
|
|
38
|
+
command: 'skrypt audit ./src --docs ./docs',
|
|
39
|
+
priority: 50,
|
|
40
|
+
},
|
|
41
|
+
// ── After test ──────────────────────────────────────────────
|
|
42
|
+
{
|
|
43
|
+
id: 'audit-test',
|
|
44
|
+
afterCommands: ['test'],
|
|
45
|
+
category: 'advanced',
|
|
46
|
+
message: 'Check documentation coverage',
|
|
47
|
+
command: 'skrypt audit ./src --docs ./docs',
|
|
48
|
+
priority: 60,
|
|
49
|
+
},
|
|
50
|
+
// ── After qa ────────────────────────────────────────────────
|
|
51
|
+
{
|
|
52
|
+
id: 'heal-qa',
|
|
53
|
+
afterCommands: ['qa'],
|
|
54
|
+
category: 'quality',
|
|
55
|
+
message: 'Auto-fix QA issues',
|
|
56
|
+
command: 'skrypt heal',
|
|
57
|
+
priority: 90,
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: 'lint-qa',
|
|
61
|
+
afterCommands: ['qa'],
|
|
62
|
+
category: 'quality',
|
|
63
|
+
message: 'Lint markdown docs',
|
|
64
|
+
command: 'skrypt lint ./docs',
|
|
65
|
+
priority: 70,
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: 'security-qa',
|
|
69
|
+
afterCommands: ['qa'],
|
|
70
|
+
category: 'quality',
|
|
71
|
+
message: 'Check for security issues',
|
|
72
|
+
command: 'skrypt security ./docs',
|
|
73
|
+
priority: 60,
|
|
74
|
+
},
|
|
75
|
+
// ── After audit ─────────────────────────────────────────────
|
|
76
|
+
{
|
|
77
|
+
id: 'refresh-audit',
|
|
78
|
+
afterCommands: ['audit'],
|
|
79
|
+
category: 'advanced',
|
|
80
|
+
message: 'Update stale docs based on code changes',
|
|
81
|
+
command: 'skrypt refresh ./src --docs ./docs',
|
|
82
|
+
priority: 80,
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
id: 'review-audit',
|
|
86
|
+
afterCommands: ['audit'],
|
|
87
|
+
category: 'advanced',
|
|
88
|
+
message: 'Review docs quality with feedback',
|
|
89
|
+
command: 'skrypt review ./docs',
|
|
90
|
+
priority: 60,
|
|
91
|
+
},
|
|
92
|
+
// ── After refresh / review / heal / autofix ─────────────────
|
|
93
|
+
{
|
|
94
|
+
id: 'test-after-fix',
|
|
95
|
+
afterCommands: ['refresh', 'review', 'heal', 'autofix'],
|
|
96
|
+
category: 'workflow',
|
|
97
|
+
message: 'Verify updated docs pass tests',
|
|
98
|
+
command: 'skrypt test ./docs',
|
|
99
|
+
priority: 100,
|
|
100
|
+
},
|
|
101
|
+
// ── After deploy ────────────────────────────────────────────
|
|
102
|
+
{
|
|
103
|
+
id: 'watch-deploy',
|
|
104
|
+
afterCommands: ['deploy'],
|
|
105
|
+
category: 'workflow',
|
|
106
|
+
message: 'Watch for changes in development',
|
|
107
|
+
command: 'skrypt watch',
|
|
108
|
+
priority: 50,
|
|
109
|
+
},
|
|
110
|
+
// ── After import ────────────────────────────────────────────
|
|
111
|
+
{
|
|
112
|
+
id: 'qa-import',
|
|
113
|
+
afterCommands: ['import'],
|
|
114
|
+
category: 'quality',
|
|
115
|
+
message: 'Check imported docs quality',
|
|
116
|
+
command: 'skrypt qa ./docs',
|
|
117
|
+
priority: 90,
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
id: 'gen-import',
|
|
121
|
+
afterCommands: ['import'],
|
|
122
|
+
category: 'workflow',
|
|
123
|
+
message: 'Generate docs to fill gaps',
|
|
124
|
+
command: 'skrypt generate ./src -o ./content/docs',
|
|
125
|
+
priority: 70,
|
|
126
|
+
},
|
|
127
|
+
// ── After login ─────────────────────────────────────────────
|
|
128
|
+
{
|
|
129
|
+
id: 'pro-login',
|
|
130
|
+
afterCommands: ['login'],
|
|
131
|
+
category: 'workflow',
|
|
132
|
+
message: 'Unlock Pro: test, heal, autofix, refresh',
|
|
133
|
+
command: 'skrypt test ./docs',
|
|
134
|
+
priority: 100,
|
|
135
|
+
},
|
|
136
|
+
// ── After gh-action ─────────────────────────────────────────
|
|
137
|
+
{
|
|
138
|
+
id: 'push-ci',
|
|
139
|
+
afterCommands: ['gh-action'],
|
|
140
|
+
category: 'cicd',
|
|
141
|
+
message: 'Push to GitHub to trigger CI',
|
|
142
|
+
command: 'git push',
|
|
143
|
+
priority: 100,
|
|
144
|
+
},
|
|
145
|
+
// ── CI/CD setup (cross-command) ─────────────────────────────
|
|
146
|
+
{
|
|
147
|
+
id: 'ci-setup',
|
|
148
|
+
afterCommands: ['generate', 'test', 'deploy'],
|
|
149
|
+
category: 'cicd',
|
|
150
|
+
message: 'Set up GitHub Actions for CI',
|
|
151
|
+
command: 'skrypt gh-action',
|
|
152
|
+
priority: 70,
|
|
153
|
+
condition: noWorkflowFile,
|
|
154
|
+
},
|
|
155
|
+
// ── Deploy (cross-command) ──────────────────────────────────
|
|
156
|
+
{
|
|
157
|
+
id: 'deploy-docs',
|
|
158
|
+
afterCommands: ['generate', 'test', 'heal', 'review', 'refresh', 'autofix'],
|
|
159
|
+
category: 'workflow',
|
|
160
|
+
message: 'Deploy docs to skrypt.sh',
|
|
161
|
+
command: 'skrypt deploy',
|
|
162
|
+
priority: 40,
|
|
163
|
+
},
|
|
164
|
+
// ── After lint / check-links / security ─────────────────────
|
|
165
|
+
{
|
|
166
|
+
id: 'deploy-quality',
|
|
167
|
+
afterCommands: ['lint', 'check-links', 'security'],
|
|
168
|
+
category: 'workflow',
|
|
169
|
+
message: 'Deploy docs to skrypt.sh',
|
|
170
|
+
command: 'skrypt deploy',
|
|
171
|
+
priority: 40,
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
id: 'qa-quality',
|
|
175
|
+
afterCommands: ['lint', 'check-links', 'security'],
|
|
176
|
+
category: 'quality',
|
|
177
|
+
message: 'Run full quality checks',
|
|
178
|
+
command: 'skrypt qa ./docs',
|
|
179
|
+
priority: 60,
|
|
180
|
+
},
|
|
181
|
+
// ── After llms-txt ──────────────────────────────────────────
|
|
182
|
+
{
|
|
183
|
+
id: 'deploy-llms',
|
|
184
|
+
afterCommands: ['llms-txt'],
|
|
185
|
+
category: 'workflow',
|
|
186
|
+
message: 'Deploy docs with llms.txt',
|
|
187
|
+
command: 'skrypt deploy',
|
|
188
|
+
priority: 80,
|
|
189
|
+
},
|
|
190
|
+
];
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { readPreferences, writePreferences, DEFAULT_PREFERENCES } from './state.js';
|
|
2
|
+
export { getSuggestions, printSuggestions } from './suggest.js';
|
|
3
|
+
export { runFirstTimeSetup } from './setup.js';
|
|
4
|
+
export { ACTION_DEFINITIONS } from './actions.js';
|
|
5
|
+
export type { NextActionPreferences, ProjectActionState, ActionDefinition, Suggestion } from './types.js';
|
|
6
|
+
export declare function handlePostAction(commandName: string): Promise<void>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { readPreferences, markCommandCompleted } from './state.js';
|
|
2
|
+
import { getSuggestions, printSuggestions } from './suggest.js';
|
|
3
|
+
import { runFirstTimeSetup } from './setup.js';
|
|
4
|
+
export { readPreferences, writePreferences, DEFAULT_PREFERENCES } from './state.js';
|
|
5
|
+
export { getSuggestions, printSuggestions } from './suggest.js';
|
|
6
|
+
export { runFirstTimeSetup } from './setup.js';
|
|
7
|
+
export { ACTION_DEFINITIONS } from './actions.js';
|
|
8
|
+
const SKIP_COMMANDS = new Set(['config', 'mcp', 'whoami', 'version', 'logout']);
|
|
9
|
+
export async function handlePostAction(commandName) {
|
|
10
|
+
try {
|
|
11
|
+
// Only in interactive mode
|
|
12
|
+
if (!process.stdin.isTTY)
|
|
13
|
+
return;
|
|
14
|
+
// Skip meta commands
|
|
15
|
+
if (SKIP_COMMANDS.has(commandName))
|
|
16
|
+
return;
|
|
17
|
+
let prefs = readPreferences();
|
|
18
|
+
// First-time setup
|
|
19
|
+
if (!prefs) {
|
|
20
|
+
try {
|
|
21
|
+
prefs = await runFirstTimeSetup();
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// Non-interactive or error — skip silently
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (!prefs.enabled)
|
|
29
|
+
return;
|
|
30
|
+
// Track this command
|
|
31
|
+
markCommandCompleted(commandName);
|
|
32
|
+
// Show suggestions
|
|
33
|
+
const suggestions = getSuggestions(commandName, prefs);
|
|
34
|
+
printSuggestions(suggestions);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// Never crash the CLI over suggestions
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import * as readline from 'readline';
|
|
2
|
+
import { writePreferences, DEFAULT_PREFERENCES } from './state.js';
|
|
3
|
+
function ask(rl, question) {
|
|
4
|
+
return new Promise((resolve, reject) => {
|
|
5
|
+
rl.question(question, answer => resolve(answer.trim()));
|
|
6
|
+
rl.once('close', () => reject(new Error('EOF')));
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
export async function runFirstTimeSetup() {
|
|
10
|
+
console.log('');
|
|
11
|
+
console.log(' \x1b[36m─────────────────────────────────────\x1b[0m');
|
|
12
|
+
console.log('');
|
|
13
|
+
console.log(' Skrypt can suggest next steps after each command.');
|
|
14
|
+
console.log('');
|
|
15
|
+
console.log(' 1) All suggestions (recommended)');
|
|
16
|
+
console.log(' 2) Workflow only (generate \u2192 test \u2192 deploy)');
|
|
17
|
+
console.log(' 3) Customize');
|
|
18
|
+
console.log(' 4) Disable');
|
|
19
|
+
console.log('');
|
|
20
|
+
const rl = readline.createInterface({
|
|
21
|
+
input: process.stdin,
|
|
22
|
+
output: process.stdout,
|
|
23
|
+
});
|
|
24
|
+
try {
|
|
25
|
+
const choice = await ask(rl, ' Choice [1]: ');
|
|
26
|
+
const selected = choice || '1';
|
|
27
|
+
let prefs;
|
|
28
|
+
if (selected === '4') {
|
|
29
|
+
prefs = {
|
|
30
|
+
enabled: false,
|
|
31
|
+
categories: { workflow: false, quality: false, cicd: false, advanced: false },
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
else if (selected === '2') {
|
|
35
|
+
prefs = {
|
|
36
|
+
enabled: true,
|
|
37
|
+
categories: { workflow: true, quality: false, cicd: false, advanced: false },
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
else if (selected === '3') {
|
|
41
|
+
console.log('');
|
|
42
|
+
const w = await ask(rl, ' Workflow (generate, test, deploy)? [Y/n] ');
|
|
43
|
+
const q = await ask(rl, ' Quality (qa, lint, security)? [Y/n] ');
|
|
44
|
+
const c = await ask(rl, ' CI/CD (GitHub Actions, deploy)? [Y/n] ');
|
|
45
|
+
const a = await ask(rl, ' Advanced (audit, refresh, review)? [Y/n] ');
|
|
46
|
+
prefs = {
|
|
47
|
+
enabled: true,
|
|
48
|
+
categories: {
|
|
49
|
+
workflow: w.toLowerCase() !== 'n',
|
|
50
|
+
quality: q.toLowerCase() !== 'n',
|
|
51
|
+
cicd: c.toLowerCase() !== 'n',
|
|
52
|
+
advanced: a.toLowerCase() !== 'n',
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
prefs = { ...DEFAULT_PREFERENCES };
|
|
58
|
+
}
|
|
59
|
+
writePreferences(prefs);
|
|
60
|
+
console.log('');
|
|
61
|
+
if (prefs.enabled) {
|
|
62
|
+
console.log(' \x1b[32mSaved!\x1b[0m Change anytime with: skrypt config --suggestions');
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
console.log(' Suggestions disabled. Re-enable with: skrypt config --suggestions');
|
|
66
|
+
}
|
|
67
|
+
return prefs;
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
rl.close();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { NextActionPreferences, ProjectActionState } from './types.js';
|
|
2
|
+
export declare const DEFAULT_PREFERENCES: NextActionPreferences;
|
|
3
|
+
export declare function readPreferences(): NextActionPreferences | null;
|
|
4
|
+
export declare function writePreferences(prefs: NextActionPreferences): void;
|
|
5
|
+
export declare function readProjectState(): ProjectActionState;
|
|
6
|
+
export declare function writeProjectState(state: ProjectActionState): void;
|
|
7
|
+
export declare function markCommandCompleted(commandName: string): void;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
import { createHash } from 'crypto';
|
|
5
|
+
function skryptHome() {
|
|
6
|
+
return join(homedir(), '.skrypt');
|
|
7
|
+
}
|
|
8
|
+
function preferencesFile() {
|
|
9
|
+
return join(skryptHome(), 'preferences.json');
|
|
10
|
+
}
|
|
11
|
+
function projectsDir() {
|
|
12
|
+
return join(skryptHome(), 'projects');
|
|
13
|
+
}
|
|
14
|
+
export const DEFAULT_PREFERENCES = {
|
|
15
|
+
enabled: true,
|
|
16
|
+
categories: {
|
|
17
|
+
workflow: true,
|
|
18
|
+
quality: true,
|
|
19
|
+
cicd: true,
|
|
20
|
+
advanced: true,
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
function projectHash() {
|
|
24
|
+
return createHash('sha256').update(process.cwd()).digest('hex').slice(0, 12);
|
|
25
|
+
}
|
|
26
|
+
function projectStatePath() {
|
|
27
|
+
return join(projectsDir(), `${projectHash()}.json`);
|
|
28
|
+
}
|
|
29
|
+
export function readPreferences() {
|
|
30
|
+
try {
|
|
31
|
+
const path = preferencesFile();
|
|
32
|
+
if (!existsSync(path))
|
|
33
|
+
return null;
|
|
34
|
+
return JSON.parse(readFileSync(path, 'utf-8'));
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function writePreferences(prefs) {
|
|
41
|
+
const home = skryptHome();
|
|
42
|
+
mkdirSync(home, { recursive: true });
|
|
43
|
+
writeFileSync(preferencesFile(), JSON.stringify(prefs, null, 2), { mode: 0o600 });
|
|
44
|
+
}
|
|
45
|
+
export function readProjectState() {
|
|
46
|
+
try {
|
|
47
|
+
const path = projectStatePath();
|
|
48
|
+
if (!existsSync(path)) {
|
|
49
|
+
return { completedCommands: [], lastUpdated: '' };
|
|
50
|
+
}
|
|
51
|
+
return JSON.parse(readFileSync(path, 'utf-8'));
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return { completedCommands: [], lastUpdated: '' };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export function writeProjectState(state) {
|
|
58
|
+
mkdirSync(projectsDir(), { recursive: true });
|
|
59
|
+
state.lastUpdated = new Date().toISOString();
|
|
60
|
+
writeFileSync(projectStatePath(), JSON.stringify(state, null, 2), { mode: 0o600 });
|
|
61
|
+
}
|
|
62
|
+
export function markCommandCompleted(commandName) {
|
|
63
|
+
const state = readProjectState();
|
|
64
|
+
if (!state.completedCommands.includes(commandName)) {
|
|
65
|
+
state.completedCommands.push(commandName);
|
|
66
|
+
}
|
|
67
|
+
writeProjectState(state);
|
|
68
|
+
}
|