sigmap 8.17.0 → 8.19.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/AGENTS.md +65 -92
- package/CHANGELOG.md +28 -0
- package/README.md +14 -12
- package/gen-context.js +237 -41
- package/llms-full.txt +6 -6
- package/llms.txt +6 -6
- package/package.json +3 -1
- package/packages/cli/package.json +1 -1
- package/packages/core/package.json +1 -1
- package/src/config/defaults.js +2 -0
- package/src/extractors/dart.js +32 -8
- package/src/extractors/kotlin.js +32 -8
- package/src/extractors/php.js +31 -6
- package/src/extractors/scala.js +18 -6
- package/src/extractors/swift.js +31 -7
- package/src/map/route-table.js +14 -2
- package/src/mcp/handlers.js +4 -1
- package/src/mcp/server.js +1 -1
- package/src/retrieval/bm25.js +4 -1
- package/src/retrieval/enrich-from-maps.js +55 -0
package/gen-context.js
CHANGED
|
@@ -1533,6 +1533,8 @@ __factories["./src/config/defaults"] = function(module, exports) {
|
|
|
1533
1533
|
recencyBoost: 1.5,
|
|
1534
1534
|
// Boost files call-graph-connected to query matches (opt-in, measure-gated)
|
|
1535
1535
|
callGraphBoost: false,
|
|
1536
|
+
// Append route pseudo-signatures to the rankable index (opt-in, measure-gated)
|
|
1537
|
+
surfaceEnrichment: false,
|
|
1536
1538
|
},
|
|
1537
1539
|
|
|
1538
1540
|
// Impact layer settings (v2.5)
|
|
@@ -5377,8 +5379,12 @@ __factories["./src/extractors/css"] = function(module, exports) {
|
|
|
5377
5379
|
// ── ./src/extractors/dart ──
|
|
5378
5380
|
__factories["./src/extractors/dart"] = function(module, exports) {
|
|
5379
5381
|
|
|
5382
|
+
const { lineAt, withAnchor } = __require('./src/extractors/line-anchor');
|
|
5383
|
+
|
|
5380
5384
|
/**
|
|
5381
5385
|
* Extract signatures from Dart source code.
|
|
5386
|
+
* Signatures carry `:start-end` line anchors (Surgical Context); the comment
|
|
5387
|
+
* strip below is newline-preserving so anchor lines match the original file.
|
|
5382
5388
|
* @param {string} src - Raw file content
|
|
5383
5389
|
* @returns {string[]} Array of signature strings
|
|
5384
5390
|
*/
|
|
@@ -5388,21 +5394,37 @@ __factories["./src/extractors/dart"] = function(module, exports) {
|
|
|
5388
5394
|
|
|
5389
5395
|
const stripped = src
|
|
5390
5396
|
.replace(/\/\/.*$/gm, '')
|
|
5391
|
-
.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
5397
|
+
.replace(/\/\*[\s\S]*?\*\//g, (s) => s.replace(/[^\n]/g, ' '));
|
|
5398
|
+
|
|
5399
|
+
// Anchor range: scan past same-line trivia (`async`, `=>` stops) to a body `{`.
|
|
5400
|
+
const rangeFor = (declIdx, afterIdx) => {
|
|
5401
|
+
let k = afterIdx;
|
|
5402
|
+
while (k < stripped.length && /[ \tA-Za-z0-9_]/.test(stripped[k])) k++;
|
|
5403
|
+
if (stripped[k] === '{') {
|
|
5404
|
+
const end = k + 1 + extractBlock(stripped, k + 1).length;
|
|
5405
|
+
return [lineAt(stripped, declIdx), lineAt(stripped, end)];
|
|
5406
|
+
}
|
|
5407
|
+
const line = lineAt(stripped, declIdx);
|
|
5408
|
+
return [line, line];
|
|
5409
|
+
};
|
|
5392
5410
|
|
|
5393
5411
|
// Classes and abstract classes
|
|
5394
5412
|
for (const m of stripped.matchAll(/^(?:abstract\s+)?class\s+(\w+)(?:<[^{]*>)?(?:\s+extends\s+[\w<>, ]+)?(?:\s+(?:implements|with|on)\s+[\w<>, ]+)?\s*\{/gm)) {
|
|
5395
5413
|
const abs = m[0].trimStart().startsWith('abstract') ? 'abstract ' : '';
|
|
5396
|
-
|
|
5397
|
-
const block = extractBlock(stripped,
|
|
5398
|
-
|
|
5414
|
+
const bodyStart = m.index + m[0].length;
|
|
5415
|
+
const block = extractBlock(stripped, bodyStart);
|
|
5416
|
+
sigs.push(withAnchor(`${abs}class ${m[1]}`, lineAt(stripped, m.index), lineAt(stripped, bodyStart + block.length)));
|
|
5417
|
+
for (const meth of extractMembers(block)) {
|
|
5418
|
+
sigs.push(withAnchor(` ${meth.text}`, lineAt(stripped, bodyStart + meth.declIdx), lineAt(stripped, bodyStart + meth.endIdx)));
|
|
5419
|
+
}
|
|
5399
5420
|
}
|
|
5400
5421
|
|
|
5401
5422
|
// Top-level functions — capture return type (prefix before name) and show as suffix
|
|
5402
5423
|
for (const m of stripped.matchAll(/^((?:Future<[\w<>?,\s]*>|[\w<>?]+))\s+(\w+)\s*\(([^)]*)\)/gm)) {
|
|
5403
5424
|
if (m[2].startsWith('_')) continue;
|
|
5404
|
-
const retStr = (m[1] && m[1] !== 'void') ? `
|
|
5405
|
-
|
|
5425
|
+
const retStr = (m[1] && m[1] !== 'void') ? ` → ${m[1].replace(/\s+/g, '').slice(0, 25)}` : '';
|
|
5426
|
+
const [s, e] = rangeFor(m.index, m.index + m[0].length);
|
|
5427
|
+
sigs.push(withAnchor(`${m[2]}(${normalizeParams(m[3])})${retStr}`, s, e));
|
|
5406
5428
|
}
|
|
5407
5429
|
|
|
5408
5430
|
return sigs.slice(0, 25);
|
|
@@ -5423,8 +5445,12 @@ __factories["./src/extractors/dart"] = function(module, exports) {
|
|
|
5423
5445
|
const members = [];
|
|
5424
5446
|
for (const m of block.matchAll(/^\s+(?:@override\s+)?(?:@\w+\s+)*((?:Future<[\w<>?,\s]*>|[\w<>?]+))\s+(\w+)\s*\(([^)]*)\)/gm)) {
|
|
5425
5447
|
if (m[2].startsWith('_')) continue;
|
|
5426
|
-
const retStr = (m[1] && m[1] !== 'void') ? `
|
|
5427
|
-
members.push(
|
|
5448
|
+
const retStr = (m[1] && m[1] !== 'void') ? ` → ${m[1].replace(/\s+/g, '').slice(0, 25)}` : '';
|
|
5449
|
+
members.push({
|
|
5450
|
+
text: `${m[2]}(${normalizeParams(m[3])})${retStr}`,
|
|
5451
|
+
declIdx: m.index + (m[0].length - m[0].trimStart().length),
|
|
5452
|
+
endIdx: m.index + m[0].length,
|
|
5453
|
+
});
|
|
5428
5454
|
}
|
|
5429
5455
|
return members.slice(0, 8);
|
|
5430
5456
|
}
|
|
@@ -6314,8 +6340,12 @@ __factories["./src/extractors/javascript"] = function(module, exports) {
|
|
|
6314
6340
|
// ── ./src/extractors/kotlin ──
|
|
6315
6341
|
__factories["./src/extractors/kotlin"] = function(module, exports) {
|
|
6316
6342
|
|
|
6343
|
+
const { lineAt, withAnchor } = __require('./src/extractors/line-anchor');
|
|
6344
|
+
|
|
6317
6345
|
/**
|
|
6318
6346
|
* Extract signatures from Kotlin source code.
|
|
6347
|
+
* Signatures carry `:start-end` line anchors (Surgical Context); the comment
|
|
6348
|
+
* strip below is newline-preserving so anchor lines match the original file.
|
|
6319
6349
|
* @param {string} src - Raw file content
|
|
6320
6350
|
* @returns {string[]} Array of signature strings
|
|
6321
6351
|
*/
|
|
@@ -6325,21 +6355,37 @@ __factories["./src/extractors/kotlin"] = function(module, exports) {
|
|
|
6325
6355
|
|
|
6326
6356
|
const stripped = src
|
|
6327
6357
|
.replace(/\/\/.*$/gm, '')
|
|
6328
|
-
.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
6358
|
+
.replace(/\/\*[\s\S]*?\*\//g, (s) => s.replace(/[^\n]/g, ' '));
|
|
6359
|
+
|
|
6360
|
+
// Anchor range: scan past same-line modifiers to a body `{` (range) else single line.
|
|
6361
|
+
const rangeFor = (declIdx, afterIdx) => {
|
|
6362
|
+
let k = afterIdx;
|
|
6363
|
+
while (k < stripped.length && /[ \tA-Za-z0-9_]/.test(stripped[k])) k++;
|
|
6364
|
+
if (stripped[k] === '{') {
|
|
6365
|
+
const end = k + 1 + extractBlock(stripped, k + 1).length;
|
|
6366
|
+
return [lineAt(stripped, declIdx), lineAt(stripped, end)];
|
|
6367
|
+
}
|
|
6368
|
+
const line = lineAt(stripped, declIdx);
|
|
6369
|
+
return [line, line];
|
|
6370
|
+
};
|
|
6329
6371
|
|
|
6330
6372
|
// Classes, objects, interfaces
|
|
6331
6373
|
for (const m of stripped.matchAll(/^(?:public\s+|internal\s+)?(?:data\s+|sealed\s+|abstract\s+|open\s+)?(class|object|interface)\s+(\w+)(?:[^{]*)\{/gm)) {
|
|
6332
|
-
|
|
6333
|
-
const block = extractBlock(stripped,
|
|
6334
|
-
|
|
6374
|
+
const bodyStart = m.index + m[0].length;
|
|
6375
|
+
const block = extractBlock(stripped, bodyStart);
|
|
6376
|
+
sigs.push(withAnchor(`${m[1]} ${m[2]}`, lineAt(stripped, m.index), lineAt(stripped, bodyStart + block.length)));
|
|
6377
|
+
for (const meth of extractMembers(block)) {
|
|
6378
|
+
sigs.push(withAnchor(` ${meth.text}`, lineAt(stripped, bodyStart + meth.declIdx), lineAt(stripped, bodyStart + meth.endIdx)));
|
|
6379
|
+
}
|
|
6335
6380
|
}
|
|
6336
6381
|
|
|
6337
6382
|
// Top-level functions — capture `: RetType` after params
|
|
6338
6383
|
for (const m of stripped.matchAll(/^(?:public\s+|internal\s+)?(?:suspend\s+)?fun\s+(\w+)\s*(?:<[^(]*>)?\s*\(([^)]*)\)(?:\s*:\s*([^\n{=]+))?/gm)) {
|
|
6339
6384
|
const suspend = m[0].includes('suspend') ? 'suspend ' : '';
|
|
6340
6385
|
const retType = m[3] ? m[3].trim().replace(/\s+/g, ' ') : '';
|
|
6341
|
-
const retStr = retType ? `
|
|
6342
|
-
|
|
6386
|
+
const retStr = retType ? ` → ${retType.slice(0, 25)}` : '';
|
|
6387
|
+
const [s, e] = rangeFor(m.index, m.index + m[0].length);
|
|
6388
|
+
sigs.push(withAnchor(`${suspend}fun ${m[1]}(${normalizeParams(m[2])})${retStr}`, s, e));
|
|
6343
6389
|
}
|
|
6344
6390
|
|
|
6345
6391
|
return sigs.slice(0, 25);
|
|
@@ -6362,8 +6408,12 @@ __factories["./src/extractors/kotlin"] = function(module, exports) {
|
|
|
6362
6408
|
if (m[1].startsWith('_')) continue;
|
|
6363
6409
|
const suspend = m[0].includes('suspend') ? 'suspend ' : '';
|
|
6364
6410
|
const retType = m[3] ? m[3].trim().replace(/\s+/g, ' ') : '';
|
|
6365
|
-
const retStr = retType ? `
|
|
6366
|
-
members.push(
|
|
6411
|
+
const retStr = retType ? ` → ${retType.slice(0, 25)}` : '';
|
|
6412
|
+
members.push({
|
|
6413
|
+
text: `${suspend}fun ${m[1]}(${normalizeParams(m[2])})${retStr}`,
|
|
6414
|
+
declIdx: m.index + (m[0].length - m[0].trimStart().length),
|
|
6415
|
+
endIdx: m.index + m[0].length,
|
|
6416
|
+
});
|
|
6367
6417
|
}
|
|
6368
6418
|
return members.slice(0, 8);
|
|
6369
6419
|
}
|
|
@@ -6613,8 +6663,12 @@ __factories["./src/extractors/patterns"] = function(module, exports) {
|
|
|
6613
6663
|
// ── ./src/extractors/php ──
|
|
6614
6664
|
__factories["./src/extractors/php"] = function(module, exports) {
|
|
6615
6665
|
|
|
6666
|
+
const { lineAt, withAnchor } = __require('./src/extractors/line-anchor');
|
|
6667
|
+
|
|
6616
6668
|
/**
|
|
6617
6669
|
* Extract signatures from PHP source code.
|
|
6670
|
+
* Signatures carry `:start-end` line anchors (Surgical Context); the comment
|
|
6671
|
+
* strips below are newline-preserving so anchor lines match the original file.
|
|
6618
6672
|
* @param {string} src - Raw file content
|
|
6619
6673
|
* @returns {string[]} Array of signature strings
|
|
6620
6674
|
*/
|
|
@@ -6625,23 +6679,40 @@ __factories["./src/extractors/php"] = function(module, exports) {
|
|
|
6625
6679
|
const stripped = src
|
|
6626
6680
|
.replace(/\/\/.*$/gm, '')
|
|
6627
6681
|
.replace(/#.*$/gm, '')
|
|
6628
|
-
.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
6682
|
+
.replace(/\/\*[\s\S]*?\*\//g, (s) => s.replace(/[^\n]/g, ' '));
|
|
6683
|
+
|
|
6684
|
+
// Anchor range: scan past same-line trivia to a body `{` (range) else single line.
|
|
6685
|
+
const rangeFor = (declIdx, afterIdx) => {
|
|
6686
|
+
let k = afterIdx;
|
|
6687
|
+
while (k < stripped.length && /[ \tA-Za-z0-9_:?\\]/.test(stripped[k])) k++;
|
|
6688
|
+
if (stripped[k] === '{' || (stripped[k] === '\n' && stripped[k + 1] === '{')) {
|
|
6689
|
+
const open = stripped[k] === '{' ? k : k + 1;
|
|
6690
|
+
const end = open + 1 + extractBlock(stripped, open + 1).length;
|
|
6691
|
+
return [lineAt(stripped, declIdx), lineAt(stripped, end)];
|
|
6692
|
+
}
|
|
6693
|
+
const line = lineAt(stripped, declIdx);
|
|
6694
|
+
return [line, line];
|
|
6695
|
+
};
|
|
6629
6696
|
|
|
6630
6697
|
// Classes and interfaces
|
|
6631
6698
|
const typeRe = /^(?:abstract\s+)?(?:class|interface|trait)\s+(\w+)(?:\s+extends\s+\w+)?(?:\s+implements\s+[\w, ]+)?\s*\{/gm;
|
|
6632
6699
|
for (const m of stripped.matchAll(typeRe)) {
|
|
6633
6700
|
const kind = m[0].trimStart().startsWith('interface') ? 'interface' :
|
|
6634
6701
|
m[0].trimStart().startsWith('trait') ? 'trait' : 'class';
|
|
6635
|
-
|
|
6636
|
-
const block = extractBlock(stripped,
|
|
6637
|
-
|
|
6702
|
+
const bodyStart = m.index + m[0].length;
|
|
6703
|
+
const block = extractBlock(stripped, bodyStart);
|
|
6704
|
+
sigs.push(withAnchor(`${kind} ${m[1]}`, lineAt(stripped, m.index), lineAt(stripped, bodyStart + block.length)));
|
|
6705
|
+
for (const meth of extractMembers(block)) {
|
|
6706
|
+
sigs.push(withAnchor(` ${meth.text}`, lineAt(stripped, bodyStart + meth.declIdx), lineAt(stripped, bodyStart + meth.endIdx)));
|
|
6707
|
+
}
|
|
6638
6708
|
}
|
|
6639
6709
|
|
|
6640
6710
|
// Top-level functions
|
|
6641
6711
|
for (const m of stripped.matchAll(/^function\s+(\w+)\s*\(([^)]*)\)\s*(?::\s*([^\n{]+))?/gm)) {
|
|
6642
6712
|
const ret = normalizeType(m[3]);
|
|
6643
6713
|
const retStr = ret ? ` → ${ret}` : '';
|
|
6644
|
-
|
|
6714
|
+
const [s, e] = rangeFor(m.index, m.index + m[0].length);
|
|
6715
|
+
sigs.push(withAnchor(`function ${m[1]}(${normalizeParams(m[2])})${retStr}`, s, e));
|
|
6645
6716
|
}
|
|
6646
6717
|
|
|
6647
6718
|
return sigs.slice(0, 25);
|
|
@@ -6666,7 +6737,11 @@ __factories["./src/extractors/php"] = function(module, exports) {
|
|
|
6666
6737
|
const isStatic = m[0].includes('static ') ? 'static ' : '';
|
|
6667
6738
|
const ret = normalizeType(m[3]);
|
|
6668
6739
|
const retStr = ret ? ` → ${ret}` : '';
|
|
6669
|
-
members.push(
|
|
6740
|
+
members.push({
|
|
6741
|
+
text: `${isStatic}function ${m[1]}(${normalizeParams(m[2])})${retStr}`,
|
|
6742
|
+
declIdx: m.index + (m[0].length - m[0].trimStart().length),
|
|
6743
|
+
endIdx: m.index + m[0].length,
|
|
6744
|
+
});
|
|
6670
6745
|
}
|
|
6671
6746
|
return members.slice(0, 8);
|
|
6672
6747
|
}
|
|
@@ -7666,8 +7741,12 @@ __factories["./src/extractors/rust"] = function(module, exports) {
|
|
|
7666
7741
|
// ── ./src/extractors/scala ──
|
|
7667
7742
|
__factories["./src/extractors/scala"] = function(module, exports) {
|
|
7668
7743
|
|
|
7744
|
+
const { lineAt, withAnchor } = __require('./src/extractors/line-anchor');
|
|
7745
|
+
|
|
7669
7746
|
/**
|
|
7670
7747
|
* Extract signatures from Scala source code.
|
|
7748
|
+
* Signatures carry `:start-end` line anchors (Surgical Context); the comment
|
|
7749
|
+
* strip below is newline-preserving so anchor lines match the original file.
|
|
7671
7750
|
* @param {string} src - Raw file content
|
|
7672
7751
|
* @returns {string[]} Array of signature strings
|
|
7673
7752
|
*/
|
|
@@ -7677,7 +7756,7 @@ __factories["./src/extractors/scala"] = function(module, exports) {
|
|
|
7677
7756
|
|
|
7678
7757
|
const stripped = src
|
|
7679
7758
|
.replace(/\/\/.*$/gm, '')
|
|
7680
|
-
.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
7759
|
+
.replace(/\/\*[\s\S]*?\*\//g, (s) => s.replace(/[^\n]/g, ' '));
|
|
7681
7760
|
|
|
7682
7761
|
// Classes, traits, objects
|
|
7683
7762
|
const typeRe = /^(?:case\s+)?(?:class|trait|object)\s+(\w+)(?:\[[\w, ]+\])?(?:[^{]*)\{/gm;
|
|
@@ -7685,9 +7764,12 @@ __factories["./src/extractors/scala"] = function(module, exports) {
|
|
|
7685
7764
|
const kind = m[0].trimStart().startsWith('case class') ? 'case class' :
|
|
7686
7765
|
m[0].trimStart().startsWith('trait') ? 'trait' :
|
|
7687
7766
|
m[0].trimStart().startsWith('object') ? 'object' : 'class';
|
|
7688
|
-
|
|
7689
|
-
const block = extractBlock(stripped,
|
|
7690
|
-
|
|
7767
|
+
const bodyStart = m.index + m[0].length;
|
|
7768
|
+
const block = extractBlock(stripped, bodyStart);
|
|
7769
|
+
sigs.push(withAnchor(`${kind} ${m[1]}`, lineAt(stripped, m.index), lineAt(stripped, bodyStart + block.length)));
|
|
7770
|
+
for (const fn of extractMembers(block)) {
|
|
7771
|
+
sigs.push(withAnchor(` ${fn.text}`, lineAt(stripped, bodyStart + fn.declIdx), lineAt(stripped, bodyStart + fn.endIdx)));
|
|
7772
|
+
}
|
|
7691
7773
|
}
|
|
7692
7774
|
|
|
7693
7775
|
// Top-level defs
|
|
@@ -7696,7 +7778,8 @@ __factories["./src/extractors/scala"] = function(module, exports) {
|
|
|
7696
7778
|
const params = m[2] ? `(${normalizeParams(m[2])})` : '';
|
|
7697
7779
|
const ret = normalizeType(m[3]);
|
|
7698
7780
|
const retStr = ret ? ` → ${ret}` : '';
|
|
7699
|
-
|
|
7781
|
+
const line = lineAt(stripped, m.index);
|
|
7782
|
+
sigs.push(withAnchor(`def ${m[1]}${params}${retStr}`, line, line));
|
|
7700
7783
|
}
|
|
7701
7784
|
|
|
7702
7785
|
return sigs.slice(0, 25);
|
|
@@ -7720,7 +7803,11 @@ __factories["./src/extractors/scala"] = function(module, exports) {
|
|
|
7720
7803
|
const params = m[2] ? `(${normalizeParams(m[2])})` : '';
|
|
7721
7804
|
const ret = normalizeType(m[3]);
|
|
7722
7805
|
const retStr = ret ? ` → ${ret}` : '';
|
|
7723
|
-
members.push(
|
|
7806
|
+
members.push({
|
|
7807
|
+
text: `def ${m[1]}${params}${retStr}`,
|
|
7808
|
+
declIdx: m.index + (m[0].length - m[0].trimStart().length),
|
|
7809
|
+
endIdx: m.index + m[0].length,
|
|
7810
|
+
});
|
|
7724
7811
|
}
|
|
7725
7812
|
return members.slice(0, 8);
|
|
7726
7813
|
}
|
|
@@ -7952,8 +8039,12 @@ __factories["./src/extractors/svelte"] = function(module, exports) {
|
|
|
7952
8039
|
// ── ./src/extractors/swift ──
|
|
7953
8040
|
__factories["./src/extractors/swift"] = function(module, exports) {
|
|
7954
8041
|
|
|
8042
|
+
const { lineAt, withAnchor } = __require('./src/extractors/line-anchor');
|
|
8043
|
+
|
|
7955
8044
|
/**
|
|
7956
8045
|
* Extract signatures from Swift source code.
|
|
8046
|
+
* Signatures carry `:start-end` line anchors (Surgical Context); the comment
|
|
8047
|
+
* strip below is newline-preserving so anchor lines match the original file.
|
|
7957
8048
|
* @param {string} src - Raw file content
|
|
7958
8049
|
* @returns {string[]} Array of signature strings
|
|
7959
8050
|
*/
|
|
@@ -7963,21 +8054,37 @@ __factories["./src/extractors/swift"] = function(module, exports) {
|
|
|
7963
8054
|
|
|
7964
8055
|
const stripped = src
|
|
7965
8056
|
.replace(/\/\/.*$/gm, '')
|
|
7966
|
-
.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
8057
|
+
.replace(/\/\*[\s\S]*?\*\//g, (s) => s.replace(/[^\n]/g, ' '));
|
|
8058
|
+
|
|
8059
|
+
// Anchor range: scan past same-line modifiers to a body `{` (range) else single line.
|
|
8060
|
+
const rangeFor = (declIdx, afterIdx) => {
|
|
8061
|
+
let k = afterIdx;
|
|
8062
|
+
while (k < stripped.length && /[ \tA-Za-z0-9_>-]/.test(stripped[k])) k++;
|
|
8063
|
+
if (stripped[k] === '{') {
|
|
8064
|
+
const end = k + 1 + extractBlock(stripped, k + 1).length;
|
|
8065
|
+
return [lineAt(stripped, declIdx), lineAt(stripped, end)];
|
|
8066
|
+
}
|
|
8067
|
+
const line = lineAt(stripped, declIdx);
|
|
8068
|
+
return [line, line];
|
|
8069
|
+
};
|
|
7967
8070
|
|
|
7968
8071
|
// Classes, structs, protocols, enums
|
|
7969
8072
|
const typeRe = /^(?:public\s+|internal\s+|open\s+)?(?:final\s+)?(class|struct|protocol|enum|actor)\s+(\w+)(?:<[^{]*>)?(?:\s*:\s*[\w, <>.]+)?\s*\{/gm;
|
|
7970
8073
|
for (const m of stripped.matchAll(typeRe)) {
|
|
7971
|
-
|
|
7972
|
-
const block = extractBlock(stripped,
|
|
7973
|
-
|
|
8074
|
+
const bodyStart = m.index + m[0].length;
|
|
8075
|
+
const block = extractBlock(stripped, bodyStart);
|
|
8076
|
+
sigs.push(withAnchor(`${m[1]} ${m[2]}`, lineAt(stripped, m.index), lineAt(stripped, bodyStart + block.length)));
|
|
8077
|
+
for (const fn of extractMembers(block)) {
|
|
8078
|
+
sigs.push(withAnchor(` ${fn.text}`, lineAt(stripped, bodyStart + fn.declIdx), lineAt(stripped, bodyStart + fn.endIdx)));
|
|
8079
|
+
}
|
|
7974
8080
|
}
|
|
7975
8081
|
|
|
7976
8082
|
// Top-level public functions — capture everything after ) to end of line for arrow type
|
|
7977
8083
|
for (const m of stripped.matchAll(/^(?:public\s+|internal\s+)?(?:static\s+)?(?:async\s+)?func\s+(\w+)(?:<[^(]*>)?\s*\(([^)]*)\)([^{\n]*)/gm)) {
|
|
7978
8084
|
const asyncKw = m[0].includes('async') ? 'async ' : '';
|
|
7979
8085
|
const retStr = extractArrowType(m[3]);
|
|
7980
|
-
|
|
8086
|
+
const [s, e] = rangeFor(m.index, m.index + m[0].length);
|
|
8087
|
+
sigs.push(withAnchor(`${asyncKw}func ${m[1]}(${normalizeParams(m[2])})${retStr}`, s, e));
|
|
7981
8088
|
}
|
|
7982
8089
|
|
|
7983
8090
|
return sigs.slice(0, 25);
|
|
@@ -8000,7 +8107,11 @@ __factories["./src/extractors/swift"] = function(module, exports) {
|
|
|
8000
8107
|
if (m[1].startsWith('_')) continue;
|
|
8001
8108
|
const asyncKw = m[0].includes('async') ? 'async ' : '';
|
|
8002
8109
|
const retStr = extractArrowType(m[3]);
|
|
8003
|
-
members.push(
|
|
8110
|
+
members.push({
|
|
8111
|
+
text: `${asyncKw}func ${m[1]}(${normalizeParams(m[2])})${retStr}`,
|
|
8112
|
+
declIdx: m.index + (m[0].length - m[0].trimStart().length),
|
|
8113
|
+
endIdx: m.index + m[0].length,
|
|
8114
|
+
});
|
|
8004
8115
|
}
|
|
8005
8116
|
return members.slice(0, 8);
|
|
8006
8117
|
}
|
|
@@ -8019,7 +8130,7 @@ __factories["./src/extractors/swift"] = function(module, exports) {
|
|
|
8019
8130
|
const m = str.match(/->\s*([^\n{]+)/);
|
|
8020
8131
|
if (!m) return '';
|
|
8021
8132
|
const rt = m[1].trim().replace(/\s+/g, ' ');
|
|
8022
|
-
return `
|
|
8133
|
+
return ` → ${rt.length > 25 ? rt.slice(0, 22) + '...' : rt}`;
|
|
8023
8134
|
}
|
|
8024
8135
|
|
|
8025
8136
|
module.exports = { extract };
|
|
@@ -13227,7 +13338,14 @@ __factories["./src/map/route-table"] = function(module, exports) {
|
|
|
13227
13338
|
return /(^|\/)(gen-context|gen-project-map)\.js$/.test(normalized);
|
|
13228
13339
|
}
|
|
13229
13340
|
|
|
13230
|
-
|
|
13341
|
+
/**
|
|
13342
|
+
* Structured route rows across the supported frameworks — the data behind
|
|
13343
|
+
* `analyze`, exposed for retrieval surface-enrichment (#488).
|
|
13344
|
+
* @param {string[]} files absolute paths
|
|
13345
|
+
* @param {string} cwd
|
|
13346
|
+
* @returns {{ method:string, path:string, file:string }[]} file is cwd-relative
|
|
13347
|
+
*/
|
|
13348
|
+
function collectRoutes(files, cwd) {
|
|
13231
13349
|
const routes = [];
|
|
13232
13350
|
|
|
13233
13351
|
for (const filePath of files) {
|
|
@@ -13319,6 +13437,11 @@ __factories["./src/map/route-table"] = function(module, exports) {
|
|
|
13319
13437
|
}
|
|
13320
13438
|
}
|
|
13321
13439
|
|
|
13440
|
+
return routes;
|
|
13441
|
+
}
|
|
13442
|
+
|
|
13443
|
+
function analyze(files, cwd) {
|
|
13444
|
+
const routes = collectRoutes(files, cwd);
|
|
13322
13445
|
if (routes.length === 0) return '';
|
|
13323
13446
|
|
|
13324
13447
|
const lines = [
|
|
@@ -13331,7 +13454,7 @@ __factories["./src/map/route-table"] = function(module, exports) {
|
|
|
13331
13454
|
return lines.join('\n');
|
|
13332
13455
|
}
|
|
13333
13456
|
|
|
13334
|
-
module.exports = { analyze };
|
|
13457
|
+
module.exports = { analyze, collectRoutes };
|
|
13335
13458
|
|
|
13336
13459
|
};
|
|
13337
13460
|
|
|
@@ -13759,7 +13882,7 @@ __factories["./src/mcp/handlers"] = function(module, exports) {
|
|
|
13759
13882
|
// Build dependency graph for neighbor boost — non-fatal if it fails
|
|
13760
13883
|
let graph = null;
|
|
13761
13884
|
try { graph = buildFromCwd(cwd); } catch (_) {}
|
|
13762
|
-
// Opt-in call-graph neighbor boost
|
|
13885
|
+
// Opt-in call-graph neighbor boost + surface enrichment — non-fatal
|
|
13763
13886
|
let callGraph = null;
|
|
13764
13887
|
try {
|
|
13765
13888
|
const { loadConfig } = __require('./src/config/loader');
|
|
@@ -13767,6 +13890,9 @@ __factories["./src/mcp/handlers"] = function(module, exports) {
|
|
|
13767
13890
|
if (retrieval && retrieval.callGraphBoost) {
|
|
13768
13891
|
callGraph = __require('./src/graph/call-graph').buildCallFileGraph(cwd);
|
|
13769
13892
|
}
|
|
13893
|
+
if (retrieval && retrieval.surfaceEnrichment) {
|
|
13894
|
+
__require('./src/retrieval/enrich-from-maps').enrichWithSurfaces(index, cwd);
|
|
13895
|
+
}
|
|
13770
13896
|
} catch (_) {}
|
|
13771
13897
|
const results = rank(args.query, index, { topK, cwd, graph, callGraph });
|
|
13772
13898
|
return formatRankTable(results, args.query);
|
|
@@ -14481,7 +14607,7 @@ __factories["./src/mcp/server"] = function(module, exports) {
|
|
|
14481
14607
|
|
|
14482
14608
|
const SERVER_INFO = {
|
|
14483
14609
|
name: 'sigmap',
|
|
14484
|
-
version: '8.
|
|
14610
|
+
version: '8.19.0',
|
|
14485
14611
|
description: 'SigMap MCP server — code signatures on demand',
|
|
14486
14612
|
};
|
|
14487
14613
|
|
|
@@ -15469,7 +15595,10 @@ __factories["./src/retrieval/bm25"] = function(module, exports) {
|
|
|
15469
15595
|
|
|
15470
15596
|
const docs = candidates.map((c) => {
|
|
15471
15597
|
const pathToks = tokenize(c.file || '');
|
|
15472
|
-
|
|
15598
|
+
// Ranking is anchor-invariant: `:start-end` line anchors are metadata,
|
|
15599
|
+
// not content — strip them before tokenizing so adding anchors to an
|
|
15600
|
+
// extractor never shifts BM25 length normalization or token counts.
|
|
15601
|
+
const toks = tokenize((c.sigs || []).map((s) => String(s).replace(/\s*:\d+(?:-\d+)?\s*$/, '')).join(' '));
|
|
15473
15602
|
for (let i = 0; i < PATH_BOOST; i++) toks.push(...pathToks);
|
|
15474
15603
|
const tf = new Map();
|
|
15475
15604
|
for (const t of toks) tf.set(t, (tf.get(t) || 0) + 1);
|
|
@@ -15506,6 +15635,65 @@ __factories["./src/retrieval/bm25"] = function(module, exports) {
|
|
|
15506
15635
|
|
|
15507
15636
|
};
|
|
15508
15637
|
|
|
15638
|
+
// ── ./src/retrieval/enrich-from-maps ──
|
|
15639
|
+
__factories["./src/retrieval/enrich-from-maps"] = function(module, exports) {
|
|
15640
|
+
|
|
15641
|
+
/**
|
|
15642
|
+
* Retrieval surface-enrichment (#488, §7.4 Retrieval ceiling — opt-in via
|
|
15643
|
+
* `retrieval.surfaceEnrichment`, measure-gated).
|
|
15644
|
+
*
|
|
15645
|
+
* The map analyzers extract surfaces (routes) that never reach the rankable
|
|
15646
|
+
* signature index — a query like "payment webhook route" cannot match a
|
|
15647
|
+
* controller whose signatures never mention the route path. This module
|
|
15648
|
+
* appends deterministic pseudo-signatures (`route GET /api/users`) to the
|
|
15649
|
+
* defining file's signature list so the ranker's tokenizer can see them.
|
|
15650
|
+
*
|
|
15651
|
+
* Deterministic: rows are deduped and sorted; entries are copy-on-write so
|
|
15652
|
+
* arrays shared with the signature cache are never mutated.
|
|
15653
|
+
*/
|
|
15654
|
+
|
|
15655
|
+
const path = require('path');
|
|
15656
|
+
|
|
15657
|
+
/**
|
|
15658
|
+
* Enrich a signature index with route pseudo-signatures.
|
|
15659
|
+
* @param {Map<string,string[]>} index cwd-relative file → sigs (mutated: enriched entries are replaced with fresh arrays)
|
|
15660
|
+
* @param {string} cwd
|
|
15661
|
+
* @returns {number} pseudo-signatures added
|
|
15662
|
+
*/
|
|
15663
|
+
function enrichWithSurfaces(index, cwd) {
|
|
15664
|
+
if (!(index instanceof Map) || index.size === 0) return 0;
|
|
15665
|
+
|
|
15666
|
+
let collectRoutes;
|
|
15667
|
+
try { ({ collectRoutes } = __require('./src/map/route-table')); } catch (_) { return 0; }
|
|
15668
|
+
|
|
15669
|
+
const rels = [...index.keys()];
|
|
15670
|
+
const files = rels.map((rel) => path.join(cwd, rel));
|
|
15671
|
+
let routes = [];
|
|
15672
|
+
try { routes = collectRoutes(files, cwd) || []; } catch (_) { return 0; }
|
|
15673
|
+
|
|
15674
|
+
const byFile = new Map();
|
|
15675
|
+
for (const r of routes) {
|
|
15676
|
+
const rel = String(r.file).replace(/\\/g, '/');
|
|
15677
|
+
if (!byFile.has(rel)) byFile.set(rel, new Set());
|
|
15678
|
+
byFile.get(rel).add(`route ${r.method} ${r.path}`);
|
|
15679
|
+
}
|
|
15680
|
+
|
|
15681
|
+
let added = 0;
|
|
15682
|
+
for (const [rel, extras] of [...byFile.entries()].sort()) {
|
|
15683
|
+
const sigs = index.get(rel);
|
|
15684
|
+
if (!sigs) continue;
|
|
15685
|
+
const fresh = [...extras].sort().filter((x) => !sigs.includes(x));
|
|
15686
|
+
if (!fresh.length) continue;
|
|
15687
|
+
index.set(rel, [...sigs, ...fresh]); // copy-on-write: never mutate cached arrays
|
|
15688
|
+
added += fresh.length;
|
|
15689
|
+
}
|
|
15690
|
+
return added;
|
|
15691
|
+
}
|
|
15692
|
+
|
|
15693
|
+
module.exports = { enrichWithSurfaces };
|
|
15694
|
+
|
|
15695
|
+
};
|
|
15696
|
+
|
|
15509
15697
|
// ── ./src/retrieval/ranker ──
|
|
15510
15698
|
__factories["./src/retrieval/ranker"] = function(module, exports) {
|
|
15511
15699
|
|
|
@@ -19522,7 +19710,7 @@ function __tryGit(args, opts = {}) {
|
|
|
19522
19710
|
catch (_) { return ''; }
|
|
19523
19711
|
}
|
|
19524
19712
|
|
|
19525
|
-
const VERSION = '8.
|
|
19713
|
+
const VERSION = '8.19.0';
|
|
19526
19714
|
const MARKER = '\n\n## Auto-generated signatures\n<!-- Updated by gen-context.js -->\n';
|
|
19527
19715
|
|
|
19528
19716
|
function requireSourceOrBundled(key) {
|
|
@@ -21850,6 +22038,10 @@ function main() {
|
|
|
21850
22038
|
if (config && config.retrieval && config.retrieval.callGraphBoost) {
|
|
21851
22039
|
try { askCallGraph = requireSourceOrBundled('./src/graph/call-graph').buildCallFileGraph(cwd); } catch (_) {}
|
|
21852
22040
|
}
|
|
22041
|
+
// Opt-in route surface-enrichment (retrieval.surfaceEnrichment) — non-fatal
|
|
22042
|
+
if (config && config.retrieval && config.retrieval.surfaceEnrichment) {
|
|
22043
|
+
try { requireSourceOrBundled('./src/retrieval/enrich-from-maps').enrichWithSurfaces(sigIndex, cwd); } catch (_) {}
|
|
22044
|
+
}
|
|
21853
22045
|
|
|
21854
22046
|
let ranked = rank(query, sigIndex, { topK: 5, weights: intentWeights, cwd, callGraph: askCallGraph });
|
|
21855
22047
|
|
|
@@ -24028,6 +24220,10 @@ function main() {
|
|
|
24028
24220
|
if (config && config.retrieval && config.retrieval.callGraphBoost) {
|
|
24029
24221
|
try { queryCallGraph = requireSourceOrBundled('./src/graph/call-graph').buildCallFileGraph(cwd); } catch (_) {}
|
|
24030
24222
|
}
|
|
24223
|
+
// Opt-in route surface-enrichment (retrieval.surfaceEnrichment) — non-fatal
|
|
24224
|
+
if (config && config.retrieval && config.retrieval.surfaceEnrichment) {
|
|
24225
|
+
try { requireSourceOrBundled('./src/retrieval/enrich-from-maps').enrichWithSurfaces(index, cwd); } catch (_) {}
|
|
24226
|
+
}
|
|
24031
24227
|
const results = rank(query, index, { topK, recencyBoost, cwd, callGraph: queryCallGraph });
|
|
24032
24228
|
if (args.includes('--context')) {
|
|
24033
24229
|
const miniCtx = buildMiniContext(results, cwd);
|
package/llms-full.txt
CHANGED
|
@@ -11,19 +11,19 @@ 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.
|
|
14
|
+
# Version: 8.19.0 | Benchmark: sigmap-v8.19-main (2026-07-19)
|
|
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.
|
|
20
|
+
## Core metrics (benchmark: sigmap-v8.19-main, 2026-07-19)
|
|
21
21
|
|
|
22
22
|
| Metric | Without SigMap | With SigMap |
|
|
23
23
|
|--------|----------------|-------------|
|
|
24
|
-
| Retrieval hit@5 |
|
|
25
|
-
| Token reduction | — |
|
|
26
|
-
| Task
|
|
24
|
+
| Retrieval hit@5 | 42.7% (single-shot grep) | 86.7% (2.02× lift) |
|
|
25
|
+
| Token reduction | — | 96.9% average |
|
|
26
|
+
| Task-success proxy (modeled) | — | 68.9% |
|
|
27
27
|
| Prompts per task | 2.84 | 1.44 (49.2% fewer) |
|
|
28
28
|
| Supported languages | — | 33 |
|
|
29
29
|
| MCP tools | — | 20 |
|
|
@@ -343,7 +343,7 @@ testCoverage = false
|
|
|
343
343
|
testDirs = ["tests","test","__tests__","spec"]
|
|
344
344
|
sigCache = false
|
|
345
345
|
impactRadius = false
|
|
346
|
-
retrieval = {"topK":10,"recencyBoost":1.5,"callGraphBoost":false}
|
|
346
|
+
retrieval = {"topK":10,"recencyBoost":1.5,"callGraphBoost":false,"surfaceEnrichment":false}
|
|
347
347
|
impact = {"depth":3,"includeSigs":true}
|
|
348
348
|
```
|
|
349
349
|
|
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.
|
|
14
|
+
# Version: 8.19.0 | Benchmark: sigmap-v8.19-main (2026-07-19)
|
|
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,12 +23,12 @@ 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.
|
|
26
|
+
## Core metrics (benchmark: sigmap-v8.19-main, 2026-07-19)
|
|
27
27
|
|
|
28
|
-
- hit@5 retrieval:
|
|
29
|
-
- Token reduction:
|
|
30
|
-
- Task
|
|
31
|
-
- Prompts per task: 1.44 vs 2.84 baseline (49.2% fewer)
|
|
28
|
+
- hit@5 retrieval: 86.7% vs 42.7% single-shot grep baseline (2.02× lift)
|
|
29
|
+
- Token reduction: 96.9% average across benchmark repos
|
|
30
|
+
- Task-success proxy: 68.9% (modeled from retrieval tiers, not measured LLM sessions)
|
|
31
|
+
- Prompts per task: 1.44 vs 2.84 baseline (49.2% fewer, modeled)
|
|
32
32
|
- Languages: 33 supported · MCP tools: 20
|
|
33
33
|
- Dependencies: zero npm runtime dependencies · fully offline
|
|
34
34
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sigmap",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.19.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": {
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"benchmark:test-discovery": "node scripts/run-test-discovery-benchmark.mjs --save",
|
|
32
32
|
"benchmark:terse": "node scripts/run-terse-benchmark.mjs --save",
|
|
33
33
|
"benchmark:callgraph-boost": "node scripts/run-callgraph-boost-benchmark.mjs --save",
|
|
34
|
+
"benchmark:surface-enrichment": "node scripts/run-surface-enrichment-benchmark.mjs --save",
|
|
34
35
|
"validate:squeeze": "node scripts/run-squeeze-benchmark.mjs --gate",
|
|
35
36
|
"health": "node gen-context.js --health",
|
|
36
37
|
"map": "node gen-project-map.js",
|
|
@@ -49,6 +50,7 @@
|
|
|
49
50
|
"check:metrics": "node scripts/gen-benchmark-latest.mjs --check && node scripts/check-version-meta.mjs && node scripts/sync-metrics.mjs --check",
|
|
50
51
|
"prepublishOnly": "node scripts/check-bundle.mjs && node scripts/build-bundle.mjs --check && node scripts/gen-benchmark-latest.mjs --check && node scripts/check-version-meta.mjs && node scripts/sync-metrics.mjs --check && node scripts/generate-llms.mjs",
|
|
51
52
|
"benchmark:grounding": "node scripts/run-hallucination-benchmark.mjs",
|
|
53
|
+
"benchmark:honest": "node scripts/run-honest-benchmark.mjs --save",
|
|
52
54
|
"benchmark:llm-ablation": "node scripts/run-llm-ablation.mjs"
|
|
53
55
|
},
|
|
54
56
|
"files": [
|
package/src/config/defaults.js
CHANGED
|
@@ -149,6 +149,8 @@ const DEFAULTS = {
|
|
|
149
149
|
recencyBoost: 1.5,
|
|
150
150
|
// Boost files call-graph-connected to query matches (opt-in, measure-gated)
|
|
151
151
|
callGraphBoost: false,
|
|
152
|
+
// Append route pseudo-signatures to the rankable index (opt-in, measure-gated)
|
|
153
|
+
surfaceEnrichment: false,
|
|
152
154
|
},
|
|
153
155
|
|
|
154
156
|
// Impact layer settings (v2.5)
|