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.
Files changed (44) hide show
  1. package/.contextignore.example +34 -0
  2. package/CHANGELOG.md +402 -0
  3. package/LICENSE +21 -0
  4. package/README.md +601 -0
  5. package/gen-context.config.json.example +40 -0
  6. package/gen-context.js +4316 -0
  7. package/gen-project-map.js +172 -0
  8. package/package.json +67 -0
  9. package/src/config/defaults.js +61 -0
  10. package/src/config/loader.js +60 -0
  11. package/src/extractors/cpp.js +60 -0
  12. package/src/extractors/csharp.js +48 -0
  13. package/src/extractors/css.js +51 -0
  14. package/src/extractors/dart.js +58 -0
  15. package/src/extractors/dockerfile.js +49 -0
  16. package/src/extractors/go.js +61 -0
  17. package/src/extractors/html.js +39 -0
  18. package/src/extractors/java.js +49 -0
  19. package/src/extractors/javascript.js +82 -0
  20. package/src/extractors/kotlin.js +62 -0
  21. package/src/extractors/php.js +62 -0
  22. package/src/extractors/python.js +69 -0
  23. package/src/extractors/ruby.js +43 -0
  24. package/src/extractors/rust.js +72 -0
  25. package/src/extractors/scala.js +67 -0
  26. package/src/extractors/shell.js +43 -0
  27. package/src/extractors/svelte.js +51 -0
  28. package/src/extractors/swift.js +63 -0
  29. package/src/extractors/typescript.js +109 -0
  30. package/src/extractors/vue.js +66 -0
  31. package/src/extractors/yaml.js +59 -0
  32. package/src/format/cache.js +53 -0
  33. package/src/health/scorer.js +123 -0
  34. package/src/map/class-hierarchy.js +117 -0
  35. package/src/map/import-graph.js +148 -0
  36. package/src/map/route-table.js +127 -0
  37. package/src/mcp/handlers.js +433 -0
  38. package/src/mcp/server.js +128 -0
  39. package/src/mcp/tools.js +125 -0
  40. package/src/routing/classifier.js +102 -0
  41. package/src/routing/hints.js +103 -0
  42. package/src/security/patterns.js +51 -0
  43. package/src/security/scanner.js +36 -0
  44. 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 };