sigmap 8.16.1 → 8.18.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/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)
@@ -5229,8 +5231,12 @@ __factories["./src/extractors/cpp"] = function(module, exports) {
5229
5231
  // ── ./src/extractors/csharp ──
5230
5232
  __factories["./src/extractors/csharp"] = function(module, exports) {
5231
5233
 
5234
+ const { lineAt, withAnchor } = __require('./src/extractors/line-anchor');
5235
+
5232
5236
  /**
5233
5237
  * Extract signatures from C# source code.
5238
+ * Signatures carry `:start-end` line anchors (Surgical Context); the comment
5239
+ * strip below is newline-preserving so anchor lines match the original file.
5234
5240
  * @param {string} src - Raw file content
5235
5241
  * @returns {string[]} Array of signature strings
5236
5242
  */
@@ -5240,14 +5246,18 @@ __factories["./src/extractors/csharp"] = function(module, exports) {
5240
5246
 
5241
5247
  const stripped = src
5242
5248
  .replace(/\/\/.*$/gm, '')
5243
- .replace(/\/\*[\s\S]*?\*\//g, '');
5249
+ .replace(/\/\*[\s\S]*?\*\//g, (s) => s.replace(/[^\n]/g, ' '));
5244
5250
 
5245
5251
  // Classes and interfaces
5246
5252
  const typeRe = /^\s*(?:public\s+|internal\s+|protected\s+)?(?:abstract\s+|sealed\s+|static\s+)?(class|interface|enum|record|struct)\s+(\w+)(?:<[^{]*>)?(?:\s*:\s*[\w<>, .]+)?\s*\{/gm;
5247
5253
  for (const m of stripped.matchAll(typeRe)) {
5248
- sigs.push(`${m[1]} ${m[2]}`);
5249
- const block = extractBlock(stripped, m.index + m[0].length);
5250
- for (const meth of extractMembers(block)) sigs.push(` ${meth}`);
5254
+ const declIdx = m.index + (m[0].length - m[0].trimStart().length);
5255
+ const bodyStart = m.index + m[0].length;
5256
+ const block = extractBlock(stripped, bodyStart);
5257
+ sigs.push(withAnchor(`${m[1]} ${m[2]}`, lineAt(stripped, declIdx), lineAt(stripped, bodyStart + block.length)));
5258
+ for (const meth of extractMembers(block)) {
5259
+ sigs.push(withAnchor(` ${meth.text}`, lineAt(stripped, bodyStart + meth.declIdx), lineAt(stripped, bodyStart + meth.endIdx)));
5260
+ }
5251
5261
  }
5252
5262
 
5253
5263
  return sigs.slice(0, 25);
@@ -5270,7 +5280,11 @@ __factories["./src/extractors/csharp"] = function(module, exports) {
5270
5280
  for (const m of block.matchAll(methodRe)) {
5271
5281
  const ret = normalizeType(m[1]);
5272
5282
  const retStr = ret ? ` → ${ret}` : '';
5273
- members.push(`${m[2]}(${normalizeParams(m[3])})${retStr}`);
5283
+ members.push({
5284
+ text: `${m[2]}(${normalizeParams(m[3])})${retStr}`,
5285
+ declIdx: m.index + (m[0].length - m[0].trimStart().length),
5286
+ endIdx: m.index + m[0].length,
5287
+ });
5274
5288
  }
5275
5289
  return members.slice(0, 8);
5276
5290
  }
@@ -5365,8 +5379,12 @@ __factories["./src/extractors/css"] = function(module, exports) {
5365
5379
  // ── ./src/extractors/dart ──
5366
5380
  __factories["./src/extractors/dart"] = function(module, exports) {
5367
5381
 
5382
+ const { lineAt, withAnchor } = __require('./src/extractors/line-anchor');
5383
+
5368
5384
  /**
5369
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.
5370
5388
  * @param {string} src - Raw file content
5371
5389
  * @returns {string[]} Array of signature strings
5372
5390
  */
@@ -5376,21 +5394,37 @@ __factories["./src/extractors/dart"] = function(module, exports) {
5376
5394
 
5377
5395
  const stripped = src
5378
5396
  .replace(/\/\/.*$/gm, '')
5379
- .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
+ };
5380
5410
 
5381
5411
  // Classes and abstract classes
5382
5412
  for (const m of stripped.matchAll(/^(?:abstract\s+)?class\s+(\w+)(?:<[^{]*>)?(?:\s+extends\s+[\w<>, ]+)?(?:\s+(?:implements|with|on)\s+[\w<>, ]+)?\s*\{/gm)) {
5383
5413
  const abs = m[0].trimStart().startsWith('abstract') ? 'abstract ' : '';
5384
- sigs.push(`${abs}class ${m[1]}`);
5385
- const block = extractBlock(stripped, m.index + m[0].length);
5386
- for (const meth of extractMembers(block)) sigs.push(` ${meth}`);
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
+ }
5387
5420
  }
5388
5421
 
5389
5422
  // Top-level functions — capture return type (prefix before name) and show as suffix
5390
5423
  for (const m of stripped.matchAll(/^((?:Future<[\w<>?,\s]*>|[\w<>?]+))\s+(\w+)\s*\(([^)]*)\)/gm)) {
5391
5424
  if (m[2].startsWith('_')) continue;
5392
- const retStr = (m[1] && m[1] !== 'void') ? ` \u2192 ${m[1].replace(/\s+/g, '').slice(0, 25)}` : '';
5393
- sigs.push(`${m[2]}(${normalizeParams(m[3])})${retStr}`);
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));
5394
5428
  }
5395
5429
 
5396
5430
  return sigs.slice(0, 25);
@@ -5411,8 +5445,12 @@ __factories["./src/extractors/dart"] = function(module, exports) {
5411
5445
  const members = [];
5412
5446
  for (const m of block.matchAll(/^\s+(?:@override\s+)?(?:@\w+\s+)*((?:Future<[\w<>?,\s]*>|[\w<>?]+))\s+(\w+)\s*\(([^)]*)\)/gm)) {
5413
5447
  if (m[2].startsWith('_')) continue;
5414
- const retStr = (m[1] && m[1] !== 'void') ? ` \u2192 ${m[1].replace(/\s+/g, '').slice(0, 25)}` : '';
5415
- members.push(`${m[2]}(${normalizeParams(m[3])})${retStr}`);
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
+ });
5416
5454
  }
5417
5455
  return members.slice(0, 8);
5418
5456
  }
@@ -5878,8 +5916,12 @@ __factories["./src/extractors/generic"] = function(module, exports) {
5878
5916
  // ── ./src/extractors/go ──
5879
5917
  __factories["./src/extractors/go"] = function(module, exports) {
5880
5918
 
5919
+ const { lineAt, withAnchor } = __require('./src/extractors/line-anchor');
5920
+
5881
5921
  /**
5882
5922
  * Extract signatures from Go source code.
5923
+ * Signatures carry `:start-end` line anchors (Surgical Context); the comment
5924
+ * strip below is newline-preserving so anchor lines match the original file.
5883
5925
  * @param {string} src - Raw file content
5884
5926
  * @returns {string[]} Array of signature strings
5885
5927
  */
@@ -5889,26 +5931,34 @@ __factories["./src/extractors/go"] = function(module, exports) {
5889
5931
 
5890
5932
  const stripped = src
5891
5933
  .replace(/\/\/.*$/gm, '')
5892
- .replace(/\/\*[\s\S]*?\*\//g, '');
5934
+ .replace(/\/\*[\s\S]*?\*\//g, (s) => s.replace(/[^\n]/g, ' '));
5935
+
5936
+ // Index of the closing brace for a block opened just before startIndex.
5937
+ const blockEndIdx = (startIndex) => startIndex + extractBlock(stripped, startIndex).length;
5893
5938
 
5894
5939
  // Structs
5895
5940
  for (const m of stripped.matchAll(/^type\s+(\w+)\s+struct\s*\{/gm)) {
5896
- sigs.push(`type ${m[1]} struct`);
5941
+ const end = blockEndIdx(m.index + m[0].length);
5942
+ sigs.push(withAnchor(`type ${m[1]} struct`, lineAt(stripped, m.index), lineAt(stripped, end)));
5897
5943
  }
5898
5944
 
5899
5945
  // Interfaces
5900
5946
  for (const m of stripped.matchAll(/^type\s+(\w+)\s+interface\s*\{/gm)) {
5901
- sigs.push(`type ${m[1]} interface`);
5902
- const block = extractBlock(stripped, m.index + m[0].length);
5903
- for (const method of extractInterfaceMethods(block)) sigs.push(` ${method}`);
5947
+ const bodyStart = m.index + m[0].length;
5948
+ const block = extractBlock(stripped, bodyStart);
5949
+ sigs.push(withAnchor(`type ${m[1]} interface`, lineAt(stripped, m.index), lineAt(stripped, bodyStart + block.length)));
5950
+ for (const meth of extractInterfaceMethods(block)) {
5951
+ sigs.push(withAnchor(` ${meth.text}`, lineAt(stripped, bodyStart + meth.declIdx), lineAt(stripped, bodyStart + meth.endIdx)));
5952
+ }
5904
5953
  }
5905
5954
 
5906
5955
  // Functions and methods — capture return type between ) and {
5907
5956
  for (const m of stripped.matchAll(/^func\s+(?:\((\w+)\s+[\w*]+\)\s+)?(\w+)\s*\(([^)]*)\)([^{]*)\{/gm)) {
5908
5957
  const receiver = m[1] ? `(${m[1]}) ` : '';
5909
5958
  const retType = m[4] ? m[4].trim().replace(/\s+/g, ' ') : '';
5910
- const retStr = retType ? ` \u2192 ${retType.slice(0, 30)}` : '';
5911
- sigs.push(`func ${receiver}${m[2]}(${normalizeParams(m[3])})${retStr}`);
5959
+ const retStr = retType ? ` ${retType.slice(0, 30)}` : '';
5960
+ const end = blockEndIdx(m.index + m[0].length);
5961
+ sigs.push(withAnchor(`func ${receiver}${m[2]}(${normalizeParams(m[3])})${retStr}`, lineAt(stripped, m.index), lineAt(stripped, end)));
5912
5962
  }
5913
5963
 
5914
5964
  return sigs.slice(0, 25);
@@ -5929,8 +5979,12 @@ __factories["./src/extractors/go"] = function(module, exports) {
5929
5979
  const methods = [];
5930
5980
  for (const m of block.matchAll(/^\s+(\w+)\s*\(([^)]*)\)([^\n]*)/gm)) {
5931
5981
  const retType = m[3] ? m[3].trim().replace(/\s+/g, ' ') : '';
5932
- const retStr = retType ? ` \u2192 ${retType.slice(0, 30)}` : '';
5933
- methods.push(`${m[1]}(${normalizeParams(m[2])})${retStr}`);
5982
+ const retStr = retType ? ` ${retType.slice(0, 30)}` : '';
5983
+ methods.push({
5984
+ text: `${m[1]}(${normalizeParams(m[2])})${retStr}`,
5985
+ declIdx: m.index + (m[0].length - m[0].trimStart().length),
5986
+ endIdx: m.index + m[0].length,
5987
+ });
5934
5988
  }
5935
5989
  return methods.slice(0, 8);
5936
5990
  }
@@ -6060,8 +6114,12 @@ __factories["./src/extractors/html"] = function(module, exports) {
6060
6114
  // ── ./src/extractors/java ──
6061
6115
  __factories["./src/extractors/java"] = function(module, exports) {
6062
6116
 
6117
+ const { lineAt, withAnchor } = __require('./src/extractors/line-anchor');
6118
+
6063
6119
  /**
6064
6120
  * Extract signatures from Java source code.
6121
+ * Signatures carry `:start-end` line anchors (Surgical Context); the comment
6122
+ * strip below is newline-preserving so anchor lines match the original file.
6065
6123
  * @param {string} src - Raw file content
6066
6124
  * @returns {string[]} Array of signature strings
6067
6125
  */
@@ -6071,14 +6129,17 @@ __factories["./src/extractors/java"] = function(module, exports) {
6071
6129
 
6072
6130
  const stripped = src
6073
6131
  .replace(/\/\/.*$/gm, '')
6074
- .replace(/\/\*[\s\S]*?\*\//g, '');
6132
+ .replace(/\/\*[\s\S]*?\*\//g, (s) => s.replace(/[^\n]/g, ' '));
6075
6133
 
6076
6134
  // Classes and interfaces
6077
6135
  const typeRegex = /^(?:public\s+|protected\s+)?(?:abstract\s+|final\s+)?(class|interface|enum)\s+(\w+)(?:\s+extends\s+[\w<>, .]+)?(?:\s+implements\s+[\w<>, .]+)?\s*\{/gm;
6078
6136
  for (const m of stripped.matchAll(typeRegex)) {
6079
- sigs.push(`${m[1]} ${m[2]}`);
6080
- const block = extractBlock(stripped, m.index + m[0].length);
6081
- for (const meth of extractMembers(block)) sigs.push(` ${meth}`);
6137
+ const bodyStart = m.index + m[0].length;
6138
+ const block = extractBlock(stripped, bodyStart);
6139
+ sigs.push(withAnchor(`${m[1]} ${m[2]}`, lineAt(stripped, m.index), lineAt(stripped, bodyStart + block.length)));
6140
+ for (const meth of extractMembers(block)) {
6141
+ sigs.push(withAnchor(` ${meth.text}`, lineAt(stripped, bodyStart + meth.declIdx), lineAt(stripped, bodyStart + meth.endIdx)));
6142
+ }
6082
6143
  }
6083
6144
 
6084
6145
  return sigs.slice(0, 25);
@@ -6102,7 +6163,11 @@ __factories["./src/extractors/java"] = function(module, exports) {
6102
6163
  for (const m of block.matchAll(methodRe)) {
6103
6164
  const ret = normalizeType(m[1]);
6104
6165
  const retStr = ret ? ` → ${ret}` : '';
6105
- members.push(`${m[2]}(${normalizeParams(m[3])})${retStr}`);
6166
+ members.push({
6167
+ text: `${m[2]}(${normalizeParams(m[3])})${retStr}`,
6168
+ declIdx: m.index + (m[0].length - m[0].trimStart().length),
6169
+ endIdx: m.index + m[0].length,
6170
+ });
6106
6171
  }
6107
6172
  return members.slice(0, 8);
6108
6173
  }
@@ -6275,8 +6340,12 @@ __factories["./src/extractors/javascript"] = function(module, exports) {
6275
6340
  // ── ./src/extractors/kotlin ──
6276
6341
  __factories["./src/extractors/kotlin"] = function(module, exports) {
6277
6342
 
6343
+ const { lineAt, withAnchor } = __require('./src/extractors/line-anchor');
6344
+
6278
6345
  /**
6279
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.
6280
6349
  * @param {string} src - Raw file content
6281
6350
  * @returns {string[]} Array of signature strings
6282
6351
  */
@@ -6286,21 +6355,37 @@ __factories["./src/extractors/kotlin"] = function(module, exports) {
6286
6355
 
6287
6356
  const stripped = src
6288
6357
  .replace(/\/\/.*$/gm, '')
6289
- .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
+ };
6290
6371
 
6291
6372
  // Classes, objects, interfaces
6292
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)) {
6293
- sigs.push(`${m[1]} ${m[2]}`);
6294
- const block = extractBlock(stripped, m.index + m[0].length);
6295
- for (const meth of extractMembers(block)) sigs.push(` ${meth}`);
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
+ }
6296
6380
  }
6297
6381
 
6298
6382
  // Top-level functions — capture `: RetType` after params
6299
6383
  for (const m of stripped.matchAll(/^(?:public\s+|internal\s+)?(?:suspend\s+)?fun\s+(\w+)\s*(?:<[^(]*>)?\s*\(([^)]*)\)(?:\s*:\s*([^\n{=]+))?/gm)) {
6300
6384
  const suspend = m[0].includes('suspend') ? 'suspend ' : '';
6301
6385
  const retType = m[3] ? m[3].trim().replace(/\s+/g, ' ') : '';
6302
- const retStr = retType ? ` \u2192 ${retType.slice(0, 25)}` : '';
6303
- sigs.push(`${suspend}fun ${m[1]}(${normalizeParams(m[2])})${retStr}`);
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));
6304
6389
  }
6305
6390
 
6306
6391
  return sigs.slice(0, 25);
@@ -6323,8 +6408,12 @@ __factories["./src/extractors/kotlin"] = function(module, exports) {
6323
6408
  if (m[1].startsWith('_')) continue;
6324
6409
  const suspend = m[0].includes('suspend') ? 'suspend ' : '';
6325
6410
  const retType = m[3] ? m[3].trim().replace(/\s+/g, ' ') : '';
6326
- const retStr = retType ? ` \u2192 ${retType.slice(0, 25)}` : '';
6327
- members.push(`${suspend}fun ${m[1]}(${normalizeParams(m[2])})${retStr}`);
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
+ });
6328
6417
  }
6329
6418
  return members.slice(0, 8);
6330
6419
  }
@@ -6574,8 +6663,12 @@ __factories["./src/extractors/patterns"] = function(module, exports) {
6574
6663
  // ── ./src/extractors/php ──
6575
6664
  __factories["./src/extractors/php"] = function(module, exports) {
6576
6665
 
6666
+ const { lineAt, withAnchor } = __require('./src/extractors/line-anchor');
6667
+
6577
6668
  /**
6578
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.
6579
6672
  * @param {string} src - Raw file content
6580
6673
  * @returns {string[]} Array of signature strings
6581
6674
  */
@@ -6586,23 +6679,40 @@ __factories["./src/extractors/php"] = function(module, exports) {
6586
6679
  const stripped = src
6587
6680
  .replace(/\/\/.*$/gm, '')
6588
6681
  .replace(/#.*$/gm, '')
6589
- .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
+ };
6590
6696
 
6591
6697
  // Classes and interfaces
6592
6698
  const typeRe = /^(?:abstract\s+)?(?:class|interface|trait)\s+(\w+)(?:\s+extends\s+\w+)?(?:\s+implements\s+[\w, ]+)?\s*\{/gm;
6593
6699
  for (const m of stripped.matchAll(typeRe)) {
6594
6700
  const kind = m[0].trimStart().startsWith('interface') ? 'interface' :
6595
6701
  m[0].trimStart().startsWith('trait') ? 'trait' : 'class';
6596
- sigs.push(`${kind} ${m[1]}`);
6597
- const block = extractBlock(stripped, m.index + m[0].length);
6598
- for (const meth of extractMembers(block)) sigs.push(` ${meth}`);
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
+ }
6599
6708
  }
6600
6709
 
6601
6710
  // Top-level functions
6602
6711
  for (const m of stripped.matchAll(/^function\s+(\w+)\s*\(([^)]*)\)\s*(?::\s*([^\n{]+))?/gm)) {
6603
6712
  const ret = normalizeType(m[3]);
6604
6713
  const retStr = ret ? ` → ${ret}` : '';
6605
- sigs.push(`function ${m[1]}(${normalizeParams(m[2])})${retStr}`);
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));
6606
6716
  }
6607
6717
 
6608
6718
  return sigs.slice(0, 25);
@@ -6627,7 +6737,11 @@ __factories["./src/extractors/php"] = function(module, exports) {
6627
6737
  const isStatic = m[0].includes('static ') ? 'static ' : '';
6628
6738
  const ret = normalizeType(m[3]);
6629
6739
  const retStr = ret ? ` → ${ret}` : '';
6630
- members.push(`${isStatic}function ${m[1]}(${normalizeParams(m[2])})${retStr}`);
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
+ });
6631
6745
  }
6632
6746
  return members.slice(0, 8);
6633
6747
  }
@@ -7513,8 +7627,12 @@ __factories["./src/extractors/ruby"] = function(module, exports) {
7513
7627
  // ── ./src/extractors/rust ──
7514
7628
  __factories["./src/extractors/rust"] = function(module, exports) {
7515
7629
 
7630
+ const { lineAt, withAnchor } = __require('./src/extractors/line-anchor');
7631
+
7516
7632
  /**
7517
7633
  * Extract signatures from Rust source code.
7634
+ * Signatures carry `:start-end` line anchors (Surgical Context); the comment
7635
+ * strip below is newline-preserving so anchor lines match the original file.
7518
7636
  * @param {string} src - Raw file content
7519
7637
  * @returns {string[]} Array of signature strings
7520
7638
  */
@@ -7524,35 +7642,55 @@ __factories["./src/extractors/rust"] = function(module, exports) {
7524
7642
 
7525
7643
  const stripped = src
7526
7644
  .replace(/\/\/.*$/gm, '')
7527
- .replace(/\/\*[\s\S]*?\*\//g, '');
7645
+ .replace(/\/\*[\s\S]*?\*\//g, (s) => s.replace(/[^\n]/g, ' '));
7646
+
7647
+ // Anchor range for a declaration at declIdx whose header ends at afterIdx:
7648
+ // if a `{` body follows, range to its closing brace; else single-line.
7649
+ const rangeFor = (declIdx, afterIdx) => {
7650
+ let k = afterIdx;
7651
+ while (k < stripped.length && /[ \t]/.test(stripped[k])) k++;
7652
+ if (stripped[k] === '{') {
7653
+ const end = k + 1 + extractBlock(stripped, k + 1).length;
7654
+ return [lineAt(stripped, declIdx), lineAt(stripped, end)];
7655
+ }
7656
+ const line = lineAt(stripped, declIdx);
7657
+ return [line, line];
7658
+ };
7528
7659
 
7529
7660
  // Structs
7530
7661
  for (const m of stripped.matchAll(/^pub\s+struct\s+(\w+)(?:<[^{]*>)?/gm)) {
7531
- sigs.push(`pub struct ${m[1]}`);
7662
+ const [s, e] = rangeFor(m.index, m.index + m[0].length);
7663
+ sigs.push(withAnchor(`pub struct ${m[1]}`, s, e));
7532
7664
  }
7533
7665
 
7534
7666
  // Enums
7535
7667
  for (const m of stripped.matchAll(/^pub\s+enum\s+(\w+)(?:<[^{]*>)?/gm)) {
7536
- sigs.push(`pub enum ${m[1]}`);
7668
+ const [s, e] = rangeFor(m.index, m.index + m[0].length);
7669
+ sigs.push(withAnchor(`pub enum ${m[1]}`, s, e));
7537
7670
  }
7538
7671
 
7539
7672
  // Traits
7540
7673
  for (const m of stripped.matchAll(/^pub\s+trait\s+(\w+)(?:<[^{]*>)?/gm)) {
7541
- sigs.push(`pub trait ${m[1]}`);
7674
+ const [s, e] = rangeFor(m.index, m.index + m[0].length);
7675
+ sigs.push(withAnchor(`pub trait ${m[1]}`, s, e));
7542
7676
  }
7543
7677
 
7544
7678
  // impl blocks
7545
7679
  for (const m of stripped.matchAll(/^impl(?:<[^>]*>)?\s+(?:[\w:]+\s+for\s+)?(\w+)(?:<[^{]*>)?\s*\{/gm)) {
7546
- sigs.push(`impl ${m[1]}`);
7547
- const block = extractBlock(stripped, m.index + m[0].length);
7548
- for (const fn of extractMethods(block)) sigs.push(` ${fn}`);
7680
+ const bodyStart = m.index + m[0].length;
7681
+ const block = extractBlock(stripped, bodyStart);
7682
+ sigs.push(withAnchor(`impl ${m[1]}`, lineAt(stripped, m.index), lineAt(stripped, bodyStart + block.length)));
7683
+ for (const fn of extractMethods(block)) {
7684
+ sigs.push(withAnchor(` ${fn.text}`, lineAt(stripped, bodyStart + fn.declIdx), lineAt(stripped, bodyStart + fn.endIdx)));
7685
+ }
7549
7686
  }
7550
7687
 
7551
7688
  // Top-level pub fns — capture everything after ) up to { or ; for return type
7552
7689
  for (const m of stripped.matchAll(/^pub(?:\s+async)?\s+fn\s+(\w+)(?:<[^(]*>)?\s*\(([^)]*)\)([^{;]*)/gm)) {
7553
7690
  const asyncKw = m[0].includes('async') ? 'async ' : '';
7554
7691
  const retStr = extractReturnType(m[3]);
7555
- sigs.push(`pub ${asyncKw}fn ${m[1]}(${normalizeParams(m[2])})${retStr}`);
7692
+ const [s, e] = rangeFor(m.index, m.index + m[0].length);
7693
+ sigs.push(withAnchor(`pub ${asyncKw}fn ${m[1]}(${normalizeParams(m[2])})${retStr}`, s, e));
7556
7694
  }
7557
7695
 
7558
7696
  return sigs.slice(0, 25);
@@ -7574,7 +7712,11 @@ __factories["./src/extractors/rust"] = function(module, exports) {
7574
7712
  for (const m of block.matchAll(/^\s+pub(?:\s+async)?\s+fn\s+(\w+)(?:<[^(]*>)?\s*\(([^)]*)\)([^{;]*)/gm)) {
7575
7713
  const asyncKw = m[0].includes('async') ? 'async ' : '';
7576
7714
  const retStr = extractReturnType(m[3]);
7577
- methods.push(`pub ${asyncKw}fn ${m[1]}(${normalizeParams(m[2])})${retStr}`);
7715
+ methods.push({
7716
+ text: `pub ${asyncKw}fn ${m[1]}(${normalizeParams(m[2])})${retStr}`,
7717
+ declIdx: m.index + (m[0].length - m[0].trimStart().length),
7718
+ endIdx: m.index + m[0].length,
7719
+ });
7578
7720
  }
7579
7721
  return methods.slice(0, 8);
7580
7722
  }
@@ -7589,7 +7731,7 @@ __factories["./src/extractors/rust"] = function(module, exports) {
7589
7731
  const m = afterParen.match(/->\s*([^{;]+)/);
7590
7732
  if (!m) return '';
7591
7733
  const rt = m[1].trim().replace(/\s+/g, ' ');
7592
- return ` \u2192 ${rt.length > 30 ? rt.slice(0, 27) + '...' : rt}`;
7734
+ return ` ${rt.length > 30 ? rt.slice(0, 27) + '...' : rt}`;
7593
7735
  }
7594
7736
 
7595
7737
  module.exports = { extract };
@@ -7599,8 +7741,12 @@ __factories["./src/extractors/rust"] = function(module, exports) {
7599
7741
  // ── ./src/extractors/scala ──
7600
7742
  __factories["./src/extractors/scala"] = function(module, exports) {
7601
7743
 
7744
+ const { lineAt, withAnchor } = __require('./src/extractors/line-anchor');
7745
+
7602
7746
  /**
7603
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.
7604
7750
  * @param {string} src - Raw file content
7605
7751
  * @returns {string[]} Array of signature strings
7606
7752
  */
@@ -7610,7 +7756,7 @@ __factories["./src/extractors/scala"] = function(module, exports) {
7610
7756
 
7611
7757
  const stripped = src
7612
7758
  .replace(/\/\/.*$/gm, '')
7613
- .replace(/\/\*[\s\S]*?\*\//g, '');
7759
+ .replace(/\/\*[\s\S]*?\*\//g, (s) => s.replace(/[^\n]/g, ' '));
7614
7760
 
7615
7761
  // Classes, traits, objects
7616
7762
  const typeRe = /^(?:case\s+)?(?:class|trait|object)\s+(\w+)(?:\[[\w, ]+\])?(?:[^{]*)\{/gm;
@@ -7618,9 +7764,12 @@ __factories["./src/extractors/scala"] = function(module, exports) {
7618
7764
  const kind = m[0].trimStart().startsWith('case class') ? 'case class' :
7619
7765
  m[0].trimStart().startsWith('trait') ? 'trait' :
7620
7766
  m[0].trimStart().startsWith('object') ? 'object' : 'class';
7621
- sigs.push(`${kind} ${m[1]}`);
7622
- const block = extractBlock(stripped, m.index + m[0].length);
7623
- for (const fn of extractMembers(block)) sigs.push(` ${fn}`);
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
+ }
7624
7773
  }
7625
7774
 
7626
7775
  // Top-level defs
@@ -7629,7 +7778,8 @@ __factories["./src/extractors/scala"] = function(module, exports) {
7629
7778
  const params = m[2] ? `(${normalizeParams(m[2])})` : '';
7630
7779
  const ret = normalizeType(m[3]);
7631
7780
  const retStr = ret ? ` → ${ret}` : '';
7632
- sigs.push(`def ${m[1]}${params}${retStr}`);
7781
+ const line = lineAt(stripped, m.index);
7782
+ sigs.push(withAnchor(`def ${m[1]}${params}${retStr}`, line, line));
7633
7783
  }
7634
7784
 
7635
7785
  return sigs.slice(0, 25);
@@ -7653,7 +7803,11 @@ __factories["./src/extractors/scala"] = function(module, exports) {
7653
7803
  const params = m[2] ? `(${normalizeParams(m[2])})` : '';
7654
7804
  const ret = normalizeType(m[3]);
7655
7805
  const retStr = ret ? ` → ${ret}` : '';
7656
- members.push(`def ${m[1]}${params}${retStr}`);
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
+ });
7657
7811
  }
7658
7812
  return members.slice(0, 8);
7659
7813
  }
@@ -7885,8 +8039,12 @@ __factories["./src/extractors/svelte"] = function(module, exports) {
7885
8039
  // ── ./src/extractors/swift ──
7886
8040
  __factories["./src/extractors/swift"] = function(module, exports) {
7887
8041
 
8042
+ const { lineAt, withAnchor } = __require('./src/extractors/line-anchor');
8043
+
7888
8044
  /**
7889
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.
7890
8048
  * @param {string} src - Raw file content
7891
8049
  * @returns {string[]} Array of signature strings
7892
8050
  */
@@ -7896,21 +8054,37 @@ __factories["./src/extractors/swift"] = function(module, exports) {
7896
8054
 
7897
8055
  const stripped = src
7898
8056
  .replace(/\/\/.*$/gm, '')
7899
- .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
+ };
7900
8070
 
7901
8071
  // Classes, structs, protocols, enums
7902
8072
  const typeRe = /^(?:public\s+|internal\s+|open\s+)?(?:final\s+)?(class|struct|protocol|enum|actor)\s+(\w+)(?:<[^{]*>)?(?:\s*:\s*[\w, <>.]+)?\s*\{/gm;
7903
8073
  for (const m of stripped.matchAll(typeRe)) {
7904
- sigs.push(`${m[1]} ${m[2]}`);
7905
- const block = extractBlock(stripped, m.index + m[0].length);
7906
- for (const fn of extractMembers(block)) sigs.push(` ${fn}`);
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
+ }
7907
8080
  }
7908
8081
 
7909
8082
  // Top-level public functions — capture everything after ) to end of line for arrow type
7910
8083
  for (const m of stripped.matchAll(/^(?:public\s+|internal\s+)?(?:static\s+)?(?:async\s+)?func\s+(\w+)(?:<[^(]*>)?\s*\(([^)]*)\)([^{\n]*)/gm)) {
7911
8084
  const asyncKw = m[0].includes('async') ? 'async ' : '';
7912
8085
  const retStr = extractArrowType(m[3]);
7913
- sigs.push(`${asyncKw}func ${m[1]}(${normalizeParams(m[2])})${retStr}`);
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));
7914
8088
  }
7915
8089
 
7916
8090
  return sigs.slice(0, 25);
@@ -7933,7 +8107,11 @@ __factories["./src/extractors/swift"] = function(module, exports) {
7933
8107
  if (m[1].startsWith('_')) continue;
7934
8108
  const asyncKw = m[0].includes('async') ? 'async ' : '';
7935
8109
  const retStr = extractArrowType(m[3]);
7936
- members.push(`${asyncKw}func ${m[1]}(${normalizeParams(m[2])})${retStr}`);
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
+ });
7937
8115
  }
7938
8116
  return members.slice(0, 8);
7939
8117
  }
@@ -7952,7 +8130,7 @@ __factories["./src/extractors/swift"] = function(module, exports) {
7952
8130
  const m = str.match(/->\s*([^\n{]+)/);
7953
8131
  if (!m) return '';
7954
8132
  const rt = m[1].trim().replace(/\s+/g, ' ');
7955
- return ` \u2192 ${rt.length > 25 ? rt.slice(0, 22) + '...' : rt}`;
8133
+ return ` ${rt.length > 25 ? rt.slice(0, 22) + '...' : rt}`;
7956
8134
  }
7957
8135
 
7958
8136
  module.exports = { extract };
@@ -13160,7 +13338,14 @@ __factories["./src/map/route-table"] = function(module, exports) {
13160
13338
  return /(^|\/)(gen-context|gen-project-map)\.js$/.test(normalized);
13161
13339
  }
13162
13340
 
13163
- function analyze(files, cwd) {
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) {
13164
13349
  const routes = [];
13165
13350
 
13166
13351
  for (const filePath of files) {
@@ -13252,6 +13437,11 @@ __factories["./src/map/route-table"] = function(module, exports) {
13252
13437
  }
13253
13438
  }
13254
13439
 
13440
+ return routes;
13441
+ }
13442
+
13443
+ function analyze(files, cwd) {
13444
+ const routes = collectRoutes(files, cwd);
13255
13445
  if (routes.length === 0) return '';
13256
13446
 
13257
13447
  const lines = [
@@ -13264,7 +13454,7 @@ __factories["./src/map/route-table"] = function(module, exports) {
13264
13454
  return lines.join('\n');
13265
13455
  }
13266
13456
 
13267
- module.exports = { analyze };
13457
+ module.exports = { analyze, collectRoutes };
13268
13458
 
13269
13459
  };
13270
13460
 
@@ -13692,7 +13882,7 @@ __factories["./src/mcp/handlers"] = function(module, exports) {
13692
13882
  // Build dependency graph for neighbor boost — non-fatal if it fails
13693
13883
  let graph = null;
13694
13884
  try { graph = buildFromCwd(cwd); } catch (_) {}
13695
- // Opt-in call-graph neighbor boost (retrieval.callGraphBoost) — non-fatal
13885
+ // Opt-in call-graph neighbor boost + surface enrichment — non-fatal
13696
13886
  let callGraph = null;
13697
13887
  try {
13698
13888
  const { loadConfig } = __require('./src/config/loader');
@@ -13700,6 +13890,9 @@ __factories["./src/mcp/handlers"] = function(module, exports) {
13700
13890
  if (retrieval && retrieval.callGraphBoost) {
13701
13891
  callGraph = __require('./src/graph/call-graph').buildCallFileGraph(cwd);
13702
13892
  }
13893
+ if (retrieval && retrieval.surfaceEnrichment) {
13894
+ __require('./src/retrieval/enrich-from-maps').enrichWithSurfaces(index, cwd);
13895
+ }
13703
13896
  } catch (_) {}
13704
13897
  const results = rank(args.query, index, { topK, cwd, graph, callGraph });
13705
13898
  return formatRankTable(results, args.query);
@@ -14414,7 +14607,7 @@ __factories["./src/mcp/server"] = function(module, exports) {
14414
14607
 
14415
14608
  const SERVER_INFO = {
14416
14609
  name: 'sigmap',
14417
- version: '8.16.1',
14610
+ version: '8.18.0',
14418
14611
  description: 'SigMap MCP server — code signatures on demand',
14419
14612
  };
14420
14613
 
@@ -15402,7 +15595,10 @@ __factories["./src/retrieval/bm25"] = function(module, exports) {
15402
15595
 
15403
15596
  const docs = candidates.map((c) => {
15404
15597
  const pathToks = tokenize(c.file || '');
15405
- const toks = tokenize((c.sigs || []).join(' '));
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(' '));
15406
15602
  for (let i = 0; i < PATH_BOOST; i++) toks.push(...pathToks);
15407
15603
  const tf = new Map();
15408
15604
  for (const t of toks) tf.set(t, (tf.get(t) || 0) + 1);
@@ -15439,6 +15635,65 @@ __factories["./src/retrieval/bm25"] = function(module, exports) {
15439
15635
 
15440
15636
  };
15441
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
+
15442
15697
  // ── ./src/retrieval/ranker ──
15443
15698
  __factories["./src/retrieval/ranker"] = function(module, exports) {
15444
15699
 
@@ -19455,7 +19710,7 @@ function __tryGit(args, opts = {}) {
19455
19710
  catch (_) { return ''; }
19456
19711
  }
19457
19712
 
19458
- const VERSION = '8.16.1';
19713
+ const VERSION = '8.18.0';
19459
19714
  const MARKER = '\n\n## Auto-generated signatures\n<!-- Updated by gen-context.js -->\n';
19460
19715
 
19461
19716
  function requireSourceOrBundled(key) {
@@ -21783,6 +22038,10 @@ function main() {
21783
22038
  if (config && config.retrieval && config.retrieval.callGraphBoost) {
21784
22039
  try { askCallGraph = requireSourceOrBundled('./src/graph/call-graph').buildCallFileGraph(cwd); } catch (_) {}
21785
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
+ }
21786
22045
 
21787
22046
  let ranked = rank(query, sigIndex, { topK: 5, weights: intentWeights, cwd, callGraph: askCallGraph });
21788
22047
 
@@ -23961,6 +24220,10 @@ function main() {
23961
24220
  if (config && config.retrieval && config.retrieval.callGraphBoost) {
23962
24221
  try { queryCallGraph = requireSourceOrBundled('./src/graph/call-graph').buildCallFileGraph(cwd); } catch (_) {}
23963
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
+ }
23964
24227
  const results = rank(query, index, { topK, recencyBoost, cwd, callGraph: queryCallGraph });
23965
24228
  if (args.includes('--context')) {
23966
24229
  const miniCtx = buildMiniContext(results, cwd);