sigmap 4.0.2 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -12,29 +12,17 @@ Use this marker block for all appendable context files:
12
12
  ## Auto-generated signatures
13
13
  <!-- Updated by gen-context.js -->
14
14
  You are a coding assistant with full knowledge of this codebase.
15
- Below are the code signatures extracted by SigMap v4.0.2 on 2026-04-15T07:28:24.633Z.
15
+ Below are the code signatures extracted by SigMap v4.1.0 on 2026-04-15T08:05:43.080Z.
16
16
 
17
17
  Use these signatures to answer questions about the code accurately.
18
18
 
19
19
  ## Code Signatures
20
20
 
21
- <!-- Generated by SigMap gen-context.js v4.0.2 -->
21
+ <!-- Generated by SigMap gen-context.js v4.1.0 -->
22
22
  <!-- DO NOT EDIT below the marker line — run gen-context.js to regenerate -->
23
23
 
24
24
  # Code signatures
25
25
 
26
- ## changes (last 5 commits — 77 minutes ago)
27
- ```
28
- src/analysis/coverage-score.js +coverageScore +_walk
29
- src/eval/analyzer.js ~analyzeFiles
30
- packages/adapters/claude.js +_confidenceMeta ~format
31
- packages/adapters/copilot.js +_confidenceMeta ~format
32
- packages/adapters/cursor.js +_confidenceMeta ~format
33
- packages/adapters/gemini.js +_confidenceMeta ~format ~write
34
- packages/adapters/openai.js +_confidenceMeta ~format ~outputPath
35
- packages/adapters/windsurf.js +_confidenceMeta ~format
36
- ```
37
-
38
26
  ## packages
39
27
 
40
28
  ### packages/adapters/claude.js
package/CHANGELOG.md CHANGED
@@ -10,6 +10,49 @@ Format: [Semantic Versioning](https://semver.org/)
10
10
 
11
11
  ---
12
12
 
13
+ ## [4.1.0] — 2026-04-15 — Smart Budget: auto-scaling token budget
14
+
15
+ ### Added
16
+
17
+ - **Auto-scaling token budget** (`autoMaxTokens: true`, default on):
18
+ Replaces the old fixed 6 000-token default with a formula that sizes the budget to your repo:
19
+ ```
20
+ effective = clamp(ceil(totalSigTokens × coverageTarget), 4000, floor(modelContextLimit × maxTokensHeadroom))
21
+ ```
22
+ - `coverageTarget` (default `0.80`) — target fraction of source files to include
23
+ - `modelContextLimit` (default `128000`) — model context window size; hard cap = `limit × headroom`
24
+ - `maxTokensHeadroom` (default `0.20`) — fraction of the model window reserved for SigMap output (default hard cap: **25 600 tokens**)
25
+ - Minimum floor: **4 000 tokens** (prevents tiny repos from being under-budgeted)
26
+ - When the hard cap prevents hitting the coverage target by more than 10 percentage points, SigMap warns and suggests `strategy: "per-module"`
27
+
28
+ - **Four new config keys** (all optional, documented in `gen-context.config.json.example`):
29
+ | Key | Default | Description |
30
+ |---|---|---|
31
+ | `autoMaxTokens` | `true` | Enable auto-scaling |
32
+ | `coverageTarget` | `0.80` | Target fraction of source files |
33
+ | `modelContextLimit` | `128000` | Model context window (tokens) |
34
+ | `maxTokensHeadroom` | `0.20` | Fraction of context for SigMap |
35
+
36
+ - **Post-run summary annotation**: coverage line now shows `[budget: N auto-scaled]` when the formula overrode the configured `maxTokens`.
37
+
38
+ - **Per-module strategy budget fix**: each module now gets its own full effective budget instead of a proportional slice, which was the limiting factor that made `per-module` less useful than advertised.
39
+
40
+ - **Tracking log fields**: `autoBudget: true/false` and `budgetLimit: N` added to `.context/usage.ndjson` entries.
41
+
42
+ - **12 new integration tests** (`test/integration/auto-budget.test.js`): cover MIN floor, proportional scaling, hard cap, disabled auto-scaling, custom `coverageTarget`/`modelContextLimit`/`maxTokensHeadroom`, warning emission, and empty-project edge case.
43
+
44
+ ### Changed
45
+
46
+ - `autoMaxTokens: false` + explicit `maxTokens` preserves the old fixed-budget behaviour exactly — fully backwards compatible.
47
+ - `printReport` now labels the budget `(auto-scaled)` vs `(fixed)` in the report line.
48
+
49
+ ### Benchmarks (v4.1.0)
50
+ - Token reduction: **97.6% average** across 18 repos ✅
51
+ - Retrieval hit@5: **84.4%** ✅
52
+ - With auto-scaling enabled, all 18 benchmark repos now stay within a sensible budget that targets ≥ 80% file coverage rather than the old 6 K ceiling.
53
+
54
+ ---
55
+
13
56
  ## [4.0.2] — 2026-04-15 — Bundle factory fix (re-release of 4.0.1)
14
57
 
15
58
  ### Fixed
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  <h1>⚡ SigMap</h1>
6
6
 
7
7
  <p><strong>WITHOUT SIGMAP, YOUR AI IS GUESSING.</strong><br>
8
- <strong>It sees 8% of your codebase and invents the rest.</strong></p>
8
+ <strong>Without structured context, AI often reads the wrong file and fills the gaps with guesses.</strong></p>
9
9
 
10
10
  <p><sub>Run one command. Force every answer to come from real code.</sub></p>
11
11
 
@@ -19,7 +19,13 @@
19
19
  npx sigmap # 10 seconds. zero config. your AI never reads the wrong file again.
20
20
  ```
21
21
 
22
- > Latest: **v4.0.0** Intelligence Layer. Coverage score, confidence indicators in every output file, `--report` module heatmap, `--diff` risk scoring, and extractor quality-based drop order.
22
+ **What you get in ~10 seconds**
23
+ - A compact signature map of your codebase
24
+ - The right file in context far more often (84.4% hit@5 vs 13.6% random)
25
+ - Fewer retries (1.59 vs 2.84 prompts per task)
26
+ - Far smaller context (~2K–4K tokens instead of ~80K)
27
+
28
+ > Latest: **v4.1.0** — Smart Budget. Token budget now auto-scales to your repo size, targeting 80% source-file coverage by default. No config change needed — it just works.
23
29
 
24
30
  <div align="center">
25
31
  <img src="demo.gif" alt="SigMap demo — reducing 80K tokens to 4K in under 10 seconds" width="760" />
@@ -56,11 +62,13 @@ npx sigmap # 10 seconds. zero config. your AI never reads the wrong file again
56
62
  | | Without SigMap | With SigMap |
57
63
  |---|:---:|:---:|
58
64
  | Task success | 10% | **59%** |
59
- | Prompts per task | 2.84 | **1.78** |
65
+ | Prompts per task | 2.84 | **1.59** |
60
66
  | Tokens per session | ~80,000 | **~2,000** |
61
67
  | Right file found | 13.6% | **84.4%** |
62
68
  | Hallucination risk | 92% | **0%** |
63
69
 
70
+ Measured on 90 coding tasks across 18 real public repos. Full methodology and raw benchmark pages are linked below.
71
+
64
72
  </details>
65
73
 
66
74
  ---
@@ -689,7 +697,6 @@ Copy `gen-context.config.json.example` to `gen-context.config.json`:
689
697
  {
690
698
  "output": ".github/copilot-instructions.md",
691
699
  "srcDirs": ["src", "app", "lib"],
692
- "maxTokens": 6000,
693
700
  "outputs": ["copilot"],
694
701
  "secretScan": true,
695
702
  "strategy": "full",
@@ -703,10 +710,21 @@ Copy `gen-context.config.json.example` to `gen-context.config.json`:
703
710
  - **`output`** — custom path for the primary markdown output file (used by `copilot` adapter). Default: `.github/copilot-instructions.md`
704
711
  - **`outputs`** — which adapters to write to: `copilot` | `claude` | `cursor` | `windsurf`
705
712
  - **`srcDirs`** — directories to scan (relative to project root)
706
- - **`maxTokens`** — max tokens in final output before budget enforcement
707
713
  - **`secretScan`** — redact secrets (AWS keys, tokens, etc.) from output
708
714
  - **`strategy`** — output mode: `full` (default) | `per-module` | `hot-cold`
709
715
 
716
+ **Token budget (v4.1.0 — auto-scaling):**
717
+
718
+ | Key | Default | Description |
719
+ |---|---|---|
720
+ | `autoMaxTokens` | `true` | Auto-scale budget to repo size. Set `false` to pin a fixed `maxTokens`. |
721
+ | `coverageTarget` | `0.80` | Fraction of source files to target (0.0–1.0). |
722
+ | `modelContextLimit` | `128000` | Model context window size. Hard cap = `limit × maxTokensHeadroom`. |
723
+ | `maxTokensHeadroom` | `0.20` | Fraction of the context window reserved for SigMap output (default: 25 600 tokens). |
724
+ | `maxTokens` | `6000` | Used only when `autoMaxTokens: false`, or as a floor. |
725
+
726
+ The formula: `effective = clamp(ceil(totalSigTokens × coverageTarget), 4000, floor(modelContextLimit × maxTokensHeadroom))`.
727
+
710
728
  Exclusions go in `.contextignore` (gitignore syntax). Also reads `.repomixignore` if present.
711
729
 
712
730
  ```
@@ -752,11 +770,11 @@ Every run now prints a coverage line alongside token reduction:
752
770
 
753
771
  ```
754
772
  ───────────────────────────────────────────
755
- SigMap v4.0.0
773
+ SigMap v4.1.0
756
774
  Files scanned : 76
757
775
  Symbols found : 332
758
776
  Token reduction: 94% (65,227 → 4,103)
759
- Coverage : A (97%) — 76 of 78 source files included
777
+ Coverage : A (97%) — 76 of 78 source files included [budget: 4000 auto-scaled]
760
778
  Output : .github/copilot-instructions.md
761
779
  ───────────────────────────────────────────
762
780
  ```
@@ -771,7 +789,7 @@ sigmap --report
771
789
 
772
790
  ```
773
791
  [sigmap] report:
774
- version : 4.0.0
792
+ version : 4.1.0
775
793
  files processed : 76
776
794
  reduction : 93.7%
777
795
  coverage : A (97%) — 76 of 78 source files included
@@ -20,8 +20,23 @@
20
20
 
21
21
  "maxSigsPerFile": 25,
22
22
 
23
+ "_maxTokens_comment": "Used only when autoMaxTokens is false. Override to pin a fixed budget.",
23
24
  "maxTokens": 6000,
24
25
 
26
+ "_autoMaxTokens_comment": "Auto-scale budget based on repo size. Default: true.",
27
+ "_autoMaxTokens_formula": "effective = clamp(totalSigTokens × coverageTarget, 4000, modelContextLimit × maxTokensHeadroom)",
28
+ "autoMaxTokens": true,
29
+
30
+ "_coverageTarget_comment": "Fraction of source files to target for inclusion (0.0–1.0). Default: 0.80 = 80%.",
31
+ "coverageTarget": 0.80,
32
+
33
+ "_modelContextLimit_comment": "Model context window size (tokens). Hard cap = modelContextLimit × maxTokensHeadroom.",
34
+ "_modelContextLimit_examples": "128000 = GPT-4o/Claude (default) | 200000 = Claude max | 1000000 = Gemini 1M",
35
+ "modelContextLimit": 128000,
36
+
37
+ "_maxTokensHeadroom_comment": "Fraction of model context reserved for SigMap output. 0.20 = 25,600 token hard cap.",
38
+ "maxTokensHeadroom": 0.20,
39
+
25
40
  "secretScan": true,
26
41
 
27
42
  "monorepo": false,
package/gen-context.js CHANGED
@@ -59,9 +59,22 @@ __factories["./src/config/defaults"] = function(module, exports) {
59
59
  // Maximum signatures extracted per file
60
60
  maxSigsPerFile: 25,
61
61
 
62
- // Maximum tokens in final output before budget enforcement kicks in
62
+ // Maximum tokens in final output before budget enforcement kicks in.
63
+ // Used only when autoMaxTokens is false, or as a floor for auto-scaling.
63
64
  maxTokens: 6000,
64
-
65
+
66
+ // Automatically scale the token budget based on repo size.
67
+ autoMaxTokens: true,
68
+
69
+ // Fraction of source files to target for inclusion (0.0–1.0).
70
+ coverageTarget: 0.80,
71
+
72
+ // Model context window size (tokens). Used to compute the hard cap.
73
+ modelContextLimit: 128000,
74
+
75
+ // Fraction of the model context window reserved for SigMap output.
76
+ maxTokensHeadroom: 0.20,
77
+
65
78
  // Scan signatures for secrets and redact matches
66
79
  secretScan: true,
67
80
 
@@ -4641,7 +4654,7 @@ __factories["./src/mcp/server"] = function(module, exports) {
4641
4654
 
4642
4655
  const SERVER_INFO = {
4643
4656
  name: 'sigmap',
4644
- version: '4.0.2',
4657
+ version: '4.1.0',
4645
4658
  description: 'SigMap MCP server — code signatures on demand',
4646
4659
  };
4647
4660
 
@@ -6203,7 +6216,7 @@ const path = require('path');
6203
6216
  const os = require('os');
6204
6217
  const { execSync } = require('child_process');
6205
6218
 
6206
- const VERSION = '4.0.2';
6219
+ const VERSION = '4.1.0';
6207
6220
  const MARKER = '\n\n## Auto-generated signatures\n<!-- Updated by gen-context.js -->\n';
6208
6221
 
6209
6222
  function requireSourceOrBundled(key) {
@@ -6423,6 +6436,61 @@ function isMockFile(filePath) {
6423
6436
  /mock\.(ts|js|tsx|jsx)$/.test(p);
6424
6437
  }
6425
6438
 
6439
+ /**
6440
+ * Compute the effective token budget based on repo size and config.
6441
+ *
6442
+ * Formula:
6443
+ * totalSigTokens = sum of estimated tokens for all extracted sig blocks
6444
+ * needed = ceil(totalSigTokens * coverageTarget) // tokens for target% coverage
6445
+ * hardCap = floor(modelContextLimit * maxTokensHeadroom)
6446
+ * effective = clamp(needed, 4000, hardCap)
6447
+ *
6448
+ * When autoMaxTokens is false the configured maxTokens is returned unchanged.
6449
+ *
6450
+ * @param {Array} fileEntries - All file entries BEFORE budget enforcement
6451
+ * @param {object} config
6452
+ * @returns {number} effective token budget
6453
+ */
6454
+ function computeEffectiveMaxTokens(fileEntries, config) {
6455
+ if (config.autoMaxTokens === false) return config.maxTokens;
6456
+
6457
+ const coverageTarget = (config.coverageTarget != null) ? config.coverageTarget : 0.80;
6458
+ const modelContextLimit = (config.modelContextLimit != null) ? config.modelContextLimit : 128000;
6459
+ const maxTokensHeadroom = (config.maxTokensHeadroom != null) ? config.maxTokensHeadroom : 0.20;
6460
+
6461
+ const totalSigTokens = fileEntries.reduce(
6462
+ (s, e) => s + estimateTokens((e.sigs || []).join('\n')), 0
6463
+ );
6464
+ if (totalSigTokens === 0) return config.maxTokens;
6465
+
6466
+ const hardCap = Math.floor(modelContextLimit * maxTokensHeadroom);
6467
+ const needed = Math.ceil(totalSigTokens * coverageTarget);
6468
+ const MIN = 4000;
6469
+ const effective = Math.min(Math.max(MIN, needed), hardCap);
6470
+
6471
+ // Warn when repo is so large the hard cap prevents hitting the coverage target
6472
+ if (needed > hardCap) {
6473
+ const estimatedCovPct = Math.round((hardCap / totalSigTokens) * 100);
6474
+ const targetPct = Math.round(coverageTarget * 100);
6475
+ if (estimatedCovPct < targetPct - 10) {
6476
+ console.warn(
6477
+ `[sigmap] auto-budget: ${fileEntries.length} files need ~${Math.round(needed / 1000)}K tokens ` +
6478
+ `for ${targetPct}% coverage`
6479
+ );
6480
+ console.warn(
6481
+ `[sigmap] auto-budget: capped at ${hardCap} ` +
6482
+ `(${Math.round(maxTokensHeadroom * 100)}% of ${Math.round(modelContextLimit / 1000)}K model limit) ` +
6483
+ `→ est. ${estimatedCovPct}% coverage`
6484
+ );
6485
+ console.warn(
6486
+ `[sigmap] auto-budget: tip — set strategy:"per-module" for full coverage on large repos`
6487
+ );
6488
+ }
6489
+ }
6490
+
6491
+ return effective;
6492
+ }
6493
+
6426
6494
  function applyTokenBudget(fileEntries, maxTokens) {
6427
6495
  // fileEntries: [{ filePath, sigs, mtime }]
6428
6496
  // Reserve ~10% for formatting overhead (section headers, code fences, top-level header)
@@ -6889,7 +6957,7 @@ function _coverageBar(pct, width) {
6889
6957
  return '\u2588'.repeat(filled) + '\u2591'.repeat(width - filled);
6890
6958
  }
6891
6959
 
6892
- function printReport(inputTokens, finalTokens, fileCount, droppedCount, asJson, budgetLimit, coverageResult) {
6960
+ function printReport(inputTokens, finalTokens, fileCount, droppedCount, asJson, budgetLimit, coverageResult, isAutoBudget) {
6893
6961
  const reduction = inputTokens > 0 ? (100 - (finalTokens / inputTokens) * 100).toFixed(1) : 0;
6894
6962
  const overBudget = finalTokens > (budgetLimit || 6000);
6895
6963
  if (asJson) {
@@ -6904,6 +6972,7 @@ function printReport(inputTokens, finalTokens, fileCount, droppedCount, asJson,
6904
6972
  reductionPct: parseFloat(reduction),
6905
6973
  overBudget,
6906
6974
  budgetLimit: budgetLimit || 6000,
6975
+ autoBudget: !!isAutoBudget,
6907
6976
  };
6908
6977
  if (coverageResult) {
6909
6978
  payload.coverage = {
@@ -6923,13 +6992,16 @@ function printReport(inputTokens, finalTokens, fileCount, droppedCount, asJson,
6923
6992
  // Exit 1 in CI if over budget — lets pipelines fail fast
6924
6993
  if (overBudget) process.exitCode = 1;
6925
6994
  } else {
6995
+ const budgetLabel = isAutoBudget
6996
+ ? `${budgetLimit || 6000} (auto-scaled)`
6997
+ : `${budgetLimit || 6000} (fixed)`;
6926
6998
  console.log(`[sigmap] report:`);
6927
6999
  console.log(` version : ${VERSION}`);
6928
7000
  console.log(` files processed : ${fileCount}`);
6929
7001
  console.log(` files dropped : ${droppedCount}`);
6930
7002
  console.log(` input tokens : ~${inputTokens}`);
6931
7003
  console.log(` output tokens : ~${finalTokens}`);
6932
- console.log(` budget limit : ${budgetLimit || 6000}`);
7004
+ console.log(` budget limit : ${budgetLabel}`);
6933
7005
  console.log(` reduction : ${reduction}%`);
6934
7006
  if (coverageResult) {
6935
7007
  console.log(` coverage : ${coverageResult.grade} (${coverageResult.score}%) — ${coverageResult.included} of ${coverageResult.total} source files included`);
@@ -7088,8 +7160,9 @@ function runPerModuleStrategy(cwd, config, fileEntries, inputTokenTotal) {
7088
7160
  const outPath = path.join(cwd, '.github', outName);
7089
7161
  const modEntries = modules[mod];
7090
7162
 
7091
- // Per-module budget: proportional share of maxTokens
7092
- const modBudget = Math.max(1000, Math.floor(config.maxTokens / moduleNames.length));
7163
+ // Per-module budget: each module gets its own full effective budget
7164
+ // (per-module strategy is the recommended path for large repos — no sharing needed)
7165
+ const modBudget = Math.max(1000, config.maxTokens);
7093
7166
  const budgeted = applyTokenBudget(modEntries, modBudget);
7094
7167
 
7095
7168
  const content = formatOutput(budgeted, cwd, false, config, null);
@@ -7358,15 +7431,22 @@ function runGenerate(cwd, config, reportMode, reportJson = false) {
7358
7431
  });
7359
7432
  }
7360
7433
 
7434
+ // v4.1: compute effective budget once; used by all strategies
7435
+ const effectiveMaxTokens = computeEffectiveMaxTokens(fileEntries, config);
7436
+ // Propagate to config so per-module / hot-cold strategies pick it up
7437
+ const configWithBudget = effectiveMaxTokens !== config.maxTokens
7438
+ ? Object.assign({}, config, { maxTokens: effectiveMaxTokens, _autoMaxTokens: effectiveMaxTokens })
7439
+ : config;
7440
+
7361
7441
  let result;
7362
7442
  if (!reportMode) {
7363
7443
  if (strategy === 'per-module') {
7364
- result = runPerModuleStrategy(cwd, config, fileEntries, inputTokenTotal);
7444
+ result = runPerModuleStrategy(cwd, configWithBudget, fileEntries, inputTokenTotal);
7365
7445
  } else if (strategy === 'hot-cold') {
7366
- result = runHotColdStrategy(cwd, config, fileEntries, recentFiles, inputTokenTotal);
7446
+ result = runHotColdStrategy(cwd, configWithBudget, fileEntries, recentFiles, inputTokenTotal);
7367
7447
  } else {
7368
7448
  // 'full' — original behaviour
7369
- fileEntries = applyTokenBudget(fileEntries, config.maxTokens);
7449
+ fileEntries = applyTokenBudget(fileEntries, effectiveMaxTokens);
7370
7450
  const droppedCount = beforeCount - fileEntries.length;
7371
7451
  const routingEnabled = !!(config.routing || process.argv.includes('--routing'));
7372
7452
  const content = formatOutput(fileEntries, cwd, routingEnabled, config, null);
@@ -7409,21 +7489,21 @@ function runGenerate(cwd, config, reportMode, reportJson = false) {
7409
7489
  }
7410
7490
  } else {
7411
7491
  // report mode: always run full pipeline for accurate stats
7412
- const budgeted = applyTokenBudget([...fileEntries], config.maxTokens);
7492
+ const budgeted = applyTokenBudget([...fileEntries], effectiveMaxTokens);
7413
7493
  const droppedCount = beforeCount - budgeted.length;
7414
- const content = formatOutput(budgeted, cwd, false, config, null);
7494
+ const content = formatOutput(budgeted, cwd, false, configWithBudget, null);
7415
7495
  const finalTokens = estimateTokens(content);
7416
7496
  // v4.0: compute coverage score for --report heatmap
7417
7497
  let coverageResult = null;
7418
7498
  try {
7419
7499
  const { coverageScore } = requireSourceOrBundled('./src/analysis/coverage-score');
7420
- coverageResult = coverageScore(cwd, budgeted, config);
7500
+ coverageResult = coverageScore(cwd, budgeted, configWithBudget);
7421
7501
  } catch (_) {}
7422
7502
  result = { inputTokenTotal, finalTokens, fileCount: beforeCount, droppedCount, coverageResult };
7423
7503
  }
7424
7504
 
7425
7505
  if (reportMode || process.argv.includes('--report')) {
7426
- printReport(result.inputTokenTotal, result.finalTokens, result.fileCount, result.droppedCount, reportJson, config.maxTokens, result.coverageResult);
7506
+ printReport(result.inputTokenTotal, result.finalTokens, result.fileCount, result.droppedCount, reportJson, effectiveMaxTokens, result.coverageResult, config.autoMaxTokens !== false && effectiveMaxTokens !== config.maxTokens);
7427
7507
  }
7428
7508
 
7429
7509
  // Usage tracking (v0.9) — optional append-only NDJSON log
@@ -7437,8 +7517,9 @@ function runGenerate(cwd, config, reportMode, reportJson = false) {
7437
7517
  droppedCount: result.droppedCount,
7438
7518
  rawTokens: result.inputTokenTotal,
7439
7519
  finalTokens: result.finalTokens,
7440
- overBudget: result.finalTokens > config.maxTokens,
7441
- budgetLimit: config.maxTokens,
7520
+ overBudget: result.finalTokens > effectiveMaxTokens,
7521
+ budgetLimit: effectiveMaxTokens,
7522
+ autoBudget: config.autoMaxTokens !== false && effectiveMaxTokens !== config.maxTokens,
7442
7523
  }, cwd);
7443
7524
  } catch (err) {
7444
7525
  console.warn(`[sigmap] tracking: ${err.message}`);
@@ -7459,8 +7540,15 @@ function runGenerate(cwd, config, reportMode, reportJson = false) {
7459
7540
  let coverageLine = '';
7460
7541
  try {
7461
7542
  const { coverageScore } = requireSourceOrBundled('./src/analysis/coverage-score');
7462
- const cov = coverageScore(cwd, fileEntries, config);
7463
- coverageLine = ` Coverage : ${cov.grade} (${cov.score}%) \u2014 ${cov.included} of ${cov.total} source files included`;
7543
+ const cov = coverageScore(cwd, fileEntries, configWithBudget);
7544
+ const autoBudgetNote = (config.autoMaxTokens !== false && effectiveMaxTokens !== config.maxTokens)
7545
+ ? ` [budget: ${effectiveMaxTokens} auto-scaled]`
7546
+ : '';
7547
+ coverageLine = ` Coverage : ${cov.grade} (${cov.score}%) \u2014 ${cov.included} of ${cov.total} source files included${autoBudgetNote}`;
7548
+ // Extra warning line when coverage is still poor despite auto-scaling
7549
+ if (cov.score < 40 && config.strategy !== 'per-module' && config.strategy !== 'hot-cold') {
7550
+ coverageLine += '\n [sigmap] tip: large repo — consider strategy:"per-module" for full coverage';
7551
+ }
7464
7552
  } catch (_) {}
7465
7553
  const lines = [
7466
7554
  bar,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sigmap",
3
- "version": "4.0.2",
3
+ "version": "4.1.0",
4
4
  "description": "Zero-dependency AI context engine — 97% token reduction. No npm install. Runs on Node 18+.",
5
5
  "main": "gen-context.js",
6
6
  "exports": {
@@ -15,7 +15,7 @@
15
15
  },
16
16
  "scripts": {
17
17
  "test": "node test/run.js",
18
- "test:integration": "node test/integration/strategy.test.js && node test/integration/secret-scan.test.js && node test/integration/token-budget.test.js && node test/integration/mcp-server.test.js",
18
+ "test:integration": "node test/integration/strategy.test.js && node test/integration/secret-scan.test.js && node test/integration/token-budget.test.js && node test/integration/auto-budget.test.js && node test/integration/mcp-server.test.js",
19
19
  "test:integration:all": "node test/integration/all.js",
20
20
  "test:all": "node test/run.js && node test/integration/strategy.test.js && node test/integration/secret-scan.test.js",
21
21
  "generate": "node gen-context.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sigmap-cli",
3
- "version": "4.0.2",
3
+ "version": "4.1.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": "4.0.2",
3
+ "version": "4.1.0",
4
4
  "description": "SigMap core library — zero-dependency code signature extraction, retrieval, and security scanning",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -47,9 +47,30 @@ const DEFAULTS = {
47
47
  // Maximum signatures extracted per file
48
48
  maxSigsPerFile: 25,
49
49
 
50
- // Maximum tokens in final output before budget enforcement kicks in
50
+ // Maximum tokens in final output before budget enforcement kicks in.
51
+ // Used only when autoMaxTokens is false, or as a floor for auto-scaling.
51
52
  maxTokens: 6000,
52
53
 
54
+ // Automatically scale the token budget based on repo size.
55
+ // When true, SigMap targets `coverageTarget` fraction of source files and
56
+ // raises the budget up to `modelContextLimit * maxTokensHeadroom`.
57
+ // Set to false (or set maxTokens explicitly) to pin the budget.
58
+ autoMaxTokens: true,
59
+
60
+ // Fraction of source files to target for inclusion (0.0–1.0).
61
+ // 0.80 = include at least 80% of source files in the context output.
62
+ coverageTarget: 0.80,
63
+
64
+ // Model context window size (tokens). Used to compute the hard cap:
65
+ // hardCap = modelContextLimit × maxTokensHeadroom
66
+ // Default: GPT-4o / Claude Sonnet (128K). Set higher for Gemini 1M etc.
67
+ modelContextLimit: 128000,
68
+
69
+ // Fraction of the model context window reserved for SigMap output.
70
+ // Leaves the remaining fraction for the conversation, system prompt, etc.
71
+ // Default 0.20 = 20% of 128K = 25,600 token hard cap.
72
+ maxTokensHeadroom: 0.20,
73
+
53
74
  // Scan signatures for secrets and redact matches
54
75
  secretScan: true,
55
76
 
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: '4.0.2',
21
+ version: '4.1.0',
22
22
  description: 'SigMap MCP server — code signatures on demand',
23
23
  };
24
24