sigmap 8.10.0 → 8.11.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 +589 -182
- package/CHANGELOG.md +12 -0
- package/README.md +4 -5
- package/gen-context.js +117 -3
- package/llms-full.txt +4 -2
- package/llms.txt +2 -2
- package/package.json +2 -1
- package/packages/cli/package.json +1 -1
- package/packages/core/package.json +1 -1
- package/src/config/defaults.js +3 -0
- package/src/format/terse.js +86 -0
- package/src/mcp/server.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,18 @@ Format: [Semantic Versioning](https://semver.org/)
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
+
## [8.11.0] — 2026-07-11
|
|
14
|
+
|
|
15
|
+
Minor release — the **terse signature encoder** (D7 from the master plan), shipped under its measure-first gate: the public reduction number comes from a benchmark run on this repo, never borrowed from another tool's prose-compression claims.
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- **`--terse` — deterministic terse signature encoding (#462, PR #463):** opt-in compaction of the generated signature block (`function `→`fn `, tightened params/arrows/exports). Measured on this repo by `npm run benchmark:terse`: **10,232 → 8,580 sig tokens (−16.1%)** across 143 files / 780 signature lines. The `:start-end` line anchor and any trailing doc hint are preserved byte-exactly, so `get_lines`, evidence packs, `parseAnchor`, and symbol extraction keep working; the ranker parses terse context files (regression-tested). `terse: false` config default — default output is byte-identical when off. New `benchmark:terse` npm script (`scripts/run-terse-benchmark.mjs`) is the only legitimate source for this number.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- **README:** replaced the flaky Star History embed with a reliable link (PR #461); added the 'Verified on MseeP' badge below the MseeP security badge (PR #460).
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
13
25
|
## [8.10.0] — 2026-07-09
|
|
14
26
|
|
|
15
27
|
Minor release — **honesty fixes** from a brutal code-review audit of the CLI surface: closing the gap between what each command *claims* and what it *does*. All changes are zero-dependency, deterministic, and the bundle stays reproducible from `src/`. ~40 new regression tests.
|
package/README.md
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
[](https://shypd.ai/tools/sigmap)
|
|
17
17
|
|
|
18
18
|
[](https://mseep.ai/app/manojmallick-sigmap)
|
|
19
|
+
[](https://mseep.ai/app/193cd37f-1ef3-43c3-80bc-86e23fd12924)
|
|
19
20
|
|
|
20
21
|
</div>
|
|
21
22
|
|
|
@@ -121,8 +122,8 @@ Ask → Rank → Context → Validate → Judge → Learn
|
|
|
121
122
|
|
|
122
123
|
<!--SM:benchmarkBlock-->
|
|
123
124
|
```
|
|
124
|
-
Benchmark : sigmap-v8.
|
|
125
|
-
Date : 2026-07-
|
|
125
|
+
Benchmark : sigmap-v8.11-main (21 repositories, including R language)
|
|
126
|
+
Date : 2026-07-11
|
|
126
127
|
|
|
127
128
|
Hit@5 : 87.8% (baseline 13.6% — 6.5× lift)
|
|
128
129
|
Token reduction: 97.0% (across 21 repos)
|
|
@@ -339,9 +340,7 @@ If SigMap saves you context or API spend, a ⭐ on [GitHub](https://github.com/m
|
|
|
339
340
|
|
|
340
341
|
🌍 See where SigMap's stargazers are around the world on the **[StarMapper star map →](https://starmapper.bruniaux.com/manojmallick/sigmap)**.
|
|
341
342
|
|
|
342
|
-
|
|
343
|
-
<img src="https://api.star-history.com/svg?repos=manojmallick/sigmap&type=Date" alt="SigMap Star History Chart" width="600" />
|
|
344
|
-
</a>
|
|
343
|
+
📈 Watch SigMap's growth on the **[Star History chart →](https://star-history.com/#manojmallick/sigmap&Date)**.
|
|
345
344
|
|
|
346
345
|
[Report an issue](https://github.com/manojmallick/sigmap/issues) · [Changelog](CHANGELOG.md)
|
|
347
346
|
|
package/gen-context.js
CHANGED
|
@@ -1501,6 +1501,9 @@ __factories["./src/config/defaults"] = function(module, exports) {
|
|
|
1501
1501
|
// Include a compact `name@version` list of installed direct deps (D8)
|
|
1502
1502
|
versionPins: true,
|
|
1503
1503
|
|
|
1504
|
+
// Terse signature encoding — deterministic compaction of sig lines (D7, opt-in)
|
|
1505
|
+
terse: false,
|
|
1506
|
+
|
|
1504
1507
|
// Include TODO/FIXME/HACK/XXX comments as compact section
|
|
1505
1508
|
todos: true,
|
|
1506
1509
|
|
|
@@ -10011,6 +10014,96 @@ __factories["./src/format/llms-txt"] = function(module, exports) {
|
|
|
10011
10014
|
|
|
10012
10015
|
};
|
|
10013
10016
|
|
|
10017
|
+
// ── ./src/format/terse ──
|
|
10018
|
+
__factories["./src/format/terse"] = function(module, exports) {
|
|
10019
|
+
|
|
10020
|
+
/**
|
|
10021
|
+
* Terse signature encoder (D7).
|
|
10022
|
+
*
|
|
10023
|
+
* Deterministic compaction of signature lines for the generated context —
|
|
10024
|
+
* opt-in via `--terse` / `terse: true`. Every transform is a fixed string
|
|
10025
|
+
* rewrite (no heuristics, no LLM), so terse output stays byte-stable.
|
|
10026
|
+
*
|
|
10027
|
+
* The line anchor (` :start-end`) and everything after it (Python/R doc
|
|
10028
|
+
* hints) are preserved byte-exactly: `parseAnchor`, `get_lines`, and the
|
|
10029
|
+
* evidence pack keep working on terse output. Symbol extraction is safe too —
|
|
10030
|
+
* `extractName` in src/extractors/prdiff.js already recognizes `fn <name>`.
|
|
10031
|
+
*/
|
|
10032
|
+
|
|
10033
|
+
/** First ` :start[-end]` anchor token (two-space prefix, as emitted by line-anchor.js). */
|
|
10034
|
+
const ANCHOR_RE = /\s{2}:\d+(?:-\d+)?(?=\s|$)/;
|
|
10035
|
+
|
|
10036
|
+
/**
|
|
10037
|
+
* Split a signature into the compactable text and the byte-preserved suffix
|
|
10038
|
+
* (anchor + any trailing doc hint).
|
|
10039
|
+
* @param {string} sig
|
|
10040
|
+
* @returns {{ text: string, suffix: string }}
|
|
10041
|
+
*/
|
|
10042
|
+
function splitAnchor(sig) {
|
|
10043
|
+
const s = String(sig);
|
|
10044
|
+
const m = ANCHOR_RE.exec(s);
|
|
10045
|
+
if (!m) return { text: s, suffix: '' };
|
|
10046
|
+
return { text: s.slice(0, m.index), suffix: s.slice(m.index) };
|
|
10047
|
+
}
|
|
10048
|
+
|
|
10049
|
+
/**
|
|
10050
|
+
* Compact one signature line. Leading whitespace (member indentation) is kept.
|
|
10051
|
+
* @param {string} sig
|
|
10052
|
+
* @returns {string}
|
|
10053
|
+
*/
|
|
10054
|
+
function encodeTerseSig(sig) {
|
|
10055
|
+
const { text, suffix } = splitAnchor(sig);
|
|
10056
|
+
let t = text
|
|
10057
|
+
.replace(/\basync function\b/g, 'async fn')
|
|
10058
|
+
.replace(/\bfunction\b/g, 'fn')
|
|
10059
|
+
.replace(/\s+→\s+/g, '→')
|
|
10060
|
+
.replace(/,\s+/g, ',')
|
|
10061
|
+
.replace(/\s+=\s+/g, '=')
|
|
10062
|
+
.replace(/\{\s+/g, '{')
|
|
10063
|
+
.replace(/\s+\}/g, '}')
|
|
10064
|
+
.replace(/(\S)\s{2,}(?=\S)/g, '$1 ')
|
|
10065
|
+
.replace(/^module\.exports=/, 'exports=');
|
|
10066
|
+
return t + suffix;
|
|
10067
|
+
}
|
|
10068
|
+
|
|
10069
|
+
/**
|
|
10070
|
+
* Compact an array of signature lines.
|
|
10071
|
+
* @param {string[]} sigs
|
|
10072
|
+
* @returns {string[]}
|
|
10073
|
+
*/
|
|
10074
|
+
function encodeTerseSigs(sigs) {
|
|
10075
|
+
return (sigs || []).map(encodeTerseSig);
|
|
10076
|
+
}
|
|
10077
|
+
|
|
10078
|
+
/** Estimated tokens of joined signature lines (same chars/4 rule as elsewhere). */
|
|
10079
|
+
function _tokens(sigs) {
|
|
10080
|
+
return Math.ceil(sigs.join('\n').length / 4);
|
|
10081
|
+
}
|
|
10082
|
+
|
|
10083
|
+
/**
|
|
10084
|
+
* Measure the real reduction terse encoding buys over a set of signature
|
|
10085
|
+
* lists — the D7 "measure first" gate. Never quote a number this didn't produce.
|
|
10086
|
+
* @param {string[][]} sigsList one string[] per file
|
|
10087
|
+
* @returns {{ beforeTokens: number, afterTokens: number, reductionPct: number }}
|
|
10088
|
+
*/
|
|
10089
|
+
function measureTerse(sigsList) {
|
|
10090
|
+
let beforeTokens = 0;
|
|
10091
|
+
let afterTokens = 0;
|
|
10092
|
+
for (const sigs of sigsList || []) {
|
|
10093
|
+
if (!sigs || !sigs.length) continue;
|
|
10094
|
+
beforeTokens += _tokens(sigs);
|
|
10095
|
+
afterTokens += _tokens(encodeTerseSigs(sigs));
|
|
10096
|
+
}
|
|
10097
|
+
const reductionPct = beforeTokens > 0
|
|
10098
|
+
? Math.round(((beforeTokens - afterTokens) / beforeTokens) * 1000) / 10
|
|
10099
|
+
: 0;
|
|
10100
|
+
return { beforeTokens, afterTokens, reductionPct };
|
|
10101
|
+
}
|
|
10102
|
+
|
|
10103
|
+
module.exports = { encodeTerseSig, encodeTerseSigs, measureTerse, splitAnchor };
|
|
10104
|
+
|
|
10105
|
+
};
|
|
10106
|
+
|
|
10014
10107
|
// ── ./src/format/usage-guidance ──
|
|
10015
10108
|
__factories["./src/format/usage-guidance"] = function(module, exports) {
|
|
10016
10109
|
|
|
@@ -13950,7 +14043,7 @@ __factories["./src/mcp/server"] = function(module, exports) {
|
|
|
13950
14043
|
|
|
13951
14044
|
const SERVER_INFO = {
|
|
13952
14045
|
name: 'sigmap',
|
|
13953
|
-
version: '8.
|
|
14046
|
+
version: '8.11.0',
|
|
13954
14047
|
description: 'SigMap MCP server — code signatures on demand',
|
|
13955
14048
|
};
|
|
13956
14049
|
|
|
@@ -18640,7 +18733,7 @@ function __tryGit(args, opts = {}) {
|
|
|
18640
18733
|
catch (_) { return ''; }
|
|
18641
18734
|
}
|
|
18642
18735
|
|
|
18643
|
-
const VERSION = '8.
|
|
18736
|
+
const VERSION = '8.11.0';
|
|
18644
18737
|
const MARKER = '\n\n## Auto-generated signatures\n<!-- Updated by gen-context.js -->\n';
|
|
18645
18738
|
|
|
18646
18739
|
function requireSourceOrBundled(key) {
|
|
@@ -19317,6 +19410,21 @@ function formatOutput(fileEntries, cwd, routingEnabled, config, extras) {
|
|
|
19317
19410
|
groups[group].push({ rel, sigs: entry.sigs });
|
|
19318
19411
|
}
|
|
19319
19412
|
|
|
19413
|
+
// D7: terse signature encoding — anchors preserved byte-exactly
|
|
19414
|
+
const terseOn = !!(config && config.terse);
|
|
19415
|
+
let encodeTerseSigs = null;
|
|
19416
|
+
if (terseOn) {
|
|
19417
|
+
try {
|
|
19418
|
+
const terseMod = requireSourceOrBundled('./src/format/terse');
|
|
19419
|
+
encodeTerseSigs = terseMod.encodeTerseSigs;
|
|
19420
|
+
const m = terseMod.measureTerse(fileEntries.map((e) => e.sigs));
|
|
19421
|
+
console.warn(`[sigmap] terse: sig block ${m.beforeTokens} → ${m.afterTokens} tokens (-${m.reductionPct}%)`);
|
|
19422
|
+
} catch (err) {
|
|
19423
|
+
encodeTerseSigs = null;
|
|
19424
|
+
console.warn(`[sigmap] terse encoding skipped: ${err.message}`);
|
|
19425
|
+
}
|
|
19426
|
+
}
|
|
19427
|
+
|
|
19320
19428
|
for (const [group, entries] of Object.entries(groups).sort()) {
|
|
19321
19429
|
lines.push(`## ${group}`);
|
|
19322
19430
|
lines.push('');
|
|
@@ -19326,7 +19434,7 @@ function formatOutput(fileEntries, cwd, routingEnabled, config, extras) {
|
|
|
19326
19434
|
const usedByStr = usedBy && usedBy.length ? ` (used by: ${usedBy.join(', ')})` : '';
|
|
19327
19435
|
lines.push(`### ${rel}${usedByStr}`);
|
|
19328
19436
|
lines.push('```');
|
|
19329
|
-
lines.push(...sigs);
|
|
19437
|
+
lines.push(...(encodeTerseSigs ? encodeTerseSigs(sigs) : sigs));
|
|
19330
19438
|
lines.push('```');
|
|
19331
19439
|
lines.push('');
|
|
19332
19440
|
}
|
|
@@ -20440,6 +20548,7 @@ Usage:
|
|
|
20440
20548
|
${cmd} --monorepo Generate per-package context (monorepo)
|
|
20441
20549
|
${cmd} --each Run for every repo in the current directory
|
|
20442
20550
|
${cmd} --routing Include model routing hints in output
|
|
20551
|
+
${cmd} --terse Compact signature encoding (deterministic; line anchors preserved)
|
|
20443
20552
|
${cmd} --format cache Also write Anthropic prompt-cache JSON
|
|
20444
20553
|
${cmd} --track Append run metrics to .context/usage.ndjson
|
|
20445
20554
|
${cmd} --watch Generate + watch for file changes
|
|
@@ -20826,6 +20935,11 @@ function main() {
|
|
|
20826
20935
|
config.testCoverage = true;
|
|
20827
20936
|
}
|
|
20828
20937
|
|
|
20938
|
+
// --terse: compact signature encoding without editing config (D7)
|
|
20939
|
+
if (args.includes('--terse')) {
|
|
20940
|
+
config.terse = true;
|
|
20941
|
+
}
|
|
20942
|
+
|
|
20829
20943
|
// ── --output <file> — parse early so every subsequent block can use it ─────
|
|
20830
20944
|
// Resolves the custom output path and merges it into config.customOutput.
|
|
20831
20945
|
// Also persists the resolved relative path to gen-context.config.json so
|
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.
|
|
14
|
+
# Version: 8.11.0 | Benchmark: sigmap-v8.11-main (2026-07-11)
|
|
15
15
|
# Source: auto-generated from package.json, version.json, benchmarks/latest.json, src/mcp/tools.js, src/config/defaults.js
|
|
16
16
|
# Regenerate: npm run generate:llms | Validate: npm run validate:llms
|
|
17
17
|
|
|
18
18
|
---
|
|
19
19
|
|
|
20
|
-
## Core metrics (benchmark: sigmap-v8.
|
|
20
|
+
## Core metrics (benchmark: sigmap-v8.11-main, 2026-07-11)
|
|
21
21
|
|
|
22
22
|
| Metric | Without SigMap | With SigMap |
|
|
23
23
|
|--------|----------------|-------------|
|
|
@@ -55,6 +55,7 @@ sigmap Generate context once and exit
|
|
|
55
55
|
sigmap --monorepo Generate per-package context (monorepo)
|
|
56
56
|
sigmap --each Run for every repo in the current directory
|
|
57
57
|
sigmap --routing Include model routing hints in output
|
|
58
|
+
sigmap --terse Compact signature encoding (deterministic; line anchors preserved)
|
|
58
59
|
sigmap --format cache Also write Anthropic prompt-cache JSON
|
|
59
60
|
sigmap --track Append run metrics to .context/usage.ndjson
|
|
60
61
|
sigmap --watch Generate + watch for file changes
|
|
@@ -325,6 +326,7 @@ tracking = false
|
|
|
325
326
|
mcp = {"autoRegister":true}
|
|
326
327
|
depMap = true
|
|
327
328
|
versionPins = true
|
|
329
|
+
terse = false
|
|
328
330
|
todos = true
|
|
329
331
|
changes = true
|
|
330
332
|
changesCommits = 10
|
package/llms.txt
CHANGED
|
@@ -11,7 +11,7 @@ ranking keeps the relevant context in scope (cutting tokens ~97% as a side
|
|
|
11
11
|
effect), with no LLM calls, embeddings, or vector database. Works with Claude,
|
|
12
12
|
Cursor, GitHub Copilot, Aider, Windsurf, local LLMs, and MCP.
|
|
13
13
|
|
|
14
|
-
# Version: 8.
|
|
14
|
+
# Version: 8.11.0 | Benchmark: sigmap-v8.11-main (2026-07-11)
|
|
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.
|
|
26
|
+
## Core metrics (benchmark: sigmap-v8.11-main, 2026-07-11)
|
|
27
27
|
|
|
28
28
|
- hit@5 retrieval: 87.8% vs 13.6% random baseline (6.5× lift)
|
|
29
29
|
- Token reduction: 97.0% average across benchmark repos
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sigmap",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.11.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": {
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"benchmark:verify": "node scripts/run-verify-benchmark.mjs",
|
|
30
30
|
"benchmark:squeeze": "node scripts/run-squeeze-benchmark.mjs --save",
|
|
31
31
|
"benchmark:test-discovery": "node scripts/run-test-discovery-benchmark.mjs --save",
|
|
32
|
+
"benchmark:terse": "node scripts/run-terse-benchmark.mjs --save",
|
|
32
33
|
"validate:squeeze": "node scripts/run-squeeze-benchmark.mjs --gate",
|
|
33
34
|
"health": "node gen-context.js --health",
|
|
34
35
|
"map": "node gen-project-map.js",
|
package/src/config/defaults.js
CHANGED
|
@@ -117,6 +117,9 @@ const DEFAULTS = {
|
|
|
117
117
|
// Include a compact `name@version` list of installed direct deps (D8)
|
|
118
118
|
versionPins: true,
|
|
119
119
|
|
|
120
|
+
// Terse signature encoding — deterministic compaction of sig lines (D7, opt-in)
|
|
121
|
+
terse: false,
|
|
122
|
+
|
|
120
123
|
// Include TODO/FIXME/HACK/XXX comments as compact section
|
|
121
124
|
todos: true,
|
|
122
125
|
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Terse signature encoder (D7).
|
|
5
|
+
*
|
|
6
|
+
* Deterministic compaction of signature lines for the generated context —
|
|
7
|
+
* opt-in via `--terse` / `terse: true`. Every transform is a fixed string
|
|
8
|
+
* rewrite (no heuristics, no LLM), so terse output stays byte-stable.
|
|
9
|
+
*
|
|
10
|
+
* The line anchor (` :start-end`) and everything after it (Python/R doc
|
|
11
|
+
* hints) are preserved byte-exactly: `parseAnchor`, `get_lines`, and the
|
|
12
|
+
* evidence pack keep working on terse output. Symbol extraction is safe too —
|
|
13
|
+
* `extractName` in src/extractors/prdiff.js already recognizes `fn <name>`.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** First ` :start[-end]` anchor token (two-space prefix, as emitted by line-anchor.js). */
|
|
17
|
+
const ANCHOR_RE = /\s{2}:\d+(?:-\d+)?(?=\s|$)/;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Split a signature into the compactable text and the byte-preserved suffix
|
|
21
|
+
* (anchor + any trailing doc hint).
|
|
22
|
+
* @param {string} sig
|
|
23
|
+
* @returns {{ text: string, suffix: string }}
|
|
24
|
+
*/
|
|
25
|
+
function splitAnchor(sig) {
|
|
26
|
+
const s = String(sig);
|
|
27
|
+
const m = ANCHOR_RE.exec(s);
|
|
28
|
+
if (!m) return { text: s, suffix: '' };
|
|
29
|
+
return { text: s.slice(0, m.index), suffix: s.slice(m.index) };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Compact one signature line. Leading whitespace (member indentation) is kept.
|
|
34
|
+
* @param {string} sig
|
|
35
|
+
* @returns {string}
|
|
36
|
+
*/
|
|
37
|
+
function encodeTerseSig(sig) {
|
|
38
|
+
const { text, suffix } = splitAnchor(sig);
|
|
39
|
+
let t = text
|
|
40
|
+
.replace(/\basync function\b/g, 'async fn')
|
|
41
|
+
.replace(/\bfunction\b/g, 'fn')
|
|
42
|
+
.replace(/\s+→\s+/g, '→')
|
|
43
|
+
.replace(/,\s+/g, ',')
|
|
44
|
+
.replace(/\s+=\s+/g, '=')
|
|
45
|
+
.replace(/\{\s+/g, '{')
|
|
46
|
+
.replace(/\s+\}/g, '}')
|
|
47
|
+
.replace(/(\S)\s{2,}(?=\S)/g, '$1 ')
|
|
48
|
+
.replace(/^module\.exports=/, 'exports=');
|
|
49
|
+
return t + suffix;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Compact an array of signature lines.
|
|
54
|
+
* @param {string[]} sigs
|
|
55
|
+
* @returns {string[]}
|
|
56
|
+
*/
|
|
57
|
+
function encodeTerseSigs(sigs) {
|
|
58
|
+
return (sigs || []).map(encodeTerseSig);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Estimated tokens of joined signature lines (same chars/4 rule as elsewhere). */
|
|
62
|
+
function _tokens(sigs) {
|
|
63
|
+
return Math.ceil(sigs.join('\n').length / 4);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Measure the real reduction terse encoding buys over a set of signature
|
|
68
|
+
* lists — the D7 "measure first" gate. Never quote a number this didn't produce.
|
|
69
|
+
* @param {string[][]} sigsList one string[] per file
|
|
70
|
+
* @returns {{ beforeTokens: number, afterTokens: number, reductionPct: number }}
|
|
71
|
+
*/
|
|
72
|
+
function measureTerse(sigsList) {
|
|
73
|
+
let beforeTokens = 0;
|
|
74
|
+
let afterTokens = 0;
|
|
75
|
+
for (const sigs of sigsList || []) {
|
|
76
|
+
if (!sigs || !sigs.length) continue;
|
|
77
|
+
beforeTokens += _tokens(sigs);
|
|
78
|
+
afterTokens += _tokens(encodeTerseSigs(sigs));
|
|
79
|
+
}
|
|
80
|
+
const reductionPct = beforeTokens > 0
|
|
81
|
+
? Math.round(((beforeTokens - afterTokens) / beforeTokens) * 1000) / 10
|
|
82
|
+
: 0;
|
|
83
|
+
return { beforeTokens, afterTokens, reductionPct };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
module.exports = { encodeTerseSig, encodeTerseSigs, measureTerse, splitAnchor };
|
package/src/mcp/server.js
CHANGED