sigmap 1.5.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/.contextignore.example +34 -0
- package/CHANGELOG.md +402 -0
- package/LICENSE +21 -0
- package/README.md +601 -0
- package/gen-context.config.json.example +40 -0
- package/gen-context.js +4316 -0
- package/gen-project-map.js +172 -0
- package/package.json +67 -0
- package/src/config/defaults.js +61 -0
- package/src/config/loader.js +60 -0
- package/src/extractors/cpp.js +60 -0
- package/src/extractors/csharp.js +48 -0
- package/src/extractors/css.js +51 -0
- package/src/extractors/dart.js +58 -0
- package/src/extractors/dockerfile.js +49 -0
- package/src/extractors/go.js +61 -0
- package/src/extractors/html.js +39 -0
- package/src/extractors/java.js +49 -0
- package/src/extractors/javascript.js +82 -0
- package/src/extractors/kotlin.js +62 -0
- package/src/extractors/php.js +62 -0
- package/src/extractors/python.js +69 -0
- package/src/extractors/ruby.js +43 -0
- package/src/extractors/rust.js +72 -0
- package/src/extractors/scala.js +67 -0
- package/src/extractors/shell.js +43 -0
- package/src/extractors/svelte.js +51 -0
- package/src/extractors/swift.js +63 -0
- package/src/extractors/typescript.js +109 -0
- package/src/extractors/vue.js +66 -0
- package/src/extractors/yaml.js +59 -0
- package/src/format/cache.js +53 -0
- package/src/health/scorer.js +123 -0
- package/src/map/class-hierarchy.js +117 -0
- package/src/map/import-graph.js +148 -0
- package/src/map/route-table.js +127 -0
- package/src/mcp/handlers.js +433 -0
- package/src/mcp/server.js +128 -0
- package/src/mcp/tools.js +125 -0
- package/src/routing/classifier.js +102 -0
- package/src/routing/hints.js +103 -0
- package/src/security/patterns.js +51 -0
- package/src/security/scanner.js +36 -0
- package/src/tracking/logger.js +115 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extract signatures from HTML files.
|
|
5
|
+
* Focuses on id/class attributes, forms, and script tags.
|
|
6
|
+
* @param {string} src - Raw file content
|
|
7
|
+
* @returns {string[]} Array of signature strings
|
|
8
|
+
*/
|
|
9
|
+
function extract(src) {
|
|
10
|
+
if (!src || typeof src !== 'string') return [];
|
|
11
|
+
const sigs = [];
|
|
12
|
+
|
|
13
|
+
// Page title
|
|
14
|
+
const titleMatch = src.match(/<title[^>]*>([\s\S]*?)<\/title>/i);
|
|
15
|
+
if (titleMatch) sigs.push(`title: ${titleMatch[1].trim()}`);
|
|
16
|
+
|
|
17
|
+
// Forms with id/action
|
|
18
|
+
for (const m of src.matchAll(/<form\s+([^>]*)>/gi)) {
|
|
19
|
+
const attrs = m[1];
|
|
20
|
+
const id = attrs.match(/id=["']?(\w+)/i);
|
|
21
|
+
const action = attrs.match(/action=["']?([^"'\s>]+)/i);
|
|
22
|
+
if (id) sigs.push(`form#${id[1]}${action ? ` action="${action[1]}"` : ''}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Elements with id
|
|
26
|
+
for (const m of src.matchAll(/<(\w+)\s+[^>]*id=["'](\w+)["'][^>]*>/gi)) {
|
|
27
|
+
if (['html', 'head', 'body', 'script', 'style', 'link', 'meta'].includes(m[1].toLowerCase())) continue;
|
|
28
|
+
sigs.push(`${m[1]}#${m[2]}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Data attributes (data-component, data-controller etc)
|
|
32
|
+
for (const m of src.matchAll(/data-(?:component|controller|view|page)=["'](\w[\w-]*)/gi)) {
|
|
33
|
+
sigs.push(`data-${m[0].match(/data-(\w[\w-]*)/i)[1]}: ${m[1]}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return sigs.slice(0, 25);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = { extract };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extract signatures from Java source code.
|
|
5
|
+
* @param {string} src - Raw file content
|
|
6
|
+
* @returns {string[]} Array of signature strings
|
|
7
|
+
*/
|
|
8
|
+
function extract(src) {
|
|
9
|
+
if (!src || typeof src !== 'string') return [];
|
|
10
|
+
const sigs = [];
|
|
11
|
+
|
|
12
|
+
const stripped = src
|
|
13
|
+
.replace(/\/\/.*$/gm, '')
|
|
14
|
+
.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
15
|
+
|
|
16
|
+
// Classes and interfaces
|
|
17
|
+
const typeRegex = /^(?:public\s+|protected\s+)?(?:abstract\s+|final\s+)?(class|interface|enum)\s+(\w+)(?:\s+extends\s+[\w<>, .]+)?(?:\s+implements\s+[\w<>, .]+)?\s*\{/gm;
|
|
18
|
+
for (const m of stripped.matchAll(typeRegex)) {
|
|
19
|
+
sigs.push(`${m[1]} ${m[2]}`);
|
|
20
|
+
const block = extractBlock(stripped, m.index + m[0].length);
|
|
21
|
+
for (const meth of extractMembers(block)) sigs.push(` ${meth}`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return sigs.slice(0, 25);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function extractBlock(src, startIndex) {
|
|
28
|
+
let depth = 1;
|
|
29
|
+
let i = startIndex;
|
|
30
|
+
const end = Math.min(src.length, startIndex + 5000);
|
|
31
|
+
while (i < end && depth > 0) {
|
|
32
|
+
if (src[i] === '{') depth++;
|
|
33
|
+
else if (src[i] === '}') depth--;
|
|
34
|
+
i++;
|
|
35
|
+
}
|
|
36
|
+
return src.slice(startIndex, i - 1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function extractMembers(block) {
|
|
40
|
+
const members = [];
|
|
41
|
+
const methodRe = /^\s+(?:public|protected)\s+(?:static\s+)?(?:final\s+)?(?:[\w<>\[\]]+\s+)+(\w+)\s*\(([^)]*)\)/gm;
|
|
42
|
+
for (const m of block.matchAll(methodRe)) {
|
|
43
|
+
const sig = m[0].trim().split('{')[0].trim();
|
|
44
|
+
members.push(sig);
|
|
45
|
+
}
|
|
46
|
+
return members.slice(0, 8);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = { extract };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extract signatures from JavaScript source code.
|
|
5
|
+
* @param {string} src - Raw file content
|
|
6
|
+
* @returns {string[]} Array of signature strings
|
|
7
|
+
*/
|
|
8
|
+
function extract(src) {
|
|
9
|
+
if (!src || typeof src !== 'string') return [];
|
|
10
|
+
const sigs = [];
|
|
11
|
+
|
|
12
|
+
const stripped = src
|
|
13
|
+
.replace(/\/\/.*$/gm, '')
|
|
14
|
+
.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
15
|
+
|
|
16
|
+
// Classes
|
|
17
|
+
const classRegex = /^(export\s+(?:default\s+)?)?class\s+(\w+)(?:\s+extends\s+[\w.]+)?\s*\{/gm;
|
|
18
|
+
for (const m of stripped.matchAll(classRegex)) {
|
|
19
|
+
const prefix = m[1] ? m[1].trim() + ' ' : '';
|
|
20
|
+
sigs.push(`${prefix}class ${m[2]}`);
|
|
21
|
+
const block = extractBlock(stripped, m.index + m[0].length);
|
|
22
|
+
for (const meth of extractClassMembers(block)) sigs.push(` ${meth}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Exported named functions
|
|
26
|
+
for (const m of stripped.matchAll(/^export\s+(?:async\s+)?function\s+(\w+)\s*\(([^)]*)\)/gm)) {
|
|
27
|
+
const asyncKw = /export\s+async/.test(m[0]) ? 'async ' : '';
|
|
28
|
+
sigs.push(`export ${asyncKw}function ${m[1]}(${normalizeParams(m[2])})`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Exported arrow functions
|
|
32
|
+
for (const m of stripped.matchAll(/^export\s+const\s+(\w+)\s*=\s*(?:async\s+)?\(([^)]*)\)\s*=>/gm)) {
|
|
33
|
+
const asyncKw = m[0].includes('async') ? 'async ' : '';
|
|
34
|
+
sigs.push(`export const ${m[1]} = ${asyncKw}(${normalizeParams(m[2])}) =>`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// module.exports = { ... }
|
|
38
|
+
const moduleExports = stripped.match(/^module\.exports\s*=\s*\{([^}]+)\}/m);
|
|
39
|
+
if (moduleExports) {
|
|
40
|
+
const names = moduleExports[1].split(',').map((s) => s.trim()).filter(Boolean);
|
|
41
|
+
if (names.length > 0) sigs.push(`module.exports = { ${names.join(', ')} }`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Top-level named functions (non-exported)
|
|
45
|
+
for (const m of stripped.matchAll(/^(?:async\s+)?function\s+(\w+)\s*\(([^)]*)\)/gm)) {
|
|
46
|
+
const asyncKw = m[0].startsWith('async') ? 'async ' : '';
|
|
47
|
+
sigs.push(`${asyncKw}function ${m[1]}(${normalizeParams(m[2])})`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return sigs.slice(0, 25);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function extractBlock(src, startIndex) {
|
|
54
|
+
let depth = 1;
|
|
55
|
+
let i = startIndex;
|
|
56
|
+
const end = Math.min(src.length, startIndex + 4000);
|
|
57
|
+
while (i < end && depth > 0) {
|
|
58
|
+
if (src[i] === '{') depth++;
|
|
59
|
+
else if (src[i] === '}') depth--;
|
|
60
|
+
i++;
|
|
61
|
+
}
|
|
62
|
+
return src.slice(startIndex, i - 1);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function extractClassMembers(block) {
|
|
66
|
+
const members = [];
|
|
67
|
+
for (const m of block.matchAll(/^\s+(?:static\s+|async\s+|get\s+|set\s+)*(\w+)\s*\(([^)]*)\)\s*\{/gm)) {
|
|
68
|
+
if (/^_/.test(m[1])) continue;
|
|
69
|
+
if (m[1] === 'constructor') { members.push(`constructor(${normalizeParams(m[2])})`); continue; }
|
|
70
|
+
const isAsync = m[0].includes('async ') ? 'async ' : '';
|
|
71
|
+
const isStatic = m[0].includes('static ') ? 'static ' : '';
|
|
72
|
+
members.push(`${isStatic}${isAsync}${m[1]}(${normalizeParams(m[2])})`);
|
|
73
|
+
}
|
|
74
|
+
return members.slice(0, 8);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function normalizeParams(params) {
|
|
78
|
+
if (!params) return '';
|
|
79
|
+
return params.trim().replace(/\s+/g, ' ');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
module.exports = { extract };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extract signatures from Kotlin source code.
|
|
5
|
+
* @param {string} src - Raw file content
|
|
6
|
+
* @returns {string[]} Array of signature strings
|
|
7
|
+
*/
|
|
8
|
+
function extract(src) {
|
|
9
|
+
if (!src || typeof src !== 'string') return [];
|
|
10
|
+
const sigs = [];
|
|
11
|
+
|
|
12
|
+
const stripped = src
|
|
13
|
+
.replace(/\/\/.*$/gm, '')
|
|
14
|
+
.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
15
|
+
|
|
16
|
+
// Classes, objects, interfaces
|
|
17
|
+
for (const m of stripped.matchAll(/^(?:public\s+|internal\s+)?(?:data\s+|sealed\s+|abstract\s+|open\s+)?(class|object|interface)\s+(\w+)(?:[^{]*)\{/gm)) {
|
|
18
|
+
sigs.push(`${m[1]} ${m[2]}`);
|
|
19
|
+
const block = extractBlock(stripped, m.index + m[0].length);
|
|
20
|
+
for (const meth of extractMembers(block)) sigs.push(` ${meth}`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Top-level functions
|
|
24
|
+
for (const m of stripped.matchAll(/^(?:public\s+|internal\s+)?(?:suspend\s+)?fun\s+(\w+)\s*(?:<[^(]*>)?\s*\(([^)]*)\)/gm)) {
|
|
25
|
+
const suspend = m[0].includes('suspend') ? 'suspend ' : '';
|
|
26
|
+
sigs.push(`${suspend}fun ${m[1]}(${normalizeParams(m[2])})`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return sigs.slice(0, 25);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function extractBlock(src, startIndex) {
|
|
33
|
+
let depth = 1, i = startIndex;
|
|
34
|
+
const end = Math.min(src.length, startIndex + 4000);
|
|
35
|
+
while (i < end && depth > 0) {
|
|
36
|
+
if (src[i] === '{') depth++;
|
|
37
|
+
else if (src[i] === '}') depth--;
|
|
38
|
+
i++;
|
|
39
|
+
}
|
|
40
|
+
return src.slice(startIndex, i - 1);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function extractMembers(block) {
|
|
44
|
+
const members = [];
|
|
45
|
+
for (const m of block.matchAll(/^\s+(?:public\s+|internal\s+|override\s+)?(?:suspend\s+)?fun\s+(\w+)\s*(?:<[^(]*>)?\s*\(([^)]*)\)/gm)) {
|
|
46
|
+
if (m[1].startsWith('_')) continue;
|
|
47
|
+
const suspend = m[0].includes('suspend') ? 'suspend ' : '';
|
|
48
|
+
members.push(`${suspend}fun ${m[1]}(${normalizeParams(m[2])})`);
|
|
49
|
+
}
|
|
50
|
+
return members.slice(0, 8);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function normalizeParams(params) {
|
|
54
|
+
if (!params) return '';
|
|
55
|
+
return params.trim()
|
|
56
|
+
.split(',')
|
|
57
|
+
.map((p) => p.trim().split(':')[0].trim())
|
|
58
|
+
.filter(Boolean)
|
|
59
|
+
.join(', ');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = { extract };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extract signatures from PHP source code.
|
|
5
|
+
* @param {string} src - Raw file content
|
|
6
|
+
* @returns {string[]} Array of signature strings
|
|
7
|
+
*/
|
|
8
|
+
function extract(src) {
|
|
9
|
+
if (!src || typeof src !== 'string') return [];
|
|
10
|
+
const sigs = [];
|
|
11
|
+
|
|
12
|
+
const stripped = src
|
|
13
|
+
.replace(/\/\/.*$/gm, '')
|
|
14
|
+
.replace(/#.*$/gm, '')
|
|
15
|
+
.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
16
|
+
|
|
17
|
+
// Classes and interfaces
|
|
18
|
+
const typeRe = /^(?:abstract\s+)?(?:class|interface|trait)\s+(\w+)(?:\s+extends\s+\w+)?(?:\s+implements\s+[\w, ]+)?\s*\{/gm;
|
|
19
|
+
for (const m of stripped.matchAll(typeRe)) {
|
|
20
|
+
const kind = m[0].trimStart().startsWith('interface') ? 'interface' :
|
|
21
|
+
m[0].trimStart().startsWith('trait') ? 'trait' : 'class';
|
|
22
|
+
sigs.push(`${kind} ${m[1]}`);
|
|
23
|
+
const block = extractBlock(stripped, m.index + m[0].length);
|
|
24
|
+
for (const meth of extractMembers(block)) sigs.push(` ${meth}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Top-level functions
|
|
28
|
+
for (const m of stripped.matchAll(/^function\s+(\w+)\s*\(([^)]*)\)/gm)) {
|
|
29
|
+
sigs.push(`function ${m[1]}(${normalizeParams(m[2])})`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return sigs.slice(0, 25);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function extractBlock(src, startIndex) {
|
|
36
|
+
let depth = 1, i = startIndex;
|
|
37
|
+
const end = Math.min(src.length, startIndex + 4000);
|
|
38
|
+
while (i < end && depth > 0) {
|
|
39
|
+
if (src[i] === '{') depth++;
|
|
40
|
+
else if (src[i] === '}') depth--;
|
|
41
|
+
i++;
|
|
42
|
+
}
|
|
43
|
+
return src.slice(startIndex, i - 1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function extractMembers(block) {
|
|
47
|
+
const members = [];
|
|
48
|
+
const methodRe = /^\s+(?:public|protected)\s+(?:static\s+)?function\s+(\w+)\s*\(([^)]*)\)/gm;
|
|
49
|
+
for (const m of block.matchAll(methodRe)) {
|
|
50
|
+
if (m[1].startsWith('_')) continue;
|
|
51
|
+
const isStatic = m[0].includes('static ') ? 'static ' : '';
|
|
52
|
+
members.push(`${isStatic}function ${m[1]}(${normalizeParams(m[2])})`);
|
|
53
|
+
}
|
|
54
|
+
return members.slice(0, 8);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function normalizeParams(params) {
|
|
58
|
+
if (!params) return '';
|
|
59
|
+
return params.trim().replace(/\s+/g, ' ');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = { extract };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extract signatures from Python source code.
|
|
5
|
+
* @param {string} src - Raw file content
|
|
6
|
+
* @returns {string[]} Array of signature strings
|
|
7
|
+
*/
|
|
8
|
+
function extract(src) {
|
|
9
|
+
if (!src || typeof src !== 'string') return [];
|
|
10
|
+
const sigs = [];
|
|
11
|
+
|
|
12
|
+
// Strip comments and docstrings (simple approach)
|
|
13
|
+
const stripped = src
|
|
14
|
+
.replace(/#.*$/gm, '')
|
|
15
|
+
.replace(/"""[\s\S]*?"""/g, '')
|
|
16
|
+
.replace(/'''[\s\S]*?'''/g, '');
|
|
17
|
+
|
|
18
|
+
// Classes
|
|
19
|
+
for (const m of stripped.matchAll(/^class\s+(\w+)(?:\s*\(([^)]*)\))?\s*:/gm)) {
|
|
20
|
+
const base = m[2] ? `(${m[2].trim()})` : '';
|
|
21
|
+
sigs.push(`class ${m[1]}${base}`);
|
|
22
|
+
// Get class body methods
|
|
23
|
+
const bodyStart = m.index + m[0].length;
|
|
24
|
+
const methods = extractClassMethods(stripped, bodyStart);
|
|
25
|
+
for (const meth of methods) sigs.push(` ${meth}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Top-level functions
|
|
29
|
+
for (const m of stripped.matchAll(/^(?:async\s+)?def\s+(\w+)\s*\(([^)]*)\)/gm)) {
|
|
30
|
+
if (/^_/.test(m[1])) continue; // skip private
|
|
31
|
+
const asyncKw = m[0].trimStart().startsWith('async') ? 'async ' : '';
|
|
32
|
+
const params = normalizeParams(m[2]);
|
|
33
|
+
sigs.push(`${asyncKw}def ${m[1]}(${params})`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return sigs.slice(0, 25);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function extractClassMethods(src, startIndex) {
|
|
40
|
+
const methods = [];
|
|
41
|
+
// Extract indented block
|
|
42
|
+
const lines = src.slice(startIndex).split('\n');
|
|
43
|
+
for (const line of lines) {
|
|
44
|
+
if (line.trim() === '') continue;
|
|
45
|
+
// End of class body: line with no leading indent that is not blank
|
|
46
|
+
const indent = line.match(/^(\s+)/);
|
|
47
|
+
if (!indent) break;
|
|
48
|
+
const m = line.match(/^\s+(?:async\s+)?def\s+(\w+)\s*\(([^)]*)\)/);
|
|
49
|
+
if (m) {
|
|
50
|
+
if (m[1].startsWith('__') && m[1] !== '__init__') continue;
|
|
51
|
+
if (m[1].startsWith('_') && !m[1].startsWith('__')) continue;
|
|
52
|
+
const asyncKw = line.trimStart().startsWith('async') ? 'async ' : '';
|
|
53
|
+
const params = normalizeParams(m[2]).replace(/^self,?\s*/, '');
|
|
54
|
+
methods.push(`${asyncKw}def ${m[1]}(${params})`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return methods.slice(0, 8);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function normalizeParams(params) {
|
|
61
|
+
if (!params) return '';
|
|
62
|
+
return params.trim()
|
|
63
|
+
.split(',')
|
|
64
|
+
.map((p) => p.trim().split(':')[0].split('=')[0].trim())
|
|
65
|
+
.filter(Boolean)
|
|
66
|
+
.join(', ');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
module.exports = { extract };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extract signatures from Ruby source code.
|
|
5
|
+
* @param {string} src - Raw file content
|
|
6
|
+
* @returns {string[]} Array of signature strings
|
|
7
|
+
*/
|
|
8
|
+
function extract(src) {
|
|
9
|
+
if (!src || typeof src !== 'string') return [];
|
|
10
|
+
const sigs = [];
|
|
11
|
+
|
|
12
|
+
const stripped = src.replace(/#.*$/gm, '');
|
|
13
|
+
|
|
14
|
+
// Modules and classes
|
|
15
|
+
for (const m of stripped.matchAll(/^(?:module|class)\s+([\w:]+)(?:\s*<\s*[\w:]+)?\s*$/gm)) {
|
|
16
|
+
const kind = m[0].trimStart().startsWith('module') ? 'module' : 'class';
|
|
17
|
+
sigs.push(`${kind} ${m[1]}`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Public methods (not private/protected)
|
|
21
|
+
for (const m of stripped.matchAll(/^\s+def\s+(?:self\.)?(\w+)(?:\s*\(([^)]*)\))?/gm)) {
|
|
22
|
+
if (m[1].startsWith('_')) continue;
|
|
23
|
+
const params = m[2] ? `(${normalizeParams(m[2])})` : '';
|
|
24
|
+
const selfPrefix = m[0].includes('self.') ? 'self.' : '';
|
|
25
|
+
sigs.push(` def ${selfPrefix}${m[1]}${params}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Top-level def
|
|
29
|
+
for (const m of stripped.matchAll(/^def\s+(\w+)(?:\s*\(([^)]*)\))?/gm)) {
|
|
30
|
+
if (m[1].startsWith('_')) continue;
|
|
31
|
+
const params = m[2] ? `(${normalizeParams(m[2])})` : '';
|
|
32
|
+
sigs.push(`def ${m[1]}${params}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return sigs.slice(0, 25);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function normalizeParams(params) {
|
|
39
|
+
if (!params) return '';
|
|
40
|
+
return params.trim().replace(/\s+/g, ' ');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = { extract };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extract signatures from Rust source code.
|
|
5
|
+
* @param {string} src - Raw file content
|
|
6
|
+
* @returns {string[]} Array of signature strings
|
|
7
|
+
*/
|
|
8
|
+
function extract(src) {
|
|
9
|
+
if (!src || typeof src !== 'string') return [];
|
|
10
|
+
const sigs = [];
|
|
11
|
+
|
|
12
|
+
const stripped = src
|
|
13
|
+
.replace(/\/\/.*$/gm, '')
|
|
14
|
+
.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
15
|
+
|
|
16
|
+
// Structs
|
|
17
|
+
for (const m of stripped.matchAll(/^pub\s+struct\s+(\w+)(?:<[^{]*>)?/gm)) {
|
|
18
|
+
sigs.push(`pub struct ${m[1]}`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Enums
|
|
22
|
+
for (const m of stripped.matchAll(/^pub\s+enum\s+(\w+)(?:<[^{]*>)?/gm)) {
|
|
23
|
+
sigs.push(`pub enum ${m[1]}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Traits
|
|
27
|
+
for (const m of stripped.matchAll(/^pub\s+trait\s+(\w+)(?:<[^{]*>)?/gm)) {
|
|
28
|
+
sigs.push(`pub trait ${m[1]}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// impl blocks
|
|
32
|
+
for (const m of stripped.matchAll(/^impl(?:<[^>]*>)?\s+(?:[\w:]+\s+for\s+)?(\w+)(?:<[^{]*>)?\s*\{/gm)) {
|
|
33
|
+
sigs.push(`impl ${m[1]}`);
|
|
34
|
+
const block = extractBlock(stripped, m.index + m[0].length);
|
|
35
|
+
for (const fn of extractMethods(block)) sigs.push(` ${fn}`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Top-level pub fns
|
|
39
|
+
for (const m of stripped.matchAll(/^pub(?:\s+async)?\s+fn\s+(\w+)(?:<[^(]*>)?\s*\(([^)]*)\)/gm)) {
|
|
40
|
+
const asyncKw = m[0].includes('async') ? 'async ' : '';
|
|
41
|
+
sigs.push(`pub ${asyncKw}fn ${m[1]}(${normalizeParams(m[2])})`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return sigs.slice(0, 25);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function extractBlock(src, startIndex) {
|
|
48
|
+
let depth = 1, i = startIndex;
|
|
49
|
+
const end = Math.min(src.length, startIndex + 5000);
|
|
50
|
+
while (i < end && depth > 0) {
|
|
51
|
+
if (src[i] === '{') depth++;
|
|
52
|
+
else if (src[i] === '}') depth--;
|
|
53
|
+
i++;
|
|
54
|
+
}
|
|
55
|
+
return src.slice(startIndex, i - 1);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function extractMethods(block) {
|
|
59
|
+
const methods = [];
|
|
60
|
+
for (const m of block.matchAll(/^\s+pub(?:\s+async)?\s+fn\s+(\w+)(?:<[^(]*>)?\s*\(([^)]*)\)/gm)) {
|
|
61
|
+
const asyncKw = m[0].includes('async') ? 'async ' : '';
|
|
62
|
+
methods.push(`pub ${asyncKw}fn ${m[1]}(${normalizeParams(m[2])})`);
|
|
63
|
+
}
|
|
64
|
+
return methods.slice(0, 8);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function normalizeParams(params) {
|
|
68
|
+
if (!params) return '';
|
|
69
|
+
return params.trim().replace(/\s+/g, ' ');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
module.exports = { extract };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extract signatures from Scala source code.
|
|
5
|
+
* @param {string} src - Raw file content
|
|
6
|
+
* @returns {string[]} Array of signature strings
|
|
7
|
+
*/
|
|
8
|
+
function extract(src) {
|
|
9
|
+
if (!src || typeof src !== 'string') return [];
|
|
10
|
+
const sigs = [];
|
|
11
|
+
|
|
12
|
+
const stripped = src
|
|
13
|
+
.replace(/\/\/.*$/gm, '')
|
|
14
|
+
.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
15
|
+
|
|
16
|
+
// Classes, traits, objects
|
|
17
|
+
const typeRe = /^(?:case\s+)?(?:class|trait|object)\s+(\w+)(?:\[[\w, ]+\])?(?:[^{]*)\{/gm;
|
|
18
|
+
for (const m of stripped.matchAll(typeRe)) {
|
|
19
|
+
const kind = m[0].trimStart().startsWith('case class') ? 'case class' :
|
|
20
|
+
m[0].trimStart().startsWith('trait') ? 'trait' :
|
|
21
|
+
m[0].trimStart().startsWith('object') ? 'object' : 'class';
|
|
22
|
+
sigs.push(`${kind} ${m[1]}`);
|
|
23
|
+
const block = extractBlock(stripped, m.index + m[0].length);
|
|
24
|
+
for (const fn of extractMembers(block)) sigs.push(` ${fn}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Top-level defs
|
|
28
|
+
for (const m of stripped.matchAll(/^def\s+(\w+)(?:\[[\w, ]+\])?\s*(?:\(([^)]*)\))?/gm)) {
|
|
29
|
+
if (m[1].startsWith('_')) continue;
|
|
30
|
+
const params = m[2] ? `(${normalizeParams(m[2])})` : '';
|
|
31
|
+
sigs.push(`def ${m[1]}${params}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return sigs.slice(0, 25);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function extractBlock(src, startIndex) {
|
|
38
|
+
let depth = 1, i = startIndex;
|
|
39
|
+
const end = Math.min(src.length, startIndex + 4000);
|
|
40
|
+
while (i < end && depth > 0) {
|
|
41
|
+
if (src[i] === '{') depth++;
|
|
42
|
+
else if (src[i] === '}') depth--;
|
|
43
|
+
i++;
|
|
44
|
+
}
|
|
45
|
+
return src.slice(startIndex, i - 1);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function extractMembers(block) {
|
|
49
|
+
const members = [];
|
|
50
|
+
for (const m of block.matchAll(/^\s+def\s+(\w+)(?:\[[\w, ]+\])?\s*(?:\(([^)]*)\))?/gm)) {
|
|
51
|
+
if (m[1].startsWith('_')) continue;
|
|
52
|
+
const params = m[2] ? `(${normalizeParams(m[2])})` : '';
|
|
53
|
+
members.push(`def ${m[1]}${params}`);
|
|
54
|
+
}
|
|
55
|
+
return members.slice(0, 8);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function normalizeParams(params) {
|
|
59
|
+
if (!params) return '';
|
|
60
|
+
return params.trim()
|
|
61
|
+
.split(',')
|
|
62
|
+
.map((p) => p.trim().split(':')[0].trim())
|
|
63
|
+
.filter(Boolean)
|
|
64
|
+
.join(', ');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
module.exports = { extract };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extract signatures from shell scripts (bash, zsh, fish).
|
|
5
|
+
* @param {string} src - Raw file content
|
|
6
|
+
* @returns {string[]} Array of signature strings
|
|
7
|
+
*/
|
|
8
|
+
function extract(src) {
|
|
9
|
+
if (!src || typeof src !== 'string') return [];
|
|
10
|
+
const sigs = [];
|
|
11
|
+
|
|
12
|
+
const stripped = src.replace(/#.*$/gm, '');
|
|
13
|
+
|
|
14
|
+
// Function definitions (bash: name() { and function name {)
|
|
15
|
+
for (const m of stripped.matchAll(/^(?:function\s+)?([\w:-]+)\s*\(\s*\)\s*\{/gm)) {
|
|
16
|
+
if (m[1].startsWith('_')) continue;
|
|
17
|
+
sigs.push(`function ${m[1]}()`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Main entry point patterns
|
|
21
|
+
if (/^\s*main\s*\(/m.test(src) || /^\s*main\s+"?\$@/m.test(src)) {
|
|
22
|
+
sigs.push('main "$@"');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Exported variables
|
|
26
|
+
for (const m of stripped.matchAll(/^export\s+([\w]+)=/gm)) {
|
|
27
|
+
sigs.push(`export ${m[1]}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Script description (first non-comment line after shebang)
|
|
31
|
+
const lines = src.split('\n');
|
|
32
|
+
for (const line of lines.slice(0, 5)) {
|
|
33
|
+
if (line.startsWith('#!')) continue;
|
|
34
|
+
if (line.startsWith('#')) {
|
|
35
|
+
const desc = line.replace(/^#+\s*/, '').trim();
|
|
36
|
+
if (desc) { sigs.unshift(`# ${desc}`); break; }
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return sigs.slice(0, 25);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = { extract };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extract signatures from Svelte components.
|
|
5
|
+
* @param {string} src - Raw file content
|
|
6
|
+
* @returns {string[]} Array of signature strings
|
|
7
|
+
*/
|
|
8
|
+
function extract(src) {
|
|
9
|
+
if (!src || typeof src !== 'string') return [];
|
|
10
|
+
const sigs = [];
|
|
11
|
+
|
|
12
|
+
// Extract <script> block
|
|
13
|
+
const scriptMatch = src.match(/<script(?:\s[^>]*)?>(?:\s*)([\s\S]*?)<\/script>/i);
|
|
14
|
+
if (!scriptMatch) return sigs;
|
|
15
|
+
|
|
16
|
+
const script = scriptMatch[1]
|
|
17
|
+
.replace(/\/\/.*$/gm, '')
|
|
18
|
+
.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
19
|
+
|
|
20
|
+
// Exported props (writable)
|
|
21
|
+
for (const m of script.matchAll(/^\s+export\s+let\s+(\w+)(?:\s*=\s*[^;]+)?;/gm)) {
|
|
22
|
+
sigs.push(`export let ${m[1]}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Exported functions
|
|
26
|
+
for (const m of script.matchAll(/^export\s+(?:async\s+)?function\s+(\w+)\s*\(([^)]*)\)/gm)) {
|
|
27
|
+
const asyncKw = m[0].includes('async') ? 'async ' : '';
|
|
28
|
+
sigs.push(`export ${asyncKw}function ${m[1]}(${normalizeParams(m[2])})`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Top-level functions
|
|
32
|
+
for (const m of script.matchAll(/^(?:async\s+)?function\s+(\w+)\s*\(([^)]*)\)/gm)) {
|
|
33
|
+
if (m[1].startsWith('_')) continue;
|
|
34
|
+
const asyncKw = m[0].startsWith('async') ? 'async ' : '';
|
|
35
|
+
sigs.push(`${asyncKw}function ${m[1]}(${normalizeParams(m[2])})`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Reactive declarations $:
|
|
39
|
+
for (const m of script.matchAll(/^\s+\$:\s+(\w+)\s*=/gm)) {
|
|
40
|
+
sigs.push(`$: ${m[1]}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return sigs.slice(0, 25);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function normalizeParams(params) {
|
|
47
|
+
if (!params) return '';
|
|
48
|
+
return params.trim().replace(/\s+/g, ' ');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = { extract };
|