sigmap 8.32.0 → 8.32.1
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 +13 -0
- package/README.md +10 -10
- package/gen-context.js +84 -110
- package/llms-full.txt +7 -7
- package/llms.txt +5 -5
- package/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/core/package.json +1 -1
- package/src/config/loader.js +53 -3
- package/src/eval/analyzer.js +0 -1
- package/src/extractors/dispatch.js +0 -1
- package/src/extractors/markdown.js +6 -1
- package/src/extractors/properties.js +6 -1
- package/src/extractors/r.js +4 -8
- package/src/extractors/toml.js +6 -1
- package/src/extractors/typescript_react.js +6 -1
- package/src/mcp/server.js +1 -1
- package/src/extractors/vue.js +0 -86
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,18 @@ Format: [Semantic Versioning](https://semver.org/)
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
+
## [8.32.1] — 2026-09-13
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- **Retraction of a v8.32.0 claim.** That release stated "every extractor now discloses what a ceiling dropped". It did not. `vue.js` was registered but unreachable (`.vue` dispatches to `vue_sfc`), so disclosure was added to dead code while the live `.vue` handler kept truncating silently; `.tsx` (every React component), `.properties`, `.toml` and `.md` still cut output with a bare `slice()`; and `r.js` called `capWithNotice` but eight inner caps stopped collection at the ceiling so it never fired — and when forced to, reported `+1 more` where 50 signatures were hidden. All fixed, ceilings unchanged, with a test that fails against the pre-fix extractors. The root cause was that three of those languages have no test fixture (#582, #583, #584, #588)
|
|
18
|
+
- The walk depth hid most code in JVM package layouts: `maxDepth: 6` indexed **6 of 47** Java files on spring-petclinic, because Java puts one directory per package segment. #561 had already raised the *graph* walk to 12, so extraction was the shallower half of an inconsistent pair. Depth now rises to 12 only for JVM layouts; an explicit `maxDepth` always wins. Gated JVM corpus 16.4% → 23.0% (#590)
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- Published retrieval hit@5 moves 81.1% → 78.9%. One of 18 repos accounts for it: spring-petclinic 100% → 60%, previously measured against an index holding 6 of 47 Java files. The leak-free `mined` corpus is flat and the leak-free `jvm` corpus improves, so the prior figure was inflated by under-indexing rather than this being a ranking regression. The two affected tasks are tracked as a ranking weakness the missing files were concealing (#592)
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
13
25
|
## [8.32.0] — 2026-09-12
|
|
14
26
|
|
|
15
27
|
### Added
|
|
@@ -18,6 +30,7 @@ Format: [Semantic Versioning](https://semver.org/)
|
|
|
18
30
|
|
|
19
31
|
### Fixed
|
|
20
32
|
- 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)
|
|
33
|
+
> **Correction (2026-09-13):** "every extractor" was wrong as published. `vue.js` was dead code — `.vue` dispatches to `vue_sfc` — so one of the 20 was unreachable; four reachable extractors (`.tsx`, `.properties`, `.toml`, `.md`) still truncated silently; and `r.js`'s disclosure was defeated by eight inner caps. Corrected in #589 (#582, #583, #584).
|
|
21
34
|
- 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
35
|
- 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
36
|
|
package/README.md
CHANGED
|
@@ -59,12 +59,12 @@ That map is exactly what agentic grep is worst at: reproducible, auditable conte
|
|
|
59
59
|
|
|
60
60
|
**Proof it pays off** (full benchmark below):
|
|
61
61
|
<!--SM:whyMetrics-->
|
|
62
|
-
- **
|
|
62
|
+
- **78.9% hit@5** — right file in top 5 results (vs 44.0% single-shot grep baseline — 1.73× lift)
|
|
63
63
|
- **96.8% token reduction** — average across 21 real repos
|
|
64
|
-
- **
|
|
65
|
-
- **1.
|
|
64
|
+
- **62.9% task-success proxy** — modeled from retrieval tiers, not measured LLM sessions
|
|
65
|
+
- **1.58 prompts per task** — down from 2.84 (44.4% fewer retries, modeled)
|
|
66
66
|
<!--/SM:whyMetrics-->
|
|
67
|
-
- **<!--SM:languages-->
|
|
67
|
+
- **<!--SM:languages-->32<!--/SM:languages--> languages supported** — TypeScript, Python, Go, Rust, Java, R, and more
|
|
68
68
|
- **No vendor lock-in** — works with any AI assistant or local LLM
|
|
69
69
|
- **No API costs** — use local models (Ollama, llama.cpp, vLLM) with zero token fees
|
|
70
70
|
- **Full privacy** — keep your code and context on your machine
|
|
@@ -98,7 +98,7 @@ sigmap verify answer.md --report # standalone red/amber/green HTML report
|
|
|
98
98
|
| Without SigMap | With SigMap |
|
|
99
99
|
|---|---|
|
|
100
100
|
| ❌ Non-reproducible agent guesses | ✅ Deterministic map — same input, same output, every time |
|
|
101
|
-
| ❌ "Trust me" AI answers | ✅ Grounded — right file in context <!--SM:hitWhole-->
|
|
101
|
+
| ❌ "Trust me" AI answers | ✅ Grounded — right file in context <!--SM:hitWhole-->79%<!--/SM:hitWhole--> of the time, every symbol on a real line anchor |
|
|
102
102
|
| ❌ Embeddings / vector DB required | ✅ Zero deps, no infra, fully offline |
|
|
103
103
|
|
|
104
104
|
---
|
|
@@ -125,10 +125,10 @@ Ask → Rank → Context → Validate → Judge → Learn
|
|
|
125
125
|
Benchmark : sigmap-v8.32-main (21 repositories, including R language)
|
|
126
126
|
Date : 2026-09-12
|
|
127
127
|
|
|
128
|
-
Hit@5 :
|
|
128
|
+
Hit@5 : 78.9% (grep-agent baseline 44.0% — 1.73× lift)
|
|
129
129
|
Token reduction: 96.8% (across 21 repos)
|
|
130
|
-
Prompt reduction :
|
|
131
|
-
Task success :
|
|
130
|
+
Prompt reduction : 44.4% (2.84 → 1.58 prompts per task, modeled)
|
|
131
|
+
Task success : 62.9% (proxy — modeled from retrieval tiers)
|
|
132
132
|
Repos tested : 21 (JavaScript, Python, Go, Rust, Java, R, C++, C#, Dart, Swift, Ruby, PHP, Scala, Kotlin, and more)
|
|
133
133
|
```
|
|
134
134
|
<!--/SM:benchmarkBlock-->
|
|
@@ -332,7 +332,7 @@ sigmap --health
|
|
|
332
332
|
| Benchmark methodology | [benchmark.html](https://sigmap.io/guide/benchmark.html) |
|
|
333
333
|
| Config reference | [config.html](https://sigmap.io/guide/config.html) |
|
|
334
334
|
| Roadmap | [roadmap.html](https://sigmap.io/guide/roadmap.html) |
|
|
335
|
-
| <!--SM:languages-->
|
|
335
|
+
| <!--SM:languages-->32<!--/SM:languages--> languages | [generalization.html](https://sigmap.io/guide/generalization.html) |
|
|
336
336
|
|
|
337
337
|
---
|
|
338
338
|
|
|
@@ -389,7 +389,7 @@ See [.github/PULL_REQUEST_TEMPLATE.md](.github/PULL_REQUEST_TEMPLATE.md) for the
|
|
|
389
389
|
|
|
390
390
|
---
|
|
391
391
|
|
|
392
|
-
## <!--SM:languages-->
|
|
392
|
+
## <!--SM:languages-->32<!--/SM:languages--> languages
|
|
393
393
|
|
|
394
394
|
TypeScript · JavaScript · Python · Java · Kotlin · Go · Rust · C# · C/C++ · Ruby · PHP · Swift · Dart · Scala · Vue · Svelte · HTML · CSS/SCSS · YAML · Shell · SQL · GraphQL · Terraform · Protobuf · Dockerfile · TOML · XML · Properties · Markdown · R · GDScript
|
|
395
395
|
|
package/gen-context.js
CHANGED
|
@@ -1790,6 +1790,55 @@ __factories["./src/config/loader"] = function(module, exports) {
|
|
|
1790
1790
|
});
|
|
1791
1791
|
}
|
|
1792
1792
|
|
|
1793
|
+
/**
|
|
1794
|
+
* Directory depth needed to reach source under a JVM package layout.
|
|
1795
|
+
*
|
|
1796
|
+
* `maxDepth: 6` is right for the JS/Python-shaped trees it was tuned on, but a
|
|
1797
|
+
* JVM project puts one directory per package segment — real code in
|
|
1798
|
+
* `src/main/java/com/company/project/module/Class.java` sits 8-10 levels down.
|
|
1799
|
+
* At depth 6 only the top-level package was indexed: on spring-petclinic, 6 of
|
|
1800
|
+
* 47 Java files (#590).
|
|
1801
|
+
*
|
|
1802
|
+
* #561 already raised the dependency-graph walk to 12 for exactly this reason,
|
|
1803
|
+
* so extraction was the shallower half of an inconsistent pair.
|
|
1804
|
+
*
|
|
1805
|
+
* Deepening globally is not free — it adds candidates to every repo and cost
|
|
1806
|
+
* 2.2pp on the (filename-leaky) 105-task matrix corpus while the unbiased
|
|
1807
|
+
* `mined` corpus stayed flat. So the depth is raised only where the layout
|
|
1808
|
+
* demands it, leaving non-JVM repos byte-identical.
|
|
1809
|
+
*
|
|
1810
|
+
* @param {string} cwd
|
|
1811
|
+
* @returns {boolean} true when the repo looks like a Maven/Gradle/sbt project
|
|
1812
|
+
*/
|
|
1813
|
+
function _isJvmLayout(cwd) {
|
|
1814
|
+
const MARKERS = ['pom.xml', 'build.gradle', 'build.gradle.kts', 'build.sbt', 'settings.gradle', 'settings.gradle.kts'];
|
|
1815
|
+
for (const m of MARKERS) {
|
|
1816
|
+
try { if (fs.existsSync(path.join(cwd, m))) return true; } catch { /* unreadable cwd */ }
|
|
1817
|
+
}
|
|
1818
|
+
for (const d of ['src/main/java', 'src/main/kotlin', 'src/main/scala']) {
|
|
1819
|
+
try { if (fs.existsSync(path.join(cwd, d))) return true; } catch { /* ignore */ }
|
|
1820
|
+
}
|
|
1821
|
+
return false;
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
/** Walk depth for a JVM package layout — matches the graph walk from #561. */
|
|
1825
|
+
const JVM_MAX_DEPTH = 12;
|
|
1826
|
+
|
|
1827
|
+
/**
|
|
1828
|
+
* Raise `maxDepth` to the JVM depth when the layout needs it (#590).
|
|
1829
|
+
* An explicit user value always wins, including a deliberately shallow one.
|
|
1830
|
+
* @param {object} cfg resolved config (mutated and returned)
|
|
1831
|
+
* @param {string} cwd
|
|
1832
|
+
* @param {boolean} userSetDepth
|
|
1833
|
+
* @returns {object} cfg
|
|
1834
|
+
*/
|
|
1835
|
+
function _applyJvmDepth(cfg, cwd, userSetDepth) {
|
|
1836
|
+
if (!userSetDepth && cfg.maxDepth < JVM_MAX_DEPTH && _isJvmLayout(cwd)) {
|
|
1837
|
+
cfg.maxDepth = JVM_MAX_DEPTH;
|
|
1838
|
+
}
|
|
1839
|
+
return cfg;
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1793
1842
|
/**
|
|
1794
1843
|
* Load and merge configuration for a given working directory.
|
|
1795
1844
|
*
|
|
@@ -1802,7 +1851,7 @@ __factories["./src/config/loader"] = function(module, exports) {
|
|
|
1802
1851
|
const cfg = deepClone(DEFAULTS);
|
|
1803
1852
|
const detected = detectAutoSrcDirs(cwd, cfg.exclude);
|
|
1804
1853
|
if (detected.length > 0) cfg.srcDirs = detected;
|
|
1805
|
-
return cfg;
|
|
1854
|
+
return _applyJvmDepth(cfg, cwd, false);
|
|
1806
1855
|
}
|
|
1807
1856
|
|
|
1808
1857
|
let userConfig;
|
|
@@ -1814,7 +1863,7 @@ __factories["./src/config/loader"] = function(module, exports) {
|
|
|
1814
1863
|
const cfg = deepClone(DEFAULTS);
|
|
1815
1864
|
const detected = detectAutoSrcDirs(cwd, cfg.exclude);
|
|
1816
1865
|
if (detected.length > 0) cfg.srcDirs = detected;
|
|
1817
|
-
return cfg;
|
|
1866
|
+
return _applyJvmDepth(cfg, cwd, false);
|
|
1818
1867
|
}
|
|
1819
1868
|
|
|
1820
1869
|
// Warn on unknown keys (helps catch typos)
|
|
@@ -1866,7 +1915,8 @@ __factories["./src/config/loader"] = function(module, exports) {
|
|
|
1866
1915
|
} else if (Array.isArray(merged.adapters) && !userConfig.outputs) {
|
|
1867
1916
|
merged.outputs = merged.adapters.filter((a) => ['copilot','claude','cursor','windsurf'].includes(a));
|
|
1868
1917
|
}
|
|
1869
|
-
|
|
1918
|
+
|
|
1919
|
+
return _applyJvmDepth(merged, cwd, userConfig.maxDepth !== undefined);
|
|
1870
1920
|
}
|
|
1871
1921
|
|
|
1872
1922
|
function deepClone(obj) {
|
|
@@ -4118,7 +4168,6 @@ __factories["./src/eval/analyzer"] = function(module, exports) {
|
|
|
4118
4168
|
'.scala': 'scala', '.sc': 'scala',
|
|
4119
4169
|
'.gd': 'gdscript',
|
|
4120
4170
|
'.r': 'r', '.R': 'r',
|
|
4121
|
-
'.vue': 'vue',
|
|
4122
4171
|
'.svelte': 'svelte',
|
|
4123
4172
|
'.html': 'html', '.htm': 'html',
|
|
4124
4173
|
'.css': 'css', '.scss': 'css', '.sass': 'css', '.less': 'css',
|
|
@@ -5922,7 +5971,6 @@ __factories["./src/extractors/dispatch"] = function(module, exports) {
|
|
|
5922
5971
|
scala: __require('./src/extractors/scala'),
|
|
5923
5972
|
gdscript: __require('./src/extractors/gdscript'),
|
|
5924
5973
|
r: __require('./src/extractors/r'),
|
|
5925
|
-
vue: __require('./src/extractors/vue'),
|
|
5926
5974
|
vue_sfc: __require('./src/extractors/vue_sfc'),
|
|
5927
5975
|
svelte: __require('./src/extractors/svelte'),
|
|
5928
5976
|
html: __require('./src/extractors/html'),
|
|
@@ -6986,6 +7034,11 @@ __factories["./src/extractors/line-anchor"] = function(module, exports) {
|
|
|
6986
7034
|
// ── ./src/extractors/markdown ──
|
|
6987
7035
|
__factories["./src/extractors/markdown"] = function(module, exports) {
|
|
6988
7036
|
|
|
7037
|
+
const { capWithNotice } = __require('./src/util/truncate');
|
|
7038
|
+
|
|
7039
|
+
// Ceiling discloses what it drops rather than truncating silently (#583).
|
|
7040
|
+
const PER_FILE_LIMIT = 40;
|
|
7041
|
+
|
|
6989
7042
|
/**
|
|
6990
7043
|
* Lightweight markdown technical indexer.
|
|
6991
7044
|
* Captures headings and fenced code block language hints only.
|
|
@@ -7010,7 +7063,7 @@ __factories["./src/extractors/markdown"] = function(module, exports) {
|
|
|
7010
7063
|
sigs.push(`code-fence ${lang}`);
|
|
7011
7064
|
}
|
|
7012
7065
|
|
|
7013
|
-
return Array.from(new Set(sigs))
|
|
7066
|
+
return capWithNotice(Array.from(new Set(sigs)), PER_FILE_LIMIT, 'headings');
|
|
7014
7067
|
}
|
|
7015
7068
|
|
|
7016
7069
|
module.exports = { extract };
|
|
@@ -7341,6 +7394,11 @@ __factories["./src/extractors/prdiff"] = function(module, exports) {
|
|
|
7341
7394
|
// ── ./src/extractors/properties ──
|
|
7342
7395
|
__factories["./src/extractors/properties"] = function(module, exports) {
|
|
7343
7396
|
|
|
7397
|
+
const { capWithNotice } = __require('./src/util/truncate');
|
|
7398
|
+
|
|
7399
|
+
// Ceiling discloses what it drops rather than truncating silently (#583).
|
|
7400
|
+
const PER_FILE_LIMIT = 50;
|
|
7401
|
+
|
|
7344
7402
|
/**
|
|
7345
7403
|
* Extract signatures from .properties configuration files.
|
|
7346
7404
|
* Captures key names, grouped by prefixes where possible.
|
|
@@ -7372,7 +7430,7 @@ __factories["./src/extractors/properties"] = function(module, exports) {
|
|
|
7372
7430
|
sigs.push(`key ${key}`);
|
|
7373
7431
|
}
|
|
7374
7432
|
|
|
7375
|
-
return Array.from(new Set(sigs))
|
|
7433
|
+
return capWithNotice(Array.from(new Set(sigs)), PER_FILE_LIMIT, 'keys');
|
|
7376
7434
|
}
|
|
7377
7435
|
|
|
7378
7436
|
module.exports = { extract };
|
|
@@ -7845,7 +7903,7 @@ __factories["./src/extractors/r"] = function(module, exports) {
|
|
|
7845
7903
|
// ClassName <- R6::R6Class(...)
|
|
7846
7904
|
const r6Re = /([\w.]+)\s*(?:<<-|<-|=)\s*(?:R6::)?R6Class\s*\(/g;
|
|
7847
7905
|
let m;
|
|
7848
|
-
while ((m = r6Re.exec(stripped)) !== null
|
|
7906
|
+
while ((m = r6Re.exec(stripped)) !== null) {
|
|
7849
7907
|
const name = m[1];
|
|
7850
7908
|
if (name.startsWith('.')) continue;
|
|
7851
7909
|
const openIdx = r6Re.lastIndex - 1;
|
|
@@ -7856,7 +7914,6 @@ __factories["./src/extractors/r"] = function(module, exports) {
|
|
|
7856
7914
|
sigs.push(`${name} <- R6Class("${classNameLit}")` + applyHint(docHints, name));
|
|
7857
7915
|
for (const memberSig of extractListMethods(body, 8)) {
|
|
7858
7916
|
sigs.push(' ' + memberSig);
|
|
7859
|
-
if (sigs.length >= 30) break;
|
|
7860
7917
|
}
|
|
7861
7918
|
consumedRanges.push([m.index, closeIdx]);
|
|
7862
7919
|
r6Re.lastIndex = closeIdx;
|
|
@@ -7866,7 +7923,7 @@ __factories["./src/extractors/r"] = function(module, exports) {
|
|
|
7866
7923
|
// ClassName <- new_class("ClassName", properties = list(...))
|
|
7867
7924
|
const s7Classes = new Set();
|
|
7868
7925
|
const s7Re = /([\w.]+)\s*(?:<<-|<-|=)\s*(?:S7::)?new_class\s*\(/g;
|
|
7869
|
-
while ((m = s7Re.exec(stripped)) !== null
|
|
7926
|
+
while ((m = s7Re.exec(stripped)) !== null) {
|
|
7870
7927
|
const name = m[1];
|
|
7871
7928
|
if (name.startsWith('.')) continue;
|
|
7872
7929
|
const openIdx = s7Re.lastIndex - 1;
|
|
@@ -7883,7 +7940,7 @@ __factories["./src/extractors/r"] = function(module, exports) {
|
|
|
7883
7940
|
|
|
7884
7941
|
// S7 method dispatch: `method(generic, ClassName) <- function(args)`
|
|
7885
7942
|
const s7MethodRe = /^[ \t]*method\s*\(\s*([\w.]+)\s*,\s*([\w.]+)\s*\)\s*(?:<<-|<-|=)\s*function\s*\(/gm;
|
|
7886
|
-
while ((m = s7MethodRe.exec(stripped)) !== null
|
|
7943
|
+
while ((m = s7MethodRe.exec(stripped)) !== null) {
|
|
7887
7944
|
if (!s7Classes.has(m[2])) continue;
|
|
7888
7945
|
const argsStart = s7MethodRe.lastIndex - 1;
|
|
7889
7946
|
const args = readBalancedParens(stripped, argsStart);
|
|
@@ -7896,7 +7953,7 @@ __factories["./src/extractors/r"] = function(module, exports) {
|
|
|
7896
7953
|
// Skip matches whose position falls inside an R6/S7 class body — those have
|
|
7897
7954
|
// already been emitted as indented members.
|
|
7898
7955
|
const funcRe = /^(?:[ \t]*)([\w.]+)\s*(?:<<-|<-|=)\s*function\s*\(/gm;
|
|
7899
|
-
while ((m = funcRe.exec(stripped)) !== null
|
|
7956
|
+
while ((m = funcRe.exec(stripped)) !== null) {
|
|
7900
7957
|
const name = m[1];
|
|
7901
7958
|
if (name.startsWith('.')) continue;
|
|
7902
7959
|
if (inAnyRange(m.index, consumedRanges)) continue;
|
|
@@ -7908,15 +7965,12 @@ __factories["./src/extractors/r"] = function(module, exports) {
|
|
|
7908
7965
|
|
|
7909
7966
|
// ── S4 ────────────────────────────────────────────────────────────────────
|
|
7910
7967
|
for (const sm of stripped.matchAll(/^[ \t]*setGeneric\s*\(\s*["']([\w.]+)["']/gm)) {
|
|
7911
|
-
if (sigs.length >= 30) break;
|
|
7912
7968
|
sigs.push(`setGeneric("${sm[1]}")`);
|
|
7913
7969
|
}
|
|
7914
7970
|
for (const sm of stripped.matchAll(/^[ \t]*setMethod\s*\(\s*["']([\w.]+)["']\s*,\s*["']([\w.]+)["']/gm)) {
|
|
7915
|
-
if (sigs.length >= 30) break;
|
|
7916
7971
|
sigs.push(`setMethod("${sm[1]}", "${sm[2]}")`);
|
|
7917
7972
|
}
|
|
7918
7973
|
for (const sm of stripped.matchAll(/^[ \t]*setClass\s*\(\s*["']([\w.]+)["']/gm)) {
|
|
7919
|
-
if (sigs.length >= 30) break;
|
|
7920
7974
|
sigs.push(`setClass("${sm[1]}")`);
|
|
7921
7975
|
}
|
|
7922
7976
|
|
|
@@ -8925,6 +8979,11 @@ __factories["./src/extractors/todos"] = function(module, exports) {
|
|
|
8925
8979
|
// ── ./src/extractors/toml ──
|
|
8926
8980
|
__factories["./src/extractors/toml"] = function(module, exports) {
|
|
8927
8981
|
|
|
8982
|
+
const { capWithNotice } = __require('./src/util/truncate');
|
|
8983
|
+
|
|
8984
|
+
// Ceiling discloses what it drops rather than truncating silently (#583).
|
|
8985
|
+
const PER_FILE_LIMIT = 40;
|
|
8986
|
+
|
|
8928
8987
|
/**
|
|
8929
8988
|
* Extract signatures from TOML configuration files.
|
|
8930
8989
|
* Focuses on section/table names and high-value keys.
|
|
@@ -8961,7 +9020,7 @@ __factories["./src/extractors/toml"] = function(module, exports) {
|
|
|
8961
9020
|
}
|
|
8962
9021
|
}
|
|
8963
9022
|
|
|
8964
|
-
return Array.from(new Set(sigs))
|
|
9023
|
+
return capWithNotice(Array.from(new Set(sigs)), PER_FILE_LIMIT, 'entries');
|
|
8965
9024
|
}
|
|
8966
9025
|
|
|
8967
9026
|
module.exports = { extract };
|
|
@@ -9301,6 +9360,11 @@ __factories["./src/extractors/typescript"] = function(module, exports) {
|
|
|
9301
9360
|
// ── ./src/extractors/typescript_react ──
|
|
9302
9361
|
__factories["./src/extractors/typescript_react"] = function(module, exports) {
|
|
9303
9362
|
|
|
9363
|
+
const { capWithNotice } = __require('./src/util/truncate');
|
|
9364
|
+
|
|
9365
|
+
// Ceiling discloses what it drops rather than truncating silently (#583).
|
|
9366
|
+
const PER_FILE_LIMIT = 50;
|
|
9367
|
+
|
|
9304
9368
|
/**
|
|
9305
9369
|
* Extract React component signatures from .tsx files.
|
|
9306
9370
|
* Captures component props interfaces, hooks usage, and exports.
|
|
@@ -9355,97 +9419,7 @@ __factories["./src/extractors/typescript_react"] = function(module, exports) {
|
|
|
9355
9419
|
sigs.push(`handler on${h}`);
|
|
9356
9420
|
}
|
|
9357
9421
|
|
|
9358
|
-
return Array.from(new Set(sigs))
|
|
9359
|
-
}
|
|
9360
|
-
|
|
9361
|
-
module.exports = { extract };
|
|
9362
|
-
|
|
9363
|
-
};
|
|
9364
|
-
|
|
9365
|
-
// ── ./src/extractors/vue ──
|
|
9366
|
-
__factories["./src/extractors/vue"] = function(module, exports) {
|
|
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
|
-
|
|
9374
|
-
/**
|
|
9375
|
-
* Extract signatures from Vue single-file components.
|
|
9376
|
-
* @param {string} src - Raw file content
|
|
9377
|
-
* @returns {string[]} Array of signature strings
|
|
9378
|
-
*/
|
|
9379
|
-
function extract(src) {
|
|
9380
|
-
if (!src || typeof src !== 'string') return [];
|
|
9381
|
-
const sigs = [];
|
|
9382
|
-
|
|
9383
|
-
// Extract component name from filename hint if present or defineComponent
|
|
9384
|
-
const nameMatch = src.match(/name\s*:\s*['"](\w+)['"]/);
|
|
9385
|
-
if (nameMatch) sigs.push(`component ${nameMatch[1]}`);
|
|
9386
|
-
|
|
9387
|
-
// Extract <script> block
|
|
9388
|
-
const scriptMatch = src.match(/<script(?:\s[^>]*)?>(?:\s*)([\s\S]*?)<\/script>/i);
|
|
9389
|
-
if (!scriptMatch) return sigs;
|
|
9390
|
-
|
|
9391
|
-
const script = scriptMatch[1]
|
|
9392
|
-
.replace(/\/\/.*$/gm, '')
|
|
9393
|
-
.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
9394
|
-
|
|
9395
|
-
// Props
|
|
9396
|
-
const propsMatch = script.match(/props\s*:\s*(\{[\s\S]*?\})/);
|
|
9397
|
-
if (propsMatch) {
|
|
9398
|
-
const propNames = [];
|
|
9399
|
-
for (const m of propsMatch[1].matchAll(/^\s+(\w+)\s*:/gm)) {
|
|
9400
|
-
propNames.push(m[1]);
|
|
9401
|
-
}
|
|
9402
|
-
if (propNames.length > 0) sigs.push(`props: [${propNames.join(', ')}]`);
|
|
9403
|
-
}
|
|
9404
|
-
|
|
9405
|
-
// Methods in options API
|
|
9406
|
-
const methodsMatch = script.match(/methods\s*:\s*\{([\s\S]*?)\},?\s*(?:computed|watch|mounted|created|data|\})/);
|
|
9407
|
-
if (methodsMatch) {
|
|
9408
|
-
for (const m of methodsMatch[1].matchAll(/^\s+(?:async\s+)?(\w+)\s*\(([^)]*)\)(?:\s*:\s*([^{=\n]+))?/gm)) {
|
|
9409
|
-
if (m[1].startsWith('_')) continue;
|
|
9410
|
-
const asyncKw = m[0].includes('async') ? 'async ' : '';
|
|
9411
|
-
const retStr = m[3] ? ` → ${normalizeType(m[3])}` : '';
|
|
9412
|
-
sigs.push(` ${asyncKw}${m[1]}(${normalizeParams(m[2])})${retStr}`);
|
|
9413
|
-
}
|
|
9414
|
-
}
|
|
9415
|
-
|
|
9416
|
-
// Top-level functions in <script> (e.g., composition API helpers)
|
|
9417
|
-
for (const m of script.matchAll(/^(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(([^)]*)\)(?:\s*:\s*([^{=\n]+))?/gm)) {
|
|
9418
|
-
if (m[1].startsWith('_')) continue;
|
|
9419
|
-
const asyncKw = m[0].includes('async') ? 'async ' : '';
|
|
9420
|
-
const retStr = m[3] ? ` → ${normalizeType(m[3])}` : '';
|
|
9421
|
-
sigs.push(`${asyncKw}function ${m[1]}(${normalizeParams(m[2])})${retStr}`);
|
|
9422
|
-
}
|
|
9423
|
-
|
|
9424
|
-
// defineProps (Composition API)
|
|
9425
|
-
const definePropsMatch = script.match(/defineProps(?:<[^>]*>)?\s*\(\s*(\{[\s\S]*?\})\s*\)/);
|
|
9426
|
-
if (definePropsMatch) {
|
|
9427
|
-
const propNames = [];
|
|
9428
|
-
for (const m of definePropsMatch[1].matchAll(/^\s+(\w+)\s*:/gm)) {
|
|
9429
|
-
propNames.push(m[1]);
|
|
9430
|
-
}
|
|
9431
|
-
if (propNames.length > 0) sigs.push(`defineProps: [${propNames.join(', ')}]`);
|
|
9432
|
-
}
|
|
9433
|
-
|
|
9434
|
-
// Emits
|
|
9435
|
-
const emitsMatch = script.match(/(?:defineEmits|emits)\s*(?::\s*|\(\s*)(\[[\s\S]*?\])/);
|
|
9436
|
-
if (emitsMatch) sigs.push(`emits: ${emitsMatch[1].replace(/\s+/g, ' ')}`);
|
|
9437
|
-
|
|
9438
|
-
return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
|
|
9439
|
-
}
|
|
9440
|
-
|
|
9441
|
-
function normalizeParams(params) {
|
|
9442
|
-
if (!params) return '';
|
|
9443
|
-
return params.trim().replace(/\s+/g, ' ');
|
|
9444
|
-
}
|
|
9445
|
-
|
|
9446
|
-
function normalizeType(type) {
|
|
9447
|
-
if (!type) return '';
|
|
9448
|
-
return type.trim().replace(/[;\s]+$/g, '').replace(/\s+/g, ' ').slice(0, 25);
|
|
9422
|
+
return capWithNotice(Array.from(new Set(sigs)), PER_FILE_LIMIT, 'signatures');
|
|
9449
9423
|
}
|
|
9450
9424
|
|
|
9451
9425
|
module.exports = { extract };
|
|
@@ -15814,7 +15788,7 @@ __factories["./src/mcp/server"] = function(module, exports) {
|
|
|
15814
15788
|
|
|
15815
15789
|
const SERVER_INFO = {
|
|
15816
15790
|
name: 'sigmap',
|
|
15817
|
-
version: '8.32.
|
|
15791
|
+
version: '8.32.1',
|
|
15818
15792
|
description: 'SigMap MCP server — code signatures on demand',
|
|
15819
15793
|
};
|
|
15820
15794
|
|
|
@@ -22108,7 +22082,7 @@ function __tryGit(args, opts = {}) {
|
|
|
22108
22082
|
catch (_) { return ''; }
|
|
22109
22083
|
}
|
|
22110
22084
|
|
|
22111
|
-
const VERSION = '8.32.
|
|
22085
|
+
const VERSION = '8.32.1';
|
|
22112
22086
|
const MARKER = '\n\n## Auto-generated signatures\n<!-- Updated by gen-context.js -->\n';
|
|
22113
22087
|
|
|
22114
22088
|
function requireSourceOrBundled(key) {
|
|
@@ -26881,7 +26855,7 @@ function main() {
|
|
|
26881
26855
|
'.cs': 'csharp', '.cpp': 'cpp', '.rb': 'ruby', '.php': 'php',
|
|
26882
26856
|
'.swift': 'swift', '.dart': 'dart', '.scala': 'scala',
|
|
26883
26857
|
'.r': 'r', '.R': 'r',
|
|
26884
|
-
'.vue': '
|
|
26858
|
+
'.vue': 'vue_sfc', '.svelte': 'svelte', '.html': 'html',
|
|
26885
26859
|
'.css': 'css', '.yml': 'yaml', '.sh': 'shell',
|
|
26886
26860
|
};
|
|
26887
26861
|
const SPECIAL = { 'Dockerfile': 'dockerfile' };
|
package/llms-full.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.32.
|
|
14
|
+
# Version: 8.32.1 | 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
|
|
|
@@ -21,11 +21,11 @@ Cursor, GitHub Copilot, Aider, Windsurf, local LLMs, and MCP.
|
|
|
21
21
|
|
|
22
22
|
| Metric | Without SigMap | With SigMap |
|
|
23
23
|
|--------|----------------|-------------|
|
|
24
|
-
| Retrieval hit@5 | 44.0% (single-shot grep) |
|
|
24
|
+
| Retrieval hit@5 | 44.0% (single-shot grep) | 78.9% (1.73× lift) |
|
|
25
25
|
| Token reduction | — | 96.8% average |
|
|
26
|
-
| Task-success proxy (modeled) | — |
|
|
27
|
-
| Prompts per task | 2.84 | 1.
|
|
28
|
-
| Supported languages | — |
|
|
26
|
+
| Task-success proxy (modeled) | — | 62.9% |
|
|
27
|
+
| Prompts per task | 2.84 | 1.58 (44.4% fewer) |
|
|
28
|
+
| Supported languages | — | 32 |
|
|
29
29
|
| MCP tools | — | 21 |
|
|
30
30
|
| npm runtime dependencies | — | 0 |
|
|
31
31
|
|
|
@@ -370,9 +370,9 @@ impact = {"depth":3,"includeSigs":true}
|
|
|
370
370
|
|
|
371
371
|
---
|
|
372
372
|
|
|
373
|
-
## Supported languages (
|
|
373
|
+
## Supported languages (32 extractors)
|
|
374
374
|
|
|
375
|
-
cpp, csharp, css, dart, dockerfile, gdscript, go, graphql, html, java, javascript, kotlin, markdown, php, properties, protobuf, python, r, ruby, rust, scala, shell, sql, svelte, swift, terraform, toml, typescript, typescript_react,
|
|
375
|
+
cpp, csharp, css, dart, dockerfile, gdscript, go, graphql, html, java, javascript, kotlin, markdown, php, properties, protobuf, python, r, ruby, rust, scala, shell, sql, svelte, swift, terraform, toml, typescript, typescript_react, vue_sfc, xml, yaml
|
|
376
376
|
|
|
377
377
|
---
|
|
378
378
|
|
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.32.
|
|
14
|
+
# Version: 8.32.1 | 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
|
|
|
@@ -25,11 +25,11 @@ Cursor, GitHub Copilot, Aider, Windsurf, local LLMs, and MCP.
|
|
|
25
25
|
|
|
26
26
|
## Core metrics (benchmark: sigmap-v8.32-main, 2026-09-12)
|
|
27
27
|
|
|
28
|
-
- hit@5 retrieval:
|
|
28
|
+
- hit@5 retrieval: 78.9% vs 44.0% single-shot grep baseline (1.73× lift)
|
|
29
29
|
- Token reduction: 96.8% average across benchmark repos
|
|
30
|
-
- Task-success proxy:
|
|
31
|
-
- Prompts per task: 1.
|
|
32
|
-
- Languages:
|
|
30
|
+
- Task-success proxy: 62.9% (modeled from retrieval tiers, not measured LLM sessions)
|
|
31
|
+
- Prompts per task: 1.58 vs 2.84 baseline (44.4% fewer, modeled)
|
|
32
|
+
- Languages: 32 supported · MCP tools: 21
|
|
33
33
|
- Dependencies: zero npm runtime dependencies · fully offline
|
|
34
34
|
|
|
35
35
|
## Quick start
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sigmap",
|
|
3
|
-
"version": "8.32.
|
|
3
|
+
"version": "8.32.1",
|
|
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": {
|
package/src/config/loader.js
CHANGED
|
@@ -227,6 +227,55 @@ function _legacyDetectAutoSrcDirs(cwd, excludeList) {
|
|
|
227
227
|
});
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
+
/**
|
|
231
|
+
* Directory depth needed to reach source under a JVM package layout.
|
|
232
|
+
*
|
|
233
|
+
* `maxDepth: 6` is right for the JS/Python-shaped trees it was tuned on, but a
|
|
234
|
+
* JVM project puts one directory per package segment — real code in
|
|
235
|
+
* `src/main/java/com/company/project/module/Class.java` sits 8-10 levels down.
|
|
236
|
+
* At depth 6 only the top-level package was indexed: on spring-petclinic, 6 of
|
|
237
|
+
* 47 Java files (#590).
|
|
238
|
+
*
|
|
239
|
+
* #561 already raised the dependency-graph walk to 12 for exactly this reason,
|
|
240
|
+
* so extraction was the shallower half of an inconsistent pair.
|
|
241
|
+
*
|
|
242
|
+
* Deepening globally is not free — it adds candidates to every repo and cost
|
|
243
|
+
* 2.2pp on the (filename-leaky) 105-task matrix corpus while the unbiased
|
|
244
|
+
* `mined` corpus stayed flat. So the depth is raised only where the layout
|
|
245
|
+
* demands it, leaving non-JVM repos byte-identical.
|
|
246
|
+
*
|
|
247
|
+
* @param {string} cwd
|
|
248
|
+
* @returns {boolean} true when the repo looks like a Maven/Gradle/sbt project
|
|
249
|
+
*/
|
|
250
|
+
function _isJvmLayout(cwd) {
|
|
251
|
+
const MARKERS = ['pom.xml', 'build.gradle', 'build.gradle.kts', 'build.sbt', 'settings.gradle', 'settings.gradle.kts'];
|
|
252
|
+
for (const m of MARKERS) {
|
|
253
|
+
try { if (fs.existsSync(path.join(cwd, m))) return true; } catch { /* unreadable cwd */ }
|
|
254
|
+
}
|
|
255
|
+
for (const d of ['src/main/java', 'src/main/kotlin', 'src/main/scala']) {
|
|
256
|
+
try { if (fs.existsSync(path.join(cwd, d))) return true; } catch { /* ignore */ }
|
|
257
|
+
}
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** Walk depth for a JVM package layout — matches the graph walk from #561. */
|
|
262
|
+
const JVM_MAX_DEPTH = 12;
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Raise `maxDepth` to the JVM depth when the layout needs it (#590).
|
|
266
|
+
* An explicit user value always wins, including a deliberately shallow one.
|
|
267
|
+
* @param {object} cfg resolved config (mutated and returned)
|
|
268
|
+
* @param {string} cwd
|
|
269
|
+
* @param {boolean} userSetDepth
|
|
270
|
+
* @returns {object} cfg
|
|
271
|
+
*/
|
|
272
|
+
function _applyJvmDepth(cfg, cwd, userSetDepth) {
|
|
273
|
+
if (!userSetDepth && cfg.maxDepth < JVM_MAX_DEPTH && _isJvmLayout(cwd)) {
|
|
274
|
+
cfg.maxDepth = JVM_MAX_DEPTH;
|
|
275
|
+
}
|
|
276
|
+
return cfg;
|
|
277
|
+
}
|
|
278
|
+
|
|
230
279
|
/**
|
|
231
280
|
* Load and merge configuration for a given working directory.
|
|
232
281
|
*
|
|
@@ -239,7 +288,7 @@ function loadConfig(cwd) {
|
|
|
239
288
|
const cfg = deepClone(DEFAULTS);
|
|
240
289
|
const detected = detectAutoSrcDirs(cwd, cfg.exclude);
|
|
241
290
|
if (detected.length > 0) cfg.srcDirs = detected;
|
|
242
|
-
return cfg;
|
|
291
|
+
return _applyJvmDepth(cfg, cwd, false);
|
|
243
292
|
}
|
|
244
293
|
|
|
245
294
|
let userConfig;
|
|
@@ -251,7 +300,7 @@ function loadConfig(cwd) {
|
|
|
251
300
|
const cfg = deepClone(DEFAULTS);
|
|
252
301
|
const detected = detectAutoSrcDirs(cwd, cfg.exclude);
|
|
253
302
|
if (detected.length > 0) cfg.srcDirs = detected;
|
|
254
|
-
return cfg;
|
|
303
|
+
return _applyJvmDepth(cfg, cwd, false);
|
|
255
304
|
}
|
|
256
305
|
|
|
257
306
|
// Warn on unknown keys (helps catch typos)
|
|
@@ -303,7 +352,8 @@ function loadConfig(cwd) {
|
|
|
303
352
|
} else if (Array.isArray(merged.adapters) && !userConfig.outputs) {
|
|
304
353
|
merged.outputs = merged.adapters.filter((a) => ['copilot','claude','cursor','windsurf'].includes(a));
|
|
305
354
|
}
|
|
306
|
-
|
|
355
|
+
|
|
356
|
+
return _applyJvmDepth(merged, cwd, userConfig.maxDepth !== undefined);
|
|
307
357
|
}
|
|
308
358
|
|
|
309
359
|
function deepClone(obj) {
|
package/src/eval/analyzer.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { capWithNotice } = require('../util/truncate');
|
|
4
|
+
|
|
5
|
+
// Ceiling discloses what it drops rather than truncating silently (#583).
|
|
6
|
+
const PER_FILE_LIMIT = 40;
|
|
7
|
+
|
|
3
8
|
/**
|
|
4
9
|
* Lightweight markdown technical indexer.
|
|
5
10
|
* Captures headings and fenced code block language hints only.
|
|
@@ -24,7 +29,7 @@ function extract(src) {
|
|
|
24
29
|
sigs.push(`code-fence ${lang}`);
|
|
25
30
|
}
|
|
26
31
|
|
|
27
|
-
return Array.from(new Set(sigs))
|
|
32
|
+
return capWithNotice(Array.from(new Set(sigs)), PER_FILE_LIMIT, 'headings');
|
|
28
33
|
}
|
|
29
34
|
|
|
30
35
|
module.exports = { extract };
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { capWithNotice } = require('../util/truncate');
|
|
4
|
+
|
|
5
|
+
// Ceiling discloses what it drops rather than truncating silently (#583).
|
|
6
|
+
const PER_FILE_LIMIT = 50;
|
|
7
|
+
|
|
3
8
|
/**
|
|
4
9
|
* Extract signatures from .properties configuration files.
|
|
5
10
|
* Captures key names, grouped by prefixes where possible.
|
|
@@ -31,7 +36,7 @@ function extract(src) {
|
|
|
31
36
|
sigs.push(`key ${key}`);
|
|
32
37
|
}
|
|
33
38
|
|
|
34
|
-
return Array.from(new Set(sigs))
|
|
39
|
+
return capWithNotice(Array.from(new Set(sigs)), PER_FILE_LIMIT, 'keys');
|
|
35
40
|
}
|
|
36
41
|
|
|
37
42
|
module.exports = { extract };
|
package/src/extractors/r.js
CHANGED
|
@@ -44,7 +44,7 @@ function extract(src) {
|
|
|
44
44
|
// ClassName <- R6::R6Class(...)
|
|
45
45
|
const r6Re = /([\w.]+)\s*(?:<<-|<-|=)\s*(?:R6::)?R6Class\s*\(/g;
|
|
46
46
|
let m;
|
|
47
|
-
while ((m = r6Re.exec(stripped)) !== null
|
|
47
|
+
while ((m = r6Re.exec(stripped)) !== null) {
|
|
48
48
|
const name = m[1];
|
|
49
49
|
if (name.startsWith('.')) continue;
|
|
50
50
|
const openIdx = r6Re.lastIndex - 1;
|
|
@@ -55,7 +55,6 @@ function extract(src) {
|
|
|
55
55
|
sigs.push(`${name} <- R6Class("${classNameLit}")` + applyHint(docHints, name));
|
|
56
56
|
for (const memberSig of extractListMethods(body, 8)) {
|
|
57
57
|
sigs.push(' ' + memberSig);
|
|
58
|
-
if (sigs.length >= 30) break;
|
|
59
58
|
}
|
|
60
59
|
consumedRanges.push([m.index, closeIdx]);
|
|
61
60
|
r6Re.lastIndex = closeIdx;
|
|
@@ -65,7 +64,7 @@ function extract(src) {
|
|
|
65
64
|
// ClassName <- new_class("ClassName", properties = list(...))
|
|
66
65
|
const s7Classes = new Set();
|
|
67
66
|
const s7Re = /([\w.]+)\s*(?:<<-|<-|=)\s*(?:S7::)?new_class\s*\(/g;
|
|
68
|
-
while ((m = s7Re.exec(stripped)) !== null
|
|
67
|
+
while ((m = s7Re.exec(stripped)) !== null) {
|
|
69
68
|
const name = m[1];
|
|
70
69
|
if (name.startsWith('.')) continue;
|
|
71
70
|
const openIdx = s7Re.lastIndex - 1;
|
|
@@ -82,7 +81,7 @@ function extract(src) {
|
|
|
82
81
|
|
|
83
82
|
// S7 method dispatch: `method(generic, ClassName) <- function(args)`
|
|
84
83
|
const s7MethodRe = /^[ \t]*method\s*\(\s*([\w.]+)\s*,\s*([\w.]+)\s*\)\s*(?:<<-|<-|=)\s*function\s*\(/gm;
|
|
85
|
-
while ((m = s7MethodRe.exec(stripped)) !== null
|
|
84
|
+
while ((m = s7MethodRe.exec(stripped)) !== null) {
|
|
86
85
|
if (!s7Classes.has(m[2])) continue;
|
|
87
86
|
const argsStart = s7MethodRe.lastIndex - 1;
|
|
88
87
|
const args = readBalancedParens(stripped, argsStart);
|
|
@@ -95,7 +94,7 @@ function extract(src) {
|
|
|
95
94
|
// Skip matches whose position falls inside an R6/S7 class body — those have
|
|
96
95
|
// already been emitted as indented members.
|
|
97
96
|
const funcRe = /^(?:[ \t]*)([\w.]+)\s*(?:<<-|<-|=)\s*function\s*\(/gm;
|
|
98
|
-
while ((m = funcRe.exec(stripped)) !== null
|
|
97
|
+
while ((m = funcRe.exec(stripped)) !== null) {
|
|
99
98
|
const name = m[1];
|
|
100
99
|
if (name.startsWith('.')) continue;
|
|
101
100
|
if (inAnyRange(m.index, consumedRanges)) continue;
|
|
@@ -107,15 +106,12 @@ function extract(src) {
|
|
|
107
106
|
|
|
108
107
|
// ── S4 ────────────────────────────────────────────────────────────────────
|
|
109
108
|
for (const sm of stripped.matchAll(/^[ \t]*setGeneric\s*\(\s*["']([\w.]+)["']/gm)) {
|
|
110
|
-
if (sigs.length >= 30) break;
|
|
111
109
|
sigs.push(`setGeneric("${sm[1]}")`);
|
|
112
110
|
}
|
|
113
111
|
for (const sm of stripped.matchAll(/^[ \t]*setMethod\s*\(\s*["']([\w.]+)["']\s*,\s*["']([\w.]+)["']/gm)) {
|
|
114
|
-
if (sigs.length >= 30) break;
|
|
115
112
|
sigs.push(`setMethod("${sm[1]}", "${sm[2]}")`);
|
|
116
113
|
}
|
|
117
114
|
for (const sm of stripped.matchAll(/^[ \t]*setClass\s*\(\s*["']([\w.]+)["']/gm)) {
|
|
118
|
-
if (sigs.length >= 30) break;
|
|
119
115
|
sigs.push(`setClass("${sm[1]}")`);
|
|
120
116
|
}
|
|
121
117
|
|
package/src/extractors/toml.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { capWithNotice } = require('../util/truncate');
|
|
4
|
+
|
|
5
|
+
// Ceiling discloses what it drops rather than truncating silently (#583).
|
|
6
|
+
const PER_FILE_LIMIT = 40;
|
|
7
|
+
|
|
3
8
|
/**
|
|
4
9
|
* Extract signatures from TOML configuration files.
|
|
5
10
|
* Focuses on section/table names and high-value keys.
|
|
@@ -36,7 +41,7 @@ function extract(src) {
|
|
|
36
41
|
}
|
|
37
42
|
}
|
|
38
43
|
|
|
39
|
-
return Array.from(new Set(sigs))
|
|
44
|
+
return capWithNotice(Array.from(new Set(sigs)), PER_FILE_LIMIT, 'entries');
|
|
40
45
|
}
|
|
41
46
|
|
|
42
47
|
module.exports = { extract };
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { capWithNotice } = require('../util/truncate');
|
|
4
|
+
|
|
5
|
+
// Ceiling discloses what it drops rather than truncating silently (#583).
|
|
6
|
+
const PER_FILE_LIMIT = 50;
|
|
7
|
+
|
|
3
8
|
/**
|
|
4
9
|
* Extract React component signatures from .tsx files.
|
|
5
10
|
* Captures component props interfaces, hooks usage, and exports.
|
|
@@ -54,7 +59,7 @@ function extract(src) {
|
|
|
54
59
|
sigs.push(`handler on${h}`);
|
|
55
60
|
}
|
|
56
61
|
|
|
57
|
-
return Array.from(new Set(sigs))
|
|
62
|
+
return capWithNotice(Array.from(new Set(sigs)), PER_FILE_LIMIT, 'signatures');
|
|
58
63
|
}
|
|
59
64
|
|
|
60
65
|
module.exports = { extract };
|
package/src/mcp/server.js
CHANGED
package/src/extractors/vue.js
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
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
|
-
/**
|
|
10
|
-
* Extract signatures from Vue single-file components.
|
|
11
|
-
* @param {string} src - Raw file content
|
|
12
|
-
* @returns {string[]} Array of signature strings
|
|
13
|
-
*/
|
|
14
|
-
function extract(src) {
|
|
15
|
-
if (!src || typeof src !== 'string') return [];
|
|
16
|
-
const sigs = [];
|
|
17
|
-
|
|
18
|
-
// Extract component name from filename hint if present or defineComponent
|
|
19
|
-
const nameMatch = src.match(/name\s*:\s*['"](\w+)['"]/);
|
|
20
|
-
if (nameMatch) sigs.push(`component ${nameMatch[1]}`);
|
|
21
|
-
|
|
22
|
-
// Extract <script> block
|
|
23
|
-
const scriptMatch = src.match(/<script(?:\s[^>]*)?>(?:\s*)([\s\S]*?)<\/script>/i);
|
|
24
|
-
if (!scriptMatch) return sigs;
|
|
25
|
-
|
|
26
|
-
const script = scriptMatch[1]
|
|
27
|
-
.replace(/\/\/.*$/gm, '')
|
|
28
|
-
.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
29
|
-
|
|
30
|
-
// Props
|
|
31
|
-
const propsMatch = script.match(/props\s*:\s*(\{[\s\S]*?\})/);
|
|
32
|
-
if (propsMatch) {
|
|
33
|
-
const propNames = [];
|
|
34
|
-
for (const m of propsMatch[1].matchAll(/^\s+(\w+)\s*:/gm)) {
|
|
35
|
-
propNames.push(m[1]);
|
|
36
|
-
}
|
|
37
|
-
if (propNames.length > 0) sigs.push(`props: [${propNames.join(', ')}]`);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Methods in options API
|
|
41
|
-
const methodsMatch = script.match(/methods\s*:\s*\{([\s\S]*?)\},?\s*(?:computed|watch|mounted|created|data|\})/);
|
|
42
|
-
if (methodsMatch) {
|
|
43
|
-
for (const m of methodsMatch[1].matchAll(/^\s+(?:async\s+)?(\w+)\s*\(([^)]*)\)(?:\s*:\s*([^{=\n]+))?/gm)) {
|
|
44
|
-
if (m[1].startsWith('_')) continue;
|
|
45
|
-
const asyncKw = m[0].includes('async') ? 'async ' : '';
|
|
46
|
-
const retStr = m[3] ? ` → ${normalizeType(m[3])}` : '';
|
|
47
|
-
sigs.push(` ${asyncKw}${m[1]}(${normalizeParams(m[2])})${retStr}`);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Top-level functions in <script> (e.g., composition API helpers)
|
|
52
|
-
for (const m of script.matchAll(/^(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(([^)]*)\)(?:\s*:\s*([^{=\n]+))?/gm)) {
|
|
53
|
-
if (m[1].startsWith('_')) continue;
|
|
54
|
-
const asyncKw = m[0].includes('async') ? 'async ' : '';
|
|
55
|
-
const retStr = m[3] ? ` → ${normalizeType(m[3])}` : '';
|
|
56
|
-
sigs.push(`${asyncKw}function ${m[1]}(${normalizeParams(m[2])})${retStr}`);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// defineProps (Composition API)
|
|
60
|
-
const definePropsMatch = script.match(/defineProps(?:<[^>]*>)?\s*\(\s*(\{[\s\S]*?\})\s*\)/);
|
|
61
|
-
if (definePropsMatch) {
|
|
62
|
-
const propNames = [];
|
|
63
|
-
for (const m of definePropsMatch[1].matchAll(/^\s+(\w+)\s*:/gm)) {
|
|
64
|
-
propNames.push(m[1]);
|
|
65
|
-
}
|
|
66
|
-
if (propNames.length > 0) sigs.push(`defineProps: [${propNames.join(', ')}]`);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Emits
|
|
70
|
-
const emitsMatch = script.match(/(?:defineEmits|emits)\s*(?::\s*|\(\s*)(\[[\s\S]*?\])/);
|
|
71
|
-
if (emitsMatch) sigs.push(`emits: ${emitsMatch[1].replace(/\s+/g, ' ')}`);
|
|
72
|
-
|
|
73
|
-
return capWithNotice(sigs, PER_FILE_LIMIT, 'signatures');
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function normalizeParams(params) {
|
|
77
|
-
if (!params) return '';
|
|
78
|
-
return params.trim().replace(/\s+/g, ' ');
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function normalizeType(type) {
|
|
82
|
-
if (!type) return '';
|
|
83
|
-
return type.trim().replace(/[;\s]+$/g, '').replace(/\s+/g, ' ').slice(0, 25);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
module.exports = { extract };
|