sigmap 8.33.0 → 8.35.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 (48) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +12 -12
  3. package/gen-context.js +531 -74
  4. package/llms-full.txt +9 -9
  5. package/llms.txt +7 -7
  6. package/package.json +1 -1
  7. package/packages/cli/package.json +1 -1
  8. package/packages/core/index.js +1 -0
  9. package/packages/core/package.json +1 -1
  10. package/src/config/loader.js +1 -1
  11. package/src/config/tune.js +1 -1
  12. package/src/discovery/language-detector.js +1 -0
  13. package/src/discovery/source-root-registry.js +10 -1
  14. package/src/discovery/source-root-scorer.js +1 -1
  15. package/src/extractors/cpp.js +8 -3
  16. package/src/extractors/csharp.js +8 -3
  17. package/src/extractors/css.js +1 -1
  18. package/src/extractors/dart.js +8 -3
  19. package/src/extractors/deps.js +23 -1
  20. package/src/extractors/dispatch.js +2 -0
  21. package/src/extractors/dockerfile.js +1 -1
  22. package/src/extractors/gdscript.js +2 -2
  23. package/src/extractors/go.js +13 -5
  24. package/src/extractors/html.js +1 -1
  25. package/src/extractors/javascript.js +7 -3
  26. package/src/extractors/kotlin.js +8 -3
  27. package/src/extractors/lua.js +152 -0
  28. package/src/extractors/markdown.js +1 -1
  29. package/src/extractors/php.js +8 -3
  30. package/src/extractors/properties.js +1 -1
  31. package/src/extractors/python.js +5 -2
  32. package/src/extractors/python_dataclass.js +3 -1
  33. package/src/extractors/r.js +1 -1
  34. package/src/extractors/ruby.js +1 -1
  35. package/src/extractors/rust.js +13 -5
  36. package/src/extractors/scala.js +8 -3
  37. package/src/extractors/shell.js +1 -1
  38. package/src/extractors/svelte.js +1 -1
  39. package/src/extractors/swift.js +8 -3
  40. package/src/extractors/toml.js +1 -1
  41. package/src/extractors/typescript.js +8 -4
  42. package/src/extractors/typescript_react.js +1 -1
  43. package/src/extractors/vue_sfc.js +3 -1
  44. package/src/extractors/xml.js +3 -1
  45. package/src/extractors/yaml.js +1 -1
  46. package/src/graph/call-graph.js +81 -1
  47. package/src/mcp/server.js +46 -4
  48. package/src/retrieval/ranker.js +14 -5
package/llms-full.txt CHANGED
@@ -11,21 +11,21 @@ ranking keeps the relevant context in scope (cutting tokens ~97% as a side
11
11
  effect), with no LLM calls, embeddings, or vector database. Works with Claude,
12
12
  Cursor, GitHub Copilot, Aider, Windsurf, local LLMs, and MCP.
13
13
 
14
- # Version: 8.33.0 | Benchmark: sigmap-v8.33-main (2026-09-13)
14
+ # Version: 8.35.0 | Benchmark: sigmap-v8.35-main (2026-09-13)
15
15
  # Source: auto-generated from package.json, version.json, benchmarks/latest.json, src/mcp/tools.js, src/config/defaults.js
16
16
  # Regenerate: npm run generate:llms | Validate: npm run validate:llms
17
17
 
18
18
  ---
19
19
 
20
- ## Core metrics (benchmark: sigmap-v8.33-main, 2026-09-13)
20
+ ## Core metrics (benchmark: sigmap-v8.35-main, 2026-09-13)
21
21
 
22
22
  | Metric | Without SigMap | With SigMap |
23
23
  |--------|----------------|-------------|
24
- | Retrieval hit@5 | 44.0% (single-shot grep) | 78.9% (1.73× lift) |
25
- | Token reduction | — | 96.8% average |
26
- | Task-success proxy (modeled) | — | 62.9% |
27
- | Prompts per task | 2.84 | 1.58 (44.4% fewer) |
28
- | Supported languages | — | 32 |
24
+ | Retrieval hit@5 | 44.0% (single-shot grep) | 78.6% (1.73× lift) |
25
+ | Token reduction | — | 96.6% average |
26
+ | Task-success proxy (modeled) | — | 61.0% |
27
+ | Prompts per task | 2.84 | 1.61 (43.4% fewer) |
28
+ | Supported languages | — | 33 |
29
29
  | MCP tools | — | 21 |
30
30
  | npm runtime dependencies | — | 0 |
31
31
 
@@ -370,9 +370,9 @@ impact = {"depth":3,"includeSigs":true}
370
370
 
371
371
  ---
372
372
 
373
- ## Supported languages (32 extractors)
373
+ ## Supported languages (33 extractors)
374
374
 
375
- cpp, csharp, css, dart, dockerfile, gdscript, go, graphql, html, java, javascript, kotlin, markdown, php, properties, protobuf, python, r, ruby, rust, scala, shell, sql, svelte, swift, terraform, toml, typescript, typescript_react, vue_sfc, xml, yaml
375
+ cpp, csharp, css, dart, dockerfile, gdscript, go, graphql, html, java, javascript, kotlin, lua, markdown, php, properties, protobuf, python, r, ruby, rust, scala, shell, sql, svelte, swift, terraform, toml, typescript, typescript_react, vue_sfc, xml, yaml
376
376
 
377
377
  ---
378
378
 
package/llms.txt CHANGED
@@ -11,7 +11,7 @@ ranking keeps the relevant context in scope (cutting tokens ~97% as a side
11
11
  effect), with no LLM calls, embeddings, or vector database. Works with Claude,
12
12
  Cursor, GitHub Copilot, Aider, Windsurf, local LLMs, and MCP.
13
13
 
14
- # Version: 8.33.0 | Benchmark: sigmap-v8.33-main (2026-09-13)
14
+ # Version: 8.35.0 | Benchmark: sigmap-v8.35-main (2026-09-13)
15
15
  # Source: auto-generated from package.json, version.json, benchmarks/latest.json, src/mcp/tools.js, src/config/defaults.js
16
16
  # Regenerate: npm run generate:llms | Validate: npm run validate:llms
17
17
 
@@ -23,13 +23,13 @@ Cursor, GitHub Copilot, Aider, Windsurf, local LLMs, and MCP.
23
23
  - No blast-radius awareness before editing a hub file — `--impact` shows every file a change touches.
24
24
  - Pasted stack traces, CI logs, and JSON bloat the prompt — `squeeze` minimizes them and enriches the top frame from the symbol index.
25
25
 
26
- ## Core metrics (benchmark: sigmap-v8.33-main, 2026-09-13)
26
+ ## Core metrics (benchmark: sigmap-v8.35-main, 2026-09-13)
27
27
 
28
- - hit@5 retrieval: 78.9% vs 44.0% single-shot grep baseline (1.73× lift)
29
- - Token reduction: 96.8% average across benchmark repos
30
- - Task-success proxy: 62.9% (modeled from retrieval tiers, not measured LLM sessions)
31
- - Prompts per task: 1.58 vs 2.84 baseline (44.4% fewer, modeled)
32
- - Languages: 32 supported · MCP tools: 21
28
+ - hit@5 retrieval: 78.6% vs 44.0% single-shot grep baseline (1.73× lift)
29
+ - Token reduction: 96.6% average across benchmark repos
30
+ - Task-success proxy: 61.0% (modeled from retrieval tiers, not measured LLM sessions)
31
+ - Prompts per task: 1.61 vs 2.84 baseline (43.4% fewer, modeled)
32
+ - Languages: 33 supported · MCP tools: 21
33
33
  - Dependencies: zero npm runtime dependencies · fully offline
34
34
 
35
35
  ## Quick start
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sigmap",
3
- "version": "8.33.0",
3
+ "version": "8.35.0",
4
4
  "description": "The deterministic, verifiable grounding layer for AI code work — a zero-dependency signature-and-evidence map that grounds Claude, Cursor, Copilot, Aider, Windsurf, local LLMs & MCP agents against your real code (repo + installed libraries) so they stop hallucinating files, imports & APIs. Runs offline via npx; byte-stable output; ~97% token reduction as proof.",
5
5
  "main": "packages/core/index.js",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sigmap-cli",
3
- "version": "8.33.0",
3
+ "version": "8.35.0",
4
4
  "description": "SigMap CLI wrapper — thin adapter for programmatic CLI invocation",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -29,6 +29,7 @@ const EXT_MAP = {
29
29
  '.swift': 'swift',
30
30
  '.dart': 'dart',
31
31
  '.scala': 'scala', '.sc': 'scala',
32
+ '.lua': 'lua',
32
33
  '.gd': 'gdscript',
33
34
  '.r': 'r', '.R': 'r',
34
35
  '.vue': 'vue',
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sigmap-core",
3
- "version": "8.33.0",
3
+ "version": "8.35.0",
4
4
  "description": "SigMap core library — zero-dependency code signature extraction, retrieval, and security scanning",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -81,7 +81,7 @@ const SUPPORTED_CODE_EXTS = new Set([
81
81
  '.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs',
82
82
  '.py', '.pyw', '.java', '.kt', '.kts', '.go', '.rs', '.cs',
83
83
  '.cpp', '.c', '.h', '.hpp', '.cc', '.rb', '.rake', '.php',
84
- '.swift', '.dart', '.scala', '.sc', '.vue', '.svelte',
84
+ '.swift', '.dart', '.scala', '.sc', '.lua', '.vue', '.svelte',
85
85
  '.html', '.htm', '.css', '.scss', '.sass', '.less',
86
86
  '.yml', '.yaml', '.sh', '.bash', '.zsh', '.fish',
87
87
  '.sql', '.graphql', '.gql', '.tf', '.tfvars', '.proto',
@@ -38,7 +38,7 @@ const TOKENS_PER_FILE = 25;
38
38
  const SOURCE_EXTS = new Set([
39
39
  '.js', '.mjs', '.cjs', '.jsx', '.ts', '.tsx', '.py', '.rb', '.go', '.rs',
40
40
  '.java', '.kt', '.cs', '.cpp', '.c', '.h', '.hpp', '.swift', '.dart',
41
- '.scala', '.php', '.gd', '.r', '.R',
41
+ '.scala', '.php', '.lua', '.gd', '.r', '.R',
42
42
  ]);
43
43
 
44
44
  /** Raw user config file content, or null when absent/unparsable. */
@@ -19,6 +19,7 @@ const EXT_TO_LANG = {
19
19
  '.java': 'java', '.kt': 'kotlin', '.cs': 'csharp', '.cpp': 'cpp',
20
20
  '.c': 'cpp', '.h': 'cpp', '.hpp': 'cpp', '.swift': 'swift',
21
21
  '.dart': 'dart', '.scala': 'scala', '.php': 'php',
22
+ '.lua': 'lua',
22
23
  '.gd': 'gdscript',
23
24
  '.r': 'r', '.R': 'r',
24
25
  };
@@ -156,12 +156,21 @@ const REGISTRY = {
156
156
  akka: { detectionFiles: [], detectionDeps: ['akka'], srcDirs: ['src/main/scala','src'] },
157
157
  play: { detectionFiles: [], detectionDeps: ['play'], srcDirs: ['app','conf'] },
158
158
  spark: { detectionFiles: [], detectionDeps: ['spark'],srcDirs: ['src/main/scala'] },
159
- zio: { detectionFiles: [], detectionDeps: ['zio'], srcDirs: ['src/main/scala'] },
159
+ zio: { detectionFiles: [], detectionDeps: ['zio'], srcDirs: ['src/main/scala'] },
160
160
  },
161
161
  srcDirs: ['src/main/scala','src'],
162
162
  penalties: ['target'],
163
163
  },
164
164
 
165
+ lua: {
166
+ manifestFiles: ['.luarc.json', 'selene.toml', 'stylua.toml'],
167
+ frameworks: {
168
+ luarocks: { detectionFiles: ['*.rockspec'], srcDirs: ['src','lua','lib'] },
169
+ },
170
+ srcDirs: ['src','lua','lib'],
171
+ penalties: ['.luarocks','luarocks_modules'],
172
+ },
173
+
165
174
  r: {
166
175
  manifestFiles: ['DESCRIPTION','renv.lock'],
167
176
  frameworks: {
@@ -7,7 +7,7 @@ const { git } = require('../util/git');
7
7
  const CODE_EXTS = new Set([
8
8
  '.js','.mjs','.cjs','.ts','.tsx','.jsx',
9
9
  '.py','.rb','.go','.rs','.java','.kt',
10
- '.cs','.cpp','.c','.h','.swift','.dart','.scala','.php',
10
+ '.cs','.cpp','.c','.h','.swift','.dart','.scala','.php','.lua',
11
11
  ]);
12
12
 
13
13
  const AUTO_SKIP = new Set([
@@ -5,8 +5,13 @@ const { capWithNotice, capMembersWithNotice } = require('../util/truncate');
5
5
  // Ceilings sit above the default `maxSigsPerFile` so the configured budget
6
6
  // governs output rather than a literal buried here, and omissions are disclosed
7
7
  // — an undisclosed cap looks like a class that simply has eight methods (#576).
8
- const MEMBER_LIMIT = 8;
9
- const PER_FILE_LIMIT = 25;
8
+ // Class bodies are scanned to this many characters. Real classes routinely
9
+ // run past the old 4KB scan window — truncating there silently hid every
10
+ // member after ~4000 chars AND anchored class end-lines short (#576). The
11
+ // ceiling only guards against pathological input (Java parity, #551).
12
+ const MAX_CLASS_BODY_CHARS = 200000;
13
+ const MEMBER_LIMIT = 120;
14
+ const PER_FILE_LIMIT = 200;
10
15
 
11
16
  /**
12
17
  * Extract signatures from C/C++ source code.
@@ -43,7 +48,7 @@ function extract(src) {
43
48
 
44
49
  function extractBlock(src, startIndex) {
45
50
  let depth = 1, i = startIndex;
46
- const end = Math.min(src.length, startIndex + 4000);
51
+ const end = Math.min(src.length, startIndex + MAX_CLASS_BODY_CHARS);
47
52
  while (i < end && depth > 0) {
48
53
  if (src[i] === '{') depth++;
49
54
  else if (src[i] === '}') depth--;
@@ -6,8 +6,13 @@ const { capWithNotice, capMembersWithNotice } = require('../util/truncate');
6
6
  // Ceilings sit above the default `maxSigsPerFile` so the configured budget
7
7
  // governs output rather than a literal buried here, and omissions are disclosed
8
8
  // — an undisclosed cap looks like a class that simply has eight methods (#576).
9
- const MEMBER_LIMIT = 8;
10
- const PER_FILE_LIMIT = 25;
9
+ // Class bodies are scanned to this many characters. Real classes routinely
10
+ // run past the old 4KB scan window — truncating there silently hid every
11
+ // member after ~4000 chars AND anchored class end-lines short (#576). The
12
+ // ceiling only guards against pathological input (Java parity, #551).
13
+ const MAX_CLASS_BODY_CHARS = 200000;
14
+ const MEMBER_LIMIT = 120;
15
+ const PER_FILE_LIMIT = 200;
11
16
 
12
17
  /**
13
18
  * Extract signatures from C# source code.
@@ -42,7 +47,7 @@ function extract(src) {
42
47
 
43
48
  function extractBlock(src, startIndex) {
44
49
  let depth = 1, i = startIndex;
45
- const end = Math.min(src.length, startIndex + 5000);
50
+ const end = Math.min(src.length, startIndex + MAX_CLASS_BODY_CHARS);
46
51
  while (i < end && depth > 0) {
47
52
  if (src[i] === '{') depth++;
48
53
  else if (src[i] === '}') depth--;
@@ -4,7 +4,7 @@ const { capWithNotice } = require('../util/truncate');
4
4
 
5
5
  // Ceiling sits above the default `maxSigsPerFile` so the configured budget
6
6
  // governs output rather than a literal buried here, and omissions are disclosed (#576).
7
- const PER_FILE_LIMIT = 25;
7
+ const PER_FILE_LIMIT = 200;
8
8
 
9
9
  /**
10
10
  * Extract signatures from CSS/SCSS/SASS/Less source code.
@@ -6,8 +6,13 @@ const { capWithNotice, capMembersWithNotice } = require('../util/truncate');
6
6
  // Ceilings sit above the default `maxSigsPerFile` so the configured budget
7
7
  // governs output rather than a literal buried here, and omissions are disclosed
8
8
  // — an undisclosed cap looks like a class that simply has eight methods (#576).
9
- const MEMBER_LIMIT = 8;
10
- const PER_FILE_LIMIT = 25;
9
+ // Class bodies are scanned to this many characters. Real classes routinely
10
+ // run past the old 4KB scan window — truncating there silently hid every
11
+ // member after ~4000 chars AND anchored class end-lines short (#576). The
12
+ // ceiling only guards against pathological input (Java parity, #551).
13
+ const MAX_CLASS_BODY_CHARS = 200000;
14
+ const MEMBER_LIMIT = 120;
15
+ const PER_FILE_LIMIT = 200;
11
16
 
12
17
  /**
13
18
  * Extract signatures from Dart source code.
@@ -61,7 +66,7 @@ function extract(src) {
61
66
 
62
67
  function extractBlock(src, startIndex) {
63
68
  let depth = 1, i = startIndex;
64
- const end = Math.min(src.length, startIndex + 4000);
69
+ const end = Math.min(src.length, startIndex + MAX_CLASS_BODY_CHARS);
65
70
  while (i < end && depth > 0) {
66
71
  if (src[i] === '{') depth++;
67
72
  else if (src[i] === '}') depth--;
@@ -89,6 +89,28 @@ function extractRDeps(src) {
89
89
  return [...deps].slice(0, 5);
90
90
  }
91
91
 
92
+ /**
93
+ * Extract Lua require() module dependencies.
94
+ * Captures `require "mod"` and `require("mod")`, returning compact module
95
+ * names as they appear in source.
96
+ * @param {string} src
97
+ * @returns {string[]}
98
+ */
99
+ function extractLuaDeps(src) {
100
+ const deps = new Set();
101
+ const stripped = stripLuaComments(src || '');
102
+ for (const m of stripped.matchAll(/\brequire\s*(?:\(\s*)?["']([A-Za-z0-9_.\/-]+)["']\s*\)?/g)) {
103
+ if (m[1]) deps.add(m[1]);
104
+ }
105
+ return [...deps].slice(0, 5);
106
+ }
107
+
108
+ function stripLuaComments(src) {
109
+ return String(src || '')
110
+ .replace(/--\[\[[\s\S]*?\]\]/g, '')
111
+ .replace(/--.*$/gm, '');
112
+ }
113
+
92
114
  /**
93
115
  * Build reverse dependency map from forward map.
94
116
  * @param {Map<string, string[]>} forwardMap
@@ -107,4 +129,4 @@ function buildReverseDepMap(forwardMap) {
107
129
  return reverse;
108
130
  }
109
131
 
110
- module.exports = { extractPythonDeps, extractTSDeps, extractRDeps, buildReverseDepMap };
132
+ module.exports = { extractPythonDeps, extractTSDeps, extractRDeps, extractLuaDeps, buildReverseDepMap };
@@ -28,6 +28,7 @@ const EXTRACTORS = {
28
28
  swift: require('./swift'),
29
29
  dart: require('./dart'),
30
30
  scala: require('./scala'),
31
+ lua: require('./lua'),
31
32
  gdscript: require('./gdscript'),
32
33
  r: require('./r'),
33
34
  vue_sfc: require('./vue_sfc'),
@@ -78,6 +79,7 @@ const EXT_MAP = {
78
79
  '.swift': 'swift',
79
80
  '.dart': 'dart',
80
81
  '.scala': 'scala', '.sc': 'scala',
82
+ '.lua': 'lua',
81
83
  '.gd': 'gdscript',
82
84
  '.r': 'r', '.R': 'r',
83
85
  '.vue': 'vue_sfc',
@@ -4,7 +4,7 @@ const { capWithNotice } = require('../util/truncate');
4
4
 
5
5
  // Ceiling sits above the default `maxSigsPerFile` so the configured budget
6
6
  // governs output rather than a literal buried here, and omissions are disclosed (#576).
7
- const PER_FILE_LIMIT = 25;
7
+ const PER_FILE_LIMIT = 200;
8
8
 
9
9
  /**
10
10
  * Extract signatures from Dockerfiles.
@@ -4,10 +4,10 @@ const { capWithNotice } = require('../util/truncate');
4
4
 
5
5
  // Ceiling sits above the default `maxSigsPerFile` so the configured budget
6
6
  // governs output rather than a literal buried here, and omissions are disclosed (#576).
7
- const PER_FILE_LIMIT = 25;
7
+ const PER_FILE_LIMIT = 200;
8
8
 
9
9
  // Ceilings disclose what they drop rather than truncating silently (#576).
10
- const MEMBER_LIMIT = 6;
10
+ const MEMBER_LIMIT = 120;
11
11
  const ENUM_LIMIT = 24;
12
12
 
13
13
  /**
@@ -1,11 +1,19 @@
1
1
  'use strict';
2
2
 
3
3
  const { lineAt, withAnchor } = require('./line-anchor');
4
- const { capWithNotice } = require('../util/truncate');
4
+ const { capWithNotice, capMembersWithNotice } = require('../util/truncate');
5
5
 
6
6
  // Ceiling sits above the default `maxSigsPerFile` so the configured budget
7
7
  // governs output rather than a literal buried here, and omissions are disclosed (#576).
8
- const PER_FILE_LIMIT = 25;
8
+ // Class bodies are scanned to this many characters. Real classes routinely
9
+ // run past the old 4KB scan window — truncating there silently hid every
10
+ // member after ~4000 chars AND anchored class end-lines short (#576). The
11
+ // ceiling only guards against pathological input (Java parity, #551).
12
+ const MAX_CLASS_BODY_CHARS = 200000;
13
+ const PER_FILE_LIMIT = 200;
14
+
15
+ // Per-interface member ceiling, disclosed via capMembersWithNotice (#576).
16
+ const MEMBER_LIMIT = 120;
9
17
 
10
18
  /**
11
19
  * Extract signatures from Go source code.
@@ -41,7 +49,7 @@ function extract(src) {
41
49
  const block = extractBlock(stripped, bodyStart);
42
50
  sigs.push(hinted(withAnchor(`type ${m[1]} interface`, lineAt(stripped, m.index), lineAt(stripped, bodyStart + block.length)), m[1]));
43
51
  for (const meth of extractInterfaceMethods(block)) {
44
- sigs.push(withAnchor(` ${meth.text}`, lineAt(stripped, bodyStart + meth.declIdx), lineAt(stripped, bodyStart + meth.endIdx)));
52
+ sigs.push(withAnchor(` ${meth.text}`, lineAt(stripped, bodyStart + (meth.declIdx || 0)), lineAt(stripped, bodyStart + (meth.endIdx || 0))));
45
53
  }
46
54
  }
47
55
 
@@ -59,7 +67,7 @@ function extract(src) {
59
67
 
60
68
  function extractBlock(src, startIndex) {
61
69
  let depth = 1, i = startIndex;
62
- const end = Math.min(src.length, startIndex + 2000);
70
+ const end = Math.min(src.length, startIndex + MAX_CLASS_BODY_CHARS);
63
71
  while (i < end && depth > 0) {
64
72
  if (src[i] === '{') depth++;
65
73
  else if (src[i] === '}') depth--;
@@ -79,7 +87,7 @@ function extractInterfaceMethods(block) {
79
87
  endIdx: m.index + m[0].length,
80
88
  });
81
89
  }
82
- return methods.slice(0, 8);
90
+ return capMembersWithNotice(methods, MEMBER_LIMIT, 'methods');
83
91
  }
84
92
 
85
93
  function normalizeParams(params) {
@@ -4,7 +4,7 @@ const { capWithNotice } = require('../util/truncate');
4
4
 
5
5
  // Ceiling sits above the default `maxSigsPerFile` so the configured budget
6
6
  // governs output rather than a literal buried here, and omissions are disclosed (#576).
7
- const PER_FILE_LIMIT = 25;
7
+ const PER_FILE_LIMIT = 200;
8
8
 
9
9
  /**
10
10
  * Extract signatures from HTML files.
@@ -4,6 +4,10 @@ const { lineAt, withAnchor } = require('./line-anchor');
4
4
  const { capWithNotice, capMembersWithNotice } = require('../util/truncate');
5
5
  const { stripComments, maskCode, readBalanced } = require('./scan');
6
6
 
7
+ // Class bodies are scanned to this many characters — guard against
8
+ // pathological input only; the old 4KB window silently hid members (#576).
9
+ const MAX_CLASS_BODY_CHARS = 200000;
10
+
7
11
  /**
8
12
  * Extract signatures from JavaScript source code.
9
13
  * Top-level declarations and class members carry a `:start-end` line anchor
@@ -113,13 +117,13 @@ function extract(src) {
113
117
  const anchored = anchors[i] ? withAnchor(s, anchors[i][0], anchors[i][1]) : s;
114
118
  return docHintFor[i] ? `${anchored} # ${docHintFor[i]}` : anchored;
115
119
  });
116
- return capWithNotice(withAnchors, 25, 'signatures');
120
+ return capWithNotice(withAnchors, 200, 'signatures');
117
121
  }
118
122
 
119
123
  function extractBlock(src, startIndex) {
120
124
  let depth = 1;
121
125
  let i = startIndex;
122
- const end = Math.min(src.length, startIndex + 4000);
126
+ const end = Math.min(src.length, startIndex + MAX_CLASS_BODY_CHARS);
123
127
  while (i < end && depth > 0) {
124
128
  if (src[i] === '{') depth++;
125
129
  else if (src[i] === '}') depth--;
@@ -154,7 +158,7 @@ function extractClassMembers(block, maskedBlock, returnHints) {
154
158
  const retStr = formatReturnHint(returnHints.get(m[1]));
155
159
  members.push({ text: `${isStatic}${isAsync}${m[1]}(${normalizeParams(params)})${retStr}`, start, end });
156
160
  }
157
- return capMembersWithNotice(members, 8, 'methods');
161
+ return capMembersWithNotice(members, 120, 'methods');
158
162
  }
159
163
 
160
164
  function buildReturnHints(src) {
@@ -6,8 +6,13 @@ const { capWithNotice, capMembersWithNotice } = require('../util/truncate');
6
6
  // Ceilings sit above the default `maxSigsPerFile` so the configured budget
7
7
  // governs output rather than a literal buried here, and omissions are disclosed
8
8
  // — an undisclosed cap looks like a class that simply has eight methods (#576).
9
- const MEMBER_LIMIT = 8;
10
- const PER_FILE_LIMIT = 25;
9
+ // Class bodies are scanned to this many characters. Real classes routinely
10
+ // run past the old 4KB scan window — truncating there silently hid every
11
+ // member after ~4000 chars AND anchored class end-lines short (#576). The
12
+ // ceiling only guards against pathological input (Java parity, #551).
13
+ const MAX_CLASS_BODY_CHARS = 200000;
14
+ const MEMBER_LIMIT = 120;
15
+ const PER_FILE_LIMIT = 200;
11
16
 
12
17
  /**
13
18
  * Extract signatures from Kotlin source code.
@@ -61,7 +66,7 @@ function extract(src) {
61
66
 
62
67
  function extractBlock(src, startIndex) {
63
68
  let depth = 1, i = startIndex;
64
- const end = Math.min(src.length, startIndex + 4000);
69
+ const end = Math.min(src.length, startIndex + MAX_CLASS_BODY_CHARS);
65
70
  while (i < end && depth > 0) {
66
71
  if (src[i] === '{') depth++;
67
72
  else if (src[i] === '}') depth--;
@@ -0,0 +1,152 @@
1
+ 'use strict';
2
+
3
+ const { capWithNotice } = require('../util/truncate');
4
+
5
+ // Ceiling discloses what it drops rather than truncating silently (#583).
6
+ // Collection runs to completion so the marker reports the true overflow —
7
+ // stopping early made r.js report "+1 more" where 50 were hidden (#584).
8
+ const PER_FILE_LIMIT = 200;
9
+
10
+ /**
11
+ * Extract signatures from Lua source code.
12
+ *
13
+ * Recognised constructs:
14
+ * - Global functions: `function name(args)`
15
+ * - Module-table functions: `function M.name(args)` / `function M:name(args)`
16
+ * - Local functions: `local function name(args)`
17
+ * - Assigned functions: `name = function(args)` / `M.name = function(args)`
18
+ * - Module imports: `local mod = require("mod")` as compact hints
19
+ * - LDoc-style doc comments (`---`) as first-sentence hints
20
+ *
21
+ * The extractor is regex-only and zero-dependency, matching SigMap's Tier-3
22
+ * language extractor style.
23
+ *
24
+ * @param {string} src - Raw file content
25
+ * @returns {string[]} Array of signature strings
26
+ */
27
+ function extract(src) {
28
+ if (!src || typeof src !== 'string') return [];
29
+ const sigs = [];
30
+ const hints = collectDocHints(src);
31
+ const stripped = stripLuaComments(src);
32
+ const seen = new Set();
33
+
34
+ // local foo = require('bar.baz') — useful module-surface hint, capped low.
35
+ for (const m of stripped.matchAll(/^\s*(?:local\s+)?([A-Za-z_]\w*)\s*=\s*require\s*\(\s*['"]([A-Za-z0-9_.\/-]+)['"]\s*\)/gm)) {
36
+ pushUnique(sigs, seen, `require ${m[2]} as ${m[1]}`);
37
+ }
38
+
39
+ // local function name(args)
40
+ for (const m of stripped.matchAll(/^\s*local\s+function\s+([A-Za-z_]\w*)\s*\(([^)]*)\)/gm)) {
41
+ if (m[1].startsWith('_')) continue;
42
+ pushUnique(sigs, seen, `local function ${m[1]}(${normalizeParams(m[2])})${applyHint(hints, m[1])}`);
43
+ }
44
+
45
+ // function name(args), function M.name(args), function M:name(args)
46
+ for (const m of stripped.matchAll(/^\s*function\s+([A-Za-z_]\w*(?:(?:\.|:)[A-Za-z_]\w*)*)\s*\(([^)]*)\)/gm)) {
47
+ const name = m[1];
48
+ if (name.startsWith('_')) continue;
49
+ pushUnique(sigs, seen, `function ${name}(${normalizeParams(m[2])})${applyHint(hints, name)}`);
50
+ }
51
+
52
+ // name = function(args), M.name = function(args), M:name = function(args)
53
+ for (const m of stripped.matchAll(/^\s*(?:local\s+)?([A-Za-z_]\w*(?:(?:\.|:)[A-Za-z_]\w*)*)\s*=\s*function\s*\(([^)]*)\)/gm)) {
54
+ const name = m[1];
55
+ if (name.startsWith('_')) continue;
56
+ pushUnique(sigs, seen, `${name} = function(${normalizeParams(m[2])})${applyHint(hints, name)}`);
57
+ }
58
+
59
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
60
+ }
61
+
62
+ function pushUnique(out, seen, sig) {
63
+ if (!sig || seen.has(sig)) return;
64
+ seen.add(sig);
65
+ out.push(sig);
66
+ }
67
+
68
+ function normalizeParams(params) {
69
+ return String(params || '')
70
+ .replace(/--.*$/gm, '')
71
+ .split(',')
72
+ .map((p) => p.trim().replace(/\s+/g, ' '))
73
+ .filter(Boolean)
74
+ .join(', ');
75
+ }
76
+
77
+ function applyHint(hints, name) {
78
+ const h = hints.get(name);
79
+ return h ? ` # ${h}` : '';
80
+ }
81
+
82
+ /**
83
+ * Attach each contiguous `---` doc block to the next function-like declaration
84
+ * by its extracted symbol name.
85
+ */
86
+ function collectDocHints(src) {
87
+ const hints = new Map();
88
+ const lines = src.split('\n');
89
+ let block = [];
90
+ for (const line of lines) {
91
+ const doc = line.match(/^\s*---\s?(.*)$/);
92
+ if (doc) {
93
+ block.push(doc[1]);
94
+ } else if (block.length > 0) {
95
+ const decl = line.match(/^\s*local\s+function\s+([A-Za-z_]\w*)\s*\(/)
96
+ || line.match(/^\s*function\s+([A-Za-z_]\w*(?:(?:\.|:)[A-Za-z_]\w*)*)\s*\(/)
97
+ || line.match(/^\s*(?:local\s+)?([A-Za-z_]\w*(?:(?:\.|:)[A-Za-z_]\w*)*)\s*=\s*function\s*\(/);
98
+ if (decl) {
99
+ const hint = firstDocSentence(block);
100
+ if (hint) hints.set(decl[1], hint);
101
+ }
102
+ block = [];
103
+ }
104
+ }
105
+ return hints;
106
+ }
107
+
108
+ function firstDocSentence(block) {
109
+ for (const raw of block) {
110
+ const line = String(raw || '').trim();
111
+ if (!line || line.startsWith('@')) continue;
112
+ return line.replace(/\s+/g, ' ').slice(0, 60).replace(/[.,;:!?]+$/, '').trim();
113
+ }
114
+ return '';
115
+ }
116
+
117
+ /** Strip Lua line and long comments while preserving strings enough for regex scans. */
118
+ function stripLuaComments(src) {
119
+ const out = src.split('');
120
+ const blank = (a, b) => { for (let i = a; i < b; i++) if (out[i] !== '\n') out[i] = ' '; };
121
+ let i = 0;
122
+ while (i < src.length) {
123
+ const ch = src[i];
124
+ if (ch === '"' || ch === "'") {
125
+ const quote = ch;
126
+ i++;
127
+ while (i < src.length) {
128
+ if (src[i] === '\\') { i += 2; continue; }
129
+ if (src[i] === quote) { i++; break; }
130
+ if (src[i] === '\n') break;
131
+ i++;
132
+ }
133
+ continue;
134
+ }
135
+ if (src.startsWith('--[[', i)) {
136
+ const end = src.indexOf(']]', i + 4);
137
+ blank(i, end === -1 ? src.length : end + 2);
138
+ i = end === -1 ? src.length : end + 2;
139
+ continue;
140
+ }
141
+ if (src.startsWith('--', i)) {
142
+ const end = src.indexOf('\n', i + 2);
143
+ blank(i, end === -1 ? src.length : end);
144
+ i = end === -1 ? src.length : end;
145
+ continue;
146
+ }
147
+ i++;
148
+ }
149
+ return out.join('');
150
+ }
151
+
152
+ module.exports = { extract };
@@ -3,7 +3,7 @@
3
3
  const { capWithNotice } = require('../util/truncate');
4
4
 
5
5
  // Ceiling discloses what it drops rather than truncating silently (#583).
6
- const PER_FILE_LIMIT = 40;
6
+ const PER_FILE_LIMIT = 200;
7
7
 
8
8
  /**
9
9
  * Lightweight markdown technical indexer.
@@ -6,8 +6,13 @@ const { capWithNotice, capMembersWithNotice } = require('../util/truncate');
6
6
  // Ceilings sit above the default `maxSigsPerFile` so the configured budget
7
7
  // governs output rather than a literal buried here, and omissions are disclosed
8
8
  // — an undisclosed cap looks like a class that simply has eight methods (#576).
9
- const MEMBER_LIMIT = 8;
10
- const PER_FILE_LIMIT = 25;
9
+ // Class bodies are scanned to this many characters. Real classes routinely
10
+ // run past the old 4KB scan window — truncating there silently hid every
11
+ // member after ~4000 chars AND anchored class end-lines short (#576). The
12
+ // ceiling only guards against pathological input (Java parity, #551).
13
+ const MAX_CLASS_BODY_CHARS = 200000;
14
+ const MEMBER_LIMIT = 120;
15
+ const PER_FILE_LIMIT = 200;
11
16
 
12
17
  /**
13
18
  * Extract signatures from PHP source code.
@@ -65,7 +70,7 @@ function extract(src) {
65
70
 
66
71
  function extractBlock(src, startIndex) {
67
72
  let depth = 1, i = startIndex;
68
- const end = Math.min(src.length, startIndex + 4000);
73
+ const end = Math.min(src.length, startIndex + MAX_CLASS_BODY_CHARS);
69
74
  while (i < end && depth > 0) {
70
75
  if (src[i] === '{') depth++;
71
76
  else if (src[i] === '}') depth--;