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
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
/**
|
|
3
|
+
* Scanner for C# source files
|
|
4
|
+
* Extracts: public classes, interfaces, enums, structs, records, methods
|
|
5
|
+
*/
|
|
6
|
+
export class CSharpScanner {
|
|
7
|
+
languages = ['csharp'];
|
|
8
|
+
canHandle(filePath) {
|
|
9
|
+
return (/\.cs$/.test(filePath) &&
|
|
10
|
+
!filePath.endsWith('Test.cs') &&
|
|
11
|
+
!filePath.endsWith('Tests.cs') &&
|
|
12
|
+
!filePath.includes('/Tests/') &&
|
|
13
|
+
!filePath.includes('/test/') &&
|
|
14
|
+
!filePath.includes('\\Tests\\') &&
|
|
15
|
+
!filePath.includes('\\test\\'));
|
|
16
|
+
}
|
|
17
|
+
async scanFile(filePath) {
|
|
18
|
+
try {
|
|
19
|
+
const source = readFileSync(filePath, 'utf-8');
|
|
20
|
+
const elements = [];
|
|
21
|
+
const errors = [];
|
|
22
|
+
const lines = source.split('\n');
|
|
23
|
+
// Extract namespace
|
|
24
|
+
const namespaceMatch = source.match(/^namespace\s+([\w.]+)/m);
|
|
25
|
+
const packageName = namespaceMatch?.[1] ?? 'unknown';
|
|
26
|
+
// Extract using statements for context
|
|
27
|
+
const imports = this.extractImports(source);
|
|
28
|
+
// Find all public types and methods
|
|
29
|
+
this.extractTypes(source, lines, filePath, packageName, imports, elements);
|
|
30
|
+
this.extractMethods(source, lines, filePath, packageName, imports, elements);
|
|
31
|
+
return {
|
|
32
|
+
filePath,
|
|
33
|
+
language: 'csharp',
|
|
34
|
+
elements,
|
|
35
|
+
errors
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
return {
|
|
40
|
+
filePath,
|
|
41
|
+
language: 'csharp',
|
|
42
|
+
elements: [],
|
|
43
|
+
errors: [`Failed to parse: ${err}`]
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
extractImports(source) {
|
|
48
|
+
const imports = [];
|
|
49
|
+
const regex = /^using\s+([^;]+);/gm;
|
|
50
|
+
let match;
|
|
51
|
+
while ((match = regex.exec(source)) !== null) {
|
|
52
|
+
if (match[1])
|
|
53
|
+
imports.push(match[1]);
|
|
54
|
+
}
|
|
55
|
+
return imports;
|
|
56
|
+
}
|
|
57
|
+
extractTypes(source, lines, filePath, packageName, imports, elements) {
|
|
58
|
+
// Match: public class/interface/enum/struct/record TypeName
|
|
59
|
+
// Optionally with generic params, base class, interfaces
|
|
60
|
+
const typeRegex = /^[ \t]*public\s+(?:(?:abstract|sealed|static|partial)\s+)*(?:(class|interface|enum|struct|record))\s+(\w+)\s*/gm;
|
|
61
|
+
let match;
|
|
62
|
+
while ((match = typeRegex.exec(source)) !== null) {
|
|
63
|
+
const kind = match[1];
|
|
64
|
+
const name = match[2];
|
|
65
|
+
if (!kind || !name)
|
|
66
|
+
continue;
|
|
67
|
+
// Extract generics with depth counting to handle nested generics
|
|
68
|
+
const afterName = match.index + match[0].length;
|
|
69
|
+
const generics = this.extractGenerics(source, afterName);
|
|
70
|
+
const lineNumber = this.getLineNumber(source, match.index);
|
|
71
|
+
const docstring = this.getDocComment(lines, lineNumber - 1);
|
|
72
|
+
// For records with primary constructor params, extract them
|
|
73
|
+
let parameters = [];
|
|
74
|
+
if (kind === 'record') {
|
|
75
|
+
const recordParamMatch = source.slice(match.index).match(/public\s+(?:(?:abstract|sealed|static|partial)\s+)*record\s+\w+\s*(?:<[^>]+>)?\s*\(([^)]*)\)/);
|
|
76
|
+
if (recordParamMatch?.[1]) {
|
|
77
|
+
parameters = this.parseCSharpParams(recordParamMatch[1]);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const signature = `public ${kind} ${name}${generics}`;
|
|
81
|
+
elements.push({
|
|
82
|
+
kind: 'class', // Using 'class' for all C# types
|
|
83
|
+
name,
|
|
84
|
+
signature,
|
|
85
|
+
parameters,
|
|
86
|
+
docstring,
|
|
87
|
+
filePath,
|
|
88
|
+
lineNumber,
|
|
89
|
+
isExported: true,
|
|
90
|
+
isPublic: true,
|
|
91
|
+
imports,
|
|
92
|
+
packageName,
|
|
93
|
+
sourceContext: this.getSourceContext(lines, lineNumber, 15)
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
extractMethods(source, lines, filePath, packageName, imports, elements) {
|
|
98
|
+
// We need to find which class each method belongs to.
|
|
99
|
+
// First, find all class/struct/record blocks and their ranges.
|
|
100
|
+
const classRanges = this.findClassRanges(source);
|
|
101
|
+
// Match public methods (instance or static, sync or async)
|
|
102
|
+
// Patterns:
|
|
103
|
+
// public ReturnType MethodName(params)
|
|
104
|
+
// public static ReturnType MethodName(params)
|
|
105
|
+
// public async Task<T> MethodName(params)
|
|
106
|
+
// public override ReturnType MethodName(params)
|
|
107
|
+
// public virtual ReturnType MethodName(params)
|
|
108
|
+
// Exclude constructors (name matches class name) — we include them as methods
|
|
109
|
+
// Exclude property accessors, event handlers
|
|
110
|
+
const methodRegex = /^[ \t]*public\s+((?:(?:static|async|virtual|override|abstract|sealed|new)\s+)*)(\w+)/gm;
|
|
111
|
+
let match;
|
|
112
|
+
while ((match = methodRegex.exec(source)) !== null) {
|
|
113
|
+
const modifiers = match[1]?.trim() || '';
|
|
114
|
+
const baseReturnType = match[2];
|
|
115
|
+
if (!baseReturnType)
|
|
116
|
+
continue;
|
|
117
|
+
// After the base return type, extract any generics with depth counting
|
|
118
|
+
let pos = match.index + match[0].length;
|
|
119
|
+
const returnGenerics = this.extractGenerics(source, pos);
|
|
120
|
+
pos += returnGenerics.length;
|
|
121
|
+
const returnType = baseReturnType + returnGenerics;
|
|
122
|
+
// Skip whitespace
|
|
123
|
+
while (pos < source.length && (source[pos] === ' ' || source[pos] === '\t'))
|
|
124
|
+
pos++;
|
|
125
|
+
// Next token should be the method name (identifier)
|
|
126
|
+
const nameMatch = source.slice(pos).match(/^(\w+)\s*\(/);
|
|
127
|
+
if (!nameMatch || !nameMatch[1])
|
|
128
|
+
continue;
|
|
129
|
+
const name = nameMatch[1];
|
|
130
|
+
pos += nameMatch[0].length - 1; // position at '('
|
|
131
|
+
// Extract params up to the matching closing paren
|
|
132
|
+
const paramsStart = pos + 1;
|
|
133
|
+
let parenDepth = 1;
|
|
134
|
+
let parenPos = paramsStart;
|
|
135
|
+
while (parenPos < source.length && parenDepth > 0) {
|
|
136
|
+
if (source[parenPos] === '(')
|
|
137
|
+
parenDepth++;
|
|
138
|
+
else if (source[parenPos] === ')')
|
|
139
|
+
parenDepth--;
|
|
140
|
+
parenPos++;
|
|
141
|
+
}
|
|
142
|
+
const paramsStr = source.slice(paramsStart, parenPos - 1);
|
|
143
|
+
if (paramsStr === undefined)
|
|
144
|
+
continue;
|
|
145
|
+
// Skip type declarations (class, interface, enum, struct, record)
|
|
146
|
+
if (returnType === 'class' ||
|
|
147
|
+
returnType === 'interface' ||
|
|
148
|
+
returnType === 'enum' ||
|
|
149
|
+
returnType === 'struct' ||
|
|
150
|
+
returnType === 'record') {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
const lineNumber = this.getLineNumber(source, match.index);
|
|
154
|
+
const docstring = this.getDocComment(lines, lineNumber - 1);
|
|
155
|
+
const parameters = this.parseCSharpParams(paramsStr);
|
|
156
|
+
const isAsync = modifiers.includes('async');
|
|
157
|
+
// Find parent class
|
|
158
|
+
const parentClass = this.findParentClass(match.index, classRanges);
|
|
159
|
+
const fullModifiers = modifiers ? `${modifiers} ` : '';
|
|
160
|
+
const signature = `public ${fullModifiers}${returnType} ${name}(${paramsStr})`;
|
|
161
|
+
elements.push({
|
|
162
|
+
kind: 'method',
|
|
163
|
+
name,
|
|
164
|
+
signature,
|
|
165
|
+
parameters,
|
|
166
|
+
returnType: returnType || undefined,
|
|
167
|
+
docstring,
|
|
168
|
+
filePath,
|
|
169
|
+
lineNumber,
|
|
170
|
+
parentClass,
|
|
171
|
+
isAsync,
|
|
172
|
+
isExported: true,
|
|
173
|
+
isPublic: true,
|
|
174
|
+
imports,
|
|
175
|
+
packageName,
|
|
176
|
+
sourceContext: this.getSourceContext(lines, lineNumber)
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
// Also extract interface method signatures (no body, just declarations)
|
|
180
|
+
this.extractInterfaceMethods(source, lines, filePath, packageName, imports, elements, classRanges);
|
|
181
|
+
}
|
|
182
|
+
extractInterfaceMethods(source, lines, filePath, packageName, imports, elements, classRanges) {
|
|
183
|
+
// Interface methods don't have access modifiers in C#
|
|
184
|
+
// They look like: ReturnType MethodName(params);
|
|
185
|
+
// We only extract these inside interface blocks
|
|
186
|
+
const interfaceRanges = classRanges.filter(r => r.kind === 'interface');
|
|
187
|
+
for (const range of interfaceRanges) {
|
|
188
|
+
const body = source.slice(range.bodyStart, range.bodyEnd);
|
|
189
|
+
const methodRegex = /^[ \t]*(\w+)/gm;
|
|
190
|
+
let match;
|
|
191
|
+
while ((match = methodRegex.exec(body)) !== null) {
|
|
192
|
+
const baseReturnType = match[1];
|
|
193
|
+
if (!baseReturnType)
|
|
194
|
+
continue;
|
|
195
|
+
// Skip lines that look like properties or non-method declarations
|
|
196
|
+
if (baseReturnType === 'get' || baseReturnType === 'set' || baseReturnType === '//' || baseReturnType === '///')
|
|
197
|
+
continue;
|
|
198
|
+
// Extract generics for return type
|
|
199
|
+
let pos = match.index + match[0].length;
|
|
200
|
+
const returnGenerics = this.extractGenerics(body, pos);
|
|
201
|
+
pos += returnGenerics.length;
|
|
202
|
+
const returnType = baseReturnType + returnGenerics;
|
|
203
|
+
// Skip whitespace
|
|
204
|
+
while (pos < body.length && (body[pos] === ' ' || body[pos] === '\t'))
|
|
205
|
+
pos++;
|
|
206
|
+
// Next should be method name followed by '('
|
|
207
|
+
const nameMatch = body.slice(pos).match(/^(\w+)\s*\(/);
|
|
208
|
+
if (!nameMatch || !nameMatch[1])
|
|
209
|
+
continue;
|
|
210
|
+
const name = nameMatch[1];
|
|
211
|
+
pos += nameMatch[0].length - 1; // position at '('
|
|
212
|
+
// Extract params
|
|
213
|
+
const paramsStart = pos + 1;
|
|
214
|
+
let parenDepth = 1;
|
|
215
|
+
let parenPos = paramsStart;
|
|
216
|
+
while (parenPos < body.length && parenDepth > 0) {
|
|
217
|
+
if (body[parenPos] === '(')
|
|
218
|
+
parenDepth++;
|
|
219
|
+
else if (body[parenPos] === ')')
|
|
220
|
+
parenDepth--;
|
|
221
|
+
parenPos++;
|
|
222
|
+
}
|
|
223
|
+
const paramsStr = body.slice(paramsStart, parenPos - 1);
|
|
224
|
+
// Must end with semicolon (interface method declaration)
|
|
225
|
+
const afterParen = body.slice(parenPos - 1).match(/^\)\s*;/);
|
|
226
|
+
if (!afterParen)
|
|
227
|
+
continue;
|
|
228
|
+
if (paramsStr === undefined)
|
|
229
|
+
continue;
|
|
230
|
+
const absoluteIndex = range.bodyStart + match.index;
|
|
231
|
+
const lineNumber = this.getLineNumber(source, absoluteIndex);
|
|
232
|
+
const docstring = this.getDocComment(lines, lineNumber - 1);
|
|
233
|
+
const parameters = this.parseCSharpParams(paramsStr);
|
|
234
|
+
const signature = `${returnType} ${name}(${paramsStr})`;
|
|
235
|
+
elements.push({
|
|
236
|
+
kind: 'method',
|
|
237
|
+
name,
|
|
238
|
+
signature,
|
|
239
|
+
parameters,
|
|
240
|
+
returnType: returnType || undefined,
|
|
241
|
+
docstring,
|
|
242
|
+
filePath,
|
|
243
|
+
lineNumber,
|
|
244
|
+
parentClass: range.name,
|
|
245
|
+
isAsync: false,
|
|
246
|
+
isExported: true,
|
|
247
|
+
isPublic: true,
|
|
248
|
+
imports,
|
|
249
|
+
packageName,
|
|
250
|
+
sourceContext: this.getSourceContext(lines, lineNumber)
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
findClassRanges(source) {
|
|
256
|
+
const ranges = [];
|
|
257
|
+
const typeRegex = /^[ \t]*public\s+(?:(?:abstract|sealed|static|partial)\s+)*(?:(class|interface|enum|struct|record))\s+(\w+)/gm;
|
|
258
|
+
let match;
|
|
259
|
+
while ((match = typeRegex.exec(source)) !== null) {
|
|
260
|
+
const kind = match[1];
|
|
261
|
+
const name = match[2];
|
|
262
|
+
if (!kind || !name)
|
|
263
|
+
continue;
|
|
264
|
+
// Find the opening brace after this match
|
|
265
|
+
// But first check if a semicolon comes before the brace (e.g., single-line records)
|
|
266
|
+
const afterMatchStart = match.index + match[0].length;
|
|
267
|
+
const afterMatch = source.indexOf('{', afterMatchStart);
|
|
268
|
+
if (afterMatch === -1)
|
|
269
|
+
continue;
|
|
270
|
+
const semicolonBefore = source.indexOf(';', afterMatchStart);
|
|
271
|
+
if (semicolonBefore !== -1 && semicolonBefore < afterMatch)
|
|
272
|
+
continue;
|
|
273
|
+
// Find matching closing brace
|
|
274
|
+
const bodyEnd = this.findMatchingBrace(source, afterMatch);
|
|
275
|
+
if (bodyEnd === -1)
|
|
276
|
+
continue;
|
|
277
|
+
ranges.push({
|
|
278
|
+
name,
|
|
279
|
+
kind,
|
|
280
|
+
start: match.index,
|
|
281
|
+
bodyStart: afterMatch + 1,
|
|
282
|
+
bodyEnd
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
return ranges;
|
|
286
|
+
}
|
|
287
|
+
findMatchingBrace(source, openIndex) {
|
|
288
|
+
let depth = 0;
|
|
289
|
+
for (let i = openIndex; i < source.length; i++) {
|
|
290
|
+
if (source[i] === '{')
|
|
291
|
+
depth++;
|
|
292
|
+
else if (source[i] === '}') {
|
|
293
|
+
depth--;
|
|
294
|
+
if (depth === 0)
|
|
295
|
+
return i;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return -1;
|
|
299
|
+
}
|
|
300
|
+
findParentClass(index, classRanges) {
|
|
301
|
+
for (const range of classRanges) {
|
|
302
|
+
if (index > range.bodyStart && index < range.bodyEnd) {
|
|
303
|
+
return range.name;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return undefined;
|
|
307
|
+
}
|
|
308
|
+
parseCSharpParams(paramsStr) {
|
|
309
|
+
if (!paramsStr.trim())
|
|
310
|
+
return [];
|
|
311
|
+
const params = [];
|
|
312
|
+
const parts = this.splitParams(paramsStr);
|
|
313
|
+
for (const part of parts) {
|
|
314
|
+
const trimmed = part.trim();
|
|
315
|
+
if (!trimmed)
|
|
316
|
+
continue;
|
|
317
|
+
// Remove parameter modifiers: ref, out, in, params, this
|
|
318
|
+
let cleaned = trimmed.replace(/^(?:ref|out|in|params|this)\s+/, '');
|
|
319
|
+
// Format: "Type name" or "Type name = default"
|
|
320
|
+
const defaultMatch = cleaned.match(/^(.+?)\s*=\s*(.+)$/);
|
|
321
|
+
let defaultValue;
|
|
322
|
+
if (defaultMatch) {
|
|
323
|
+
cleaned = defaultMatch[1] ?? cleaned;
|
|
324
|
+
defaultValue = defaultMatch[2];
|
|
325
|
+
}
|
|
326
|
+
// Split into type and name: last word is name, rest is type
|
|
327
|
+
const lastSpace = cleaned.lastIndexOf(' ');
|
|
328
|
+
if (lastSpace > 0) {
|
|
329
|
+
const type = cleaned.slice(0, lastSpace).trim();
|
|
330
|
+
const name = cleaned.slice(lastSpace + 1).trim();
|
|
331
|
+
params.push({ name, type, default: defaultValue });
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
// Just a name or type
|
|
335
|
+
params.push({ name: cleaned, type: undefined });
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return params;
|
|
339
|
+
}
|
|
340
|
+
splitParams(str) {
|
|
341
|
+
const parts = [];
|
|
342
|
+
let depth = 0;
|
|
343
|
+
let current = '';
|
|
344
|
+
for (const char of str) {
|
|
345
|
+
if (char === '<' || char === '(' || char === '[' || char === '{')
|
|
346
|
+
depth++;
|
|
347
|
+
else if (char === '>' || char === ')' || char === ']' || char === '}')
|
|
348
|
+
depth--;
|
|
349
|
+
else if (char === ',' && depth === 0) {
|
|
350
|
+
parts.push(current);
|
|
351
|
+
current = '';
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
current += char;
|
|
355
|
+
}
|
|
356
|
+
if (current.trim())
|
|
357
|
+
parts.push(current);
|
|
358
|
+
return parts;
|
|
359
|
+
}
|
|
360
|
+
getLineNumber(source, index) {
|
|
361
|
+
return source.slice(0, index).split('\n').length;
|
|
362
|
+
}
|
|
363
|
+
getDocComment(lines, lineIndex) {
|
|
364
|
+
const comments = [];
|
|
365
|
+
let i = lineIndex - 1;
|
|
366
|
+
while (i >= 0) {
|
|
367
|
+
const lineContent = lines[i];
|
|
368
|
+
if (!lineContent)
|
|
369
|
+
break;
|
|
370
|
+
const line = lineContent.trim();
|
|
371
|
+
if (line.startsWith('///')) {
|
|
372
|
+
// XML doc comment line
|
|
373
|
+
let content = line.replace(/^\/\/\/\s?/, '');
|
|
374
|
+
// Strip XML tags like <summary>, </summary>, <param>, <returns>
|
|
375
|
+
content = content
|
|
376
|
+
.replace(/<\/?summary>/g, '')
|
|
377
|
+
.replace(/<\/?remarks>/g, '')
|
|
378
|
+
.replace(/<\/?returns>/g, '')
|
|
379
|
+
.replace(/<param\s+name="[^"]*">/g, '')
|
|
380
|
+
.replace(/<\/param>/g, '')
|
|
381
|
+
.trim();
|
|
382
|
+
if (content) {
|
|
383
|
+
comments.unshift(content);
|
|
384
|
+
}
|
|
385
|
+
i--;
|
|
386
|
+
}
|
|
387
|
+
else if (line === '') {
|
|
388
|
+
i--;
|
|
389
|
+
}
|
|
390
|
+
else if (line.startsWith('[') && line.endsWith(']')) {
|
|
391
|
+
i--;
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
break;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return comments.length > 0 ? comments.join('\n') : undefined;
|
|
398
|
+
}
|
|
399
|
+
extractGenerics(source, startIndex) {
|
|
400
|
+
if (startIndex >= source.length || source[startIndex] !== '<')
|
|
401
|
+
return '';
|
|
402
|
+
let depth = 0;
|
|
403
|
+
let i = startIndex;
|
|
404
|
+
while (i < source.length) {
|
|
405
|
+
if (source[i] === '<')
|
|
406
|
+
depth++;
|
|
407
|
+
else if (source[i] === '>') {
|
|
408
|
+
depth--;
|
|
409
|
+
if (depth === 0)
|
|
410
|
+
return source.slice(startIndex, i + 1);
|
|
411
|
+
}
|
|
412
|
+
i++;
|
|
413
|
+
}
|
|
414
|
+
return '';
|
|
415
|
+
}
|
|
416
|
+
getSourceContext(lines, lineNumber, context = 5) {
|
|
417
|
+
const start = Math.max(0, lineNumber - context - 1);
|
|
418
|
+
const end = Math.min(lines.length, lineNumber + context);
|
|
419
|
+
return lines.slice(start, end).join('\n');
|
|
420
|
+
}
|
|
421
|
+
}
|
package/dist/scanner/index.js
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import { readdir, stat } from 'fs/promises';
|
|
2
|
-
import { join,
|
|
2
|
+
import { join, parse as parsePath } from 'path';
|
|
3
3
|
import { PythonScanner } from './python.js';
|
|
4
4
|
import { TypeScriptScanner } from './typescript.js';
|
|
5
5
|
import { GoScanner } from './go.js';
|
|
6
6
|
import { RustScanner } from './rust.js';
|
|
7
|
+
import { JavaScanner } from './java.js';
|
|
8
|
+
import { CSharpScanner } from './csharp.js';
|
|
9
|
+
import { PHPScanner } from './php.js';
|
|
10
|
+
import { KotlinScanner } from './kotlin.js';
|
|
11
|
+
import { SwiftScanner } from './swift.js';
|
|
12
|
+
import { RubyScanner } from './ruby.js';
|
|
7
13
|
export * from './types.js';
|
|
8
14
|
export * from './content-type.js';
|
|
9
15
|
// All available scanners
|
|
@@ -12,6 +18,12 @@ const SCANNERS = [
|
|
|
12
18
|
new TypeScriptScanner(),
|
|
13
19
|
new GoScanner(),
|
|
14
20
|
new RustScanner(),
|
|
21
|
+
new JavaScanner(),
|
|
22
|
+
new CSharpScanner(),
|
|
23
|
+
new PHPScanner(),
|
|
24
|
+
new KotlinScanner(),
|
|
25
|
+
new SwiftScanner(),
|
|
26
|
+
new RubyScanner(),
|
|
15
27
|
];
|
|
16
28
|
/**
|
|
17
29
|
* Get the appropriate scanner for a file
|
|
@@ -24,25 +36,30 @@ function getScannerForFile(filePath) {
|
|
|
24
36
|
}
|
|
25
37
|
return null;
|
|
26
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Convert a glob pattern to a RegExp for proper matching.
|
|
41
|
+
* Supports: ** (any path), * (any segment), ? (any char)
|
|
42
|
+
*/
|
|
43
|
+
function globToRegex(pattern) {
|
|
44
|
+
// Normalize separators
|
|
45
|
+
let re = pattern.replace(/\\/g, '/');
|
|
46
|
+
// Escape regex special chars except * and ?
|
|
47
|
+
re = re.replace(/[.+^${}()|[\]\\]/g, '\\$&');
|
|
48
|
+
// Convert glob tokens to regex
|
|
49
|
+
re = re.replace(/\*\*/g, '\0GLOBSTAR\0');
|
|
50
|
+
re = re.replace(/\*/g, '[^/]*');
|
|
51
|
+
re = re.replace(/\?/g, '[^/]');
|
|
52
|
+
re = re.replace(/\0GLOBSTAR\0/g, '.*');
|
|
53
|
+
return new RegExp(`(^|/)${re}($|/)`);
|
|
54
|
+
}
|
|
27
55
|
/**
|
|
28
56
|
* Check if a path should be excluded based on patterns
|
|
29
57
|
*/
|
|
30
58
|
function shouldExclude(filePath, excludePatterns) {
|
|
59
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
31
60
|
for (const pattern of excludePatterns) {
|
|
32
|
-
|
|
33
|
-
if (pattern.includes('**')) {
|
|
34
|
-
const parts = pattern.split('**');
|
|
35
|
-
if (parts.length === 2) {
|
|
36
|
-
const [prefix, suffix] = parts;
|
|
37
|
-
const prefixMatch = !prefix || filePath.includes(prefix.replace(/^\//, '').replace(/\/$/, ''));
|
|
38
|
-
const suffixMatch = !suffix || filePath.includes(suffix.replace(/^\//, '').replace(/\/$/, ''));
|
|
39
|
-
if (prefixMatch && suffixMatch)
|
|
40
|
-
return true;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
else if (filePath.includes(pattern.replace(/\*/g, ''))) {
|
|
61
|
+
if (globToRegex(pattern).test(normalized))
|
|
44
62
|
return true;
|
|
45
|
-
}
|
|
46
63
|
}
|
|
47
64
|
return false;
|
|
48
65
|
}
|
|
@@ -52,31 +69,39 @@ function shouldExclude(filePath, excludePatterns) {
|
|
|
52
69
|
function shouldInclude(filePath, includePatterns) {
|
|
53
70
|
if (includePatterns.length === 0)
|
|
54
71
|
return true;
|
|
55
|
-
const
|
|
72
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
56
73
|
for (const pattern of includePatterns) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const extPattern = pattern.split('*').pop() || '';
|
|
60
|
-
if (ext === extPattern)
|
|
61
|
-
return true;
|
|
62
|
-
}
|
|
74
|
+
if (globToRegex(pattern).test(normalized))
|
|
75
|
+
return true;
|
|
63
76
|
}
|
|
64
77
|
return false;
|
|
65
78
|
}
|
|
79
|
+
const MAX_SCAN_DEPTH = 30;
|
|
80
|
+
const MAX_SCAN_FILES = 10000;
|
|
66
81
|
/**
|
|
67
82
|
* Recursively find all files in a directory
|
|
68
83
|
*/
|
|
69
84
|
async function findFiles(dir, include, exclude) {
|
|
70
85
|
const files = [];
|
|
71
|
-
async function walk(currentDir) {
|
|
86
|
+
async function walk(currentDir, depth) {
|
|
87
|
+
if (depth > MAX_SCAN_DEPTH)
|
|
88
|
+
return;
|
|
89
|
+
if (files.length >= MAX_SCAN_FILES)
|
|
90
|
+
return;
|
|
72
91
|
const entries = await readdir(currentDir, { withFileTypes: true });
|
|
73
92
|
for (const entry of entries) {
|
|
93
|
+
if (files.length >= MAX_SCAN_FILES)
|
|
94
|
+
return;
|
|
74
95
|
const fullPath = join(currentDir, entry.name);
|
|
75
96
|
if (shouldExclude(fullPath, exclude)) {
|
|
76
97
|
continue;
|
|
77
98
|
}
|
|
99
|
+
// Skip symlinks to prevent infinite loops and path escape
|
|
100
|
+
if (entry.isSymbolicLink()) {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
78
103
|
if (entry.isDirectory()) {
|
|
79
|
-
await walk(fullPath);
|
|
104
|
+
await walk(fullPath, depth + 1);
|
|
80
105
|
}
|
|
81
106
|
else if (entry.isFile()) {
|
|
82
107
|
if (shouldInclude(fullPath, include) && getScannerForFile(fullPath)) {
|
|
@@ -85,15 +110,15 @@ async function findFiles(dir, include, exclude) {
|
|
|
85
110
|
}
|
|
86
111
|
}
|
|
87
112
|
}
|
|
88
|
-
await walk(dir);
|
|
113
|
+
await walk(dir, 0);
|
|
89
114
|
return files;
|
|
90
115
|
}
|
|
91
116
|
/**
|
|
92
117
|
* Scan a directory (or single file) for all API elements
|
|
93
118
|
*/
|
|
94
119
|
export async function scanDirectory(dir, options = {}) {
|
|
95
|
-
const include = options.include || ['**/*.py', '**/*.ts', '**/*.js', '**/*.go', '**/*.rs'];
|
|
96
|
-
const exclude = options.exclude || ['**/node_modules/**', '**/__pycache__/**', '**/dist/**'];
|
|
120
|
+
const include = options.include || ['**/*.py', '**/*.ts', '**/*.js', '**/*.go', '**/*.rs', '**/*.java', '**/*.cs', '**/*.php', '**/*.kt', '**/*.kts', '**/*.swift', '**/*.rb'];
|
|
121
|
+
const exclude = options.exclude || ['**/node_modules/**', '**/__pycache__/**', '**/dist/**', '**/target/**', '**/vendor/**', '**/build/**', '**/bin/**', '**/obj/**', '**/.build/**'];
|
|
97
122
|
// Check if input is a file or directory
|
|
98
123
|
const stats = await stat(dir);
|
|
99
124
|
let files;
|
|
@@ -139,6 +164,8 @@ export async function scanFile(filePath) {
|
|
|
139
164
|
const langMap = {
|
|
140
165
|
ts: 'typescript', tsx: 'typescript', js: 'javascript', jsx: 'javascript',
|
|
141
166
|
py: 'python', go: 'go', rs: 'rust',
|
|
167
|
+
java: 'java', cs: 'csharp', php: 'php',
|
|
168
|
+
kt: 'kotlin', kts: 'kotlin', swift: 'swift', rb: 'ruby',
|
|
142
169
|
};
|
|
143
170
|
const language = langMap[ext] ?? 'typescript';
|
|
144
171
|
return {
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Scanner, ScanResult } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Scanner for Java source files
|
|
4
|
+
* Extracts: public classes, public methods, public static methods
|
|
5
|
+
*/
|
|
6
|
+
export declare class JavaScanner implements Scanner {
|
|
7
|
+
languages: string[];
|
|
8
|
+
canHandle(filePath: string): boolean;
|
|
9
|
+
scanFile(filePath: string): Promise<ScanResult>;
|
|
10
|
+
private extractPackage;
|
|
11
|
+
private extractImports;
|
|
12
|
+
/**
|
|
13
|
+
* Extracts the full generics string starting at startIndex by counting `<>` depth.
|
|
14
|
+
* Returns empty string if source[startIndex] is not '<'.
|
|
15
|
+
*/
|
|
16
|
+
private extractGenerics;
|
|
17
|
+
private extractClasses;
|
|
18
|
+
private extractMethods;
|
|
19
|
+
/**
|
|
20
|
+
* Extracts interface methods that don't have explicit `public` modifier.
|
|
21
|
+
* In Java, interface methods are implicitly public.
|
|
22
|
+
*/
|
|
23
|
+
private extractInterfaceMethods;
|
|
24
|
+
/**
|
|
25
|
+
* Finds all class/interface/enum/record declarations and their brace-delimited ranges.
|
|
26
|
+
*/
|
|
27
|
+
private findClassRanges;
|
|
28
|
+
private findMatchingBrace;
|
|
29
|
+
/**
|
|
30
|
+
* Finds the innermost class that contains the given source index,
|
|
31
|
+
* using brace-depth-aware class ranges.
|
|
32
|
+
*/
|
|
33
|
+
private findParentClass;
|
|
34
|
+
private parseParams;
|
|
35
|
+
private splitParams;
|
|
36
|
+
private getLineNumber;
|
|
37
|
+
private getJavadoc;
|
|
38
|
+
private getSourceContext;
|
|
39
|
+
}
|