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/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
  [![Discover on ShyPD](https://img.shields.io/badge/ShyPD-Discover-7c6af7?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDE2IDE2Ij48Y2lyY2xlIGN4PSI4IiBjeT0iOCIgcj0iOCIgZmlsbD0id2hpdGUiLz48L3N2Zz4=&logoColor=7c6af7)](https://shypd.ai/tools/sigmap)
17
17
 
18
18
  [![MseeP.ai Security Assessment Badge](https://mseep.net/pr/manojmallick-sigmap-badge.png)](https://mseep.ai/app/manojmallick-sigmap)
19
+ [![Verified on MseeP](https://mseep.ai/badge.svg)](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.10-main (21 repositories, including R language)
125
- Date : 2026-07-09
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
- <a href="https://star-history.com/#manojmallick/sigmap&Date">
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.10.0',
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.10.0';
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.10.0 | Benchmark: sigmap-v8.10-main (2026-07-09)
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.10-main, 2026-07-09)
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.10.0 | Benchmark: sigmap-v8.10-main (2026-07-09)
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.10-main, 2026-07-09)
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.10.0",
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",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sigmap-cli",
3
- "version": "8.10.0",
3
+ "version": "8.12.0",
4
4
  "description": "SigMap CLI wrapper — thin adapter for programmatic CLI invocation",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sigmap-core",
3
- "version": "8.10.0",
3
+ "version": "8.12.0",
4
4
  "description": "SigMap core library — zero-dependency code signature extraction, retrieval, and security scanning",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -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