skrypt-ai 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/index.js +8 -1
- package/dist/autofix/index.d.ts +0 -4
- package/dist/autofix/index.js +0 -21
- package/dist/capture/browser.d.ts +11 -0
- package/dist/capture/browser.js +173 -0
- package/dist/capture/diff.d.ts +23 -0
- package/dist/capture/diff.js +52 -0
- package/dist/capture/index.d.ts +23 -0
- package/dist/capture/index.js +210 -0
- package/dist/capture/naming.d.ts +17 -0
- package/dist/capture/naming.js +45 -0
- package/dist/capture/parser.d.ts +15 -0
- package/dist/capture/parser.js +80 -0
- package/dist/capture/types.d.ts +57 -0
- package/dist/capture/types.js +1 -0
- package/dist/cli.js +4 -0
- package/dist/commands/autofix.js +136 -120
- package/dist/commands/cron.js +58 -47
- package/dist/commands/deploy.js +123 -102
- package/dist/commands/generate.js +88 -6
- package/dist/commands/heal.d.ts +10 -0
- package/dist/commands/heal.js +201 -0
- package/dist/commands/i18n.js +146 -111
- package/dist/commands/lint.js +50 -44
- package/dist/commands/llms-txt.js +59 -49
- package/dist/commands/login.js +61 -43
- package/dist/commands/mcp.js +6 -0
- package/dist/commands/monitor.js +13 -8
- package/dist/commands/qa.d.ts +2 -0
- package/dist/commands/qa.js +43 -0
- package/dist/commands/review-pr.js +108 -102
- package/dist/commands/sdk.js +128 -122
- package/dist/commands/security.js +86 -80
- package/dist/commands/test.js +91 -92
- package/dist/commands/version.js +104 -75
- package/dist/commands/watch.js +130 -114
- package/dist/config/types.js +2 -2
- package/dist/context-hub/index.d.ts +23 -0
- package/dist/context-hub/index.js +179 -0
- package/dist/context-hub/mappings.d.ts +8 -0
- package/dist/context-hub/mappings.js +55 -0
- package/dist/context-hub/types.d.ts +33 -0
- package/dist/context-hub/types.js +1 -0
- package/dist/generator/generator.js +39 -6
- package/dist/generator/types.d.ts +7 -0
- package/dist/generator/writer.d.ts +3 -1
- package/dist/generator/writer.js +24 -4
- package/dist/llm/anthropic-client.d.ts +1 -0
- package/dist/llm/anthropic-client.js +3 -1
- package/dist/llm/index.d.ts +6 -4
- package/dist/llm/index.js +76 -261
- package/dist/llm/openai-client.d.ts +1 -0
- package/dist/llm/openai-client.js +7 -2
- package/dist/qa/checks.d.ts +10 -0
- package/dist/qa/checks.js +492 -0
- package/dist/qa/fixes.d.ts +30 -0
- package/dist/qa/fixes.js +277 -0
- package/dist/qa/index.d.ts +29 -0
- package/dist/qa/index.js +187 -0
- package/dist/qa/types.d.ts +24 -0
- package/dist/qa/types.js +1 -0
- package/dist/scanner/csharp.d.ts +23 -0
- package/dist/scanner/csharp.js +421 -0
- package/dist/scanner/index.js +16 -2
- package/dist/scanner/java.d.ts +39 -0
- package/dist/scanner/java.js +318 -0
- package/dist/scanner/kotlin.d.ts +23 -0
- package/dist/scanner/kotlin.js +389 -0
- package/dist/scanner/php.d.ts +57 -0
- package/dist/scanner/php.js +351 -0
- package/dist/scanner/ruby.d.ts +36 -0
- package/dist/scanner/ruby.js +431 -0
- package/dist/scanner/swift.d.ts +25 -0
- package/dist/scanner/swift.js +392 -0
- package/dist/scanner/types.d.ts +1 -1
- package/dist/template/content/docs/_navigation.json +46 -0
- package/dist/template/content/docs/_sidebars.json +684 -0
- package/dist/template/content/docs/core.md +4544 -0
- package/dist/template/content/docs/index.mdx +89 -0
- package/dist/template/content/docs/integrations.md +1158 -0
- package/dist/template/content/docs/llms-full.md +403 -0
- package/dist/template/content/docs/llms.txt +4588 -0
- package/dist/template/content/docs/other.md +10379 -0
- package/dist/template/content/docs/tools.md +746 -0
- package/dist/template/content/docs/types.md +531 -0
- package/dist/template/docs.json +13 -11
- package/dist/template/mdx-components.tsx +27 -2
- package/dist/template/package.json +6 -0
- package/dist/template/public/search-index.json +1 -1
- package/dist/template/scripts/build-search-index.mjs +84 -6
- package/dist/template/src/app/api/chat/route.ts +83 -128
- package/dist/template/src/app/docs/[...slug]/page.tsx +75 -20
- package/dist/template/src/app/docs/llms-full.md +151 -4
- package/dist/template/src/app/docs/llms.txt +2464 -847
- package/dist/template/src/app/docs/page.mdx +48 -38
- package/dist/template/src/app/layout.tsx +3 -1
- package/dist/template/src/app/page.tsx +22 -8
- package/dist/template/src/components/ai-chat.tsx +73 -64
- package/dist/template/src/components/breadcrumbs.tsx +21 -23
- package/dist/template/src/components/copy-button.tsx +13 -9
- package/dist/template/src/components/copy-page-button.tsx +54 -0
- package/dist/template/src/components/docs-layout.tsx +37 -25
- package/dist/template/src/components/header.tsx +51 -10
- package/dist/template/src/components/mdx/card.tsx +17 -3
- package/dist/template/src/components/mdx/code-block.tsx +13 -9
- package/dist/template/src/components/mdx/code-group.tsx +13 -8
- package/dist/template/src/components/mdx/heading.tsx +15 -2
- package/dist/template/src/components/mdx/highlighted-code.tsx +13 -8
- package/dist/template/src/components/mdx/index.tsx +2 -0
- package/dist/template/src/components/mdx/mermaid.tsx +110 -0
- package/dist/template/src/components/mdx/screenshot.tsx +150 -0
- package/dist/template/src/components/scroll-to-hash.tsx +48 -0
- package/dist/template/src/components/sidebar.tsx +12 -18
- package/dist/template/src/components/table-of-contents.tsx +9 -0
- package/dist/template/src/lib/highlight.ts +3 -88
- package/dist/template/src/lib/navigation.ts +159 -0
- package/dist/template/src/styles/globals.css +17 -6
- package/dist/utils/validation.d.ts +0 -3
- package/dist/utils/validation.js +0 -26
- package/package.json +3 -2
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
/**
|
|
3
|
+
* Scanner for Kotlin source files
|
|
4
|
+
* Extracts: classes, data classes, sealed classes, objects, interfaces, enum classes, functions
|
|
5
|
+
*/
|
|
6
|
+
export class KotlinScanner {
|
|
7
|
+
languages = ['kotlin'];
|
|
8
|
+
canHandle(filePath) {
|
|
9
|
+
return /\.kts?$/.test(filePath) &&
|
|
10
|
+
!filePath.includes('Test.kt') &&
|
|
11
|
+
!filePath.includes('/test/');
|
|
12
|
+
}
|
|
13
|
+
async scanFile(filePath) {
|
|
14
|
+
try {
|
|
15
|
+
const source = readFileSync(filePath, 'utf-8');
|
|
16
|
+
const elements = [];
|
|
17
|
+
const errors = [];
|
|
18
|
+
const lines = source.split('\n');
|
|
19
|
+
// Extract package name
|
|
20
|
+
const packageMatch = source.match(/^package\s+([\w.]+)/m);
|
|
21
|
+
const packageName = packageMatch?.[1] ?? 'unknown';
|
|
22
|
+
// Extract imports for context
|
|
23
|
+
const imports = this.extractImports(source);
|
|
24
|
+
// Find all classes, interfaces, objects, enums
|
|
25
|
+
this.extractTypes(source, lines, filePath, packageName, imports, elements);
|
|
26
|
+
// Find top-level and class-level functions
|
|
27
|
+
this.extractFunctions(source, lines, filePath, packageName, imports, elements);
|
|
28
|
+
return {
|
|
29
|
+
filePath,
|
|
30
|
+
language: 'kotlin',
|
|
31
|
+
elements,
|
|
32
|
+
errors
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
return {
|
|
37
|
+
filePath,
|
|
38
|
+
language: 'kotlin',
|
|
39
|
+
elements: [],
|
|
40
|
+
errors: [`Failed to parse: ${err}`]
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
extractImports(source) {
|
|
45
|
+
const imports = [];
|
|
46
|
+
const regex = /^import\s+([\w.*]+)/gm;
|
|
47
|
+
let match;
|
|
48
|
+
while ((match = regex.exec(source)) !== null) {
|
|
49
|
+
if (match[1])
|
|
50
|
+
imports.push(match[1]);
|
|
51
|
+
}
|
|
52
|
+
return imports;
|
|
53
|
+
}
|
|
54
|
+
extractParenContent(source, openIndex) {
|
|
55
|
+
if (source[openIndex] !== '(')
|
|
56
|
+
return null;
|
|
57
|
+
let depth = 0;
|
|
58
|
+
for (let i = openIndex; i < source.length; i++) {
|
|
59
|
+
if (source[i] === '(')
|
|
60
|
+
depth++;
|
|
61
|
+
else if (source[i] === ')') {
|
|
62
|
+
depth--;
|
|
63
|
+
if (depth === 0)
|
|
64
|
+
return source.slice(openIndex + 1, i);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
extractGenerics(source, startIndex) {
|
|
70
|
+
if (startIndex >= source.length || source[startIndex] !== '<')
|
|
71
|
+
return '';
|
|
72
|
+
let depth = 0;
|
|
73
|
+
let i = startIndex;
|
|
74
|
+
while (i < source.length) {
|
|
75
|
+
if (source[i] === '<')
|
|
76
|
+
depth++;
|
|
77
|
+
else if (source[i] === '>') {
|
|
78
|
+
depth--;
|
|
79
|
+
if (depth === 0)
|
|
80
|
+
return source.slice(startIndex, i + 1);
|
|
81
|
+
}
|
|
82
|
+
i++;
|
|
83
|
+
}
|
|
84
|
+
return '';
|
|
85
|
+
}
|
|
86
|
+
extractTypes(source, lines, filePath, packageName, imports, elements) {
|
|
87
|
+
// Match: class, data class, sealed class, abstract class, object, interface, enum class
|
|
88
|
+
// Captures optional leading whitespace, visibility modifier, type keyword(s), and name
|
|
89
|
+
// Generics and constructor params are extracted post-match via helpers
|
|
90
|
+
const typeRegex = /^[ \t]*(?:(?:public|open)\s+)?(?:(data|sealed|enum|abstract|annotation|value|inner)\s+)?(class|interface|object)\s+(\w+)/gm;
|
|
91
|
+
let match;
|
|
92
|
+
while ((match = typeRegex.exec(source)) !== null) {
|
|
93
|
+
const modifier = match[1] ?? ''; // data, sealed, enum, abstract
|
|
94
|
+
const keyword = match[2]; // class, interface, object
|
|
95
|
+
const name = match[3];
|
|
96
|
+
if (!keyword || !name)
|
|
97
|
+
continue;
|
|
98
|
+
const lineNumber = this.getLineNumber(source, match.index);
|
|
99
|
+
// Skip private and internal types
|
|
100
|
+
const fullLine = lines[lineNumber - 1];
|
|
101
|
+
if (fullLine && /\b(private|internal)\b/.test(fullLine))
|
|
102
|
+
continue;
|
|
103
|
+
const docstring = this.getDocComment(lines, lineNumber - 1);
|
|
104
|
+
// Extract generics post-match (handles nested generics like <T : Comparable<T>>)
|
|
105
|
+
let afterName = match.index + match[0].length;
|
|
106
|
+
// Skip whitespace
|
|
107
|
+
while (afterName < source.length && /\s/.test(source[afterName]))
|
|
108
|
+
afterName++;
|
|
109
|
+
const generics = this.extractGenerics(source, afterName);
|
|
110
|
+
if (generics)
|
|
111
|
+
afterName += generics.length;
|
|
112
|
+
// Skip whitespace after generics
|
|
113
|
+
while (afterName < source.length && /\s/.test(source[afterName]))
|
|
114
|
+
afterName++;
|
|
115
|
+
// Extract constructor params via depth-counting helper
|
|
116
|
+
let paramsStr;
|
|
117
|
+
if (source[afterName] === '(') {
|
|
118
|
+
const content = this.extractParenContent(source, afterName);
|
|
119
|
+
if (content !== null)
|
|
120
|
+
paramsStr = content;
|
|
121
|
+
}
|
|
122
|
+
// Build signature
|
|
123
|
+
const prefix = modifier ? `${modifier} ${keyword}` : keyword;
|
|
124
|
+
const signature = `${prefix} ${name}${generics}${paramsStr !== undefined ? `(${paramsStr})` : ''}`;
|
|
125
|
+
// Parse constructor params for data classes
|
|
126
|
+
const parameters = paramsStr !== undefined ? this.parseKotlinParams(paramsStr) : [];
|
|
127
|
+
elements.push({
|
|
128
|
+
kind: 'class',
|
|
129
|
+
name,
|
|
130
|
+
signature,
|
|
131
|
+
parameters,
|
|
132
|
+
docstring,
|
|
133
|
+
filePath,
|
|
134
|
+
lineNumber,
|
|
135
|
+
isExported: true,
|
|
136
|
+
isPublic: true,
|
|
137
|
+
imports,
|
|
138
|
+
packageName,
|
|
139
|
+
sourceContext: this.getSourceContext(lines, lineNumber, 15)
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
extractFunctions(source, lines, filePath, packageName, imports, elements) {
|
|
144
|
+
// Find class bodies so we can determine parentClass for methods
|
|
145
|
+
const classMap = this.buildClassMap(source);
|
|
146
|
+
// Match up to the opening paren; params and generics extracted via helpers
|
|
147
|
+
// Skip private and internal functions
|
|
148
|
+
const funcRegex = /^(\s*)(?:(?:public|protected|open|override)\s+)*(suspend\s+)?fun\s+(?:[\w<>,\s?*]+\.)?([\w]+)\s*(?:<|\()/gm;
|
|
149
|
+
let match;
|
|
150
|
+
while ((match = funcRegex.exec(source)) !== null) {
|
|
151
|
+
const indent = match[1] ?? '';
|
|
152
|
+
const suspendKeyword = match[2];
|
|
153
|
+
const name = match[3];
|
|
154
|
+
if (!name)
|
|
155
|
+
continue;
|
|
156
|
+
// Check if this function is preceded by 'private' or 'internal' on the same line
|
|
157
|
+
const lineNumber = this.getLineNumber(source, match.index);
|
|
158
|
+
const fullLine = lines[lineNumber - 1];
|
|
159
|
+
if (fullLine && /\b(private|internal)\b/.test(fullLine))
|
|
160
|
+
continue;
|
|
161
|
+
// After the function name, extract optional generics and then params
|
|
162
|
+
const nameEnd = match.index + match[0].length - 1; // position of '<' or '('
|
|
163
|
+
let cursor = nameEnd;
|
|
164
|
+
// Skip whitespace before potential generics
|
|
165
|
+
while (cursor < source.length && /\s/.test(source[cursor]))
|
|
166
|
+
cursor++;
|
|
167
|
+
// Extract generics if present (handles nested like <T : Comparable<T>>)
|
|
168
|
+
let generics = '';
|
|
169
|
+
if (source[cursor] === '<') {
|
|
170
|
+
generics = this.extractGenerics(source, cursor);
|
|
171
|
+
if (generics)
|
|
172
|
+
cursor += generics.length;
|
|
173
|
+
// Skip whitespace after generics
|
|
174
|
+
while (cursor < source.length && /\s/.test(source[cursor]))
|
|
175
|
+
cursor++;
|
|
176
|
+
}
|
|
177
|
+
// Extract params via depth-counting
|
|
178
|
+
if (source[cursor] !== '(')
|
|
179
|
+
continue;
|
|
180
|
+
const paramsStr = this.extractParenContent(source, cursor);
|
|
181
|
+
if (paramsStr === null)
|
|
182
|
+
continue;
|
|
183
|
+
// Move cursor past closing paren
|
|
184
|
+
cursor += paramsStr.length + 2; // +2 for '(' and ')'
|
|
185
|
+
// Extract return type: look for ': ReturnType' before '{', '=', or newline
|
|
186
|
+
let returnType;
|
|
187
|
+
let afterParen = cursor;
|
|
188
|
+
while (afterParen < source.length && /\s/.test(source[afterParen]))
|
|
189
|
+
afterParen++;
|
|
190
|
+
if (source[afterParen] === ':') {
|
|
191
|
+
afterParen++; // skip ':'
|
|
192
|
+
while (afterParen < source.length && source[afterParen] === ' ')
|
|
193
|
+
afterParen++;
|
|
194
|
+
let retEnd = afterParen;
|
|
195
|
+
while (retEnd < source.length && source[retEnd] !== '\n' && source[retEnd] !== '{' && source[retEnd] !== '=') {
|
|
196
|
+
retEnd++;
|
|
197
|
+
}
|
|
198
|
+
const retStr = source.slice(afterParen, retEnd).trim();
|
|
199
|
+
if (retStr)
|
|
200
|
+
returnType = retStr;
|
|
201
|
+
}
|
|
202
|
+
// Verify the line ends with '{', '=', or end-of-line (to avoid matching partial text)
|
|
203
|
+
// This is already handled by the extraction logic above
|
|
204
|
+
const isAsync = !!suspendKeyword;
|
|
205
|
+
const parentClass = this.findParentClass(source, match.index, classMap, indent);
|
|
206
|
+
const isSuspend = isAsync ? 'suspend ' : '';
|
|
207
|
+
const signature = `${isSuspend}fun ${name}${generics}(${paramsStr})${returnType ? ': ' + returnType : ''}`;
|
|
208
|
+
const parameters = this.parseKotlinParams(paramsStr);
|
|
209
|
+
const docstring = this.getDocComment(lines, lineNumber - 1);
|
|
210
|
+
elements.push({
|
|
211
|
+
kind: parentClass ? 'method' : 'function',
|
|
212
|
+
name,
|
|
213
|
+
signature,
|
|
214
|
+
parameters,
|
|
215
|
+
returnType,
|
|
216
|
+
docstring,
|
|
217
|
+
filePath,
|
|
218
|
+
lineNumber,
|
|
219
|
+
parentClass,
|
|
220
|
+
isAsync,
|
|
221
|
+
isExported: true,
|
|
222
|
+
isPublic: true,
|
|
223
|
+
imports,
|
|
224
|
+
packageName,
|
|
225
|
+
sourceContext: this.getSourceContext(lines, lineNumber)
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
buildClassMap(source) {
|
|
230
|
+
const classes = [];
|
|
231
|
+
// Match class/object/interface declarations with opening brace (including indented nested classes)
|
|
232
|
+
const classRegex = /^[ \t]*(?:(?:public|open|abstract|sealed|data|enum)\s+)*(?:class|interface|object)\s+(\w+)[^{]*\{/gm;
|
|
233
|
+
let match;
|
|
234
|
+
while ((match = classRegex.exec(source)) !== null) {
|
|
235
|
+
const name = match[1];
|
|
236
|
+
if (!name)
|
|
237
|
+
continue;
|
|
238
|
+
const start = match.index;
|
|
239
|
+
const braceStart = source.indexOf('{', start + match[0].length - 1);
|
|
240
|
+
const end = this.findClosingBrace(source, braceStart);
|
|
241
|
+
if (end > start) {
|
|
242
|
+
classes.push({ name, start, end });
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return classes;
|
|
246
|
+
}
|
|
247
|
+
findClosingBrace(source, openBraceIndex) {
|
|
248
|
+
let depth = 0;
|
|
249
|
+
for (let i = openBraceIndex; i < source.length; i++) {
|
|
250
|
+
if (source[i] === '{')
|
|
251
|
+
depth++;
|
|
252
|
+
else if (source[i] === '}') {
|
|
253
|
+
depth--;
|
|
254
|
+
if (depth === 0)
|
|
255
|
+
return i;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return source.length;
|
|
259
|
+
}
|
|
260
|
+
findParentClass(source, index, classMap, indent) {
|
|
261
|
+
// A function is a method if it's inside a class body (indented or within class braces)
|
|
262
|
+
for (const cls of classMap) {
|
|
263
|
+
if (index > cls.start && index < cls.end && indent.length > 0) {
|
|
264
|
+
return cls.name;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return undefined;
|
|
268
|
+
}
|
|
269
|
+
parseKotlinParams(paramsStr) {
|
|
270
|
+
if (!paramsStr.trim())
|
|
271
|
+
return [];
|
|
272
|
+
const params = [];
|
|
273
|
+
const parts = this.splitParams(paramsStr);
|
|
274
|
+
for (const part of parts) {
|
|
275
|
+
let trimmed = part.trim();
|
|
276
|
+
if (!trimmed)
|
|
277
|
+
continue;
|
|
278
|
+
// Remove val/var prefix (constructor params)
|
|
279
|
+
trimmed = trimmed.replace(/^(val|var)\s+/, '');
|
|
280
|
+
// Format: "name: Type" or "name: Type = default"
|
|
281
|
+
const colonIdx = trimmed.indexOf(':');
|
|
282
|
+
if (colonIdx > 0) {
|
|
283
|
+
const name = trimmed.slice(0, colonIdx).trim();
|
|
284
|
+
let typeAndDefault = trimmed.slice(colonIdx + 1).trim();
|
|
285
|
+
let defaultValue;
|
|
286
|
+
// Check for default value — split on '=' but not '==' or '=>'
|
|
287
|
+
const eqIdx = typeAndDefault.search(/(?<!=)=(?!=|>)/);
|
|
288
|
+
if (eqIdx > 0) {
|
|
289
|
+
defaultValue = typeAndDefault.slice(eqIdx + 1).trim();
|
|
290
|
+
typeAndDefault = typeAndDefault.slice(0, eqIdx).trim();
|
|
291
|
+
}
|
|
292
|
+
const type = typeAndDefault || undefined;
|
|
293
|
+
const param = { name, type };
|
|
294
|
+
if (defaultValue !== undefined) {
|
|
295
|
+
param.default = defaultValue;
|
|
296
|
+
}
|
|
297
|
+
params.push(param);
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
// Just a name with no type annotation
|
|
301
|
+
params.push({ name: trimmed, type: undefined });
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return params;
|
|
305
|
+
}
|
|
306
|
+
splitParams(str) {
|
|
307
|
+
const parts = [];
|
|
308
|
+
let depth = 0;
|
|
309
|
+
let current = '';
|
|
310
|
+
for (const char of str) {
|
|
311
|
+
if (char === '<' || char === '(' || char === '[' || char === '{')
|
|
312
|
+
depth++;
|
|
313
|
+
else if (char === '>' || char === ')' || char === ']' || char === '}')
|
|
314
|
+
depth--;
|
|
315
|
+
else if (char === ',' && depth === 0) {
|
|
316
|
+
parts.push(current);
|
|
317
|
+
current = '';
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
current += char;
|
|
321
|
+
}
|
|
322
|
+
if (current.trim())
|
|
323
|
+
parts.push(current);
|
|
324
|
+
return parts;
|
|
325
|
+
}
|
|
326
|
+
getLineNumber(source, index) {
|
|
327
|
+
return source.slice(0, index).split('\n').length;
|
|
328
|
+
}
|
|
329
|
+
getDocComment(lines, lineIndex) {
|
|
330
|
+
// Look for KDoc comments (/** ... */) above the declaration
|
|
331
|
+
let i = lineIndex - 1;
|
|
332
|
+
// Skip blank lines
|
|
333
|
+
while (i >= 0 && lines[i]?.trim() === '') {
|
|
334
|
+
i--;
|
|
335
|
+
}
|
|
336
|
+
if (i < 0)
|
|
337
|
+
return undefined;
|
|
338
|
+
const currentLine = lines[i];
|
|
339
|
+
if (!currentLine)
|
|
340
|
+
return undefined;
|
|
341
|
+
// Check if the line ends with */ (end of KDoc block)
|
|
342
|
+
if (currentLine.trim().endsWith('*/')) {
|
|
343
|
+
// Find the start of the block comment
|
|
344
|
+
let start = i;
|
|
345
|
+
while (start >= 0 && !lines[start]?.includes('/**')) {
|
|
346
|
+
start--;
|
|
347
|
+
}
|
|
348
|
+
if (start >= 0 && lines[start]) {
|
|
349
|
+
const block = lines.slice(start, i + 1).join('\n');
|
|
350
|
+
const cleaned = block
|
|
351
|
+
.replace(/\/\*\*/, '')
|
|
352
|
+
.replace(/\*\//, '')
|
|
353
|
+
.split('\n')
|
|
354
|
+
.map(l => l.trim().replace(/^\*\s?/, ''))
|
|
355
|
+
.filter(Boolean)
|
|
356
|
+
.join('\n');
|
|
357
|
+
return cleaned || undefined;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
// Check for single-line KDoc: /** ... */
|
|
361
|
+
const singleLineMatch = currentLine.trim().match(/^\/\*\*\s*(.*?)\s*\*\/$/);
|
|
362
|
+
if (singleLineMatch?.[1]) {
|
|
363
|
+
return singleLineMatch[1];
|
|
364
|
+
}
|
|
365
|
+
// Check for // style comments
|
|
366
|
+
const comments = [];
|
|
367
|
+
while (i >= 0) {
|
|
368
|
+
const line = lines[i]?.trim();
|
|
369
|
+
if (line === undefined)
|
|
370
|
+
break;
|
|
371
|
+
if (line.startsWith('//')) {
|
|
372
|
+
comments.unshift(line.replace(/^\/\/\s?/, ''));
|
|
373
|
+
i--;
|
|
374
|
+
}
|
|
375
|
+
else if (line === '') {
|
|
376
|
+
i--;
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
break;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
return comments.length > 0 ? comments.join('\n') : undefined;
|
|
383
|
+
}
|
|
384
|
+
getSourceContext(lines, lineNumber, context = 5) {
|
|
385
|
+
const start = Math.max(0, lineNumber - context - 1);
|
|
386
|
+
const end = Math.min(lines.length, lineNumber + context);
|
|
387
|
+
return lines.slice(start, end).join('\n');
|
|
388
|
+
}
|
|
389
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Scanner, ScanResult } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Scanner for PHP source files
|
|
4
|
+
* Extracts: classes, interfaces, enums, traits, abstract classes,
|
|
5
|
+
* public functions, public methods, public static methods
|
|
6
|
+
*/
|
|
7
|
+
export declare class PHPScanner implements Scanner {
|
|
8
|
+
languages: string[];
|
|
9
|
+
canHandle(filePath: string): boolean;
|
|
10
|
+
scanFile(filePath: string): Promise<ScanResult>;
|
|
11
|
+
private extractImports;
|
|
12
|
+
/**
|
|
13
|
+
* Extract classes, interfaces, enums, traits, abstract classes
|
|
14
|
+
*/
|
|
15
|
+
private extractTypes;
|
|
16
|
+
/**
|
|
17
|
+
* Extract the content between matching parentheses, handling nested parens.
|
|
18
|
+
* Starts at the given index which must point to an opening '('.
|
|
19
|
+
*/
|
|
20
|
+
private extractParenContent;
|
|
21
|
+
/**
|
|
22
|
+
* Extract public methods (instance and static) from classes/interfaces/traits
|
|
23
|
+
* Skips private and protected methods
|
|
24
|
+
*/
|
|
25
|
+
private extractMethods;
|
|
26
|
+
/**
|
|
27
|
+
* Extract standalone functions (not inside a class/interface/trait)
|
|
28
|
+
*/
|
|
29
|
+
private extractFunctions;
|
|
30
|
+
/**
|
|
31
|
+
* Build a map of class/interface/trait/enum boundaries (start offset, end offset, name)
|
|
32
|
+
*/
|
|
33
|
+
private getClassBoundaries;
|
|
34
|
+
/**
|
|
35
|
+
* Find the matching closing brace for an opening brace
|
|
36
|
+
*/
|
|
37
|
+
private findMatchingBrace;
|
|
38
|
+
/**
|
|
39
|
+
* Find which class/interface/trait contains the given source offset
|
|
40
|
+
*/
|
|
41
|
+
private findParentClass;
|
|
42
|
+
/**
|
|
43
|
+
* Parse PHP function parameters into Parameter objects
|
|
44
|
+
* Handles: type hints (?string, int|string, array), default values
|
|
45
|
+
*/
|
|
46
|
+
private parsePHPParams;
|
|
47
|
+
/**
|
|
48
|
+
* Split parameter string by commas, respecting nested brackets
|
|
49
|
+
*/
|
|
50
|
+
private splitParams;
|
|
51
|
+
private getLineNumber;
|
|
52
|
+
/**
|
|
53
|
+
* Extract PHPDoc block comment above a given line
|
|
54
|
+
*/
|
|
55
|
+
private getDocComment;
|
|
56
|
+
private getSourceContext;
|
|
57
|
+
}
|