sigmap 8.10.0 → 8.12.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 +599 -177
- package/CHANGELOG.md +21 -0
- package/README.md +4 -5
- package/gen-context.js +396 -3
- package/llms-full.txt +5 -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/src/wiki/generate.js +250 -0
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,27 @@ Format: [Semantic Versioning](https://semver.org/)
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
+
## [8.12.0] — 2026-07-11
|
|
14
|
+
|
|
15
|
+
Minor release — **`sigmap wiki`** (D9, the final unstarted in-boundary item from the master plan §3.5 backlog). With this, every in-boundary initiative D1–D9 has shipped.
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- **`sigmap wiki` — deterministic architecture narrative (#465, PR #466):** writes `.context/WIKI.md` composed entirely from data SigMap already computes — overview (indexed files, modules, signature tokens, health grade), module rollup with key files, dependency flow (hub files, entry points, cycle count from the import graph), conventions summary, and navigation pointers. Template prose only: **no LLM, no network, no timestamps** — two consecutive runs on an unchanged repo are byte-identical (regression-tested). `--out <path>` overrides the target; `--json` emits the structured data instead. Graph paths are relativized against the builder's normalized base so hubs/entries render repo-relative on macOS tmpdirs.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## [8.11.0] — 2026-07-11
|
|
23
|
+
|
|
24
|
+
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.
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
- **`--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.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
- **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).
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
13
34
|
## [8.10.0] — 2026-07-09
|
|
14
35
|
|
|
15
36
|
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.12-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.12.0',
|
|
13954
14047
|
description: 'SigMap MCP server — code signatures on demand',
|
|
13955
14048
|
};
|
|
13956
14049
|
|
|
@@ -18532,6 +18625,260 @@ __factories["./src/verify/parsers"] = function(module, exports) {
|
|
|
18532
18625
|
|
|
18533
18626
|
};
|
|
18534
18627
|
|
|
18628
|
+
// ── ./src/wiki/generate ──
|
|
18629
|
+
__factories["./src/wiki/generate"] = function(module, exports) {
|
|
18630
|
+
|
|
18631
|
+
/**
|
|
18632
|
+
* Wiki generation (D9) — `sigmap wiki`.
|
|
18633
|
+
*
|
|
18634
|
+
* Deterministic architecture narrative composed from data SigMap already
|
|
18635
|
+
* computes: the signature index, the dependency graph, conventions, and the
|
|
18636
|
+
* health score. Template prose only — no LLM, no network, no timestamps —
|
|
18637
|
+
* so two runs on an unchanged repo produce byte-identical markdown.
|
|
18638
|
+
*/
|
|
18639
|
+
|
|
18640
|
+
const fs = require('fs');
|
|
18641
|
+
const path = require('path');
|
|
18642
|
+
|
|
18643
|
+
const HUB_LIMIT = 8;
|
|
18644
|
+
const ENTRY_LIMIT = 8;
|
|
18645
|
+
const MODULE_LIMIT = 20;
|
|
18646
|
+
const KEY_FILE_LIMIT = 3;
|
|
18647
|
+
|
|
18648
|
+
// Graph keys come from src/graph/builder's normalizePath (normalized +
|
|
18649
|
+
// lowercased), so relativize against the same normalization of cwd.
|
|
18650
|
+
function _rel(cwd, f) {
|
|
18651
|
+
return path.relative(path.normalize(cwd).toLowerCase(), f).replace(/\\/g, '/');
|
|
18652
|
+
}
|
|
18653
|
+
|
|
18654
|
+
function _pct(fraction) {
|
|
18655
|
+
return Math.round(fraction * 100);
|
|
18656
|
+
}
|
|
18657
|
+
|
|
18658
|
+
/** Project name + version from package.json, falling back to the dir name. */
|
|
18659
|
+
function _identity(cwd) {
|
|
18660
|
+
try {
|
|
18661
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));
|
|
18662
|
+
if (pkg && pkg.name) return { name: pkg.name, version: pkg.version || null };
|
|
18663
|
+
} catch (_) {}
|
|
18664
|
+
return { name: path.basename(cwd), version: null };
|
|
18665
|
+
}
|
|
18666
|
+
|
|
18667
|
+
/** Module rollup from the signature index (keys are cwd-relative paths). */
|
|
18668
|
+
function _modules(index) {
|
|
18669
|
+
const groups = new Map();
|
|
18670
|
+
let totalTokens = 0;
|
|
18671
|
+
for (const [rel, sigs] of index.entries()) {
|
|
18672
|
+
const parts = String(rel).replace(/\\/g, '/').split('/');
|
|
18673
|
+
const mod = parts.length > 1 ? parts[0] : '.';
|
|
18674
|
+
const tokens = Math.ceil((sigs || []).join('\n').length / 4);
|
|
18675
|
+
totalTokens += tokens;
|
|
18676
|
+
if (!groups.has(mod)) groups.set(mod, { name: mod, files: 0, tokens: 0, fileSigs: [] });
|
|
18677
|
+
const g = groups.get(mod);
|
|
18678
|
+
g.files++;
|
|
18679
|
+
g.tokens += tokens;
|
|
18680
|
+
g.fileSigs.push({ file: rel, sigCount: (sigs || []).length });
|
|
18681
|
+
}
|
|
18682
|
+
const modules = [...groups.values()]
|
|
18683
|
+
.sort((a, b) => b.tokens - a.tokens || a.name.localeCompare(b.name))
|
|
18684
|
+
.slice(0, MODULE_LIMIT)
|
|
18685
|
+
.map((g) => ({
|
|
18686
|
+
name: g.name,
|
|
18687
|
+
files: g.files,
|
|
18688
|
+
tokens: g.tokens,
|
|
18689
|
+
keyFiles: g.fileSigs
|
|
18690
|
+
.sort((a, b) => b.sigCount - a.sigCount || a.file.localeCompare(b.file))
|
|
18691
|
+
.slice(0, KEY_FILE_LIMIT)
|
|
18692
|
+
.map((f) => f.file),
|
|
18693
|
+
}));
|
|
18694
|
+
return { modules, totalTokens };
|
|
18695
|
+
}
|
|
18696
|
+
|
|
18697
|
+
/** Hubs, entry points, and cycle count from the dependency graph. */
|
|
18698
|
+
function _flow(cwd) {
|
|
18699
|
+
try {
|
|
18700
|
+
const { buildFromCwd } = __require('./src/graph/builder');
|
|
18701
|
+
const { detectCycles } = __require('./src/map/import-graph');
|
|
18702
|
+
const graph = buildFromCwd(cwd);
|
|
18703
|
+
if (!graph || !graph.forward || graph.forward.size === 0) return null;
|
|
18704
|
+
|
|
18705
|
+
const importersOf = (f) => (graph.reverse.get(f) || []).length;
|
|
18706
|
+
const hubs = [...graph.reverse.entries()]
|
|
18707
|
+
.map(([f, importers]) => ({ file: _rel(cwd, f), importers: importers.length }))
|
|
18708
|
+
.filter((h) => h.importers > 0)
|
|
18709
|
+
.sort((a, b) => b.importers - a.importers || a.file.localeCompare(b.file))
|
|
18710
|
+
.slice(0, HUB_LIMIT);
|
|
18711
|
+
|
|
18712
|
+
const entryPoints = [...graph.forward.entries()]
|
|
18713
|
+
.filter(([f, deps]) => deps.length > 0 && importersOf(f) === 0)
|
|
18714
|
+
.map(([f, deps]) => ({ file: _rel(cwd, f), imports: deps.length }))
|
|
18715
|
+
.sort((a, b) => b.imports - a.imports || a.file.localeCompare(b.file))
|
|
18716
|
+
.slice(0, ENTRY_LIMIT);
|
|
18717
|
+
|
|
18718
|
+
let cycles = 0;
|
|
18719
|
+
try { cycles = detectCycles(graph.forward).length; } catch (_) {}
|
|
18720
|
+
|
|
18721
|
+
return { hubs, entryPoints, cycles, edges: graph.forward.size };
|
|
18722
|
+
} catch (_) {
|
|
18723
|
+
return null;
|
|
18724
|
+
}
|
|
18725
|
+
}
|
|
18726
|
+
|
|
18727
|
+
/** Conventions summary; index keys are resolved back to absolute paths. */
|
|
18728
|
+
function _conventions(cwd, index) {
|
|
18729
|
+
try {
|
|
18730
|
+
const { extractConventions } = __require('./src/conventions/extract');
|
|
18731
|
+
const files = [...index.keys()].map((rel) => path.join(cwd, rel));
|
|
18732
|
+
const c = extractConventions(cwd, files);
|
|
18733
|
+
return {
|
|
18734
|
+
fileNaming: c.fileNaming
|
|
18735
|
+
? { dominant: c.fileNaming.dominant, pct: _pct(c.fileNaming.dominantPct || 0), tier: c.fileNaming.tier }
|
|
18736
|
+
: null,
|
|
18737
|
+
exportStyle: c.exportStyle
|
|
18738
|
+
? { dominant: c.exportStyle.dominant, pct: _pct(c.exportStyle.dominantPct || 0), tier: c.exportStyle.tier }
|
|
18739
|
+
: null,
|
|
18740
|
+
testFramework: c.testFramework || null,
|
|
18741
|
+
};
|
|
18742
|
+
} catch (_) {
|
|
18743
|
+
return null;
|
|
18744
|
+
}
|
|
18745
|
+
}
|
|
18746
|
+
|
|
18747
|
+
function _health(cwd) {
|
|
18748
|
+
try {
|
|
18749
|
+
const { score } = __require('./src/health/scorer');
|
|
18750
|
+
const h = score(cwd);
|
|
18751
|
+
return { score: h.score, grade: h.grade };
|
|
18752
|
+
} catch (_) {
|
|
18753
|
+
return null;
|
|
18754
|
+
}
|
|
18755
|
+
}
|
|
18756
|
+
|
|
18757
|
+
/**
|
|
18758
|
+
* Build the wiki. Every data source is optional — a repo with no context file
|
|
18759
|
+
* or no resolvable graph still yields a valid document.
|
|
18760
|
+
* @param {string} cwd
|
|
18761
|
+
* @param {object} [opts]
|
|
18762
|
+
* @param {string} [opts.version] SigMap version stamped in the header
|
|
18763
|
+
* @returns {{ data: object, markdown: string }}
|
|
18764
|
+
*/
|
|
18765
|
+
function buildWiki(cwd, opts = {}) {
|
|
18766
|
+
let index = new Map();
|
|
18767
|
+
try {
|
|
18768
|
+
const { buildSigIndex } = __require('./src/retrieval/ranker');
|
|
18769
|
+
index = buildSigIndex(cwd);
|
|
18770
|
+
} catch (_) {}
|
|
18771
|
+
|
|
18772
|
+
const identity = _identity(cwd);
|
|
18773
|
+
const { modules, totalTokens } = _modules(index);
|
|
18774
|
+
const flow = _flow(cwd);
|
|
18775
|
+
const conventions = index.size ? _conventions(cwd, index) : null;
|
|
18776
|
+
const health = _health(cwd);
|
|
18777
|
+
|
|
18778
|
+
const data = {
|
|
18779
|
+
name: identity.name,
|
|
18780
|
+
version: identity.version,
|
|
18781
|
+
files: index.size,
|
|
18782
|
+
modules,
|
|
18783
|
+
totalTokens,
|
|
18784
|
+
flow,
|
|
18785
|
+
conventions,
|
|
18786
|
+
health,
|
|
18787
|
+
};
|
|
18788
|
+
|
|
18789
|
+
return { data, markdown: renderWikiMarkdown(data, opts.version) };
|
|
18790
|
+
}
|
|
18791
|
+
|
|
18792
|
+
/**
|
|
18793
|
+
* Render the narrative markdown. Pure function of `data` — no clocks, no
|
|
18794
|
+
* randomness — so output is byte-stable for a fixed repo state.
|
|
18795
|
+
* @param {object} data
|
|
18796
|
+
* @param {string} [sigmapVersion]
|
|
18797
|
+
* @returns {string}
|
|
18798
|
+
*/
|
|
18799
|
+
function renderWikiMarkdown(data, sigmapVersion) {
|
|
18800
|
+
const L = [];
|
|
18801
|
+
const title = data.version ? `${data.name} v${data.version}` : data.name;
|
|
18802
|
+
L.push(`# ${title} — Architecture Wiki`);
|
|
18803
|
+
L.push('');
|
|
18804
|
+
L.push(`_Deterministically generated from signatures + dependency graph by SigMap${sigmapVersion ? ` v${sigmapVersion}` : ''} — no LLM. Regenerate: \`sigmap wiki\`._`);
|
|
18805
|
+
L.push('');
|
|
18806
|
+
|
|
18807
|
+
L.push('## Overview');
|
|
18808
|
+
if (data.files === 0) {
|
|
18809
|
+
L.push('No signature index found yet — run `sigmap` (or `node gen-context.js`) to generate context, then regenerate this wiki.');
|
|
18810
|
+
} else {
|
|
18811
|
+
const fileWord = data.files === 1 ? 'indexed file' : 'indexed files';
|
|
18812
|
+
const modWord = data.modules.length === 1 ? 'top-level module' : 'top-level modules';
|
|
18813
|
+
L.push(`The codebase spans **${data.files} ${fileWord}** across **${data.modules.length} ${modWord}**, with ~${data.totalTokens} tokens of extracted signatures.`);
|
|
18814
|
+
if (data.health) {
|
|
18815
|
+
L.push(`Context health: **${data.health.score}/100 (${data.health.grade})**.`);
|
|
18816
|
+
}
|
|
18817
|
+
}
|
|
18818
|
+
L.push('');
|
|
18819
|
+
|
|
18820
|
+
if (data.modules.length) {
|
|
18821
|
+
L.push('## Modules');
|
|
18822
|
+
L.push('| Module | Files | Sig tokens | Key files |');
|
|
18823
|
+
L.push('|--------|-------|------------|-----------|');
|
|
18824
|
+
for (const m of data.modules) {
|
|
18825
|
+
L.push(`| \`${m.name}\` | ${m.files} | ~${m.tokens} | ${m.keyFiles.map((f) => `\`${f}\``).join(', ')} |`);
|
|
18826
|
+
}
|
|
18827
|
+
const top = data.modules[0];
|
|
18828
|
+
L.push('');
|
|
18829
|
+
L.push(`The largest module by signature volume is \`${top.name}\` (${top.files} files, ~${top.tokens} tokens) — start there for the core logic.`);
|
|
18830
|
+
L.push('');
|
|
18831
|
+
}
|
|
18832
|
+
|
|
18833
|
+
if (data.flow) {
|
|
18834
|
+
L.push('## Dependency flow');
|
|
18835
|
+
if (data.flow.hubs.length) {
|
|
18836
|
+
L.push('The most depended-on files — changes here have the widest blast radius:');
|
|
18837
|
+
L.push('');
|
|
18838
|
+
L.push('| Hub file | Importers |');
|
|
18839
|
+
L.push('|----------|-----------|');
|
|
18840
|
+
for (const h of data.flow.hubs) L.push(`| \`${h.file}\` | ${h.importers} |`);
|
|
18841
|
+
L.push('');
|
|
18842
|
+
}
|
|
18843
|
+
if (data.flow.entryPoints.length) {
|
|
18844
|
+
L.push('Entry points (imported by nothing, importing the rest):');
|
|
18845
|
+
L.push('');
|
|
18846
|
+
for (const e of data.flow.entryPoints) L.push(`- \`${e.file}\` → ${e.imports} imports`);
|
|
18847
|
+
L.push('');
|
|
18848
|
+
}
|
|
18849
|
+
L.push(data.flow.cycles
|
|
18850
|
+
? `**Dependency cycles:** ${data.flow.cycles} — untangle these first when refactoring.`
|
|
18851
|
+
: '**Dependency cycles:** none detected.');
|
|
18852
|
+
L.push('');
|
|
18853
|
+
}
|
|
18854
|
+
|
|
18855
|
+
if (data.conventions) {
|
|
18856
|
+
L.push('## Conventions');
|
|
18857
|
+
const c = data.conventions;
|
|
18858
|
+
const bits = [];
|
|
18859
|
+
if (c.fileNaming && c.fileNaming.dominant) bits.push(`file naming is predominantly **${c.fileNaming.dominant}** (${c.fileNaming.pct}%, ${c.fileNaming.tier})`);
|
|
18860
|
+
if (c.exportStyle && c.exportStyle.dominant) bits.push(`exports use the **${c.exportStyle.dominant}** style (${c.exportStyle.pct}%, ${c.exportStyle.tier})`);
|
|
18861
|
+
if (c.testFramework) bits.push(`tests run on **${c.testFramework}**`);
|
|
18862
|
+
L.push(bits.length
|
|
18863
|
+
? `In this repo, ${bits.join('; ')}. New code should match.`
|
|
18864
|
+
: 'No dominant conventions detected (repo too small or styles mixed).');
|
|
18865
|
+
L.push('');
|
|
18866
|
+
}
|
|
18867
|
+
|
|
18868
|
+
L.push('## Navigating');
|
|
18869
|
+
L.push('- `sigmap ask "<question>"` — ranked, budgeted mini-context for any task');
|
|
18870
|
+
L.push('- `sigmap --impact <file>` / `--callers <symbol>` — blast radius before you change something');
|
|
18871
|
+
L.push('- `sigmap evidence "<query>"` — machine-consumable Evidence Pack (JSON) for agents/CI');
|
|
18872
|
+
L.push('- MCP: `get_architecture_overview`, `get_map`, `get_callee_signatures` for live agent access');
|
|
18873
|
+
L.push('');
|
|
18874
|
+
|
|
18875
|
+
return L.join('\n');
|
|
18876
|
+
}
|
|
18877
|
+
|
|
18878
|
+
module.exports = { buildWiki, renderWikiMarkdown };
|
|
18879
|
+
|
|
18880
|
+
};
|
|
18881
|
+
|
|
18535
18882
|
// ── ./src/workspace/detector ──
|
|
18536
18883
|
__factories["./src/workspace/detector"] = function(module, exports) {
|
|
18537
18884
|
|
|
@@ -18640,7 +18987,7 @@ function __tryGit(args, opts = {}) {
|
|
|
18640
18987
|
catch (_) { return ''; }
|
|
18641
18988
|
}
|
|
18642
18989
|
|
|
18643
|
-
const VERSION = '8.
|
|
18990
|
+
const VERSION = '8.12.0';
|
|
18644
18991
|
const MARKER = '\n\n## Auto-generated signatures\n<!-- Updated by gen-context.js -->\n';
|
|
18645
18992
|
|
|
18646
18993
|
function requireSourceOrBundled(key) {
|
|
@@ -19317,6 +19664,21 @@ function formatOutput(fileEntries, cwd, routingEnabled, config, extras) {
|
|
|
19317
19664
|
groups[group].push({ rel, sigs: entry.sigs });
|
|
19318
19665
|
}
|
|
19319
19666
|
|
|
19667
|
+
// D7: terse signature encoding — anchors preserved byte-exactly
|
|
19668
|
+
const terseOn = !!(config && config.terse);
|
|
19669
|
+
let encodeTerseSigs = null;
|
|
19670
|
+
if (terseOn) {
|
|
19671
|
+
try {
|
|
19672
|
+
const terseMod = requireSourceOrBundled('./src/format/terse');
|
|
19673
|
+
encodeTerseSigs = terseMod.encodeTerseSigs;
|
|
19674
|
+
const m = terseMod.measureTerse(fileEntries.map((e) => e.sigs));
|
|
19675
|
+
console.warn(`[sigmap] terse: sig block ${m.beforeTokens} → ${m.afterTokens} tokens (-${m.reductionPct}%)`);
|
|
19676
|
+
} catch (err) {
|
|
19677
|
+
encodeTerseSigs = null;
|
|
19678
|
+
console.warn(`[sigmap] terse encoding skipped: ${err.message}`);
|
|
19679
|
+
}
|
|
19680
|
+
}
|
|
19681
|
+
|
|
19320
19682
|
for (const [group, entries] of Object.entries(groups).sort()) {
|
|
19321
19683
|
lines.push(`## ${group}`);
|
|
19322
19684
|
lines.push('');
|
|
@@ -19326,7 +19688,7 @@ function formatOutput(fileEntries, cwd, routingEnabled, config, extras) {
|
|
|
19326
19688
|
const usedByStr = usedBy && usedBy.length ? ` (used by: ${usedBy.join(', ')})` : '';
|
|
19327
19689
|
lines.push(`### ${rel}${usedByStr}`);
|
|
19328
19690
|
lines.push('```');
|
|
19329
|
-
lines.push(...sigs);
|
|
19691
|
+
lines.push(...(encodeTerseSigs ? encodeTerseSigs(sigs) : sigs));
|
|
19330
19692
|
lines.push('```');
|
|
19331
19693
|
lines.push('');
|
|
19332
19694
|
}
|
|
@@ -20440,6 +20802,7 @@ Usage:
|
|
|
20440
20802
|
${cmd} --monorepo Generate per-package context (monorepo)
|
|
20441
20803
|
${cmd} --each Run for every repo in the current directory
|
|
20442
20804
|
${cmd} --routing Include model routing hints in output
|
|
20805
|
+
${cmd} --terse Compact signature encoding (deterministic; line anchors preserved)
|
|
20443
20806
|
${cmd} --format cache Also write Anthropic prompt-cache JSON
|
|
20444
20807
|
${cmd} --track Append run metrics to .context/usage.ndjson
|
|
20445
20808
|
${cmd} --watch Generate + watch for file changes
|
|
@@ -20498,6 +20861,7 @@ Usage:
|
|
|
20498
20861
|
${cmd} review-pr Audit a diff — scope drift, god-node edits, missing tests, security files (--staged, --base, --json, --markdown)
|
|
20499
20862
|
${cmd} review-pr --markdown PR Evidence Report — branded Markdown (signatures + blast radius + tests) to post as a PR comment
|
|
20500
20863
|
${cmd} create "<task>" Grounded-creation pipeline: scaffold → verify-plan → verify-ai-output → review-pr (--staged)
|
|
20864
|
+
${cmd} wiki Deterministic architecture wiki from signatures + graph — no LLM (--json, --out <path>)
|
|
20501
20865
|
${cmd} squeeze <file|-> Minimize a pasted stacktrace/CI-log/JSON blob (--json for stats)
|
|
20502
20866
|
${cmd} squeeze --response <file|-> Minimize an agent/tool response (same engine; also exposed as the squeeze_output MCP tool)
|
|
20503
20867
|
${cmd} ask "<query>" --squeeze Auto-accept input minimization (no prompt; for scripts/CI)
|
|
@@ -20826,6 +21190,11 @@ function main() {
|
|
|
20826
21190
|
config.testCoverage = true;
|
|
20827
21191
|
}
|
|
20828
21192
|
|
|
21193
|
+
// --terse: compact signature encoding without editing config (D7)
|
|
21194
|
+
if (args.includes('--terse')) {
|
|
21195
|
+
config.terse = true;
|
|
21196
|
+
}
|
|
21197
|
+
|
|
20829
21198
|
// ── --output <file> — parse early so every subsequent block can use it ─────
|
|
20830
21199
|
// Resolves the custom output path and merges it into config.customOutput.
|
|
20831
21200
|
// Also persists the resolved relative path to gen-context.config.json so
|
|
@@ -22113,6 +22482,30 @@ function main() {
|
|
|
22113
22482
|
process.exit(result.ok ? 0 : 1);
|
|
22114
22483
|
}
|
|
22115
22484
|
|
|
22485
|
+
// D9: `sigmap wiki` — deterministic architecture narrative (no LLM).
|
|
22486
|
+
if (args[0] === 'wiki') {
|
|
22487
|
+
const { buildWiki } = requireSourceOrBundled('./src/wiki/generate');
|
|
22488
|
+
const result = buildWiki(cwd, { version: VERSION });
|
|
22489
|
+
if (args.includes('--json')) {
|
|
22490
|
+
process.stdout.write(JSON.stringify(result.data, null, 2) + '\n');
|
|
22491
|
+
process.exit(0);
|
|
22492
|
+
}
|
|
22493
|
+
const outIdx = args.indexOf('--out');
|
|
22494
|
+
const outRel = outIdx >= 0 && args[outIdx + 1] && !args[outIdx + 1].startsWith('--')
|
|
22495
|
+
? args[outIdx + 1]
|
|
22496
|
+
: path.join('.context', 'WIKI.md');
|
|
22497
|
+
const outPath = path.resolve(cwd, outRel);
|
|
22498
|
+
try {
|
|
22499
|
+
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
|
22500
|
+
fs.writeFileSync(outPath, result.markdown, 'utf8');
|
|
22501
|
+
} catch (e) {
|
|
22502
|
+
console.error(`[sigmap] cannot write ${outPath}: ${e.message}`);
|
|
22503
|
+
process.exit(1);
|
|
22504
|
+
}
|
|
22505
|
+
console.log(`[sigmap] wiki → ${path.relative(cwd, outPath)} (${result.data.files} files · ${result.data.modules.length} modules)`);
|
|
22506
|
+
process.exit(0);
|
|
22507
|
+
}
|
|
22508
|
+
|
|
22116
22509
|
// Layer 3: `sigmap conventions` — extract & report repo coding conventions
|
|
22117
22510
|
// (file naming, export style, test framework) for TS/JS/Python so generated
|
|
22118
22511
|
// code matches the house style. Writes .context/conventions.json.
|
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.12.0 | Benchmark: sigmap-v8.12-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.12-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
|
|
@@ -113,6 +114,7 @@ sigmap verify-plan <plan.md|-> Check a plan vs the live index — file
|
|
|
113
114
|
sigmap review-pr Audit a diff — scope drift, god-node edits, missing tests, security files (--staged, --base, --json, --markdown)
|
|
114
115
|
sigmap review-pr --markdown PR Evidence Report — branded Markdown (signatures + blast radius + tests) to post as a PR comment
|
|
115
116
|
sigmap create "<task>" Grounded-creation pipeline: scaffold → verify-plan → verify-ai-output → review-pr (--staged)
|
|
117
|
+
sigmap wiki Deterministic architecture wiki from signatures + graph — no LLM (--json, --out <path>)
|
|
116
118
|
sigmap squeeze <file|-> Minimize a pasted stacktrace/CI-log/JSON blob (--json for stats)
|
|
117
119
|
sigmap squeeze --response <file|-> Minimize an agent/tool response (same engine; also exposed as the squeeze_output MCP tool)
|
|
118
120
|
sigmap ask "<query>" --squeeze Auto-accept input minimization (no prompt; for scripts/CI)
|
|
@@ -325,6 +327,7 @@ tracking = false
|
|
|
325
327
|
mcp = {"autoRegister":true}
|
|
326
328
|
depMap = true
|
|
327
329
|
versionPins = true
|
|
330
|
+
terse = false
|
|
328
331
|
todos = true
|
|
329
332
|
changes = true
|
|
330
333
|
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.12.0 | Benchmark: sigmap-v8.12-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.12-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.12.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
|
|