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.
- package/CHANGELOG.md +24 -0
- package/README.md +12 -12
- package/gen-context.js +531 -74
- package/llms-full.txt +9 -9
- package/llms.txt +7 -7
- package/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/core/index.js +1 -0
- package/packages/core/package.json +1 -1
- package/src/config/loader.js +1 -1
- package/src/config/tune.js +1 -1
- package/src/discovery/language-detector.js +1 -0
- package/src/discovery/source-root-registry.js +10 -1
- package/src/discovery/source-root-scorer.js +1 -1
- package/src/extractors/cpp.js +8 -3
- package/src/extractors/csharp.js +8 -3
- package/src/extractors/css.js +1 -1
- package/src/extractors/dart.js +8 -3
- package/src/extractors/deps.js +23 -1
- package/src/extractors/dispatch.js +2 -0
- package/src/extractors/dockerfile.js +1 -1
- package/src/extractors/gdscript.js +2 -2
- package/src/extractors/go.js +13 -5
- package/src/extractors/html.js +1 -1
- package/src/extractors/javascript.js +7 -3
- package/src/extractors/kotlin.js +8 -3
- package/src/extractors/lua.js +152 -0
- package/src/extractors/markdown.js +1 -1
- package/src/extractors/php.js +8 -3
- package/src/extractors/properties.js +1 -1
- package/src/extractors/python.js +5 -2
- package/src/extractors/python_dataclass.js +3 -1
- package/src/extractors/r.js +1 -1
- package/src/extractors/ruby.js +1 -1
- package/src/extractors/rust.js +13 -5
- package/src/extractors/scala.js +8 -3
- package/src/extractors/shell.js +1 -1
- package/src/extractors/svelte.js +1 -1
- package/src/extractors/swift.js +8 -3
- package/src/extractors/toml.js +1 -1
- package/src/extractors/typescript.js +8 -4
- package/src/extractors/typescript_react.js +1 -1
- package/src/extractors/vue_sfc.js +3 -1
- package/src/extractors/xml.js +3 -1
- package/src/extractors/yaml.js +1 -1
- package/src/graph/call-graph.js +81 -1
- package/src/mcp/server.js +46 -4
- package/src/retrieval/ranker.js +14 -5
package/gen-context.js
CHANGED
|
@@ -1644,7 +1644,7 @@ __factories["./src/config/loader"] = function(module, exports) {
|
|
|
1644
1644
|
'.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs',
|
|
1645
1645
|
'.py', '.pyw', '.java', '.kt', '.kts', '.go', '.rs', '.cs',
|
|
1646
1646
|
'.cpp', '.c', '.h', '.hpp', '.cc', '.rb', '.rake', '.php',
|
|
1647
|
-
'.swift', '.dart', '.scala', '.sc', '.vue', '.svelte',
|
|
1647
|
+
'.swift', '.dart', '.scala', '.sc', '.lua', '.vue', '.svelte',
|
|
1648
1648
|
'.html', '.htm', '.css', '.scss', '.sass', '.less',
|
|
1649
1649
|
'.yml', '.yaml', '.sh', '.bash', '.zsh', '.fish',
|
|
1650
1650
|
'.sql', '.graphql', '.gql', '.tf', '.tfvars', '.proto',
|
|
@@ -1968,7 +1968,7 @@ __factories["./src/config/tune"] = function(module, exports) {
|
|
|
1968
1968
|
const SOURCE_EXTS = new Set([
|
|
1969
1969
|
'.js', '.mjs', '.cjs', '.jsx', '.ts', '.tsx', '.py', '.rb', '.go', '.rs',
|
|
1970
1970
|
'.java', '.kt', '.cs', '.cpp', '.c', '.h', '.hpp', '.swift', '.dart',
|
|
1971
|
-
'.scala', '.php', '.gd', '.r', '.R',
|
|
1971
|
+
'.scala', '.php', '.lua', '.gd', '.r', '.R',
|
|
1972
1972
|
]);
|
|
1973
1973
|
|
|
1974
1974
|
/** Raw user config file content, or null when absent/unparsable. */
|
|
@@ -3128,6 +3128,7 @@ __factories["./src/discovery/language-detector"] = function(module, exports) {
|
|
|
3128
3128
|
'.java': 'java', '.kt': 'kotlin', '.cs': 'csharp', '.cpp': 'cpp',
|
|
3129
3129
|
'.c': 'cpp', '.h': 'cpp', '.hpp': 'cpp', '.swift': 'swift',
|
|
3130
3130
|
'.dart': 'dart', '.scala': 'scala', '.php': 'php',
|
|
3131
|
+
'.lua': 'lua',
|
|
3131
3132
|
'.gd': 'gdscript',
|
|
3132
3133
|
'.r': 'r', '.R': 'r',
|
|
3133
3134
|
};
|
|
@@ -3558,12 +3559,21 @@ __factories["./src/discovery/source-root-registry"] = function(module, exports)
|
|
|
3558
3559
|
akka: { detectionFiles: [], detectionDeps: ['akka'], srcDirs: ['src/main/scala','src'] },
|
|
3559
3560
|
play: { detectionFiles: [], detectionDeps: ['play'], srcDirs: ['app','conf'] },
|
|
3560
3561
|
spark: { detectionFiles: [], detectionDeps: ['spark'],srcDirs: ['src/main/scala'] },
|
|
3561
|
-
zio: { detectionFiles: [], detectionDeps: ['zio'],
|
|
3562
|
+
zio: { detectionFiles: [], detectionDeps: ['zio'], srcDirs: ['src/main/scala'] },
|
|
3562
3563
|
},
|
|
3563
3564
|
srcDirs: ['src/main/scala','src'],
|
|
3564
3565
|
penalties: ['target'],
|
|
3565
3566
|
},
|
|
3566
3567
|
|
|
3568
|
+
lua: {
|
|
3569
|
+
manifestFiles: ['.luarc.json', 'selene.toml', 'stylua.toml'],
|
|
3570
|
+
frameworks: {
|
|
3571
|
+
luarocks: { detectionFiles: ['*.rockspec'], srcDirs: ['src','lua','lib'] },
|
|
3572
|
+
},
|
|
3573
|
+
srcDirs: ['src','lua','lib'],
|
|
3574
|
+
penalties: ['.luarocks','luarocks_modules'],
|
|
3575
|
+
},
|
|
3576
|
+
|
|
3567
3577
|
r: {
|
|
3568
3578
|
manifestFiles: ['DESCRIPTION','renv.lock'],
|
|
3569
3579
|
frameworks: {
|
|
@@ -3797,7 +3807,7 @@ __factories["./src/discovery/source-root-scorer"] = function(module, exports) {
|
|
|
3797
3807
|
const CODE_EXTS = new Set([
|
|
3798
3808
|
'.js','.mjs','.cjs','.ts','.tsx','.jsx',
|
|
3799
3809
|
'.py','.rb','.go','.rs','.java','.kt',
|
|
3800
|
-
'.cs','.cpp','.c','.h','.swift','.dart','.scala','.php',
|
|
3810
|
+
'.cs','.cpp','.c','.h','.swift','.dart','.scala','.php','.lua',
|
|
3801
3811
|
]);
|
|
3802
3812
|
|
|
3803
3813
|
const AUTO_SKIP = new Set([
|
|
@@ -5459,8 +5469,13 @@ __factories["./src/extractors/cpp"] = function(module, exports) {
|
|
|
5459
5469
|
// Ceilings sit above the default `maxSigsPerFile` so the configured budget
|
|
5460
5470
|
// governs output rather than a literal buried here, and omissions are disclosed
|
|
5461
5471
|
// — an undisclosed cap looks like a class that simply has eight methods (#576).
|
|
5462
|
-
|
|
5463
|
-
|
|
5472
|
+
// Class bodies are scanned to this many characters. Real classes routinely
|
|
5473
|
+
// run past the old 4KB scan window — truncating there silently hid every
|
|
5474
|
+
// member after ~4000 chars AND anchored class end-lines short (#576). The
|
|
5475
|
+
// ceiling only guards against pathological input (Java parity, #551).
|
|
5476
|
+
const MAX_CLASS_BODY_CHARS = 200000;
|
|
5477
|
+
const MEMBER_LIMIT = 120;
|
|
5478
|
+
const PER_FILE_LIMIT = 200;
|
|
5464
5479
|
|
|
5465
5480
|
/**
|
|
5466
5481
|
* Extract signatures from C/C++ source code.
|
|
@@ -5497,7 +5512,7 @@ __factories["./src/extractors/cpp"] = function(module, exports) {
|
|
|
5497
5512
|
|
|
5498
5513
|
function extractBlock(src, startIndex) {
|
|
5499
5514
|
let depth = 1, i = startIndex;
|
|
5500
|
-
const end = Math.min(src.length, startIndex +
|
|
5515
|
+
const end = Math.min(src.length, startIndex + MAX_CLASS_BODY_CHARS);
|
|
5501
5516
|
while (i < end && depth > 0) {
|
|
5502
5517
|
if (src[i] === '{') depth++;
|
|
5503
5518
|
else if (src[i] === '}') depth--;
|
|
@@ -5541,8 +5556,13 @@ __factories["./src/extractors/csharp"] = function(module, exports) {
|
|
|
5541
5556
|
// Ceilings sit above the default `maxSigsPerFile` so the configured budget
|
|
5542
5557
|
// governs output rather than a literal buried here, and omissions are disclosed
|
|
5543
5558
|
// — an undisclosed cap looks like a class that simply has eight methods (#576).
|
|
5544
|
-
|
|
5545
|
-
|
|
5559
|
+
// Class bodies are scanned to this many characters. Real classes routinely
|
|
5560
|
+
// run past the old 4KB scan window — truncating there silently hid every
|
|
5561
|
+
// member after ~4000 chars AND anchored class end-lines short (#576). The
|
|
5562
|
+
// ceiling only guards against pathological input (Java parity, #551).
|
|
5563
|
+
const MAX_CLASS_BODY_CHARS = 200000;
|
|
5564
|
+
const MEMBER_LIMIT = 120;
|
|
5565
|
+
const PER_FILE_LIMIT = 200;
|
|
5546
5566
|
|
|
5547
5567
|
/**
|
|
5548
5568
|
* Extract signatures from C# source code.
|
|
@@ -5577,7 +5597,7 @@ __factories["./src/extractors/csharp"] = function(module, exports) {
|
|
|
5577
5597
|
|
|
5578
5598
|
function extractBlock(src, startIndex) {
|
|
5579
5599
|
let depth = 1, i = startIndex;
|
|
5580
|
-
const end = Math.min(src.length, startIndex +
|
|
5600
|
+
const end = Math.min(src.length, startIndex + MAX_CLASS_BODY_CHARS);
|
|
5581
5601
|
while (i < end && depth > 0) {
|
|
5582
5602
|
if (src[i] === '{') depth++;
|
|
5583
5603
|
else if (src[i] === '}') depth--;
|
|
@@ -5622,7 +5642,7 @@ __factories["./src/extractors/css"] = function(module, exports) {
|
|
|
5622
5642
|
|
|
5623
5643
|
// Ceiling sits above the default `maxSigsPerFile` so the configured budget
|
|
5624
5644
|
// governs output rather than a literal buried here, and omissions are disclosed (#576).
|
|
5625
|
-
const PER_FILE_LIMIT =
|
|
5645
|
+
const PER_FILE_LIMIT = 200;
|
|
5626
5646
|
|
|
5627
5647
|
/**
|
|
5628
5648
|
* Extract signatures from CSS/SCSS/SASS/Less source code.
|
|
@@ -5703,8 +5723,13 @@ __factories["./src/extractors/dart"] = function(module, exports) {
|
|
|
5703
5723
|
// Ceilings sit above the default `maxSigsPerFile` so the configured budget
|
|
5704
5724
|
// governs output rather than a literal buried here, and omissions are disclosed
|
|
5705
5725
|
// — an undisclosed cap looks like a class that simply has eight methods (#576).
|
|
5706
|
-
|
|
5707
|
-
|
|
5726
|
+
// Class bodies are scanned to this many characters. Real classes routinely
|
|
5727
|
+
// run past the old 4KB scan window — truncating there silently hid every
|
|
5728
|
+
// member after ~4000 chars AND anchored class end-lines short (#576). The
|
|
5729
|
+
// ceiling only guards against pathological input (Java parity, #551).
|
|
5730
|
+
const MAX_CLASS_BODY_CHARS = 200000;
|
|
5731
|
+
const MEMBER_LIMIT = 120;
|
|
5732
|
+
const PER_FILE_LIMIT = 200;
|
|
5708
5733
|
|
|
5709
5734
|
/**
|
|
5710
5735
|
* Extract signatures from Dart source code.
|
|
@@ -5758,7 +5783,7 @@ __factories["./src/extractors/dart"] = function(module, exports) {
|
|
|
5758
5783
|
|
|
5759
5784
|
function extractBlock(src, startIndex) {
|
|
5760
5785
|
let depth = 1, i = startIndex;
|
|
5761
|
-
const end = Math.min(src.length, startIndex +
|
|
5786
|
+
const end = Math.min(src.length, startIndex + MAX_CLASS_BODY_CHARS);
|
|
5762
5787
|
while (i < end && depth > 0) {
|
|
5763
5788
|
if (src[i] === '{') depth++;
|
|
5764
5789
|
else if (src[i] === '}') depth--;
|
|
@@ -5882,6 +5907,28 @@ __factories["./src/extractors/deps"] = function(module, exports) {
|
|
|
5882
5907
|
return [...deps].slice(0, 5);
|
|
5883
5908
|
}
|
|
5884
5909
|
|
|
5910
|
+
/**
|
|
5911
|
+
* Extract Lua require() module dependencies.
|
|
5912
|
+
* Captures `require "mod"` and `require("mod")`, returning compact module
|
|
5913
|
+
* names as they appear in source.
|
|
5914
|
+
* @param {string} src
|
|
5915
|
+
* @returns {string[]}
|
|
5916
|
+
*/
|
|
5917
|
+
function extractLuaDeps(src) {
|
|
5918
|
+
const deps = new Set();
|
|
5919
|
+
const stripped = stripLuaComments(src || '');
|
|
5920
|
+
for (const m of stripped.matchAll(/\brequire\s*(?:\(\s*)?["']([A-Za-z0-9_.\/-]+)["']\s*\)?/g)) {
|
|
5921
|
+
if (m[1]) deps.add(m[1]);
|
|
5922
|
+
}
|
|
5923
|
+
return [...deps].slice(0, 5);
|
|
5924
|
+
}
|
|
5925
|
+
|
|
5926
|
+
function stripLuaComments(src) {
|
|
5927
|
+
return String(src || '')
|
|
5928
|
+
.replace(/--\[\[[\s\S]*?\]\]/g, '')
|
|
5929
|
+
.replace(/--.*$/gm, '');
|
|
5930
|
+
}
|
|
5931
|
+
|
|
5885
5932
|
/**
|
|
5886
5933
|
* Build reverse dependency map from forward map.
|
|
5887
5934
|
* @param {Map<string, string[]>} forwardMap
|
|
@@ -5900,7 +5947,7 @@ __factories["./src/extractors/deps"] = function(module, exports) {
|
|
|
5900
5947
|
return reverse;
|
|
5901
5948
|
}
|
|
5902
5949
|
|
|
5903
|
-
module.exports = { extractPythonDeps, extractTSDeps, extractRDeps, buildReverseDepMap };
|
|
5950
|
+
module.exports = { extractPythonDeps, extractTSDeps, extractRDeps, extractLuaDeps, buildReverseDepMap };
|
|
5904
5951
|
|
|
5905
5952
|
};
|
|
5906
5953
|
|
|
@@ -5935,6 +5982,7 @@ __factories["./src/extractors/dispatch"] = function(module, exports) {
|
|
|
5935
5982
|
swift: __require('./src/extractors/swift'),
|
|
5936
5983
|
dart: __require('./src/extractors/dart'),
|
|
5937
5984
|
scala: __require('./src/extractors/scala'),
|
|
5985
|
+
lua: __require('./src/extractors/lua'),
|
|
5938
5986
|
gdscript: __require('./src/extractors/gdscript'),
|
|
5939
5987
|
r: __require('./src/extractors/r'),
|
|
5940
5988
|
vue_sfc: __require('./src/extractors/vue_sfc'),
|
|
@@ -5985,6 +6033,7 @@ __factories["./src/extractors/dispatch"] = function(module, exports) {
|
|
|
5985
6033
|
'.swift': 'swift',
|
|
5986
6034
|
'.dart': 'dart',
|
|
5987
6035
|
'.scala': 'scala', '.sc': 'scala',
|
|
6036
|
+
'.lua': 'lua',
|
|
5988
6037
|
'.gd': 'gdscript',
|
|
5989
6038
|
'.r': 'r', '.R': 'r',
|
|
5990
6039
|
'.vue': 'vue_sfc',
|
|
@@ -6041,7 +6090,7 @@ __factories["./src/extractors/dockerfile"] = function(module, exports) {
|
|
|
6041
6090
|
|
|
6042
6091
|
// Ceiling sits above the default `maxSigsPerFile` so the configured budget
|
|
6043
6092
|
// governs output rather than a literal buried here, and omissions are disclosed (#576).
|
|
6044
|
-
const PER_FILE_LIMIT =
|
|
6093
|
+
const PER_FILE_LIMIT = 200;
|
|
6045
6094
|
|
|
6046
6095
|
/**
|
|
6047
6096
|
* Extract signatures from Dockerfiles.
|
|
@@ -6100,10 +6149,10 @@ __factories["./src/extractors/gdscript"] = function(module, exports) {
|
|
|
6100
6149
|
|
|
6101
6150
|
// Ceiling sits above the default `maxSigsPerFile` so the configured budget
|
|
6102
6151
|
// governs output rather than a literal buried here, and omissions are disclosed (#576).
|
|
6103
|
-
const PER_FILE_LIMIT =
|
|
6152
|
+
const PER_FILE_LIMIT = 200;
|
|
6104
6153
|
|
|
6105
6154
|
// Ceilings disclose what they drop rather than truncating silently (#576).
|
|
6106
|
-
const MEMBER_LIMIT =
|
|
6155
|
+
const MEMBER_LIMIT = 120;
|
|
6107
6156
|
const ENUM_LIMIT = 24;
|
|
6108
6157
|
|
|
6109
6158
|
/**
|
|
@@ -6273,11 +6322,19 @@ __factories["./src/extractors/generic"] = function(module, exports) {
|
|
|
6273
6322
|
__factories["./src/extractors/go"] = function(module, exports) {
|
|
6274
6323
|
|
|
6275
6324
|
const { lineAt, withAnchor } = __require('./src/extractors/line-anchor');
|
|
6276
|
-
const { capWithNotice } = __require('./src/util/truncate');
|
|
6325
|
+
const { capWithNotice, capMembersWithNotice } = __require('./src/util/truncate');
|
|
6277
6326
|
|
|
6278
6327
|
// Ceiling sits above the default `maxSigsPerFile` so the configured budget
|
|
6279
6328
|
// governs output rather than a literal buried here, and omissions are disclosed (#576).
|
|
6280
|
-
|
|
6329
|
+
// Class bodies are scanned to this many characters. Real classes routinely
|
|
6330
|
+
// run past the old 4KB scan window — truncating there silently hid every
|
|
6331
|
+
// member after ~4000 chars AND anchored class end-lines short (#576). The
|
|
6332
|
+
// ceiling only guards against pathological input (Java parity, #551).
|
|
6333
|
+
const MAX_CLASS_BODY_CHARS = 200000;
|
|
6334
|
+
const PER_FILE_LIMIT = 200;
|
|
6335
|
+
|
|
6336
|
+
// Per-interface member ceiling, disclosed via capMembersWithNotice (#576).
|
|
6337
|
+
const MEMBER_LIMIT = 120;
|
|
6281
6338
|
|
|
6282
6339
|
/**
|
|
6283
6340
|
* Extract signatures from Go source code.
|
|
@@ -6313,7 +6370,7 @@ __factories["./src/extractors/go"] = function(module, exports) {
|
|
|
6313
6370
|
const block = extractBlock(stripped, bodyStart);
|
|
6314
6371
|
sigs.push(hinted(withAnchor(`type ${m[1]} interface`, lineAt(stripped, m.index), lineAt(stripped, bodyStart + block.length)), m[1]));
|
|
6315
6372
|
for (const meth of extractInterfaceMethods(block)) {
|
|
6316
|
-
sigs.push(withAnchor(` ${meth.text}`, lineAt(stripped, bodyStart + meth.declIdx), lineAt(stripped, bodyStart + meth.endIdx)));
|
|
6373
|
+
sigs.push(withAnchor(` ${meth.text}`, lineAt(stripped, bodyStart + (meth.declIdx || 0)), lineAt(stripped, bodyStart + (meth.endIdx || 0))));
|
|
6317
6374
|
}
|
|
6318
6375
|
}
|
|
6319
6376
|
|
|
@@ -6331,7 +6388,7 @@ __factories["./src/extractors/go"] = function(module, exports) {
|
|
|
6331
6388
|
|
|
6332
6389
|
function extractBlock(src, startIndex) {
|
|
6333
6390
|
let depth = 1, i = startIndex;
|
|
6334
|
-
const end = Math.min(src.length, startIndex +
|
|
6391
|
+
const end = Math.min(src.length, startIndex + MAX_CLASS_BODY_CHARS);
|
|
6335
6392
|
while (i < end && depth > 0) {
|
|
6336
6393
|
if (src[i] === '{') depth++;
|
|
6337
6394
|
else if (src[i] === '}') depth--;
|
|
@@ -6351,7 +6408,7 @@ __factories["./src/extractors/go"] = function(module, exports) {
|
|
|
6351
6408
|
endIdx: m.index + m[0].length,
|
|
6352
6409
|
});
|
|
6353
6410
|
}
|
|
6354
|
-
return methods
|
|
6411
|
+
return capMembersWithNotice(methods, MEMBER_LIMIT, 'methods');
|
|
6355
6412
|
}
|
|
6356
6413
|
|
|
6357
6414
|
function normalizeParams(params) {
|
|
@@ -6464,7 +6521,7 @@ __factories["./src/extractors/html"] = function(module, exports) {
|
|
|
6464
6521
|
|
|
6465
6522
|
// Ceiling sits above the default `maxSigsPerFile` so the configured budget
|
|
6466
6523
|
// governs output rather than a literal buried here, and omissions are disclosed (#576).
|
|
6467
|
-
const PER_FILE_LIMIT =
|
|
6524
|
+
const PER_FILE_LIMIT = 200;
|
|
6468
6525
|
|
|
6469
6526
|
/**
|
|
6470
6527
|
* Extract signatures from HTML files.
|
|
@@ -6639,6 +6696,10 @@ __factories["./src/extractors/javascript"] = function(module, exports) {
|
|
|
6639
6696
|
const { capWithNotice, capMembersWithNotice } = __require('./src/util/truncate');
|
|
6640
6697
|
const { stripComments, maskCode, readBalanced } = __require('./src/extractors/scan');
|
|
6641
6698
|
|
|
6699
|
+
// Class bodies are scanned to this many characters — guard against
|
|
6700
|
+
// pathological input only; the old 4KB window silently hid members (#576).
|
|
6701
|
+
const MAX_CLASS_BODY_CHARS = 200000;
|
|
6702
|
+
|
|
6642
6703
|
/**
|
|
6643
6704
|
* Extract signatures from JavaScript source code.
|
|
6644
6705
|
* Top-level declarations and class members carry a `:start-end` line anchor
|
|
@@ -6748,13 +6809,13 @@ __factories["./src/extractors/javascript"] = function(module, exports) {
|
|
|
6748
6809
|
const anchored = anchors[i] ? withAnchor(s, anchors[i][0], anchors[i][1]) : s;
|
|
6749
6810
|
return docHintFor[i] ? `${anchored} # ${docHintFor[i]}` : anchored;
|
|
6750
6811
|
});
|
|
6751
|
-
return capWithNotice(withAnchors,
|
|
6812
|
+
return capWithNotice(withAnchors, 200, 'signatures');
|
|
6752
6813
|
}
|
|
6753
6814
|
|
|
6754
6815
|
function extractBlock(src, startIndex) {
|
|
6755
6816
|
let depth = 1;
|
|
6756
6817
|
let i = startIndex;
|
|
6757
|
-
const end = Math.min(src.length, startIndex +
|
|
6818
|
+
const end = Math.min(src.length, startIndex + MAX_CLASS_BODY_CHARS);
|
|
6758
6819
|
while (i < end && depth > 0) {
|
|
6759
6820
|
if (src[i] === '{') depth++;
|
|
6760
6821
|
else if (src[i] === '}') depth--;
|
|
@@ -6789,7 +6850,7 @@ __factories["./src/extractors/javascript"] = function(module, exports) {
|
|
|
6789
6850
|
const retStr = formatReturnHint(returnHints.get(m[1]));
|
|
6790
6851
|
members.push({ text: `${isStatic}${isAsync}${m[1]}(${normalizeParams(params)})${retStr}`, start, end });
|
|
6791
6852
|
}
|
|
6792
|
-
return capMembersWithNotice(members,
|
|
6853
|
+
return capMembersWithNotice(members, 120, 'methods');
|
|
6793
6854
|
}
|
|
6794
6855
|
|
|
6795
6856
|
function buildReturnHints(src) {
|
|
@@ -6863,8 +6924,13 @@ __factories["./src/extractors/kotlin"] = function(module, exports) {
|
|
|
6863
6924
|
// Ceilings sit above the default `maxSigsPerFile` so the configured budget
|
|
6864
6925
|
// governs output rather than a literal buried here, and omissions are disclosed
|
|
6865
6926
|
// — an undisclosed cap looks like a class that simply has eight methods (#576).
|
|
6866
|
-
|
|
6867
|
-
|
|
6927
|
+
// Class bodies are scanned to this many characters. Real classes routinely
|
|
6928
|
+
// run past the old 4KB scan window — truncating there silently hid every
|
|
6929
|
+
// member after ~4000 chars AND anchored class end-lines short (#576). The
|
|
6930
|
+
// ceiling only guards against pathological input (Java parity, #551).
|
|
6931
|
+
const MAX_CLASS_BODY_CHARS = 200000;
|
|
6932
|
+
const MEMBER_LIMIT = 120;
|
|
6933
|
+
const PER_FILE_LIMIT = 200;
|
|
6868
6934
|
|
|
6869
6935
|
/**
|
|
6870
6936
|
* Extract signatures from Kotlin source code.
|
|
@@ -6918,7 +6984,7 @@ __factories["./src/extractors/kotlin"] = function(module, exports) {
|
|
|
6918
6984
|
|
|
6919
6985
|
function extractBlock(src, startIndex) {
|
|
6920
6986
|
let depth = 1, i = startIndex;
|
|
6921
|
-
const end = Math.min(src.length, startIndex +
|
|
6987
|
+
const end = Math.min(src.length, startIndex + MAX_CLASS_BODY_CHARS);
|
|
6922
6988
|
while (i < end && depth > 0) {
|
|
6923
6989
|
if (src[i] === '{') depth++;
|
|
6924
6990
|
else if (src[i] === '}') depth--;
|
|
@@ -7012,13 +7078,169 @@ __factories["./src/extractors/line-anchor"] = function(module, exports) {
|
|
|
7012
7078
|
|
|
7013
7079
|
};
|
|
7014
7080
|
|
|
7081
|
+
// ── ./src/extractors/lua ──
|
|
7082
|
+
__factories["./src/extractors/lua"] = function(module, exports) {
|
|
7083
|
+
|
|
7084
|
+
const { capWithNotice } = __require('./src/util/truncate');
|
|
7085
|
+
|
|
7086
|
+
// Ceiling discloses what it drops rather than truncating silently (#583).
|
|
7087
|
+
// Collection runs to completion so the marker reports the true overflow —
|
|
7088
|
+
// stopping early made r.js report "+1 more" where 50 were hidden (#584).
|
|
7089
|
+
const PER_FILE_LIMIT = 200;
|
|
7090
|
+
|
|
7091
|
+
/**
|
|
7092
|
+
* Extract signatures from Lua source code.
|
|
7093
|
+
*
|
|
7094
|
+
* Recognised constructs:
|
|
7095
|
+
* - Global functions: `function name(args)`
|
|
7096
|
+
* - Module-table functions: `function M.name(args)` / `function M:name(args)`
|
|
7097
|
+
* - Local functions: `local function name(args)`
|
|
7098
|
+
* - Assigned functions: `name = function(args)` / `M.name = function(args)`
|
|
7099
|
+
* - Module imports: `local mod = require("mod")` as compact hints
|
|
7100
|
+
* - LDoc-style doc comments (`---`) as first-sentence hints
|
|
7101
|
+
*
|
|
7102
|
+
* The extractor is regex-only and zero-dependency, matching SigMap's Tier-3
|
|
7103
|
+
* language extractor style.
|
|
7104
|
+
*
|
|
7105
|
+
* @param {string} src - Raw file content
|
|
7106
|
+
* @returns {string[]} Array of signature strings
|
|
7107
|
+
*/
|
|
7108
|
+
function extract(src) {
|
|
7109
|
+
if (!src || typeof src !== 'string') return [];
|
|
7110
|
+
const sigs = [];
|
|
7111
|
+
const hints = collectDocHints(src);
|
|
7112
|
+
const stripped = stripLuaComments(src);
|
|
7113
|
+
const seen = new Set();
|
|
7114
|
+
|
|
7115
|
+
// local foo = require('bar.baz') — useful module-surface hint, capped low.
|
|
7116
|
+
for (const m of stripped.matchAll(/^\s*(?:local\s+)?([A-Za-z_]\w*)\s*=\s*require\s*\(\s*['"]([A-Za-z0-9_.\/-]+)['"]\s*\)/gm)) {
|
|
7117
|
+
pushUnique(sigs, seen, `require ${m[2]} as ${m[1]}`);
|
|
7118
|
+
}
|
|
7119
|
+
|
|
7120
|
+
// local function name(args)
|
|
7121
|
+
for (const m of stripped.matchAll(/^\s*local\s+function\s+([A-Za-z_]\w*)\s*\(([^)]*)\)/gm)) {
|
|
7122
|
+
if (m[1].startsWith('_')) continue;
|
|
7123
|
+
pushUnique(sigs, seen, `local function ${m[1]}(${normalizeParams(m[2])})${applyHint(hints, m[1])}`);
|
|
7124
|
+
}
|
|
7125
|
+
|
|
7126
|
+
// function name(args), function M.name(args), function M:name(args)
|
|
7127
|
+
for (const m of stripped.matchAll(/^\s*function\s+([A-Za-z_]\w*(?:(?:\.|:)[A-Za-z_]\w*)*)\s*\(([^)]*)\)/gm)) {
|
|
7128
|
+
const name = m[1];
|
|
7129
|
+
if (name.startsWith('_')) continue;
|
|
7130
|
+
pushUnique(sigs, seen, `function ${name}(${normalizeParams(m[2])})${applyHint(hints, name)}`);
|
|
7131
|
+
}
|
|
7132
|
+
|
|
7133
|
+
// name = function(args), M.name = function(args), M:name = function(args)
|
|
7134
|
+
for (const m of stripped.matchAll(/^\s*(?:local\s+)?([A-Za-z_]\w*(?:(?:\.|:)[A-Za-z_]\w*)*)\s*=\s*function\s*\(([^)]*)\)/gm)) {
|
|
7135
|
+
const name = m[1];
|
|
7136
|
+
if (name.startsWith('_')) continue;
|
|
7137
|
+
pushUnique(sigs, seen, `${name} = function(${normalizeParams(m[2])})${applyHint(hints, name)}`);
|
|
7138
|
+
}
|
|
7139
|
+
|
|
7140
|
+
return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
|
|
7141
|
+
}
|
|
7142
|
+
|
|
7143
|
+
function pushUnique(out, seen, sig) {
|
|
7144
|
+
if (!sig || seen.has(sig)) return;
|
|
7145
|
+
seen.add(sig);
|
|
7146
|
+
out.push(sig);
|
|
7147
|
+
}
|
|
7148
|
+
|
|
7149
|
+
function normalizeParams(params) {
|
|
7150
|
+
return String(params || '')
|
|
7151
|
+
.replace(/--.*$/gm, '')
|
|
7152
|
+
.split(',')
|
|
7153
|
+
.map((p) => p.trim().replace(/\s+/g, ' '))
|
|
7154
|
+
.filter(Boolean)
|
|
7155
|
+
.join(', ');
|
|
7156
|
+
}
|
|
7157
|
+
|
|
7158
|
+
function applyHint(hints, name) {
|
|
7159
|
+
const h = hints.get(name);
|
|
7160
|
+
return h ? ` # ${h}` : '';
|
|
7161
|
+
}
|
|
7162
|
+
|
|
7163
|
+
/**
|
|
7164
|
+
* Attach each contiguous `---` doc block to the next function-like declaration
|
|
7165
|
+
* by its extracted symbol name.
|
|
7166
|
+
*/
|
|
7167
|
+
function collectDocHints(src) {
|
|
7168
|
+
const hints = new Map();
|
|
7169
|
+
const lines = src.split('\n');
|
|
7170
|
+
let block = [];
|
|
7171
|
+
for (const line of lines) {
|
|
7172
|
+
const doc = line.match(/^\s*---\s?(.*)$/);
|
|
7173
|
+
if (doc) {
|
|
7174
|
+
block.push(doc[1]);
|
|
7175
|
+
} else if (block.length > 0) {
|
|
7176
|
+
const decl = line.match(/^\s*local\s+function\s+([A-Za-z_]\w*)\s*\(/)
|
|
7177
|
+
|| line.match(/^\s*function\s+([A-Za-z_]\w*(?:(?:\.|:)[A-Za-z_]\w*)*)\s*\(/)
|
|
7178
|
+
|| line.match(/^\s*(?:local\s+)?([A-Za-z_]\w*(?:(?:\.|:)[A-Za-z_]\w*)*)\s*=\s*function\s*\(/);
|
|
7179
|
+
if (decl) {
|
|
7180
|
+
const hint = firstDocSentence(block);
|
|
7181
|
+
if (hint) hints.set(decl[1], hint);
|
|
7182
|
+
}
|
|
7183
|
+
block = [];
|
|
7184
|
+
}
|
|
7185
|
+
}
|
|
7186
|
+
return hints;
|
|
7187
|
+
}
|
|
7188
|
+
|
|
7189
|
+
function firstDocSentence(block) {
|
|
7190
|
+
for (const raw of block) {
|
|
7191
|
+
const line = String(raw || '').trim();
|
|
7192
|
+
if (!line || line.startsWith('@')) continue;
|
|
7193
|
+
return line.replace(/\s+/g, ' ').slice(0, 60).replace(/[.,;:!?]+$/, '').trim();
|
|
7194
|
+
}
|
|
7195
|
+
return '';
|
|
7196
|
+
}
|
|
7197
|
+
|
|
7198
|
+
/** Strip Lua line and long comments while preserving strings enough for regex scans. */
|
|
7199
|
+
function stripLuaComments(src) {
|
|
7200
|
+
const out = src.split('');
|
|
7201
|
+
const blank = (a, b) => { for (let i = a; i < b; i++) if (out[i] !== '\n') out[i] = ' '; };
|
|
7202
|
+
let i = 0;
|
|
7203
|
+
while (i < src.length) {
|
|
7204
|
+
const ch = src[i];
|
|
7205
|
+
if (ch === '"' || ch === "'") {
|
|
7206
|
+
const quote = ch;
|
|
7207
|
+
i++;
|
|
7208
|
+
while (i < src.length) {
|
|
7209
|
+
if (src[i] === '\\') { i += 2; continue; }
|
|
7210
|
+
if (src[i] === quote) { i++; break; }
|
|
7211
|
+
if (src[i] === '\n') break;
|
|
7212
|
+
i++;
|
|
7213
|
+
}
|
|
7214
|
+
continue;
|
|
7215
|
+
}
|
|
7216
|
+
if (src.startsWith('--[[', i)) {
|
|
7217
|
+
const end = src.indexOf(']]', i + 4);
|
|
7218
|
+
blank(i, end === -1 ? src.length : end + 2);
|
|
7219
|
+
i = end === -1 ? src.length : end + 2;
|
|
7220
|
+
continue;
|
|
7221
|
+
}
|
|
7222
|
+
if (src.startsWith('--', i)) {
|
|
7223
|
+
const end = src.indexOf('\n', i + 2);
|
|
7224
|
+
blank(i, end === -1 ? src.length : end);
|
|
7225
|
+
i = end === -1 ? src.length : end;
|
|
7226
|
+
continue;
|
|
7227
|
+
}
|
|
7228
|
+
i++;
|
|
7229
|
+
}
|
|
7230
|
+
return out.join('');
|
|
7231
|
+
}
|
|
7232
|
+
|
|
7233
|
+
module.exports = { extract };
|
|
7234
|
+
|
|
7235
|
+
};
|
|
7236
|
+
|
|
7015
7237
|
// ── ./src/extractors/markdown ──
|
|
7016
7238
|
__factories["./src/extractors/markdown"] = function(module, exports) {
|
|
7017
7239
|
|
|
7018
7240
|
const { capWithNotice } = __require('./src/util/truncate');
|
|
7019
7241
|
|
|
7020
7242
|
// Ceiling discloses what it drops rather than truncating silently (#583).
|
|
7021
|
-
const PER_FILE_LIMIT =
|
|
7243
|
+
const PER_FILE_LIMIT = 200;
|
|
7022
7244
|
|
|
7023
7245
|
/**
|
|
7024
7246
|
* Lightweight markdown technical indexer.
|
|
@@ -7199,8 +7421,13 @@ __factories["./src/extractors/php"] = function(module, exports) {
|
|
|
7199
7421
|
// Ceilings sit above the default `maxSigsPerFile` so the configured budget
|
|
7200
7422
|
// governs output rather than a literal buried here, and omissions are disclosed
|
|
7201
7423
|
// — an undisclosed cap looks like a class that simply has eight methods (#576).
|
|
7202
|
-
|
|
7203
|
-
|
|
7424
|
+
// Class bodies are scanned to this many characters. Real classes routinely
|
|
7425
|
+
// run past the old 4KB scan window — truncating there silently hid every
|
|
7426
|
+
// member after ~4000 chars AND anchored class end-lines short (#576). The
|
|
7427
|
+
// ceiling only guards against pathological input (Java parity, #551).
|
|
7428
|
+
const MAX_CLASS_BODY_CHARS = 200000;
|
|
7429
|
+
const MEMBER_LIMIT = 120;
|
|
7430
|
+
const PER_FILE_LIMIT = 200;
|
|
7204
7431
|
|
|
7205
7432
|
/**
|
|
7206
7433
|
* Extract signatures from PHP source code.
|
|
@@ -7258,7 +7485,7 @@ __factories["./src/extractors/php"] = function(module, exports) {
|
|
|
7258
7485
|
|
|
7259
7486
|
function extractBlock(src, startIndex) {
|
|
7260
7487
|
let depth = 1, i = startIndex;
|
|
7261
|
-
const end = Math.min(src.length, startIndex +
|
|
7488
|
+
const end = Math.min(src.length, startIndex + MAX_CLASS_BODY_CHARS);
|
|
7262
7489
|
while (i < end && depth > 0) {
|
|
7263
7490
|
if (src[i] === '{') depth++;
|
|
7264
7491
|
else if (src[i] === '}') depth--;
|
|
@@ -7378,7 +7605,7 @@ __factories["./src/extractors/properties"] = function(module, exports) {
|
|
|
7378
7605
|
const { capWithNotice } = __require('./src/util/truncate');
|
|
7379
7606
|
|
|
7380
7607
|
// Ceiling discloses what it drops rather than truncating silently (#583).
|
|
7381
|
-
const PER_FILE_LIMIT =
|
|
7608
|
+
const PER_FILE_LIMIT = 200;
|
|
7382
7609
|
|
|
7383
7610
|
/**
|
|
7384
7611
|
* Extract signatures from .properties configuration files.
|
|
@@ -7494,7 +7721,10 @@ __factories["./src/extractors/python"] = function(module, exports) {
|
|
|
7494
7721
|
|
|
7495
7722
|
// Ceiling sits above the default `maxSigsPerFile` so the configured budget
|
|
7496
7723
|
// governs output rather than a literal buried here, and omissions are disclosed (#576).
|
|
7497
|
-
const PER_FILE_LIMIT =
|
|
7724
|
+
const PER_FILE_LIMIT = 200;
|
|
7725
|
+
|
|
7726
|
+
// Per-class member ceiling, disclosed via capWithNotice (#576).
|
|
7727
|
+
const MEMBER_LIMIT = 120;
|
|
7498
7728
|
|
|
7499
7729
|
/**
|
|
7500
7730
|
* 1-based line of the last source line belonging to a top-level (indent 0)
|
|
@@ -7650,7 +7880,7 @@ __factories["./src/extractors/python"] = function(module, exports) {
|
|
|
7650
7880
|
methods.push(`${asyncKw}def ${m[1]}(${params})${retStr}`);
|
|
7651
7881
|
}
|
|
7652
7882
|
}
|
|
7653
|
-
return methods
|
|
7883
|
+
return capWithNotice(methods, MEMBER_LIMIT, 'methods');
|
|
7654
7884
|
}
|
|
7655
7885
|
|
|
7656
7886
|
function tryExtractDataclassFields(stripped, classIndex) {
|
|
@@ -7759,6 +7989,8 @@ __factories["./src/extractors/python"] = function(module, exports) {
|
|
|
7759
7989
|
// ── ./src/extractors/python_dataclass ──
|
|
7760
7990
|
__factories["./src/extractors/python_dataclass"] = function(module, exports) {
|
|
7761
7991
|
|
|
7992
|
+
const { capWithNotice } = __require('./src/util/truncate');
|
|
7993
|
+
|
|
7762
7994
|
/**
|
|
7763
7995
|
* Extract Python dataclass, Pydantic model, and SQLAlchemy ORM metadata.
|
|
7764
7996
|
* Focuses on model fields, validation, and relationships.
|
|
@@ -7830,7 +8062,7 @@ __factories["./src/extractors/python_dataclass"] = function(module, exports) {
|
|
|
7830
8062
|
sigs.push('config-class');
|
|
7831
8063
|
}
|
|
7832
8064
|
|
|
7833
|
-
return Array.from(new Set(sigs))
|
|
8065
|
+
return capWithNotice(Array.from(new Set(sigs)), 200, 'signatures');
|
|
7834
8066
|
}
|
|
7835
8067
|
|
|
7836
8068
|
module.exports = { extract };
|
|
@@ -7844,7 +8076,7 @@ __factories["./src/extractors/r"] = function(module, exports) {
|
|
|
7844
8076
|
|
|
7845
8077
|
// Ceiling sits above the default `maxSigsPerFile` so the configured budget
|
|
7846
8078
|
// governs output rather than a literal buried here, and omissions are disclosed (#576).
|
|
7847
|
-
const PER_FILE_LIMIT =
|
|
8079
|
+
const PER_FILE_LIMIT = 200;
|
|
7848
8080
|
|
|
7849
8081
|
/**
|
|
7850
8082
|
* Extract signatures from R source code.
|
|
@@ -8123,7 +8355,7 @@ __factories["./src/extractors/ruby"] = function(module, exports) {
|
|
|
8123
8355
|
|
|
8124
8356
|
// Ceiling sits above the default `maxSigsPerFile` so the configured budget
|
|
8125
8357
|
// governs output rather than a literal buried here, and omissions are disclosed (#576).
|
|
8126
|
-
const PER_FILE_LIMIT =
|
|
8358
|
+
const PER_FILE_LIMIT = 200;
|
|
8127
8359
|
|
|
8128
8360
|
/**
|
|
8129
8361
|
* Extract signatures from Ruby source code.
|
|
@@ -8184,11 +8416,19 @@ __factories["./src/extractors/ruby"] = function(module, exports) {
|
|
|
8184
8416
|
__factories["./src/extractors/rust"] = function(module, exports) {
|
|
8185
8417
|
|
|
8186
8418
|
const { lineAt, withAnchor } = __require('./src/extractors/line-anchor');
|
|
8187
|
-
const { capWithNotice } = __require('./src/util/truncate');
|
|
8419
|
+
const { capWithNotice, capMembersWithNotice } = __require('./src/util/truncate');
|
|
8188
8420
|
|
|
8189
8421
|
// Ceiling sits above the default `maxSigsPerFile` so the configured budget
|
|
8190
8422
|
// governs output rather than a literal buried here, and omissions are disclosed (#576).
|
|
8191
|
-
|
|
8423
|
+
// Class bodies are scanned to this many characters. Real classes routinely
|
|
8424
|
+
// run past the old 4KB scan window — truncating there silently hid every
|
|
8425
|
+
// member after ~4000 chars AND anchored class end-lines short (#576). The
|
|
8426
|
+
// ceiling only guards against pathological input (Java parity, #551).
|
|
8427
|
+
const MAX_CLASS_BODY_CHARS = 200000;
|
|
8428
|
+
const PER_FILE_LIMIT = 200;
|
|
8429
|
+
|
|
8430
|
+
// Per-impl member ceiling, disclosed via capMembersWithNotice (#576).
|
|
8431
|
+
const MEMBER_LIMIT = 120;
|
|
8192
8432
|
|
|
8193
8433
|
/**
|
|
8194
8434
|
* Extract signatures from Rust source code.
|
|
@@ -8246,7 +8486,7 @@ __factories["./src/extractors/rust"] = function(module, exports) {
|
|
|
8246
8486
|
const block = extractBlock(stripped, bodyStart);
|
|
8247
8487
|
sigs.push(withAnchor(`impl ${m[1]}`, lineAt(stripped, m.index), lineAt(stripped, bodyStart + block.length)));
|
|
8248
8488
|
for (const fn of extractMethods(block)) {
|
|
8249
|
-
sigs.push(hinted(withAnchor(` ${fn.text}`, lineAt(stripped, bodyStart + fn.declIdx), lineAt(stripped, bodyStart + fn.endIdx)), fn.name));
|
|
8489
|
+
sigs.push(hinted(withAnchor(` ${fn.text}`, lineAt(stripped, bodyStart + (fn.declIdx || 0)), lineAt(stripped, bodyStart + (fn.endIdx || 0))), fn.name));
|
|
8250
8490
|
}
|
|
8251
8491
|
}
|
|
8252
8492
|
|
|
@@ -8263,7 +8503,7 @@ __factories["./src/extractors/rust"] = function(module, exports) {
|
|
|
8263
8503
|
|
|
8264
8504
|
function extractBlock(src, startIndex) {
|
|
8265
8505
|
let depth = 1, i = startIndex;
|
|
8266
|
-
const end = Math.min(src.length, startIndex +
|
|
8506
|
+
const end = Math.min(src.length, startIndex + MAX_CLASS_BODY_CHARS);
|
|
8267
8507
|
while (i < end && depth > 0) {
|
|
8268
8508
|
if (src[i] === '{') depth++;
|
|
8269
8509
|
else if (src[i] === '}') depth--;
|
|
@@ -8284,7 +8524,7 @@ __factories["./src/extractors/rust"] = function(module, exports) {
|
|
|
8284
8524
|
endIdx: m.index + m[0].length,
|
|
8285
8525
|
});
|
|
8286
8526
|
}
|
|
8287
|
-
return methods
|
|
8527
|
+
return capMembersWithNotice(methods, MEMBER_LIMIT, 'methods');
|
|
8288
8528
|
}
|
|
8289
8529
|
|
|
8290
8530
|
function normalizeParams(params) {
|
|
@@ -8336,8 +8576,13 @@ __factories["./src/extractors/scala"] = function(module, exports) {
|
|
|
8336
8576
|
// Ceilings sit above the default `maxSigsPerFile` so the configured budget
|
|
8337
8577
|
// governs output rather than a literal buried here, and omissions are disclosed
|
|
8338
8578
|
// — an undisclosed cap looks like a class that simply has eight methods (#576).
|
|
8339
|
-
|
|
8340
|
-
|
|
8579
|
+
// Class bodies are scanned to this many characters. Real classes routinely
|
|
8580
|
+
// run past the old 4KB scan window — truncating there silently hid every
|
|
8581
|
+
// member after ~4000 chars AND anchored class end-lines short (#576). The
|
|
8582
|
+
// ceiling only guards against pathological input (Java parity, #551).
|
|
8583
|
+
const MAX_CLASS_BODY_CHARS = 200000;
|
|
8584
|
+
const MEMBER_LIMIT = 120;
|
|
8585
|
+
const PER_FILE_LIMIT = 200;
|
|
8341
8586
|
|
|
8342
8587
|
/**
|
|
8343
8588
|
* Extract signatures from Scala source code.
|
|
@@ -8384,7 +8629,7 @@ __factories["./src/extractors/scala"] = function(module, exports) {
|
|
|
8384
8629
|
|
|
8385
8630
|
function extractBlock(src, startIndex) {
|
|
8386
8631
|
let depth = 1, i = startIndex;
|
|
8387
|
-
const end = Math.min(src.length, startIndex +
|
|
8632
|
+
const end = Math.min(src.length, startIndex + MAX_CLASS_BODY_CHARS);
|
|
8388
8633
|
while (i < end && depth > 0) {
|
|
8389
8634
|
if (src[i] === '{') depth++;
|
|
8390
8635
|
else if (src[i] === '}') depth--;
|
|
@@ -8529,7 +8774,7 @@ __factories["./src/extractors/shell"] = function(module, exports) {
|
|
|
8529
8774
|
|
|
8530
8775
|
// Ceiling sits above the default `maxSigsPerFile` so the configured budget
|
|
8531
8776
|
// governs output rather than a literal buried here, and omissions are disclosed (#576).
|
|
8532
|
-
const PER_FILE_LIMIT =
|
|
8777
|
+
const PER_FILE_LIMIT = 200;
|
|
8533
8778
|
|
|
8534
8779
|
/**
|
|
8535
8780
|
* Extract signatures from shell scripts (bash, zsh, fish).
|
|
@@ -8679,7 +8924,7 @@ __factories["./src/extractors/svelte"] = function(module, exports) {
|
|
|
8679
8924
|
|
|
8680
8925
|
// Ceiling sits above the default `maxSigsPerFile` so the configured budget
|
|
8681
8926
|
// governs output rather than a literal buried here, and omissions are disclosed (#576).
|
|
8682
|
-
const PER_FILE_LIMIT =
|
|
8927
|
+
const PER_FILE_LIMIT = 200;
|
|
8683
8928
|
|
|
8684
8929
|
/**
|
|
8685
8930
|
* Extract signatures from Svelte components.
|
|
@@ -8749,8 +8994,13 @@ __factories["./src/extractors/swift"] = function(module, exports) {
|
|
|
8749
8994
|
// Ceilings sit above the default `maxSigsPerFile` so the configured budget
|
|
8750
8995
|
// governs output rather than a literal buried here, and omissions are disclosed
|
|
8751
8996
|
// — an undisclosed cap looks like a class that simply has eight methods (#576).
|
|
8752
|
-
|
|
8753
|
-
|
|
8997
|
+
// Class bodies are scanned to this many characters. Real classes routinely
|
|
8998
|
+
// run past the old 4KB scan window — truncating there silently hid every
|
|
8999
|
+
// member after ~4000 chars AND anchored class end-lines short (#576). The
|
|
9000
|
+
// ceiling only guards against pathological input (Java parity, #551).
|
|
9001
|
+
const MAX_CLASS_BODY_CHARS = 200000;
|
|
9002
|
+
const MEMBER_LIMIT = 120;
|
|
9003
|
+
const PER_FILE_LIMIT = 200;
|
|
8754
9004
|
|
|
8755
9005
|
/**
|
|
8756
9006
|
* Extract signatures from Swift source code.
|
|
@@ -8804,7 +9054,7 @@ __factories["./src/extractors/swift"] = function(module, exports) {
|
|
|
8804
9054
|
|
|
8805
9055
|
function extractBlock(src, startIndex) {
|
|
8806
9056
|
let depth = 1, i = startIndex;
|
|
8807
|
-
const end = Math.min(src.length, startIndex +
|
|
9057
|
+
const end = Math.min(src.length, startIndex + MAX_CLASS_BODY_CHARS);
|
|
8808
9058
|
while (i < end && depth > 0) {
|
|
8809
9059
|
if (src[i] === '{') depth++;
|
|
8810
9060
|
else if (src[i] === '}') depth--;
|
|
@@ -8963,7 +9213,7 @@ __factories["./src/extractors/toml"] = function(module, exports) {
|
|
|
8963
9213
|
const { capWithNotice } = __require('./src/util/truncate');
|
|
8964
9214
|
|
|
8965
9215
|
// Ceiling discloses what it drops rather than truncating silently (#583).
|
|
8966
|
-
const PER_FILE_LIMIT =
|
|
9216
|
+
const PER_FILE_LIMIT = 200;
|
|
8967
9217
|
|
|
8968
9218
|
/**
|
|
8969
9219
|
* Extract signatures from TOML configuration files.
|
|
@@ -9015,6 +9265,10 @@ __factories["./src/extractors/typescript"] = function(module, exports) {
|
|
|
9015
9265
|
const { capWithNotice, capMembersWithNotice } = __require('./src/util/truncate');
|
|
9016
9266
|
const { stripComments, maskCode, readBalanced } = __require('./src/extractors/scan');
|
|
9017
9267
|
|
|
9268
|
+
// Class bodies are scanned to this many characters — guard against
|
|
9269
|
+
// pathological input only; the old 4KB window silently hid members (#576).
|
|
9270
|
+
const MAX_CLASS_BODY_CHARS = 200000;
|
|
9271
|
+
|
|
9018
9272
|
/**
|
|
9019
9273
|
* Extract signatures from TypeScript source code.
|
|
9020
9274
|
* Top-level declarations carry a `:start-end` line anchor (see line-anchor.js);
|
|
@@ -9200,13 +9454,13 @@ __factories["./src/extractors/typescript"] = function(module, exports) {
|
|
|
9200
9454
|
const anchored = anchors[i] ? withAnchor(s, anchors[i][0], anchors[i][1]) : s;
|
|
9201
9455
|
return docHintFor[i] ? `${anchored} # ${docHintFor[i]}` : anchored;
|
|
9202
9456
|
});
|
|
9203
|
-
return capWithNotice(withAnchors,
|
|
9457
|
+
return capWithNotice(withAnchors, 200, 'signatures');
|
|
9204
9458
|
}
|
|
9205
9459
|
|
|
9206
9460
|
function extractBlock(src, startIndex) {
|
|
9207
9461
|
let depth = 1;
|
|
9208
9462
|
let i = startIndex;
|
|
9209
|
-
const end = Math.min(src.length, startIndex +
|
|
9463
|
+
const end = Math.min(src.length, startIndex + MAX_CLASS_BODY_CHARS);
|
|
9210
9464
|
while (i < end && depth > 0) {
|
|
9211
9465
|
if (src[i] === '{') depth++;
|
|
9212
9466
|
else if (src[i] === '}') depth--;
|
|
@@ -9234,7 +9488,7 @@ __factories["./src/extractors/typescript"] = function(module, exports) {
|
|
|
9234
9488
|
const start = m.index + (m[0].length - m[0].replace(/^\s+/, '').length);
|
|
9235
9489
|
members.push({ text: `${m[1]}(${normalizeParams(block.slice(openIdx + 1, closeIdx))})`, start, end: closeIdx + 1 });
|
|
9236
9490
|
}
|
|
9237
|
-
return capMembersWithNotice(members,
|
|
9491
|
+
return capMembersWithNotice(members, 120, 'members');
|
|
9238
9492
|
}
|
|
9239
9493
|
|
|
9240
9494
|
const _CTRL_KEYWORDS = new Set(['if', 'for', 'while', 'switch', 'do', 'try', 'catch', 'finally', 'else', 'return']);
|
|
@@ -9269,7 +9523,7 @@ __factories["./src/extractors/typescript"] = function(module, exports) {
|
|
|
9269
9523
|
const retStr = retType ? ` → ${retType}` : '';
|
|
9270
9524
|
members.push({ text: `${isStatic}${isAsync}${m[1]}(${normalizeParams(params)})${retStr}`, start, end });
|
|
9271
9525
|
}
|
|
9272
|
-
return capMembersWithNotice(members,
|
|
9526
|
+
return capMembersWithNotice(members, 120, 'methods');
|
|
9273
9527
|
}
|
|
9274
9528
|
|
|
9275
9529
|
function normalizeParams(params) {
|
|
@@ -9344,7 +9598,7 @@ __factories["./src/extractors/typescript_react"] = function(module, exports) {
|
|
|
9344
9598
|
const { capWithNotice } = __require('./src/util/truncate');
|
|
9345
9599
|
|
|
9346
9600
|
// Ceiling discloses what it drops rather than truncating silently (#583).
|
|
9347
|
-
const PER_FILE_LIMIT =
|
|
9601
|
+
const PER_FILE_LIMIT = 200;
|
|
9348
9602
|
|
|
9349
9603
|
/**
|
|
9350
9604
|
* Extract React component signatures from .tsx files.
|
|
@@ -9410,6 +9664,8 @@ __factories["./src/extractors/typescript_react"] = function(module, exports) {
|
|
|
9410
9664
|
// ── ./src/extractors/vue_sfc ──
|
|
9411
9665
|
__factories["./src/extractors/vue_sfc"] = function(module, exports) {
|
|
9412
9666
|
|
|
9667
|
+
const { capWithNotice } = __require('./src/util/truncate');
|
|
9668
|
+
|
|
9413
9669
|
/**
|
|
9414
9670
|
* Extract Vue Single-File Component (SFC) signatures from .vue files.
|
|
9415
9671
|
* Captures component metadata: name, props, emits, slots, composables, lifecycle.
|
|
@@ -9503,7 +9759,7 @@ __factories["./src/extractors/vue_sfc"] = function(module, exports) {
|
|
|
9503
9759
|
sigs.push(`slot ${s}`);
|
|
9504
9760
|
}
|
|
9505
9761
|
|
|
9506
|
-
return Array.from(new Set(sigs))
|
|
9762
|
+
return capWithNotice(Array.from(new Set(sigs)), 200, 'signatures');
|
|
9507
9763
|
}
|
|
9508
9764
|
|
|
9509
9765
|
module.exports = { extract };
|
|
@@ -9513,6 +9769,8 @@ __factories["./src/extractors/vue_sfc"] = function(module, exports) {
|
|
|
9513
9769
|
// ── ./src/extractors/xml ──
|
|
9514
9770
|
__factories["./src/extractors/xml"] = function(module, exports) {
|
|
9515
9771
|
|
|
9772
|
+
const { capWithNotice } = __require('./src/util/truncate');
|
|
9773
|
+
|
|
9516
9774
|
/**
|
|
9517
9775
|
* Lightweight XML config extractor.
|
|
9518
9776
|
* Captures root tags, key config tags, and id/name/class attributes.
|
|
@@ -9553,7 +9811,7 @@ __factories["./src/extractors/xml"] = function(module, exports) {
|
|
|
9553
9811
|
if (cls) sigs.push(`${tag} -> ${cls[1]}`);
|
|
9554
9812
|
}
|
|
9555
9813
|
|
|
9556
|
-
return Array.from(new Set(sigs))
|
|
9814
|
+
return capWithNotice(Array.from(new Set(sigs)), 200, 'signatures');
|
|
9557
9815
|
}
|
|
9558
9816
|
|
|
9559
9817
|
module.exports = { extract };
|
|
@@ -9567,7 +9825,7 @@ __factories["./src/extractors/yaml"] = function(module, exports) {
|
|
|
9567
9825
|
|
|
9568
9826
|
// Ceiling sits above the default `maxSigsPerFile` so the configured budget
|
|
9569
9827
|
// governs output rather than a literal buried here, and omissions are disclosed (#576).
|
|
9570
|
-
const PER_FILE_LIMIT =
|
|
9828
|
+
const PER_FILE_LIMIT = 200;
|
|
9571
9829
|
|
|
9572
9830
|
/**
|
|
9573
9831
|
* Extract signatures from YAML configuration files.
|
|
@@ -11977,6 +12235,8 @@ __factories["./src/graph/call-graph"] = function(module, exports) {
|
|
|
11977
12235
|
const JAVA_EXTS = new Set(['.java']);
|
|
11978
12236
|
const GO_EXTS = new Set(['.go']);
|
|
11979
12237
|
const RS_EXTS = new Set(['.rs']);
|
|
12238
|
+
const KT_EXTS = new Set(['.kt', '.kts']);
|
|
12239
|
+
const SCALA_EXTS = new Set(['.scala', '.sc']);
|
|
11980
12240
|
|
|
11981
12241
|
// Tokens that look like `name(` calls or definition headers but are language
|
|
11982
12242
|
// keywords, not user symbols — never treated as a call or a definition.
|
|
@@ -12300,6 +12560,81 @@ __factories["./src/graph/call-graph"] = function(module, exports) {
|
|
|
12300
12560
|
|
|
12301
12561
|
// Pick the masker whose comment/string syntax matches the language.
|
|
12302
12562
|
// Java and Go share JS syntax (Go raw strings mask like template literals).
|
|
12563
|
+
/**
|
|
12564
|
+
* Body range for a JVM-family member that may use either a brace body or an
|
|
12565
|
+
* expression body (`fun f() = expr` / `def f = expr`), which Java has not.
|
|
12566
|
+
* An expression body runs to the end of its line — enough to capture the calls
|
|
12567
|
+
* it makes, which is all the graph needs.
|
|
12568
|
+
* @returns {{bodyStart:number, bodyEnd:number}|null} null when neither form follows
|
|
12569
|
+
*/
|
|
12570
|
+
function jvmBodyRange(masked, from) {
|
|
12571
|
+
let k = from;
|
|
12572
|
+
while (k < masked.length && masked[k] !== '{' && masked[k] !== '=' && masked[k] !== '\n' && masked[k] !== ';') k++;
|
|
12573
|
+
if (masked[k] === '{') return { bodyStart: k, bodyEnd: matchDelim(masked, k, '{', '}') };
|
|
12574
|
+
if (masked[k] === '=') {
|
|
12575
|
+
// `= {` is still a brace body, just written with an assignment.
|
|
12576
|
+
let j = k + 1;
|
|
12577
|
+
while (j < masked.length && /\s/.test(masked[j])) j++;
|
|
12578
|
+
if (masked[j] === '{') return { bodyStart: j, bodyEnd: matchDelim(masked, j, '{', '}') };
|
|
12579
|
+
const eol = masked.indexOf('\n', k);
|
|
12580
|
+
return { bodyStart: k, bodyEnd: eol === -1 ? masked.length : eol };
|
|
12581
|
+
}
|
|
12582
|
+
return null; // abstract member or interface declaration
|
|
12583
|
+
}
|
|
12584
|
+
|
|
12585
|
+
/**
|
|
12586
|
+
* Kotlin `fun` definitions, including extension functions (`fun Foo.bar()`,
|
|
12587
|
+
* recorded as `bar`) and expression bodies.
|
|
12588
|
+
*/
|
|
12589
|
+
function ktDefs(masked) {
|
|
12590
|
+
const defs = [];
|
|
12591
|
+
const seen = new Set();
|
|
12592
|
+
const re = /(?:^|\n)[ \t]*((?:(?:public|private|protected|internal|open|override|abstract|final|suspend|inline|operator|infix|tailrec|external|expect|actual|inner|companion)\s+)*)fun\s+(?:<[^>\n]{0,80}>\s*)?(?:[A-Za-z_][\w.<>]*\.)?([A-Za-z_][\w]*)\s*\(/g;
|
|
12593
|
+
let m;
|
|
12594
|
+
while ((m = re.exec(masked)) !== null) {
|
|
12595
|
+
const name = m[2];
|
|
12596
|
+
if (NON_CALL.has(name)) continue;
|
|
12597
|
+
const paren = masked.indexOf('(', m.index + m[0].length - 1);
|
|
12598
|
+
const close = matchDelim(masked, paren, '(', ')');
|
|
12599
|
+
if (close < 0) continue;
|
|
12600
|
+
const range = jvmBodyRange(masked, close + 1);
|
|
12601
|
+
const line = lineAt(masked, m.index + 1);
|
|
12602
|
+
const key = name + ':' + (range ? range.bodyStart : line);
|
|
12603
|
+
if (seen.has(key)) continue;
|
|
12604
|
+
seen.add(key);
|
|
12605
|
+
// An abstract/interface member owns no body: it can receive edges but
|
|
12606
|
+
// never produces them, the same treatment javaDefs gives declarations.
|
|
12607
|
+
defs.push(range ? { name, line, ...range } : { name, line, bodyStart: close, bodyEnd: close });
|
|
12608
|
+
}
|
|
12609
|
+
return defs;
|
|
12610
|
+
}
|
|
12611
|
+
|
|
12612
|
+
/**
|
|
12613
|
+
* Scala `def` definitions. Handles parameterless members (`def foo: Int = 1`),
|
|
12614
|
+
* type parameters, and expression bodies.
|
|
12615
|
+
*/
|
|
12616
|
+
function scalaDefs(masked) {
|
|
12617
|
+
const defs = [];
|
|
12618
|
+
const seen = new Set();
|
|
12619
|
+
const re = /(?:^|\n)[ \t]*((?:(?:private|protected|override|implicit|final|lazy|sealed|abstract|inline)(?:\s*\[[^\]\n]{0,60}\])?\s+)*)def\s+([A-Za-z_][\w]*)\s*(?=[\[\(:=])/g;
|
|
12620
|
+
let m;
|
|
12621
|
+
while ((m = re.exec(masked)) !== null) {
|
|
12622
|
+
const name = m[2];
|
|
12623
|
+
if (NON_CALL.has(name)) continue;
|
|
12624
|
+
// Step past an optional type-parameter list, then an optional value list.
|
|
12625
|
+
let k = m.index + m[0].length;
|
|
12626
|
+
if (masked[k] === '[') { const e = matchDelim(masked, k, '[', ']'); if (e < 0) continue; k = e + 1; }
|
|
12627
|
+
while (masked[k] === '(') { const e = matchDelim(masked, k, '(', ')'); if (e < 0) break; k = e + 1; }
|
|
12628
|
+
const range = jvmBodyRange(masked, k);
|
|
12629
|
+
const line = lineAt(masked, m.index + 1);
|
|
12630
|
+
const key = name + ':' + (range ? range.bodyStart : line);
|
|
12631
|
+
if (seen.has(key)) continue;
|
|
12632
|
+
seen.add(key);
|
|
12633
|
+
defs.push(range ? { name, line, ...range } : { name, line, bodyStart: k, bodyEnd: k });
|
|
12634
|
+
}
|
|
12635
|
+
return defs;
|
|
12636
|
+
}
|
|
12637
|
+
|
|
12303
12638
|
function maskFor(filePath, src) {
|
|
12304
12639
|
const ext = path.extname(filePath).toLowerCase();
|
|
12305
12640
|
if (PY_EXTS.has(ext)) return maskPy(src);
|
|
@@ -12314,6 +12649,8 @@ __factories["./src/graph/call-graph"] = function(module, exports) {
|
|
|
12314
12649
|
if (JAVA_EXTS.has(ext)) return javaDefs(maskJs(src));
|
|
12315
12650
|
if (GO_EXTS.has(ext)) return goDefs(maskJs(src));
|
|
12316
12651
|
if (RS_EXTS.has(ext)) return rustDefs(maskRust(src));
|
|
12652
|
+
if (KT_EXTS.has(ext)) return ktDefs(maskJs(src));
|
|
12653
|
+
if (SCALA_EXTS.has(ext)) return scalaDefs(maskJs(src));
|
|
12317
12654
|
return null; // unsupported language
|
|
12318
12655
|
}
|
|
12319
12656
|
|
|
@@ -12403,7 +12740,8 @@ __factories["./src/graph/call-graph"] = function(module, exports) {
|
|
|
12403
12740
|
if (e.isDirectory()) _walk(full, excludeSet, out, depth + 1, maxDepth);
|
|
12404
12741
|
else if (e.isFile()) {
|
|
12405
12742
|
const ext = path.extname(e.name).toLowerCase();
|
|
12406
|
-
if (JS_EXTS.has(ext) || PY_EXTS.has(ext) || JAVA_EXTS.has(ext) || GO_EXTS.has(ext)
|
|
12743
|
+
if (JS_EXTS.has(ext) || PY_EXTS.has(ext) || JAVA_EXTS.has(ext) || GO_EXTS.has(ext)
|
|
12744
|
+
|| RS_EXTS.has(ext) || KT_EXTS.has(ext) || SCALA_EXTS.has(ext)) out.push(full);
|
|
12407
12745
|
}
|
|
12408
12746
|
}
|
|
12409
12747
|
}
|
|
@@ -15795,8 +16133,8 @@ __factories["./src/mcp/server"] = function(module, exports) {
|
|
|
15795
16133
|
* One JSON object per line on both stdin and stdout.
|
|
15796
16134
|
*
|
|
15797
16135
|
* Supported methods:
|
|
15798
|
-
* initialize → serverInfo + capabilities
|
|
15799
|
-
* tools/list →
|
|
16136
|
+
* initialize → serverInfo + capabilities + negotiated protocolVersion
|
|
16137
|
+
* tools/list → 21 tool definitions
|
|
15800
16138
|
* tools/call → dispatch to handler, return result
|
|
15801
16139
|
*/
|
|
15802
16140
|
|
|
@@ -15806,10 +16144,22 @@ __factories["./src/mcp/server"] = function(module, exports) {
|
|
|
15806
16144
|
|
|
15807
16145
|
const SERVER_INFO = {
|
|
15808
16146
|
name: 'sigmap',
|
|
15809
|
-
version: '8.
|
|
16147
|
+
version: '8.35.0',
|
|
15810
16148
|
description: 'SigMap MCP server — code signatures on demand',
|
|
15811
16149
|
};
|
|
15812
16150
|
|
|
16151
|
+
// Protocol revisions this server actually speaks. The tools-only surface —
|
|
16152
|
+
// initialize / tools/list / tools/call plus the initialized/cancelled
|
|
16153
|
+
// notifications — is identical across these revisions, and the
|
|
16154
|
+
// @hasmcp/mcp-spec-test suite passes every applicable 2025-11-25 case against
|
|
16155
|
+
// it. 2026-07-28 is deliberately absent: that revision requires
|
|
16156
|
+
// server/discover, which is not implemented. Newest first: a client offering
|
|
16157
|
+
// a version outside this list is downgraded to [0], never echoed back —
|
|
16158
|
+
// echoing an unspeakable version is itself a spec violation (#544), and it
|
|
16159
|
+
// made the conformance suite believe 2026-07-28 was supported, producing six
|
|
16160
|
+
// phantom server/discover failures on a revision never really offered (#545).
|
|
16161
|
+
const SUPPORTED_PROTOCOL_VERSIONS = ['2025-11-25', '2025-06-18', '2025-03-26', '2024-11-05'];
|
|
16162
|
+
|
|
15813
16163
|
// ---------------------------------------------------------------------------
|
|
15814
16164
|
// JSON-RPC helpers
|
|
15815
16165
|
// ---------------------------------------------------------------------------
|
|
@@ -15834,9 +16184,32 @@ __factories["./src/mcp/server"] = function(module, exports) {
|
|
|
15834
16184
|
return;
|
|
15835
16185
|
}
|
|
15836
16186
|
|
|
16187
|
+
// server/discover (spec 2026-07-28) — session-less discovery, answerable
|
|
16188
|
+
// before any handshake, so a client can learn the honest version list
|
|
16189
|
+
// instead of offering versions and hoping. The response is a
|
|
16190
|
+
// CacheableResult: deterministic, so the TTL promise of stability holds.
|
|
16191
|
+
// Note supportedVersions does NOT include 2026-07-28 — answering discover
|
|
16192
|
+
// is forward-compatible plumbing, not a claim to serve that revision's
|
|
16193
|
+
// whole surface (per-result envelopes, inline _meta negotiation).
|
|
16194
|
+
if (method === 'server/discover') {
|
|
16195
|
+
respond(id, {
|
|
16196
|
+
resultType: 'complete',
|
|
16197
|
+
cacheScope: 'public',
|
|
16198
|
+
ttlMs: 3600000,
|
|
16199
|
+
supportedVersions: SUPPORTED_PROTOCOL_VERSIONS,
|
|
16200
|
+
capabilities: { tools: {} },
|
|
16201
|
+
serverInfo: SERVER_INFO,
|
|
16202
|
+
instructions: 'SigMap serves code signatures for the working directory. Call query_context or search_signatures to rank and fetch signature blocks instead of reading whole files.',
|
|
16203
|
+
});
|
|
16204
|
+
return;
|
|
16205
|
+
}
|
|
16206
|
+
|
|
15837
16207
|
if (method === 'initialize') {
|
|
16208
|
+
const offered = params && params.protocolVersion;
|
|
15838
16209
|
respond(id, {
|
|
15839
|
-
protocolVersion: (
|
|
16210
|
+
protocolVersion: SUPPORTED_PROTOCOL_VERSIONS.includes(offered)
|
|
16211
|
+
? offered
|
|
16212
|
+
: SUPPORTED_PROTOCOL_VERSIONS[0],
|
|
15840
16213
|
serverInfo: SERVER_INFO,
|
|
15841
16214
|
capabilities: { tools: {} },
|
|
15842
16215
|
});
|
|
@@ -15844,6 +16217,13 @@ __factories["./src/mcp/server"] = function(module, exports) {
|
|
|
15844
16217
|
}
|
|
15845
16218
|
|
|
15846
16219
|
if (method === 'tools/list') {
|
|
16220
|
+
// No pagination: the full list fits one page, so any cursor a client
|
|
16221
|
+
// presents is one this server never issued — reject it (-32602, per the
|
|
16222
|
+
// spec's SHOULD) rather than silently restarting from page one.
|
|
16223
|
+
if (params && params.cursor !== undefined) {
|
|
16224
|
+
respondError(id, -32602, `Invalid cursor: ${String(params.cursor)}`);
|
|
16225
|
+
return;
|
|
16226
|
+
}
|
|
15847
16227
|
respond(id, { tools: TOOLS });
|
|
15848
16228
|
return;
|
|
15849
16229
|
}
|
|
@@ -17473,9 +17853,15 @@ __factories["./src/retrieval/ranker"] = function(module, exports) {
|
|
|
17473
17853
|
const hop1Files = new Set(); // normalised keys that received a hop1 boost
|
|
17474
17854
|
const hop1Seeds = []; // original (un-normalised) paths, for hop-2 lookup
|
|
17475
17855
|
|
|
17476
|
-
// Hop 1: direct neighbors of scored files
|
|
17477
|
-
|
|
17478
|
-
|
|
17856
|
+
// Hop 1: direct neighbors of scored files. Seeds snapshotted BEFORE the
|
|
17857
|
+
// loop (as the call-graph block below already does) so boosts never
|
|
17858
|
+
// cascade: a file whose only score is a hop-1 boost must not become a seed
|
|
17859
|
+
// itself mid-loop, or the effective seed set — and every boost total —
|
|
17860
|
+
// depends on index insertion order, which git history changes via the
|
|
17861
|
+
// recent-commits hoist. That made gate scores differ between a shallow CI
|
|
17862
|
+
// checkout and a developer clone of the same commit (#596).
|
|
17863
|
+
const hop1SeedEntries = scored.filter((e) => e.score > 0);
|
|
17864
|
+
for (const entry of hop1SeedEntries) {
|
|
17479
17865
|
const neighbors = _graphGet(graph.forward, path.resolve(cwd, entry.file)) || [];
|
|
17480
17866
|
for (const neighborAbs of neighbors) {
|
|
17481
17867
|
const nk = path.normalize(neighborAbs);
|
|
@@ -17490,7 +17876,10 @@ __factories["./src/retrieval/ranker"] = function(module, exports) {
|
|
|
17490
17876
|
}
|
|
17491
17877
|
}
|
|
17492
17878
|
|
|
17493
|
-
// Hop 2: neighbors of hop1 files (only if they didn't get a direct score)
|
|
17879
|
+
// Hop 2: neighbors of hop1 files (only if they didn't get a direct score).
|
|
17880
|
+
// Eligibility frozen after hop-1 for the same reason: a file whose first
|
|
17881
|
+
// score is a hop-2 boost must not become hop-2-eligible mid-loop (#596).
|
|
17882
|
+
const hop2Eligible = scored.map((e) => e.score > 0);
|
|
17494
17883
|
for (const hop1Key of hop1Seeds) {
|
|
17495
17884
|
if (_graphGet(keyToIdx, hop1Key) === undefined) continue; // skip files not in index
|
|
17496
17885
|
const neighbors = _graphGet(graph.forward, hop1Key) || [];
|
|
@@ -17499,7 +17888,7 @@ __factories["./src/retrieval/ranker"] = function(module, exports) {
|
|
|
17499
17888
|
if (_isHub(nk) || hubs.has(nk) || hubs.has(nk.toLowerCase())) continue;
|
|
17500
17889
|
if (hop1Files.has(nk)) continue; // skip already hop1-boosted
|
|
17501
17890
|
const idx = _graphGet(keyToIdx, nk);
|
|
17502
|
-
if (idx !== undefined &&
|
|
17891
|
+
if (idx !== undefined && hop2Eligible[idx]) {
|
|
17503
17892
|
// Only boost files that have some baseline score (not noise)
|
|
17504
17893
|
scored[idx].score += GRAPH_BOOST_AMOUNTS.hop2;
|
|
17505
17894
|
scored[idx].signals.graphBoost = (scored[idx].signals.graphBoost || 0) + GRAPH_BOOST_AMOUNTS.hop2;
|
|
@@ -22100,7 +22489,7 @@ function __tryGit(args, opts = {}) {
|
|
|
22100
22489
|
catch (_) { return ''; }
|
|
22101
22490
|
}
|
|
22102
22491
|
|
|
22103
|
-
const VERSION = '8.
|
|
22492
|
+
const VERSION = '8.35.0';
|
|
22104
22493
|
const MARKER = '\n\n## Auto-generated signatures\n<!-- Updated by gen-context.js -->\n';
|
|
22105
22494
|
|
|
22106
22495
|
function requireSourceOrBundled(key) {
|
|
@@ -22395,7 +22784,20 @@ function estimateTokens(str) {
|
|
|
22395
22784
|
}
|
|
22396
22785
|
|
|
22397
22786
|
function isTestFile(filePath) {
|
|
22398
|
-
|
|
22787
|
+
const p = filePath.replace(/\\/g, '/');
|
|
22788
|
+
// Filename conventions: foo.test.ts / foo.spec.js / foo_test.go / test_foo.py,
|
|
22789
|
+
// plus the PascalCase JVM/C#/Swift family (FooTest.java, FooTests.kt,
|
|
22790
|
+
// FooSpec.scala). The [a-z0-9] guard keeps `contest.java` and `Latest.java`
|
|
22791
|
+
// out; the suffix must be a real case boundary.
|
|
22792
|
+
if (/\.(test|spec)\.[a-z]+$/.test(p) || /_test\.[a-z]+$/.test(p)) return true;
|
|
22793
|
+
if (/(^|\/)test_[^/]+\.[a-z]+$/.test(p)) return true;
|
|
22794
|
+
if (/[a-z0-9](Test|Spec)s?\.(java|kt|kts|scala|groovy|cs|swift)$/.test(p)) return true;
|
|
22795
|
+
// Path-segment conventions (src/test/java/**, tests/, spec/, __tests__/, e2e/)
|
|
22796
|
+
// — the form the entire JVM world uses. Without this, "drop test files first"
|
|
22797
|
+
// ran INVERTED on JVM repos: spring-petclinic's budget kept all 17 src/test/
|
|
22798
|
+
// files while dropping the application entry point, the owner entities and
|
|
22799
|
+
// every owner template (#592).
|
|
22800
|
+
return /(^|\/)(test|tests|spec|specs|__tests__|e2e)(\/|$)/i.test(p);
|
|
22399
22801
|
}
|
|
22400
22802
|
|
|
22401
22803
|
function isConfigFile(filePath) {
|
|
@@ -22414,6 +22816,21 @@ function isMockFile(filePath) {
|
|
|
22414
22816
|
/mock\.(ts|js|tsx|jsx)$/.test(p);
|
|
22415
22817
|
}
|
|
22416
22818
|
|
|
22819
|
+
// The application entry point is the single most orientation-valuable file in
|
|
22820
|
+
// a repo, yet it is often tiny (a main class with 2-3 signatures), so the
|
|
22821
|
+
// fewest-sigs tie-break dropped it while keeping bulkier files: petclinic's
|
|
22822
|
+
// budget dropped PetClinicApplication.java and an agent asking for the startup
|
|
22823
|
+
// entry point could not reach it at all (#592). Deliberately narrow — bare
|
|
22824
|
+
// main/app/application names plus the Spring `*Application.java` and C#
|
|
22825
|
+
// `Program.cs` conventions. `index.*` is excluded: JS barrel files are hubs,
|
|
22826
|
+
// not entry points, and protecting every one would crowd out real code.
|
|
22827
|
+
function isEntryPointFile(filePath) {
|
|
22828
|
+
const base = path.basename(filePath.replace(/\\/g, '/'));
|
|
22829
|
+
return /^(main|app|application|__main__)\.[a-z]+$/i.test(base)
|
|
22830
|
+
|| /Application\.(java|kt|kts)$/.test(base)
|
|
22831
|
+
|| /^Program\.cs$/.test(base);
|
|
22832
|
+
}
|
|
22833
|
+
|
|
22417
22834
|
/**
|
|
22418
22835
|
* Compute the effective token budget based on repo size and config.
|
|
22419
22836
|
*
|
|
@@ -22469,6 +22886,38 @@ function computeEffectiveMaxTokens(fileEntries, config) {
|
|
|
22469
22886
|
return effective;
|
|
22470
22887
|
}
|
|
22471
22888
|
|
|
22889
|
+
/**
|
|
22890
|
+
* Delete `.github/context-*.md` split files this run did not write (#555).
|
|
22891
|
+
*
|
|
22892
|
+
* Splits are discovered by filename pattern at read time, not by consulting
|
|
22893
|
+
* the config, so a file left behind by a previous `strategy` — or by a module
|
|
22894
|
+
* since dropped from `srcDirs` — keeps being merged into the retrieval index
|
|
22895
|
+
* and silently steers every query. Observed on a 524-file Java repo: a stale
|
|
22896
|
+
* 376 KB `context-mall-mbg.md` kept generated entities at ranks 1, 3 and 4
|
|
22897
|
+
* while both files implementing the feature fell outside the top 6, even
|
|
22898
|
+
* though `sig-index.json` correctly held zero entries for that module.
|
|
22899
|
+
*
|
|
22900
|
+
* @param {string} cwd
|
|
22901
|
+
* @param {string[]} keep basenames this run wrote, e.g. ['context-core.md']
|
|
22902
|
+
* @returns {string[]} basenames removed
|
|
22903
|
+
*/
|
|
22904
|
+
function pruneStaleContextSplits(cwd, keep) {
|
|
22905
|
+
const ghDir = path.join(cwd, '.github');
|
|
22906
|
+
const kept = new Set(keep);
|
|
22907
|
+
const removed = [];
|
|
22908
|
+
let entries;
|
|
22909
|
+
try { entries = fs.readdirSync(ghDir); } catch (_) { return removed; }
|
|
22910
|
+
for (const f of entries) {
|
|
22911
|
+
if (!/^context-[\w.-]+\.md$/.test(f) || kept.has(f)) continue;
|
|
22912
|
+
try { fs.unlinkSync(path.join(ghDir, f)); removed.push(f); } catch (_) { /* best effort */ }
|
|
22913
|
+
}
|
|
22914
|
+
if (removed.length) {
|
|
22915
|
+
console.warn(`[sigmap] pruned ${removed.length} stale context split(s) from a previous `
|
|
22916
|
+
+ `strategy/srcDirs: ${removed.join(', ')}`);
|
|
22917
|
+
}
|
|
22918
|
+
return removed;
|
|
22919
|
+
}
|
|
22920
|
+
|
|
22472
22921
|
function applyTokenBudget(fileEntries, maxTokens) {
|
|
22473
22922
|
// fileEntries: [{ filePath, sigs, mtime }]
|
|
22474
22923
|
// Per-file rendered overhead: "### <path>", code fences, trailing blank line.
|
|
@@ -22501,6 +22950,7 @@ function applyTokenBudget(fileEntries, maxTokens) {
|
|
|
22501
22950
|
else if (isMockFile(e.filePath)) { priority = 9; dropReason = 'budget: mock file'; }
|
|
22502
22951
|
else if (isTestFile(e.filePath)) { priority = 8; dropReason = 'budget: test file'; }
|
|
22503
22952
|
else if (isConfigFile(e.filePath)) { priority = 6; dropReason = 'budget: config file'; }
|
|
22953
|
+
else if (isEntryPointFile(e.filePath)) { priority = 3; dropReason = 'budget: entry point'; }
|
|
22504
22954
|
else priority = 4;
|
|
22505
22955
|
const loc = e.content ? e.content.split('\n').length : 1;
|
|
22506
22956
|
const signalQuality = loc > 0 ? (e.sigs ? e.sigs.length : 0) / loc : 0;
|
|
@@ -23276,6 +23726,7 @@ function runPerModuleStrategy(cwd, config, fileEntries, inputTokenTotal) {
|
|
|
23276
23726
|
];
|
|
23277
23727
|
|
|
23278
23728
|
let totalOut = 0;
|
|
23729
|
+
const writtenSplits = [];
|
|
23279
23730
|
for (const mod of moduleNames) {
|
|
23280
23731
|
const outName = `context-${mod}.md`;
|
|
23281
23732
|
const outPath = path.join(cwd, '.github', outName);
|
|
@@ -23294,7 +23745,9 @@ function runPerModuleStrategy(cwd, config, fileEntries, inputTokenTotal) {
|
|
|
23294
23745
|
console.warn(`[sigmap] per-module: wrote .github/${outName} (~${modTokens} tokens, ${budgeted.length} files)`);
|
|
23295
23746
|
|
|
23296
23747
|
overviewLines.push(`| \`${mod}\` | \`.github/${outName}\` |`);
|
|
23748
|
+
writtenSplits.push(outName);
|
|
23297
23749
|
}
|
|
23750
|
+
pruneStaleContextSplits(cwd, writtenSplits);
|
|
23298
23751
|
|
|
23299
23752
|
overviewLines.push('');
|
|
23300
23753
|
overviewLines.push('> Inject the relevant module file into your IDE context window.');
|
|
@@ -23333,6 +23786,7 @@ function runHotColdStrategy(cwd, config, fileEntries, recentFiles, inputTokenTot
|
|
|
23333
23786
|
const coldContent = coldHeader + formatOutput(coldEntries, cwd, false, config, null);
|
|
23334
23787
|
ensureDir(coldPath);
|
|
23335
23788
|
fs.writeFileSync(coldPath, coldContent, 'utf8');
|
|
23789
|
+
pruneStaleContextSplits(cwd, ['context-cold.md']);
|
|
23336
23790
|
const coldTokens = estimateTokens(coldContent);
|
|
23337
23791
|
|
|
23338
23792
|
console.warn('[sigmap] hot-cold:');
|
|
@@ -23657,6 +24111,9 @@ function runGenerate(cwd, config, reportMode, reportJson = false) {
|
|
|
23657
24111
|
result = runHotColdStrategy(cwd, configWithBudget, fileEntries, recentFiles, inputTokenTotal);
|
|
23658
24112
|
} else {
|
|
23659
24113
|
// 'full' — original behaviour
|
|
24114
|
+
// 'full' writes no split files — anything left from a previous
|
|
24115
|
+
// strategy would still be merged into the index at read time (#555).
|
|
24116
|
+
pruneStaleContextSplits(cwd, []);
|
|
23660
24117
|
fileEntries = applyTokenBudget(fileEntries, effectiveMaxTokens);
|
|
23661
24118
|
const droppedCount = beforeCount - fileEntries.length;
|
|
23662
24119
|
const routingEnabled = !!(config.routing || process.argv.includes('--routing'));
|