sigmap 8.31.0 → 8.32.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 CHANGED
@@ -10,6 +10,21 @@ Format: [Semantic Versioning](https://semver.org/)
10
10
 
11
11
  ---
12
12
 
13
+ ## [8.32.0] — 2026-09-12
14
+
15
+ ### Added
16
+ - A gated **JVM retrieval corpus** — 61 tasks mined from `spring-petclinic` (32) and `akka` (29), leak-checked so no query shares a stemmed token with its expected file's basename. It scores against *other* repos, which puts it outside the feedback loop that makes the `hard` split move whenever sigmap's own source changes. `scripts/mine-corpus.mjs` gained `--repo` and gates its sigmap-specific rules on the target being sigmap itself, with byte-identical default output (#577, closes #575)
17
+ - `test/integration/docs-markdown.test.js` — guards the VitePress sources against the two failure modes that can break the docs build *after* a release is already tagged: unbalanced code fences, and `{{ }}` outside a fenced block, which Vue parses as an interpolation (#574, closes #573)
18
+
19
+ ### Fixed
20
+ - Every extractor now **discloses what a ceiling dropped** instead of truncating silently. 20 extractors gained `… +N more` markers on their member and per-file caps (23 now carry them in total). The ceilings themselves are unchanged — raising them is a separate, measured decision (#578, #576)
21
+ - The retrieval gate reused a gitignored index, so a "regression" could be pure staleness — this produced three separate false investigations, including one re-baseline. It now regenerates every index it scores, including one per JVM repo driven from `benchmarks/config-overrides.json` rather than a hand-written config (#579)
22
+ - The `sigmap lines` CLI example nested a fenced block inside a text fence; the inner fence closed early and a brace expression became a Vue interpolation. This failed the Pages build *after* v8.31.0 was tagged and published (#574)
23
+
24
+ ### Changed
25
+ - The `hard` corpus is now **reported but not enforced** against the previous run — only against its 70% floor. Proven necessary in CI: a probe branch containing one two-assertion test file and no source change scored 75.6% → 74.4% and failed the gate, because `hard` scores sigmap against its own source and BM25 statistics shift with the indexed file set. `mined` and `jvm` keep `--no-regress` (#579)
26
+ - JVM baseline re-recorded at 16.4% hit@5 (from 18.0%) — a single task. `akka:m018` expects `Logging.scala`, which holds a class the 8-member ceiling truncates, so disclosure adds one `… +N more methods` line and BM25's length normalisation drops it from rank 5 to 6. A fix excluding markers from the scored term space did not move the number and was reverted rather than left in as unexplained complexity (#578)
27
+
13
28
  ## [8.31.0] — 2026-09-08
14
29
 
15
30
  ### Added
package/README.md CHANGED
@@ -122,8 +122,8 @@ Ask → Rank → Context → Validate → Judge → Learn
122
122
 
123
123
  <!--SM:benchmarkBlock-->
124
124
  ```
125
- Benchmark : sigmap-v8.31-main (21 repositories, including R language)
126
- Date : 2026-09-08
125
+ Benchmark : sigmap-v8.32-main (21 repositories, including R language)
126
+ Date : 2026-09-12
127
127
 
128
128
  Hit@5 : 81.1% (grep-agent baseline 44.0% — 1.73× lift)
129
129
  Token reduction: 96.8% (across 21 repos)
package/gen-context.js CHANGED
@@ -5439,6 +5439,14 @@ __factories["./src/extractors/coverage"] = function(module, exports) {
5439
5439
  // ── ./src/extractors/cpp ──
5440
5440
  __factories["./src/extractors/cpp"] = function(module, exports) {
5441
5441
 
5442
+ const { capWithNotice, capMembersWithNotice } = __require('./src/util/truncate');
5443
+
5444
+ // Ceilings sit above the default `maxSigsPerFile` so the configured budget
5445
+ // governs output rather than a literal buried here, and omissions are disclosed
5446
+ // — an undisclosed cap looks like a class that simply has eight methods (#576).
5447
+ const MEMBER_LIMIT = 8;
5448
+ const PER_FILE_LIMIT = 25;
5449
+
5442
5450
  /**
5443
5451
  * Extract signatures from C/C++ source code.
5444
5452
  * @param {string} src - Raw file content
@@ -5469,7 +5477,7 @@ __factories["./src/extractors/cpp"] = function(module, exports) {
5469
5477
  sigs.push(`${m[2]}(${normalizeParams(m[3])})${retStr}`);
5470
5478
  }
5471
5479
 
5472
- return sigs.slice(0, 25);
5480
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
5473
5481
  }
5474
5482
 
5475
5483
  function extractBlock(src, startIndex) {
@@ -5492,7 +5500,7 @@ __factories["./src/extractors/cpp"] = function(module, exports) {
5492
5500
  const retStr = ret ? ` → ${ret}` : '';
5493
5501
  members.push(`${m[2]}(${normalizeParams(m[3])})${retStr}`);
5494
5502
  }
5495
- return members.slice(0, 8);
5503
+ return capWithNotice(members, MEMBER_LIMIT, 'members');
5496
5504
  }
5497
5505
 
5498
5506
  function normalizeParams(params) {
@@ -5513,6 +5521,13 @@ __factories["./src/extractors/cpp"] = function(module, exports) {
5513
5521
  __factories["./src/extractors/csharp"] = function(module, exports) {
5514
5522
 
5515
5523
  const { lineAt, withAnchor } = __require('./src/extractors/line-anchor');
5524
+ const { capWithNotice, capMembersWithNotice } = __require('./src/util/truncate');
5525
+
5526
+ // Ceilings sit above the default `maxSigsPerFile` so the configured budget
5527
+ // governs output rather than a literal buried here, and omissions are disclosed
5528
+ // — an undisclosed cap looks like a class that simply has eight methods (#576).
5529
+ const MEMBER_LIMIT = 8;
5530
+ const PER_FILE_LIMIT = 25;
5516
5531
 
5517
5532
  /**
5518
5533
  * Extract signatures from C# source code.
@@ -5537,11 +5552,12 @@ __factories["./src/extractors/csharp"] = function(module, exports) {
5537
5552
  const block = extractBlock(stripped, bodyStart);
5538
5553
  sigs.push(withAnchor(`${m[1]} ${m[2]}`, lineAt(stripped, declIdx), lineAt(stripped, bodyStart + block.length)));
5539
5554
  for (const meth of extractMembers(block)) {
5540
- sigs.push(withAnchor(` ${meth.text}`, lineAt(stripped, bodyStart + meth.declIdx), lineAt(stripped, bodyStart + meth.endIdx)));
5555
+ // The disclosure marker carries no offsets; anchor it at the class body.
5556
+ sigs.push(withAnchor(` ${meth.text}`, lineAt(stripped, bodyStart + (meth.declIdx || 0)), lineAt(stripped, bodyStart + (meth.endIdx || 0))));
5541
5557
  }
5542
5558
  }
5543
5559
 
5544
- return sigs.slice(0, 25);
5560
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
5545
5561
  }
5546
5562
 
5547
5563
  function extractBlock(src, startIndex) {
@@ -5567,7 +5583,7 @@ __factories["./src/extractors/csharp"] = function(module, exports) {
5567
5583
  endIdx: m.index + m[0].length,
5568
5584
  });
5569
5585
  }
5570
- return members.slice(0, 8);
5586
+ return capMembersWithNotice(members, MEMBER_LIMIT);
5571
5587
  }
5572
5588
 
5573
5589
  function normalizeParams(params) {
@@ -5587,6 +5603,12 @@ __factories["./src/extractors/csharp"] = function(module, exports) {
5587
5603
  // ── ./src/extractors/css ──
5588
5604
  __factories["./src/extractors/css"] = function(module, exports) {
5589
5605
 
5606
+ const { capWithNotice } = __require('./src/util/truncate');
5607
+
5608
+ // Ceiling sits above the default `maxSigsPerFile` so the configured budget
5609
+ // governs output rather than a literal buried here, and omissions are disclosed (#576).
5610
+ const PER_FILE_LIMIT = 25;
5611
+
5590
5612
  /**
5591
5613
  * Extract signatures from CSS/SCSS/SASS/Less source code.
5592
5614
  * @param {string} src - Raw file content
@@ -5650,7 +5672,7 @@ __factories["./src/extractors/css"] = function(module, exports) {
5650
5672
  for (const name of selected) sigs.push(`.${name}`);
5651
5673
  }
5652
5674
 
5653
- return sigs.slice(0, 25);
5675
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
5654
5676
  }
5655
5677
 
5656
5678
  module.exports = { extract };
@@ -5661,6 +5683,13 @@ __factories["./src/extractors/css"] = function(module, exports) {
5661
5683
  __factories["./src/extractors/dart"] = function(module, exports) {
5662
5684
 
5663
5685
  const { lineAt, withAnchor } = __require('./src/extractors/line-anchor');
5686
+ const { capWithNotice, capMembersWithNotice } = __require('./src/util/truncate');
5687
+
5688
+ // Ceilings sit above the default `maxSigsPerFile` so the configured budget
5689
+ // governs output rather than a literal buried here, and omissions are disclosed
5690
+ // — an undisclosed cap looks like a class that simply has eight methods (#576).
5691
+ const MEMBER_LIMIT = 8;
5692
+ const PER_FILE_LIMIT = 25;
5664
5693
 
5665
5694
  /**
5666
5695
  * Extract signatures from Dart source code.
@@ -5696,7 +5725,8 @@ __factories["./src/extractors/dart"] = function(module, exports) {
5696
5725
  const block = extractBlock(stripped, bodyStart);
5697
5726
  sigs.push(withAnchor(`${abs}class ${m[1]}`, lineAt(stripped, m.index), lineAt(stripped, bodyStart + block.length)));
5698
5727
  for (const meth of extractMembers(block)) {
5699
- sigs.push(withAnchor(` ${meth.text}`, lineAt(stripped, bodyStart + meth.declIdx), lineAt(stripped, bodyStart + meth.endIdx)));
5728
+ // The disclosure marker carries no offsets; anchor it at the class body.
5729
+ sigs.push(withAnchor(` ${meth.text}`, lineAt(stripped, bodyStart + (meth.declIdx || 0)), lineAt(stripped, bodyStart + (meth.endIdx || 0))));
5700
5730
  }
5701
5731
  }
5702
5732
 
@@ -5708,7 +5738,7 @@ __factories["./src/extractors/dart"] = function(module, exports) {
5708
5738
  sigs.push(withAnchor(`${m[2]}(${normalizeParams(m[3])})${retStr}`, s, e));
5709
5739
  }
5710
5740
 
5711
- return sigs.slice(0, 25);
5741
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
5712
5742
  }
5713
5743
 
5714
5744
  function extractBlock(src, startIndex) {
@@ -5733,7 +5763,7 @@ __factories["./src/extractors/dart"] = function(module, exports) {
5733
5763
  endIdx: m.index + m[0].length,
5734
5764
  });
5735
5765
  }
5736
- return members.slice(0, 8);
5766
+ return capMembersWithNotice(members, MEMBER_LIMIT);
5737
5767
  }
5738
5768
 
5739
5769
  function normalizeParams(params) {
@@ -5978,6 +6008,12 @@ __factories["./src/extractors/dispatch"] = function(module, exports) {
5978
6008
  // ── ./src/extractors/dockerfile ──
5979
6009
  __factories["./src/extractors/dockerfile"] = function(module, exports) {
5980
6010
 
6011
+ const { capWithNotice } = __require('./src/util/truncate');
6012
+
6013
+ // Ceiling sits above the default `maxSigsPerFile` so the configured budget
6014
+ // governs output rather than a literal buried here, and omissions are disclosed (#576).
6015
+ const PER_FILE_LIMIT = 25;
6016
+
5981
6017
  /**
5982
6018
  * Extract signatures from Dockerfiles.
5983
6019
  * @param {string} src - Raw file content
@@ -6021,7 +6057,7 @@ __factories["./src/extractors/dockerfile"] = function(module, exports) {
6021
6057
  if (m) sigs.push(`ARG ${m[1]}`);
6022
6058
  }
6023
6059
 
6024
- return sigs.slice(0, 25);
6060
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
6025
6061
  }
6026
6062
 
6027
6063
  module.exports = { extract };
@@ -6031,6 +6067,16 @@ __factories["./src/extractors/dockerfile"] = function(module, exports) {
6031
6067
  // ── ./src/extractors/gdscript ──
6032
6068
  __factories["./src/extractors/gdscript"] = function(module, exports) {
6033
6069
 
6070
+ const { capWithNotice } = __require('./src/util/truncate');
6071
+
6072
+ // Ceiling sits above the default `maxSigsPerFile` so the configured budget
6073
+ // governs output rather than a literal buried here, and omissions are disclosed (#576).
6074
+ const PER_FILE_LIMIT = 25;
6075
+
6076
+ // Ceilings disclose what they drop rather than truncating silently (#576).
6077
+ const MEMBER_LIMIT = 6;
6078
+ const ENUM_LIMIT = 24;
6079
+
6034
6080
  /**
6035
6081
  * Extract signatures from Godot GDScript source code.
6036
6082
  * @param {string} src - Raw file content
@@ -6075,7 +6121,7 @@ __factories["./src/extractors/gdscript"] = function(module, exports) {
6075
6121
  .split(',')
6076
6122
  .map((s) => s.trim().split(/\s*=/)[0].trim())
6077
6123
  .filter(Boolean);
6078
- sigs.push(`${indent}enum ${m[1]} { ${members.slice(0, 6).join(', ')} }`);
6124
+ sigs.push(`${indent}enum ${m[1]} { ${capWithNotice(members, ENUM_LIMIT, 'values').join(', ')} }`);
6079
6125
  }
6080
6126
 
6081
6127
  let constCount = 0;
@@ -6123,7 +6169,7 @@ __factories["./src/extractors/gdscript"] = function(module, exports) {
6123
6169
  }
6124
6170
  }
6125
6171
 
6126
- return sigs.slice(0, 25);
6172
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
6127
6173
  }
6128
6174
 
6129
6175
  function extractInnerMembers(stripped, startIndex) {
@@ -6141,7 +6187,7 @@ __factories["./src/extractors/gdscript"] = function(module, exports) {
6141
6187
  members.push(`${staticKw}func ${fm[2]}(${params})${retStr}`);
6142
6188
  }
6143
6189
  }
6144
- return members.slice(0, 6);
6190
+ return capWithNotice(members, MEMBER_LIMIT, 'members');
6145
6191
  }
6146
6192
 
6147
6193
  function normalizeParams(params) {
@@ -6198,6 +6244,11 @@ __factories["./src/extractors/generic"] = function(module, exports) {
6198
6244
  __factories["./src/extractors/go"] = function(module, exports) {
6199
6245
 
6200
6246
  const { lineAt, withAnchor } = __require('./src/extractors/line-anchor');
6247
+ const { capWithNotice } = __require('./src/util/truncate');
6248
+
6249
+ // Ceiling sits above the default `maxSigsPerFile` so the configured budget
6250
+ // governs output rather than a literal buried here, and omissions are disclosed (#576).
6251
+ const PER_FILE_LIMIT = 25;
6201
6252
 
6202
6253
  /**
6203
6254
  * Extract signatures from Go source code.
@@ -6246,7 +6297,7 @@ __factories["./src/extractors/go"] = function(module, exports) {
6246
6297
  sigs.push(hinted(withAnchor(`func ${receiver}${m[2]}(${normalizeParams(m[3])})${retStr}`, lineAt(stripped, m.index), lineAt(stripped, end)), m[2]));
6247
6298
  }
6248
6299
 
6249
- return sigs.slice(0, 25);
6300
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
6250
6301
  }
6251
6302
 
6252
6303
  function extractBlock(src, startIndex) {
@@ -6380,6 +6431,12 @@ __factories["./src/extractors/graphql"] = function(module, exports) {
6380
6431
  // ── ./src/extractors/html ──
6381
6432
  __factories["./src/extractors/html"] = function(module, exports) {
6382
6433
 
6434
+ const { capWithNotice } = __require('./src/util/truncate');
6435
+
6436
+ // Ceiling sits above the default `maxSigsPerFile` so the configured budget
6437
+ // governs output rather than a literal buried here, and omissions are disclosed (#576).
6438
+ const PER_FILE_LIMIT = 25;
6439
+
6383
6440
  /**
6384
6441
  * Extract signatures from HTML files.
6385
6442
  * Focuses on id/class attributes, forms, and script tags.
@@ -6413,7 +6470,7 @@ __factories["./src/extractors/html"] = function(module, exports) {
6413
6470
  sigs.push(`data-${m[0].match(/data-(\w[\w-]*)/i)[1]}: ${m[1]}`);
6414
6471
  }
6415
6472
 
6416
- return sigs.slice(0, 25);
6473
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
6417
6474
  }
6418
6475
 
6419
6476
  module.exports = { extract };
@@ -6772,6 +6829,13 @@ __factories["./src/extractors/javascript"] = function(module, exports) {
6772
6829
  __factories["./src/extractors/kotlin"] = function(module, exports) {
6773
6830
 
6774
6831
  const { lineAt, withAnchor } = __require('./src/extractors/line-anchor');
6832
+ const { capWithNotice, capMembersWithNotice } = __require('./src/util/truncate');
6833
+
6834
+ // Ceilings sit above the default `maxSigsPerFile` so the configured budget
6835
+ // governs output rather than a literal buried here, and omissions are disclosed
6836
+ // — an undisclosed cap looks like a class that simply has eight methods (#576).
6837
+ const MEMBER_LIMIT = 8;
6838
+ const PER_FILE_LIMIT = 25;
6775
6839
 
6776
6840
  /**
6777
6841
  * Extract signatures from Kotlin source code.
@@ -6806,7 +6870,8 @@ __factories["./src/extractors/kotlin"] = function(module, exports) {
6806
6870
  const block = extractBlock(stripped, bodyStart);
6807
6871
  sigs.push(withAnchor(`${m[1]} ${m[2]}`, lineAt(stripped, m.index), lineAt(stripped, bodyStart + block.length)));
6808
6872
  for (const meth of extractMembers(block)) {
6809
- sigs.push(withAnchor(` ${meth.text}`, lineAt(stripped, bodyStart + meth.declIdx), lineAt(stripped, bodyStart + meth.endIdx)));
6873
+ // The disclosure marker carries no offsets; anchor it at the class body.
6874
+ sigs.push(withAnchor(` ${meth.text}`, lineAt(stripped, bodyStart + (meth.declIdx || 0)), lineAt(stripped, bodyStart + (meth.endIdx || 0))));
6810
6875
  }
6811
6876
  }
6812
6877
 
@@ -6819,7 +6884,7 @@ __factories["./src/extractors/kotlin"] = function(module, exports) {
6819
6884
  sigs.push(withAnchor(`${suspend}fun ${m[1]}(${normalizeParams(m[2])})${retStr}`, s, e));
6820
6885
  }
6821
6886
 
6822
- return sigs.slice(0, 25);
6887
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
6823
6888
  }
6824
6889
 
6825
6890
  function extractBlock(src, startIndex) {
@@ -6846,7 +6911,7 @@ __factories["./src/extractors/kotlin"] = function(module, exports) {
6846
6911
  endIdx: m.index + m[0].length,
6847
6912
  });
6848
6913
  }
6849
- return members.slice(0, 8);
6914
+ return capMembersWithNotice(members, MEMBER_LIMIT);
6850
6915
  }
6851
6916
 
6852
6917
  function normalizeParams(params) {
@@ -7095,6 +7160,13 @@ __factories["./src/extractors/patterns"] = function(module, exports) {
7095
7160
  __factories["./src/extractors/php"] = function(module, exports) {
7096
7161
 
7097
7162
  const { lineAt, withAnchor } = __require('./src/extractors/line-anchor');
7163
+ const { capWithNotice, capMembersWithNotice } = __require('./src/util/truncate');
7164
+
7165
+ // Ceilings sit above the default `maxSigsPerFile` so the configured budget
7166
+ // governs output rather than a literal buried here, and omissions are disclosed
7167
+ // — an undisclosed cap looks like a class that simply has eight methods (#576).
7168
+ const MEMBER_LIMIT = 8;
7169
+ const PER_FILE_LIMIT = 25;
7098
7170
 
7099
7171
  /**
7100
7172
  * Extract signatures from PHP source code.
@@ -7134,7 +7206,8 @@ __factories["./src/extractors/php"] = function(module, exports) {
7134
7206
  const block = extractBlock(stripped, bodyStart);
7135
7207
  sigs.push(withAnchor(`${kind} ${m[1]}`, lineAt(stripped, m.index), lineAt(stripped, bodyStart + block.length)));
7136
7208
  for (const meth of extractMembers(block)) {
7137
- sigs.push(withAnchor(` ${meth.text}`, lineAt(stripped, bodyStart + meth.declIdx), lineAt(stripped, bodyStart + meth.endIdx)));
7209
+ // The disclosure marker carries no offsets; anchor it at the class body.
7210
+ sigs.push(withAnchor(` ${meth.text}`, lineAt(stripped, bodyStart + (meth.declIdx || 0)), lineAt(stripped, bodyStart + (meth.endIdx || 0))));
7138
7211
  }
7139
7212
  }
7140
7213
 
@@ -7146,7 +7219,7 @@ __factories["./src/extractors/php"] = function(module, exports) {
7146
7219
  sigs.push(withAnchor(`function ${m[1]}(${normalizeParams(m[2])})${retStr}`, s, e));
7147
7220
  }
7148
7221
 
7149
- return sigs.slice(0, 25);
7222
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
7150
7223
  }
7151
7224
 
7152
7225
  function extractBlock(src, startIndex) {
@@ -7174,7 +7247,7 @@ __factories["./src/extractors/php"] = function(module, exports) {
7174
7247
  endIdx: m.index + m[0].length,
7175
7248
  });
7176
7249
  }
7177
- return members.slice(0, 8);
7250
+ return capMembersWithNotice(members, MEMBER_LIMIT);
7178
7251
  }
7179
7252
 
7180
7253
  function normalizeParams(params) {
@@ -7378,6 +7451,11 @@ __factories["./src/extractors/python"] = function(module, exports) {
7378
7451
 
7379
7452
  const path = require('path');
7380
7453
  const { lineAt } = __require('./src/extractors/line-anchor');
7454
+ const { capWithNotice } = __require('./src/util/truncate');
7455
+
7456
+ // Ceiling sits above the default `maxSigsPerFile` so the configured budget
7457
+ // governs output rather than a literal buried here, and omissions are disclosed (#576).
7458
+ const PER_FILE_LIMIT = 30;
7381
7459
 
7382
7460
  /**
7383
7461
  * 1-based line of the last source line belonging to a top-level (indent 0)
@@ -7512,7 +7590,7 @@ __factories["./src/extractors/python"] = function(module, exports) {
7512
7590
  }
7513
7591
  }
7514
7592
 
7515
- return sigs.slice(0, 30);
7593
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
7516
7594
  }
7517
7595
 
7518
7596
  function extractClassMethods(stripped, startIndex) {
@@ -7723,6 +7801,12 @@ __factories["./src/extractors/python_dataclass"] = function(module, exports) {
7723
7801
  // ── ./src/extractors/r ──
7724
7802
  __factories["./src/extractors/r"] = function(module, exports) {
7725
7803
 
7804
+ const { capWithNotice } = __require('./src/util/truncate');
7805
+
7806
+ // Ceiling sits above the default `maxSigsPerFile` so the configured budget
7807
+ // governs output rather than a literal buried here, and omissions are disclosed (#576).
7808
+ const PER_FILE_LIMIT = 30;
7809
+
7726
7810
  /**
7727
7811
  * Extract signatures from R source code.
7728
7812
  *
@@ -7836,7 +7920,7 @@ __factories["./src/extractors/r"] = function(module, exports) {
7836
7920
  sigs.push(`setClass("${sm[1]}")`);
7837
7921
  }
7838
7922
 
7839
- return sigs.slice(0, 30);
7923
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
7840
7924
  }
7841
7925
 
7842
7926
  /**
@@ -8000,6 +8084,12 @@ __factories["./src/extractors/r"] = function(module, exports) {
8000
8084
  // ── ./src/extractors/ruby ──
8001
8085
  __factories["./src/extractors/ruby"] = function(module, exports) {
8002
8086
 
8087
+ const { capWithNotice } = __require('./src/util/truncate');
8088
+
8089
+ // Ceiling sits above the default `maxSigsPerFile` so the configured budget
8090
+ // governs output rather than a literal buried here, and omissions are disclosed (#576).
8091
+ const PER_FILE_LIMIT = 25;
8092
+
8003
8093
  /**
8004
8094
  * Extract signatures from Ruby source code.
8005
8095
  * @param {string} src - Raw file content
@@ -8034,7 +8124,7 @@ __factories["./src/extractors/ruby"] = function(module, exports) {
8034
8124
  sigs.push(`def ${m[1]}${params}${retStr}`);
8035
8125
  }
8036
8126
 
8037
- return sigs.slice(0, 25);
8127
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
8038
8128
  }
8039
8129
 
8040
8130
  function normalizeParams(params) {
@@ -8059,6 +8149,11 @@ __factories["./src/extractors/ruby"] = function(module, exports) {
8059
8149
  __factories["./src/extractors/rust"] = function(module, exports) {
8060
8150
 
8061
8151
  const { lineAt, withAnchor } = __require('./src/extractors/line-anchor');
8152
+ const { capWithNotice } = __require('./src/util/truncate');
8153
+
8154
+ // Ceiling sits above the default `maxSigsPerFile` so the configured budget
8155
+ // governs output rather than a literal buried here, and omissions are disclosed (#576).
8156
+ const PER_FILE_LIMIT = 25;
8062
8157
 
8063
8158
  /**
8064
8159
  * Extract signatures from Rust source code.
@@ -8128,7 +8223,7 @@ __factories["./src/extractors/rust"] = function(module, exports) {
8128
8223
  sigs.push(hinted(withAnchor(`pub ${asyncKw}fn ${m[1]}(${normalizeParams(m[2])})${retStr}`, s, e), m[1]));
8129
8224
  }
8130
8225
 
8131
- return sigs.slice(0, 25);
8226
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
8132
8227
  }
8133
8228
 
8134
8229
  function extractBlock(src, startIndex) {
@@ -8201,6 +8296,13 @@ __factories["./src/extractors/rust"] = function(module, exports) {
8201
8296
  __factories["./src/extractors/scala"] = function(module, exports) {
8202
8297
 
8203
8298
  const { lineAt, withAnchor } = __require('./src/extractors/line-anchor');
8299
+ const { capWithNotice, capMembersWithNotice } = __require('./src/util/truncate');
8300
+
8301
+ // Ceilings sit above the default `maxSigsPerFile` so the configured budget
8302
+ // governs output rather than a literal buried here, and omissions are disclosed
8303
+ // — an undisclosed cap looks like a class that simply has eight methods (#576).
8304
+ const MEMBER_LIMIT = 8;
8305
+ const PER_FILE_LIMIT = 25;
8204
8306
 
8205
8307
  /**
8206
8308
  * Extract signatures from Scala source code.
@@ -8227,7 +8329,8 @@ __factories["./src/extractors/scala"] = function(module, exports) {
8227
8329
  const block = extractBlock(stripped, bodyStart);
8228
8330
  sigs.push(withAnchor(`${kind} ${m[1]}`, lineAt(stripped, m.index), lineAt(stripped, bodyStart + block.length)));
8229
8331
  for (const fn of extractMembers(block)) {
8230
- sigs.push(withAnchor(` ${fn.text}`, lineAt(stripped, bodyStart + fn.declIdx), lineAt(stripped, bodyStart + fn.endIdx)));
8332
+ // The disclosure marker carries no offsets; anchor it at the class body.
8333
+ sigs.push(withAnchor(` ${fn.text}`, lineAt(stripped, bodyStart + (fn.declIdx || 0)), lineAt(stripped, bodyStart + (fn.endIdx || 0))));
8231
8334
  }
8232
8335
  }
8233
8336
 
@@ -8241,7 +8344,7 @@ __factories["./src/extractors/scala"] = function(module, exports) {
8241
8344
  sigs.push(withAnchor(`def ${m[1]}${params}${retStr}`, line, line));
8242
8345
  }
8243
8346
 
8244
- return sigs.slice(0, 25);
8347
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
8245
8348
  }
8246
8349
 
8247
8350
  function extractBlock(src, startIndex) {
@@ -8268,7 +8371,7 @@ __factories["./src/extractors/scala"] = function(module, exports) {
8268
8371
  endIdx: m.index + m[0].length,
8269
8372
  });
8270
8373
  }
8271
- return members.slice(0, 8);
8374
+ return capMembersWithNotice(members, MEMBER_LIMIT);
8272
8375
  }
8273
8376
 
8274
8377
  function normalizeParams(params) {
@@ -8387,6 +8490,12 @@ __factories["./src/extractors/scan"] = function(module, exports) {
8387
8490
  // ── ./src/extractors/shell ──
8388
8491
  __factories["./src/extractors/shell"] = function(module, exports) {
8389
8492
 
8493
+ const { capWithNotice } = __require('./src/util/truncate');
8494
+
8495
+ // Ceiling sits above the default `maxSigsPerFile` so the configured budget
8496
+ // governs output rather than a literal buried here, and omissions are disclosed (#576).
8497
+ const PER_FILE_LIMIT = 25;
8498
+
8390
8499
  /**
8391
8500
  * Extract signatures from shell scripts (bash, zsh, fish).
8392
8501
  * @param {string} src - Raw file content
@@ -8424,7 +8533,7 @@ __factories["./src/extractors/shell"] = function(module, exports) {
8424
8533
  }
8425
8534
  }
8426
8535
 
8427
- return sigs.slice(0, 25);
8536
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
8428
8537
  }
8429
8538
 
8430
8539
  module.exports = { extract };
@@ -8531,6 +8640,12 @@ __factories["./src/extractors/sql"] = function(module, exports) {
8531
8640
  // ── ./src/extractors/svelte ──
8532
8641
  __factories["./src/extractors/svelte"] = function(module, exports) {
8533
8642
 
8643
+ const { capWithNotice } = __require('./src/util/truncate');
8644
+
8645
+ // Ceiling sits above the default `maxSigsPerFile` so the configured budget
8646
+ // governs output rather than a literal buried here, and omissions are disclosed (#576).
8647
+ const PER_FILE_LIMIT = 25;
8648
+
8534
8649
  /**
8535
8650
  * Extract signatures from Svelte components.
8536
8651
  * @param {string} src - Raw file content
@@ -8573,7 +8688,7 @@ __factories["./src/extractors/svelte"] = function(module, exports) {
8573
8688
  sigs.push(`$: ${m[1]}`);
8574
8689
  }
8575
8690
 
8576
- return sigs.slice(0, 25);
8691
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
8577
8692
  }
8578
8693
 
8579
8694
  function normalizeParams(params) {
@@ -8594,6 +8709,13 @@ __factories["./src/extractors/svelte"] = function(module, exports) {
8594
8709
  __factories["./src/extractors/swift"] = function(module, exports) {
8595
8710
 
8596
8711
  const { lineAt, withAnchor } = __require('./src/extractors/line-anchor');
8712
+ const { capWithNotice, capMembersWithNotice } = __require('./src/util/truncate');
8713
+
8714
+ // Ceilings sit above the default `maxSigsPerFile` so the configured budget
8715
+ // governs output rather than a literal buried here, and omissions are disclosed
8716
+ // — an undisclosed cap looks like a class that simply has eight methods (#576).
8717
+ const MEMBER_LIMIT = 8;
8718
+ const PER_FILE_LIMIT = 25;
8597
8719
 
8598
8720
  /**
8599
8721
  * Extract signatures from Swift source code.
@@ -8629,7 +8751,8 @@ __factories["./src/extractors/swift"] = function(module, exports) {
8629
8751
  const block = extractBlock(stripped, bodyStart);
8630
8752
  sigs.push(withAnchor(`${m[1]} ${m[2]}`, lineAt(stripped, m.index), lineAt(stripped, bodyStart + block.length)));
8631
8753
  for (const fn of extractMembers(block)) {
8632
- sigs.push(withAnchor(` ${fn.text}`, lineAt(stripped, bodyStart + fn.declIdx), lineAt(stripped, bodyStart + fn.endIdx)));
8754
+ // The disclosure marker carries no offsets; anchor it at the class body.
8755
+ sigs.push(withAnchor(` ${fn.text}`, lineAt(stripped, bodyStart + (fn.declIdx || 0)), lineAt(stripped, bodyStart + (fn.endIdx || 0))));
8633
8756
  }
8634
8757
  }
8635
8758
 
@@ -8641,7 +8764,7 @@ __factories["./src/extractors/swift"] = function(module, exports) {
8641
8764
  sigs.push(withAnchor(`${asyncKw}func ${m[1]}(${normalizeParams(m[2])})${retStr}`, s, e));
8642
8765
  }
8643
8766
 
8644
- return sigs.slice(0, 25);
8767
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
8645
8768
  }
8646
8769
 
8647
8770
  function extractBlock(src, startIndex) {
@@ -8667,7 +8790,7 @@ __factories["./src/extractors/swift"] = function(module, exports) {
8667
8790
  endIdx: m.index + m[0].length,
8668
8791
  });
8669
8792
  }
8670
- return members.slice(0, 8);
8793
+ return capMembersWithNotice(members, MEMBER_LIMIT);
8671
8794
  }
8672
8795
 
8673
8796
  function normalizeParams(params) {
@@ -9242,6 +9365,12 @@ __factories["./src/extractors/typescript_react"] = function(module, exports) {
9242
9365
  // ── ./src/extractors/vue ──
9243
9366
  __factories["./src/extractors/vue"] = function(module, exports) {
9244
9367
 
9368
+ const { capWithNotice } = __require('./src/util/truncate');
9369
+
9370
+ // Ceiling sits above the default `maxSigsPerFile` so the configured budget
9371
+ // governs output rather than a literal buried here, and omissions are disclosed (#576).
9372
+ const PER_FILE_LIMIT = 25;
9373
+
9245
9374
  /**
9246
9375
  * Extract signatures from Vue single-file components.
9247
9376
  * @param {string} src - Raw file content
@@ -9306,7 +9435,7 @@ __factories["./src/extractors/vue"] = function(module, exports) {
9306
9435
  const emitsMatch = script.match(/(?:defineEmits|emits)\s*(?::\s*|\(\s*)(\[[\s\S]*?\])/);
9307
9436
  if (emitsMatch) sigs.push(`emits: ${emitsMatch[1].replace(/\s+/g, ' ')}`);
9308
9437
 
9309
- return sigs.slice(0, 25);
9438
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
9310
9439
  }
9311
9440
 
9312
9441
  function normalizeParams(params) {
@@ -9479,6 +9608,12 @@ __factories["./src/extractors/xml"] = function(module, exports) {
9479
9608
  // ── ./src/extractors/yaml ──
9480
9609
  __factories["./src/extractors/yaml"] = function(module, exports) {
9481
9610
 
9611
+ const { capWithNotice } = __require('./src/util/truncate');
9612
+
9613
+ // Ceiling sits above the default `maxSigsPerFile` so the configured budget
9614
+ // governs output rather than a literal buried here, and omissions are disclosed (#576).
9615
+ const PER_FILE_LIMIT = 25;
9616
+
9482
9617
  /**
9483
9618
  * Extract signatures from YAML configuration files.
9484
9619
  * @param {string} src - Raw file content
@@ -9532,7 +9667,7 @@ __factories["./src/extractors/yaml"] = function(module, exports) {
9532
9667
  }
9533
9668
  }
9534
9669
 
9535
- return sigs.slice(0, 25);
9670
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
9536
9671
  }
9537
9672
 
9538
9673
  module.exports = { extract };
@@ -15679,7 +15814,7 @@ __factories["./src/mcp/server"] = function(module, exports) {
15679
15814
 
15680
15815
  const SERVER_INFO = {
15681
15816
  name: 'sigmap',
15682
- version: '8.31.0',
15817
+ version: '8.32.0',
15683
15818
  description: 'SigMap MCP server — code signatures on demand',
15684
15819
  };
15685
15820
 
@@ -21973,7 +22108,7 @@ function __tryGit(args, opts = {}) {
21973
22108
  catch (_) { return ''; }
21974
22109
  }
21975
22110
 
21976
- const VERSION = '8.31.0';
22111
+ const VERSION = '8.32.0';
21977
22112
  const MARKER = '\n\n## Auto-generated signatures\n<!-- Updated by gen-context.js -->\n';
21978
22113
 
21979
22114
  function requireSourceOrBundled(key) {
package/llms-full.txt CHANGED
@@ -11,13 +11,13 @@ 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.31.0 | Benchmark: sigmap-v8.31-main (2026-09-08)
14
+ # Version: 8.32.0 | Benchmark: sigmap-v8.32-main (2026-09-12)
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.31-main, 2026-09-08)
20
+ ## Core metrics (benchmark: sigmap-v8.32-main, 2026-09-12)
21
21
 
22
22
  | Metric | Without SigMap | With SigMap |
23
23
  |--------|----------------|-------------|
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.31.0 | Benchmark: sigmap-v8.31-main (2026-09-08)
14
+ # Version: 8.32.0 | Benchmark: sigmap-v8.32-main (2026-09-12)
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,7 +23,7 @@ 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.31-main, 2026-09-08)
26
+ ## Core metrics (benchmark: sigmap-v8.32-main, 2026-09-12)
27
27
 
28
28
  - hit@5 retrieval: 81.1% vs 44.0% single-shot grep baseline (1.73× lift)
29
29
  - Token reduction: 96.8% average across benchmark repos
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sigmap",
3
- "version": "8.31.0",
3
+ "version": "8.32.0",
4
4
  "description": "The deterministic, verifiable grounding layer for AI code work — a zero-dependency signature-and-evidence map that grounds Claude, Cursor, Copilot, Aider, Windsurf, local LLMs & MCP agents against your real code (repo + installed libraries) so they stop hallucinating files, imports & APIs. Runs offline via npx; byte-stable output; ~97% token reduction as proof.",
5
5
  "main": "packages/core/index.js",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sigmap-cli",
3
- "version": "8.31.0",
3
+ "version": "8.32.0",
4
4
  "description": "SigMap CLI wrapper — thin adapter for programmatic CLI invocation",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sigmap-core",
3
- "version": "8.31.0",
3
+ "version": "8.32.0",
4
4
  "description": "SigMap core library — zero-dependency code signature extraction, retrieval, and security scanning",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -1,5 +1,13 @@
1
1
  'use strict';
2
2
 
3
+ const { capWithNotice, capMembersWithNotice } = require('../util/truncate');
4
+
5
+ // Ceilings sit above the default `maxSigsPerFile` so the configured budget
6
+ // governs output rather than a literal buried here, and omissions are disclosed
7
+ // — an undisclosed cap looks like a class that simply has eight methods (#576).
8
+ const MEMBER_LIMIT = 8;
9
+ const PER_FILE_LIMIT = 25;
10
+
3
11
  /**
4
12
  * Extract signatures from C/C++ source code.
5
13
  * @param {string} src - Raw file content
@@ -30,7 +38,7 @@ function extract(src) {
30
38
  sigs.push(`${m[2]}(${normalizeParams(m[3])})${retStr}`);
31
39
  }
32
40
 
33
- return sigs.slice(0, 25);
41
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
34
42
  }
35
43
 
36
44
  function extractBlock(src, startIndex) {
@@ -53,7 +61,7 @@ function extractMembers(block) {
53
61
  const retStr = ret ? ` → ${ret}` : '';
54
62
  members.push(`${m[2]}(${normalizeParams(m[3])})${retStr}`);
55
63
  }
56
- return members.slice(0, 8);
64
+ return capWithNotice(members, MEMBER_LIMIT, 'members');
57
65
  }
58
66
 
59
67
  function normalizeParams(params) {
@@ -1,6 +1,13 @@
1
1
  'use strict';
2
2
 
3
3
  const { lineAt, withAnchor } = require('./line-anchor');
4
+ const { capWithNotice, capMembersWithNotice } = require('../util/truncate');
5
+
6
+ // Ceilings sit above the default `maxSigsPerFile` so the configured budget
7
+ // governs output rather than a literal buried here, and omissions are disclosed
8
+ // — an undisclosed cap looks like a class that simply has eight methods (#576).
9
+ const MEMBER_LIMIT = 8;
10
+ const PER_FILE_LIMIT = 25;
4
11
 
5
12
  /**
6
13
  * Extract signatures from C# source code.
@@ -25,11 +32,12 @@ function extract(src) {
25
32
  const block = extractBlock(stripped, bodyStart);
26
33
  sigs.push(withAnchor(`${m[1]} ${m[2]}`, lineAt(stripped, declIdx), lineAt(stripped, bodyStart + block.length)));
27
34
  for (const meth of extractMembers(block)) {
28
- sigs.push(withAnchor(` ${meth.text}`, lineAt(stripped, bodyStart + meth.declIdx), lineAt(stripped, bodyStart + meth.endIdx)));
35
+ // The disclosure marker carries no offsets; anchor it at the class body.
36
+ sigs.push(withAnchor(` ${meth.text}`, lineAt(stripped, bodyStart + (meth.declIdx || 0)), lineAt(stripped, bodyStart + (meth.endIdx || 0))));
29
37
  }
30
38
  }
31
39
 
32
- return sigs.slice(0, 25);
40
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
33
41
  }
34
42
 
35
43
  function extractBlock(src, startIndex) {
@@ -55,7 +63,7 @@ function extractMembers(block) {
55
63
  endIdx: m.index + m[0].length,
56
64
  });
57
65
  }
58
- return members.slice(0, 8);
66
+ return capMembersWithNotice(members, MEMBER_LIMIT);
59
67
  }
60
68
 
61
69
  function normalizeParams(params) {
@@ -1,5 +1,11 @@
1
1
  'use strict';
2
2
 
3
+ const { capWithNotice } = require('../util/truncate');
4
+
5
+ // Ceiling sits above the default `maxSigsPerFile` so the configured budget
6
+ // governs output rather than a literal buried here, and omissions are disclosed (#576).
7
+ const PER_FILE_LIMIT = 25;
8
+
3
9
  /**
4
10
  * Extract signatures from CSS/SCSS/SASS/Less source code.
5
11
  * @param {string} src - Raw file content
@@ -63,7 +69,7 @@ function extract(src) {
63
69
  for (const name of selected) sigs.push(`.${name}`);
64
70
  }
65
71
 
66
- return sigs.slice(0, 25);
72
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
67
73
  }
68
74
 
69
75
  module.exports = { extract };
@@ -1,6 +1,13 @@
1
1
  'use strict';
2
2
 
3
3
  const { lineAt, withAnchor } = require('./line-anchor');
4
+ const { capWithNotice, capMembersWithNotice } = require('../util/truncate');
5
+
6
+ // Ceilings sit above the default `maxSigsPerFile` so the configured budget
7
+ // governs output rather than a literal buried here, and omissions are disclosed
8
+ // — an undisclosed cap looks like a class that simply has eight methods (#576).
9
+ const MEMBER_LIMIT = 8;
10
+ const PER_FILE_LIMIT = 25;
4
11
 
5
12
  /**
6
13
  * Extract signatures from Dart source code.
@@ -36,7 +43,8 @@ function extract(src) {
36
43
  const block = extractBlock(stripped, bodyStart);
37
44
  sigs.push(withAnchor(`${abs}class ${m[1]}`, lineAt(stripped, m.index), lineAt(stripped, bodyStart + block.length)));
38
45
  for (const meth of extractMembers(block)) {
39
- sigs.push(withAnchor(` ${meth.text}`, lineAt(stripped, bodyStart + meth.declIdx), lineAt(stripped, bodyStart + meth.endIdx)));
46
+ // The disclosure marker carries no offsets; anchor it at the class body.
47
+ sigs.push(withAnchor(` ${meth.text}`, lineAt(stripped, bodyStart + (meth.declIdx || 0)), lineAt(stripped, bodyStart + (meth.endIdx || 0))));
40
48
  }
41
49
  }
42
50
 
@@ -48,7 +56,7 @@ function extract(src) {
48
56
  sigs.push(withAnchor(`${m[2]}(${normalizeParams(m[3])})${retStr}`, s, e));
49
57
  }
50
58
 
51
- return sigs.slice(0, 25);
59
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
52
60
  }
53
61
 
54
62
  function extractBlock(src, startIndex) {
@@ -73,7 +81,7 @@ function extractMembers(block) {
73
81
  endIdx: m.index + m[0].length,
74
82
  });
75
83
  }
76
- return members.slice(0, 8);
84
+ return capMembersWithNotice(members, MEMBER_LIMIT);
77
85
  }
78
86
 
79
87
  function normalizeParams(params) {
@@ -1,5 +1,11 @@
1
1
  'use strict';
2
2
 
3
+ const { capWithNotice } = require('../util/truncate');
4
+
5
+ // Ceiling sits above the default `maxSigsPerFile` so the configured budget
6
+ // governs output rather than a literal buried here, and omissions are disclosed (#576).
7
+ const PER_FILE_LIMIT = 25;
8
+
3
9
  /**
4
10
  * Extract signatures from Dockerfiles.
5
11
  * @param {string} src - Raw file content
@@ -43,7 +49,7 @@ function extract(src) {
43
49
  if (m) sigs.push(`ARG ${m[1]}`);
44
50
  }
45
51
 
46
- return sigs.slice(0, 25);
52
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
47
53
  }
48
54
 
49
55
  module.exports = { extract };
@@ -1,5 +1,15 @@
1
1
  'use strict';
2
2
 
3
+ const { capWithNotice } = require('../util/truncate');
4
+
5
+ // Ceiling sits above the default `maxSigsPerFile` so the configured budget
6
+ // governs output rather than a literal buried here, and omissions are disclosed (#576).
7
+ const PER_FILE_LIMIT = 25;
8
+
9
+ // Ceilings disclose what they drop rather than truncating silently (#576).
10
+ const MEMBER_LIMIT = 6;
11
+ const ENUM_LIMIT = 24;
12
+
3
13
  /**
4
14
  * Extract signatures from Godot GDScript source code.
5
15
  * @param {string} src - Raw file content
@@ -44,7 +54,7 @@ function extract(src) {
44
54
  .split(',')
45
55
  .map((s) => s.trim().split(/\s*=/)[0].trim())
46
56
  .filter(Boolean);
47
- sigs.push(`${indent}enum ${m[1]} { ${members.slice(0, 6).join(', ')} }`);
57
+ sigs.push(`${indent}enum ${m[1]} { ${capWithNotice(members, ENUM_LIMIT, 'values').join(', ')} }`);
48
58
  }
49
59
 
50
60
  let constCount = 0;
@@ -92,7 +102,7 @@ function extract(src) {
92
102
  }
93
103
  }
94
104
 
95
- return sigs.slice(0, 25);
105
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
96
106
  }
97
107
 
98
108
  function extractInnerMembers(stripped, startIndex) {
@@ -110,7 +120,7 @@ function extractInnerMembers(stripped, startIndex) {
110
120
  members.push(`${staticKw}func ${fm[2]}(${params})${retStr}`);
111
121
  }
112
122
  }
113
- return members.slice(0, 6);
123
+ return capWithNotice(members, MEMBER_LIMIT, 'members');
114
124
  }
115
125
 
116
126
  function normalizeParams(params) {
@@ -1,6 +1,11 @@
1
1
  'use strict';
2
2
 
3
3
  const { lineAt, withAnchor } = require('./line-anchor');
4
+ const { capWithNotice } = require('../util/truncate');
5
+
6
+ // Ceiling sits above the default `maxSigsPerFile` so the configured budget
7
+ // governs output rather than a literal buried here, and omissions are disclosed (#576).
8
+ const PER_FILE_LIMIT = 25;
4
9
 
5
10
  /**
6
11
  * Extract signatures from Go source code.
@@ -49,7 +54,7 @@ function extract(src) {
49
54
  sigs.push(hinted(withAnchor(`func ${receiver}${m[2]}(${normalizeParams(m[3])})${retStr}`, lineAt(stripped, m.index), lineAt(stripped, end)), m[2]));
50
55
  }
51
56
 
52
- return sigs.slice(0, 25);
57
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
53
58
  }
54
59
 
55
60
  function extractBlock(src, startIndex) {
@@ -1,5 +1,11 @@
1
1
  'use strict';
2
2
 
3
+ const { capWithNotice } = require('../util/truncate');
4
+
5
+ // Ceiling sits above the default `maxSigsPerFile` so the configured budget
6
+ // governs output rather than a literal buried here, and omissions are disclosed (#576).
7
+ const PER_FILE_LIMIT = 25;
8
+
3
9
  /**
4
10
  * Extract signatures from HTML files.
5
11
  * Focuses on id/class attributes, forms, and script tags.
@@ -33,7 +39,7 @@ function extract(src) {
33
39
  sigs.push(`data-${m[0].match(/data-(\w[\w-]*)/i)[1]}: ${m[1]}`);
34
40
  }
35
41
 
36
- return sigs.slice(0, 25);
42
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
37
43
  }
38
44
 
39
45
  module.exports = { extract };
@@ -1,6 +1,13 @@
1
1
  'use strict';
2
2
 
3
3
  const { lineAt, withAnchor } = require('./line-anchor');
4
+ const { capWithNotice, capMembersWithNotice } = require('../util/truncate');
5
+
6
+ // Ceilings sit above the default `maxSigsPerFile` so the configured budget
7
+ // governs output rather than a literal buried here, and omissions are disclosed
8
+ // — an undisclosed cap looks like a class that simply has eight methods (#576).
9
+ const MEMBER_LIMIT = 8;
10
+ const PER_FILE_LIMIT = 25;
4
11
 
5
12
  /**
6
13
  * Extract signatures from Kotlin source code.
@@ -35,7 +42,8 @@ function extract(src) {
35
42
  const block = extractBlock(stripped, bodyStart);
36
43
  sigs.push(withAnchor(`${m[1]} ${m[2]}`, lineAt(stripped, m.index), lineAt(stripped, bodyStart + block.length)));
37
44
  for (const meth of extractMembers(block)) {
38
- sigs.push(withAnchor(` ${meth.text}`, lineAt(stripped, bodyStart + meth.declIdx), lineAt(stripped, bodyStart + meth.endIdx)));
45
+ // The disclosure marker carries no offsets; anchor it at the class body.
46
+ sigs.push(withAnchor(` ${meth.text}`, lineAt(stripped, bodyStart + (meth.declIdx || 0)), lineAt(stripped, bodyStart + (meth.endIdx || 0))));
39
47
  }
40
48
  }
41
49
 
@@ -48,7 +56,7 @@ function extract(src) {
48
56
  sigs.push(withAnchor(`${suspend}fun ${m[1]}(${normalizeParams(m[2])})${retStr}`, s, e));
49
57
  }
50
58
 
51
- return sigs.slice(0, 25);
59
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
52
60
  }
53
61
 
54
62
  function extractBlock(src, startIndex) {
@@ -75,7 +83,7 @@ function extractMembers(block) {
75
83
  endIdx: m.index + m[0].length,
76
84
  });
77
85
  }
78
- return members.slice(0, 8);
86
+ return capMembersWithNotice(members, MEMBER_LIMIT);
79
87
  }
80
88
 
81
89
  function normalizeParams(params) {
@@ -1,6 +1,13 @@
1
1
  'use strict';
2
2
 
3
3
  const { lineAt, withAnchor } = require('./line-anchor');
4
+ const { capWithNotice, capMembersWithNotice } = require('../util/truncate');
5
+
6
+ // Ceilings sit above the default `maxSigsPerFile` so the configured budget
7
+ // governs output rather than a literal buried here, and omissions are disclosed
8
+ // — an undisclosed cap looks like a class that simply has eight methods (#576).
9
+ const MEMBER_LIMIT = 8;
10
+ const PER_FILE_LIMIT = 25;
4
11
 
5
12
  /**
6
13
  * Extract signatures from PHP source code.
@@ -40,7 +47,8 @@ function extract(src) {
40
47
  const block = extractBlock(stripped, bodyStart);
41
48
  sigs.push(withAnchor(`${kind} ${m[1]}`, lineAt(stripped, m.index), lineAt(stripped, bodyStart + block.length)));
42
49
  for (const meth of extractMembers(block)) {
43
- sigs.push(withAnchor(` ${meth.text}`, lineAt(stripped, bodyStart + meth.declIdx), lineAt(stripped, bodyStart + meth.endIdx)));
50
+ // The disclosure marker carries no offsets; anchor it at the class body.
51
+ sigs.push(withAnchor(` ${meth.text}`, lineAt(stripped, bodyStart + (meth.declIdx || 0)), lineAt(stripped, bodyStart + (meth.endIdx || 0))));
44
52
  }
45
53
  }
46
54
 
@@ -52,7 +60,7 @@ function extract(src) {
52
60
  sigs.push(withAnchor(`function ${m[1]}(${normalizeParams(m[2])})${retStr}`, s, e));
53
61
  }
54
62
 
55
- return sigs.slice(0, 25);
63
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
56
64
  }
57
65
 
58
66
  function extractBlock(src, startIndex) {
@@ -80,7 +88,7 @@ function extractMembers(block) {
80
88
  endIdx: m.index + m[0].length,
81
89
  });
82
90
  }
83
- return members.slice(0, 8);
91
+ return capMembersWithNotice(members, MEMBER_LIMIT);
84
92
  }
85
93
 
86
94
  function normalizeParams(params) {
@@ -2,6 +2,11 @@
2
2
 
3
3
  const path = require('path');
4
4
  const { lineAt } = require('./line-anchor');
5
+ const { capWithNotice } = require('../util/truncate');
6
+
7
+ // Ceiling sits above the default `maxSigsPerFile` so the configured budget
8
+ // governs output rather than a literal buried here, and omissions are disclosed (#576).
9
+ const PER_FILE_LIMIT = 30;
5
10
 
6
11
  /**
7
12
  * 1-based line of the last source line belonging to a top-level (indent 0)
@@ -136,7 +141,7 @@ function extract(src, filePath) {
136
141
  }
137
142
  }
138
143
 
139
- return sigs.slice(0, 30);
144
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
140
145
  }
141
146
 
142
147
  function extractClassMethods(stripped, startIndex) {
@@ -1,5 +1,11 @@
1
1
  'use strict';
2
2
 
3
+ const { capWithNotice } = require('../util/truncate');
4
+
5
+ // Ceiling sits above the default `maxSigsPerFile` so the configured budget
6
+ // governs output rather than a literal buried here, and omissions are disclosed (#576).
7
+ const PER_FILE_LIMIT = 30;
8
+
3
9
  /**
4
10
  * Extract signatures from R source code.
5
11
  *
@@ -113,7 +119,7 @@ function extract(src) {
113
119
  sigs.push(`setClass("${sm[1]}")`);
114
120
  }
115
121
 
116
- return sigs.slice(0, 30);
122
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
117
123
  }
118
124
 
119
125
  /**
@@ -1,5 +1,11 @@
1
1
  'use strict';
2
2
 
3
+ const { capWithNotice } = require('../util/truncate');
4
+
5
+ // Ceiling sits above the default `maxSigsPerFile` so the configured budget
6
+ // governs output rather than a literal buried here, and omissions are disclosed (#576).
7
+ const PER_FILE_LIMIT = 25;
8
+
3
9
  /**
4
10
  * Extract signatures from Ruby source code.
5
11
  * @param {string} src - Raw file content
@@ -34,7 +40,7 @@ function extract(src) {
34
40
  sigs.push(`def ${m[1]}${params}${retStr}`);
35
41
  }
36
42
 
37
- return sigs.slice(0, 25);
43
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
38
44
  }
39
45
 
40
46
  function normalizeParams(params) {
@@ -1,6 +1,11 @@
1
1
  'use strict';
2
2
 
3
3
  const { lineAt, withAnchor } = require('./line-anchor');
4
+ const { capWithNotice } = require('../util/truncate');
5
+
6
+ // Ceiling sits above the default `maxSigsPerFile` so the configured budget
7
+ // governs output rather than a literal buried here, and omissions are disclosed (#576).
8
+ const PER_FILE_LIMIT = 25;
4
9
 
5
10
  /**
6
11
  * Extract signatures from Rust source code.
@@ -70,7 +75,7 @@ function extract(src) {
70
75
  sigs.push(hinted(withAnchor(`pub ${asyncKw}fn ${m[1]}(${normalizeParams(m[2])})${retStr}`, s, e), m[1]));
71
76
  }
72
77
 
73
- return sigs.slice(0, 25);
78
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
74
79
  }
75
80
 
76
81
  function extractBlock(src, startIndex) {
@@ -1,6 +1,13 @@
1
1
  'use strict';
2
2
 
3
3
  const { lineAt, withAnchor } = require('./line-anchor');
4
+ const { capWithNotice, capMembersWithNotice } = require('../util/truncate');
5
+
6
+ // Ceilings sit above the default `maxSigsPerFile` so the configured budget
7
+ // governs output rather than a literal buried here, and omissions are disclosed
8
+ // — an undisclosed cap looks like a class that simply has eight methods (#576).
9
+ const MEMBER_LIMIT = 8;
10
+ const PER_FILE_LIMIT = 25;
4
11
 
5
12
  /**
6
13
  * Extract signatures from Scala source code.
@@ -27,7 +34,8 @@ function extract(src) {
27
34
  const block = extractBlock(stripped, bodyStart);
28
35
  sigs.push(withAnchor(`${kind} ${m[1]}`, lineAt(stripped, m.index), lineAt(stripped, bodyStart + block.length)));
29
36
  for (const fn of extractMembers(block)) {
30
- sigs.push(withAnchor(` ${fn.text}`, lineAt(stripped, bodyStart + fn.declIdx), lineAt(stripped, bodyStart + fn.endIdx)));
37
+ // The disclosure marker carries no offsets; anchor it at the class body.
38
+ sigs.push(withAnchor(` ${fn.text}`, lineAt(stripped, bodyStart + (fn.declIdx || 0)), lineAt(stripped, bodyStart + (fn.endIdx || 0))));
31
39
  }
32
40
  }
33
41
 
@@ -41,7 +49,7 @@ function extract(src) {
41
49
  sigs.push(withAnchor(`def ${m[1]}${params}${retStr}`, line, line));
42
50
  }
43
51
 
44
- return sigs.slice(0, 25);
52
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
45
53
  }
46
54
 
47
55
  function extractBlock(src, startIndex) {
@@ -68,7 +76,7 @@ function extractMembers(block) {
68
76
  endIdx: m.index + m[0].length,
69
77
  });
70
78
  }
71
- return members.slice(0, 8);
79
+ return capMembersWithNotice(members, MEMBER_LIMIT);
72
80
  }
73
81
 
74
82
  function normalizeParams(params) {
@@ -1,5 +1,11 @@
1
1
  'use strict';
2
2
 
3
+ const { capWithNotice } = require('../util/truncate');
4
+
5
+ // Ceiling sits above the default `maxSigsPerFile` so the configured budget
6
+ // governs output rather than a literal buried here, and omissions are disclosed (#576).
7
+ const PER_FILE_LIMIT = 25;
8
+
3
9
  /**
4
10
  * Extract signatures from shell scripts (bash, zsh, fish).
5
11
  * @param {string} src - Raw file content
@@ -37,7 +43,7 @@ function extract(src) {
37
43
  }
38
44
  }
39
45
 
40
- return sigs.slice(0, 25);
46
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
41
47
  }
42
48
 
43
49
  module.exports = { extract };
@@ -1,5 +1,11 @@
1
1
  'use strict';
2
2
 
3
+ const { capWithNotice } = require('../util/truncate');
4
+
5
+ // Ceiling sits above the default `maxSigsPerFile` so the configured budget
6
+ // governs output rather than a literal buried here, and omissions are disclosed (#576).
7
+ const PER_FILE_LIMIT = 25;
8
+
3
9
  /**
4
10
  * Extract signatures from Svelte components.
5
11
  * @param {string} src - Raw file content
@@ -42,7 +48,7 @@ function extract(src) {
42
48
  sigs.push(`$: ${m[1]}`);
43
49
  }
44
50
 
45
- return sigs.slice(0, 25);
51
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
46
52
  }
47
53
 
48
54
  function normalizeParams(params) {
@@ -1,6 +1,13 @@
1
1
  'use strict';
2
2
 
3
3
  const { lineAt, withAnchor } = require('./line-anchor');
4
+ const { capWithNotice, capMembersWithNotice } = require('../util/truncate');
5
+
6
+ // Ceilings sit above the default `maxSigsPerFile` so the configured budget
7
+ // governs output rather than a literal buried here, and omissions are disclosed
8
+ // — an undisclosed cap looks like a class that simply has eight methods (#576).
9
+ const MEMBER_LIMIT = 8;
10
+ const PER_FILE_LIMIT = 25;
4
11
 
5
12
  /**
6
13
  * Extract signatures from Swift source code.
@@ -36,7 +43,8 @@ function extract(src) {
36
43
  const block = extractBlock(stripped, bodyStart);
37
44
  sigs.push(withAnchor(`${m[1]} ${m[2]}`, lineAt(stripped, m.index), lineAt(stripped, bodyStart + block.length)));
38
45
  for (const fn of extractMembers(block)) {
39
- sigs.push(withAnchor(` ${fn.text}`, lineAt(stripped, bodyStart + fn.declIdx), lineAt(stripped, bodyStart + fn.endIdx)));
46
+ // The disclosure marker carries no offsets; anchor it at the class body.
47
+ sigs.push(withAnchor(` ${fn.text}`, lineAt(stripped, bodyStart + (fn.declIdx || 0)), lineAt(stripped, bodyStart + (fn.endIdx || 0))));
40
48
  }
41
49
  }
42
50
 
@@ -48,7 +56,7 @@ function extract(src) {
48
56
  sigs.push(withAnchor(`${asyncKw}func ${m[1]}(${normalizeParams(m[2])})${retStr}`, s, e));
49
57
  }
50
58
 
51
- return sigs.slice(0, 25);
59
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
52
60
  }
53
61
 
54
62
  function extractBlock(src, startIndex) {
@@ -74,7 +82,7 @@ function extractMembers(block) {
74
82
  endIdx: m.index + m[0].length,
75
83
  });
76
84
  }
77
- return members.slice(0, 8);
85
+ return capMembersWithNotice(members, MEMBER_LIMIT);
78
86
  }
79
87
 
80
88
  function normalizeParams(params) {
@@ -1,5 +1,11 @@
1
1
  'use strict';
2
2
 
3
+ const { capWithNotice } = require('../util/truncate');
4
+
5
+ // Ceiling sits above the default `maxSigsPerFile` so the configured budget
6
+ // governs output rather than a literal buried here, and omissions are disclosed (#576).
7
+ const PER_FILE_LIMIT = 25;
8
+
3
9
  /**
4
10
  * Extract signatures from Vue single-file components.
5
11
  * @param {string} src - Raw file content
@@ -64,7 +70,7 @@ function extract(src) {
64
70
  const emitsMatch = script.match(/(?:defineEmits|emits)\s*(?::\s*|\(\s*)(\[[\s\S]*?\])/);
65
71
  if (emitsMatch) sigs.push(`emits: ${emitsMatch[1].replace(/\s+/g, ' ')}`);
66
72
 
67
- return sigs.slice(0, 25);
73
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
68
74
  }
69
75
 
70
76
  function normalizeParams(params) {
@@ -1,5 +1,11 @@
1
1
  'use strict';
2
2
 
3
+ const { capWithNotice } = require('../util/truncate');
4
+
5
+ // Ceiling sits above the default `maxSigsPerFile` so the configured budget
6
+ // governs output rather than a literal buried here, and omissions are disclosed (#576).
7
+ const PER_FILE_LIMIT = 25;
8
+
3
9
  /**
4
10
  * Extract signatures from YAML configuration files.
5
11
  * @param {string} src - Raw file content
@@ -53,7 +59,7 @@ function extract(src) {
53
59
  }
54
60
  }
55
61
 
56
- return sigs.slice(0, 25);
62
+ return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
57
63
  }
58
64
 
59
65
  module.exports = { extract };
package/src/mcp/server.js CHANGED
@@ -18,7 +18,7 @@ const { readContext, searchSignatures, getMap, createCheckpoint, getRouting, exp
18
18
 
19
19
  const SERVER_INFO = {
20
20
  name: 'sigmap',
21
- version: '8.31.0',
21
+ version: '8.32.0',
22
22
  description: 'SigMap MCP server — code signatures on demand',
23
23
  };
24
24