sigmap 8.7.0 → 8.8.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.8.0] — 2026-07-05
14
+
15
+ Minor release — **the squeeze engine, exposed mid-session (D6).** The always-on squeeze engine (`src/squeeze/`) that powers `sigmap squeeze` was reachable only from the CLI on pasted input. This release exposes it as an MCP tool an agent can call *mid-session*, and adds a named CLI entry point for compressing an agent/tool *response*. Zero-dependency, offline, deterministic — the last A+ ceiling item (Machine 9→10): the engine already shipped, this just exposes it.
16
+
17
+ ### Added
18
+ - **`squeeze_output` MCP tool (#437, PR #438):** the 19th MCP tool (18 → 19) wraps the existing `squeeze()` engine so an agent can compress noisy tool/command output — a stack trace, CI/build log, or JSON payload — before it enters context. Keeps the signal (error frames, failing steps, JSON shape), strips the noise, and enriches the top stack frame with its signature; returns the squeezed text plus token-reduction stats. Passes the input through unchanged when no squeezable structure is detected, and returns usage on empty input. No LLM, no network. New handler `squeezeOutput` in `src/mcp/handlers.js`, wired through `src/mcp/tools.js` and `src/mcp/server.js`.
19
+ - **`sigmap squeeze --response <file|->` (#437, PR #438):** names the agent/tool response input explicitly (mirroring the existing `sigmap judge --response` convention), routing it through the same squeeze engine. Supports `--json`. Documented in `--help`.
20
+
21
+ ### Fixed
22
+ - **Deterministic context output — three nondeterminism sources (#440, PR #441):** `gen-context`'s signature output was not byte-stable across runs on some repos, making the retrieval benchmark's hit@5 non-reproducible. Fixes: (1) the token-budget drop-order now has a final `filePath` tie-break, so which files survive the budget no longer falls through to filesystem `readdir` order (test files sharing a git-checkout mtime tied on every prior key); (2) the source-file walk sorts `readdir` entries by name for stable section order; (3) auto source-root detection sorts its candidate enumeration and adds tie-breaks to the score sorts, so the `MAX_ROOTS` cutoff admits a deterministic directory set. Adds a resolver determinism regression guard. A residual affecting a few large repos remains tracked in #440.
23
+
24
+ ## [8.7.1] — 2026-07-05
25
+
26
+ Patch release — **multi-model cost savings + verified pricing.** The quality benchmark reported API input-cost savings for GPT-4o only; it now reports GPT-4o, Claude Sonnet, and Claude Haiku, and the hardcoded Claude rates were corrected to current published pricing.
27
+
28
+ ### Changed
29
+ - **Multi-model cost savings (#433, PR #434):** `scripts/run-quality-benchmark.mjs` adds Claude Haiku to the pricing model and emits a per-model "cost savings by model" table (GPT-4o · Claude Sonnet · Claude Haiku). `docs-vp/guide/quality-benchmark.md` now shows all three models in the cost table, headline, and frontmatter. Pricing verified 2026-07 (per 1M input tokens): GPT-4o $2.50, Claude Sonnet 5/4.6 $3.00, Claude Haiku 4.5 $1.00.
30
+
31
+ ### Fixed
32
+ - **Stale Claude pricing (#433, PR #434):** `src/tracking/pricing.js` (the `sigmap gain` dashboard's cost assumptions) corrected to current published rates — Claude Haiku $0.80→$1.00 and Claude Opus $15.00→$5.00 (Opus 4.8). GPT-4o ($2.50) and Sonnet ($3.00) were already correct.
33
+
13
34
  ## [8.7.0] — 2026-07-05
14
35
 
15
36
  Minor release — **method/caller-level call-graph (D4 v1): the graph goes from files to functions.** SigMap's dependency graph was file-level only (imports → `--impact`/`get_impact`). This adds **symbol-level** edges — *which function calls which function* — and the **method-level blast radius** of changing a single symbol. Deterministic, zero-dependency, JS/TS + Python. This is the plan's single hardest unbuilt gap and the biggest remaining lever on graph intelligence (v9.5 GR1).
package/README.md CHANGED
@@ -59,8 +59,8 @@ That map is exactly what agentic grep is worst at: reproducible, auditable conte
59
59
  <!--SM:whyMetrics-->
60
60
  - **87.8% hit@5** — right file found in top 5 results (vs 13.6% baseline)
61
61
  - **97.0% token reduction** — average across 21 real repos
62
- - **68.9% task success rate** — up from 10% without context
63
- - **1.43 prompts per task** — down from 2.84 (49.6% fewer retries)
62
+ - **67.8% task success rate** — up from 10% without context
63
+ - **1.44 prompts per task** — down from 2.84 (49.2% fewer retries)
64
64
  <!--/SM:whyMetrics-->
65
65
  - **<!--SM:languages-->33<!--/SM:languages--> languages supported** — TypeScript, Python, Go, Rust, Java, R, and more
66
66
  - **No vendor lock-in** — works with any AI assistant or local LLM
@@ -120,13 +120,13 @@ Ask → Rank → Context → Validate → Judge → Learn
120
120
 
121
121
  <!--SM:benchmarkBlock-->
122
122
  ```
123
- Benchmark : sigmap-v8.7-main (21 repositories, including R language)
123
+ Benchmark : sigmap-v8.8-main (21 repositories, including R language)
124
124
  Date : 2026-07-05
125
125
 
126
126
  Hit@5 : 87.8% (baseline 13.6% — 6.5× lift)
127
127
  Token reduction: 97.0% (across 21 repos)
128
- Prompt reduction : 49.6% (2.84 → 1.43 prompts per task)
129
- Task success : 68.9% (baseline 10%)
128
+ Prompt reduction : 49.2% (2.84 → 1.44 prompts per task)
129
+ Task success : 67.8% (baseline 10%)
130
130
  Repos tested : 21 (JavaScript, Python, Go, Rust, Java, R, C++, C#, Dart, Swift, Ruby, PHP, Scala, Kotlin, and more)
131
131
  ```
132
132
  <!--/SM:benchmarkBlock-->
@@ -220,13 +220,13 @@ Use SigMap with open-source tools and fully self-hosted setups:
220
220
  | **JetBrains** | [Marketplace](https://plugins.jetbrains.com/plugin/31109-sigmap--ai-context-engine/) | [github.com/manojmallick/sigmap-jetbrains](https://github.com/manojmallick/sigmap-jetbrains) | IntelliJ IDEA, WebStorm, PyCharm, GoLand — tool window + actions |
221
221
  | **Neovim** | lazy.nvim / packer / vim-plug | [github.com/manojmallick/sigmap.nvim](https://github.com/manojmallick/sigmap.nvim) | `:SigMap`, `:SigMapQuery` float window, statusline widget |
222
222
 
223
- **MCP server** — 18 on-demand tools for Claude Code and Cursor:
223
+ **MCP server** — 19 on-demand tools for Claude Code and Cursor:
224
224
 
225
225
  ```bash
226
226
  sigmap --mcp
227
227
  ```
228
228
 
229
- Tools: `read_context`, `search_signatures`, `get_map`, `create_checkpoint`, `get_routing`, `explain_file`, `list_modules`, `query_context`, `get_impact`, `get_lines`, `read_memory`, `get_callee_signatures`, `get_diff_context` (changed files + signatures + blast radius), `get_architecture_overview` (modules, hub files, cycles), plus the live-index notifications `sigmap_notify_file_created`, `sigmap_notify_symbol_added`, and `sigmap_notify_file_deleted`. Full reference: [llms-full.txt](llms-full.txt).
229
+ Tools: `read_context`, `search_signatures`, `get_map`, `create_checkpoint`, `get_routing`, `explain_file`, `list_modules`, `query_context`, `get_impact`, `get_lines`, `read_memory`, `get_callee_signatures`, `get_diff_context` (changed files + signatures + blast radius), `get_architecture_overview` (modules, hub files, cycles), `verify_suggestion` (ground AI code against repo + installed libraries), `squeeze_output` (compress noisy tool/log/JSON output mid-session), plus the live-index notifications `sigmap_notify_file_created`, `sigmap_notify_symbol_added`, and `sigmap_notify_file_deleted`. Full reference: [llms-full.txt](llms-full.txt).
230
230
 
231
231
  ---
232
232
 
@@ -265,7 +265,7 @@ SigMap treats coding agents as **consumers, not competitors**: it hands them a d
265
265
 
266
266
  | Agent | One-time setup | How it consumes SigMap |
267
267
  |---|---|---|
268
- | **Claude Code** | `sigmap mcp install claude` | 17 MCP tools (`search_signatures`, `get_lines`, `get_diff_context`…) |
268
+ | **Claude Code** | `sigmap mcp install claude` | 19 MCP tools (`search_signatures`, `get_lines`, `get_diff_context`, `squeeze_output`…) |
269
269
  | **Cursor** | `sigmap mcp install cursor` | MCP tools, plus the `cursor` adapter writes `.cursorrules` |
270
270
  | **Cline** | `sigmap mcp install cursor` | Reads `.cursorrules`; same MCP server |
271
271
  | **Continue** | `sigmap mcp install vscode` | MCP tools inside the Continue extension |
package/gen-context.js CHANGED
@@ -3202,7 +3202,11 @@ __factories["./src/discovery/source-root-resolver"] = function(module, exports)
3202
3202
  score: scoreCandidate(name, full, context),
3203
3203
  }))
3204
3204
  .filter(c => c.score > 0)
3205
- .sort((a, b) => b.score - a.score);
3205
+ // Final tie-break on dir keeps selection deterministic when scores tie — the
3206
+ // top-MAX_ROOTS slice below would otherwise admit different dirs run to run
3207
+ // (candidate order comes from filesystem readdir), changing which files are
3208
+ // collected and making the generated context non-reproducible.
3209
+ .sort((a, b) => b.score - a.score || a.dir.localeCompare(b.dir));
3206
3210
 
3207
3211
  // Handle special rules
3208
3212
  let roots = _applySpecialRules(scored, cwd, primaryFw, fwEntry, frameworks);
@@ -3245,9 +3249,12 @@ __factories["./src/discovery/source-root-resolver"] = function(module, exports)
3245
3249
  const candidates = [];
3246
3250
  const excSet = new Set(excludeList);
3247
3251
 
3248
- // Root-level dirs
3252
+ // Root-level dirs (sorted so candidate order — and downstream dedupe/selection
3253
+ // — is deterministic regardless of filesystem readdir order)
3249
3254
  try {
3250
- for (const e of fs.readdirSync(cwd, { withFileTypes: true })) {
3255
+ const rootEntries = fs.readdirSync(cwd, { withFileTypes: true })
3256
+ .sort((a, b) => a.name.localeCompare(b.name));
3257
+ for (const e of rootEntries) {
3251
3258
  if (!e.isDirectory()) continue;
3252
3259
  if (excSet.has(e.name)) continue;
3253
3260
  if (matchesIgnorePattern(e.name, ignorePatterns)) continue;
@@ -3316,7 +3323,7 @@ __factories["./src/discovery/source-root-resolver"] = function(module, exports)
3316
3323
  }
3317
3324
  }
3318
3325
  } catch (_) {}
3319
- roots.sort((a, b) => b.score - a.score);
3326
+ roots.sort((a, b) => b.score - a.score || a.dir.localeCompare(b.dir));
3320
3327
  }
3321
3328
 
3322
3329
  // Swift project dir: dirs with ≥3 .swift files
@@ -3331,7 +3338,7 @@ __factories["./src/discovery/source-root-resolver"] = function(module, exports)
3331
3338
  }
3332
3339
  }
3333
3340
  } catch (_) {}
3334
- roots.sort((a, b) => b.score - a.score);
3341
+ roots.sort((a, b) => b.score - a.score || a.dir.localeCompare(b.dir));
3335
3342
  }
3336
3343
 
3337
3344
  return roots;
@@ -13345,7 +13352,50 @@ __factories["./src/mcp/handlers"] = function(module, exports) {
13345
13352
  return out.join('\n');
13346
13353
  }
13347
13354
 
13348
- module.exports = { readContext, searchSignatures, getMap, createCheckpoint, getRouting, explainFile, listModules, queryContext, getImpact, getLines, readMemory, getCalleeSignatures, notifyFileCreated, notifySymbolAdded, notifyFileDeleted, getDiffContext, getArchitectureOverview, verifySuggestion };
13355
+ /**
13356
+ * squeeze_output({ content }) → string
13357
+ *
13358
+ * Compress noisy tool/command/agent output (stack trace, CI/build log, or JSON
13359
+ * payload) through the deterministic squeeze engine. Enriches the top stack
13360
+ * frame with its signature when the symbol index is available. Passes the input
13361
+ * through unchanged when no squeezable structure is detected.
13362
+ */
13363
+ function squeezeOutput(args, cwd) {
13364
+ const content = args && typeof args.content === 'string' ? args.content : '';
13365
+ if (!content.trim()) {
13366
+ return 'Usage: squeeze_output({ content: "<raw tool/log/JSON output>" }) — provide the output to compress.';
13367
+ }
13368
+
13369
+ let sq;
13370
+ try {
13371
+ const { squeeze } = __require('./src/squeeze/index');
13372
+ let srcDirs;
13373
+ let symbolIndex = null;
13374
+ try {
13375
+ const { loadConfig } = __require('./src/config/loader');
13376
+ srcDirs = loadConfig(cwd).srcDirs;
13377
+ } catch (_) {}
13378
+ try {
13379
+ const { buildSigIndex } = __require('./src/retrieval/ranker');
13380
+ symbolIndex = buildSigIndex(cwd);
13381
+ } catch (_) {}
13382
+ sq = squeeze(content, { srcDirs, symbolIndex });
13383
+ } catch (err) {
13384
+ return `_squeeze_output failed: ${err.message}_`;
13385
+ }
13386
+
13387
+ if (!sq.category || !sq.applies) {
13388
+ return `No squeezable structure detected — content unchanged (${sq.rawTokens} tokens).\n\n${content}`;
13389
+ }
13390
+
13391
+ const pct = Math.round(sq.reduction * 100);
13392
+ const header =
13393
+ `Squeezed ${sq.category} — ${sq.rawTokens} → ${sq.squeezedTokens} tokens ` +
13394
+ `(${pct}% smaller)${sq.enriched ? ' · signature-enriched' : ''}\n\n`;
13395
+ return header + sq.squeezed;
13396
+ }
13397
+
13398
+ module.exports = { readContext, searchSignatures, getMap, createCheckpoint, getRouting, explainFile, listModules, queryContext, getImpact, getLines, readMemory, getCalleeSignatures, notifyFileCreated, notifySymbolAdded, notifyFileDeleted, getDiffContext, getArchitectureOverview, verifySuggestion, squeezeOutput };
13349
13399
 
13350
13400
  };
13351
13401
 
@@ -13506,17 +13556,17 @@ __factories["./src/mcp/server"] = function(module, exports) {
13506
13556
  *
13507
13557
  * Supported methods:
13508
13558
  * initialize → serverInfo + capabilities
13509
- * tools/list → 18 tool definitions
13559
+ * tools/list → 19 tool definitions
13510
13560
  * tools/call → dispatch to handler, return result
13511
13561
  */
13512
13562
 
13513
13563
  const readline = require('readline');
13514
13564
  const { TOOLS } = __require('./src/mcp/tools');
13515
- const { readContext, searchSignatures, getMap, createCheckpoint, getRouting, explainFile, listModules, queryContext, getImpact, getLines, readMemory, getCalleeSignatures, notifyFileCreated, notifySymbolAdded, notifyFileDeleted, getDiffContext, getArchitectureOverview, verifySuggestion } = __require('./src/mcp/handlers');
13565
+ const { readContext, searchSignatures, getMap, createCheckpoint, getRouting, explainFile, listModules, queryContext, getImpact, getLines, readMemory, getCalleeSignatures, notifyFileCreated, notifySymbolAdded, notifyFileDeleted, getDiffContext, getArchitectureOverview, verifySuggestion, squeezeOutput } = __require('./src/mcp/handlers');
13516
13566
 
13517
13567
  const SERVER_INFO = {
13518
13568
  name: 'sigmap',
13519
- version: '8.7.0',
13569
+ version: '8.8.0',
13520
13570
  description: 'SigMap MCP server — code signatures on demand',
13521
13571
  };
13522
13572
 
@@ -13582,6 +13632,7 @@ __factories["./src/mcp/server"] = function(module, exports) {
13582
13632
  else if (name === 'get_diff_context') text = getDiffContext(args, cwd);
13583
13633
  else if (name === 'get_architecture_overview') text = getArchitectureOverview(args, cwd);
13584
13634
  else if (name === 'verify_suggestion') text = verifySuggestion(args, cwd);
13635
+ else if (name === 'squeeze_output') text = squeezeOutput(args, cwd);
13585
13636
  else {
13586
13637
  respondError(id, -32601, `Unknown tool: ${name}`);
13587
13638
  return;
@@ -13642,11 +13693,12 @@ __factories["./src/mcp/server"] = function(module, exports) {
13642
13693
  __factories["./src/mcp/tools"] = function(module, exports) {
13643
13694
 
13644
13695
  /**
13645
- * MCP tool definitions for SigMap (17 tools).
13696
+ * MCP tool definitions for SigMap (19 tools).
13646
13697
  * read_context, search_signatures, get_map, create_checkpoint, get_routing,
13647
13698
  * explain_file, list_modules, query_context, get_impact, get_lines, read_memory,
13648
13699
  * get_callee_signatures, sigmap_notify_file_created, sigmap_notify_symbol_added,
13649
- * sigmap_notify_file_deleted, get_diff_context, get_architecture_overview.
13700
+ * sigmap_notify_file_deleted, get_diff_context, get_architecture_overview,
13701
+ * verify_suggestion, squeeze_output.
13650
13702
  */
13651
13703
 
13652
13704
  const TOOLS = [
@@ -13977,6 +14029,27 @@ __factories["./src/mcp/tools"] = function(module, exports) {
13977
14029
  required: ['code'],
13978
14030
  },
13979
14031
  },
14032
+ {
14033
+ name: 'squeeze_output',
14034
+ description:
14035
+ 'Compress noisy tool, command, or agent output before it enters context — a stack ' +
14036
+ 'trace, CI/build log, or JSON payload. Same deterministic, offline engine as ' +
14037
+ '`sigmap squeeze`: keeps the signal (error frames, failing steps, JSON shape), strips ' +
14038
+ 'the noise, and enriches the top stack frame with its signature. Returns the squeezed ' +
14039
+ 'text plus token-reduction stats; passes the input through unchanged when no squeezable ' +
14040
+ 'structure is detected. No LLM, no network.',
14041
+ inputSchema: {
14042
+ type: 'object',
14043
+ properties: {
14044
+ content: {
14045
+ type: 'string',
14046
+ description:
14047
+ 'The raw output to compress (a stack trace, CI/build log, or JSON payload).',
14048
+ },
14049
+ },
14050
+ required: ['content'],
14051
+ },
14052
+ },
13980
14053
  ];
13981
14054
 
13982
14055
  module.exports = { TOOLS };
@@ -16851,11 +16924,12 @@ __factories["./src/tracking/pricing"] = function(module, exports) {
16851
16924
  * presented as exact. Zero npm dependencies.
16852
16925
  */
16853
16926
 
16854
- // USD per 1,000,000 input tokens.
16927
+ // USD per 1,000,000 input tokens. Claude rates verified 2026-07 against
16928
+ // platform.claude.com (Opus 4.8 $5, Sonnet 5/4.6 $3, Haiku 4.5 $1); GPT-4o $2.50.
16855
16929
  const PRICES = {
16856
16930
  'claude-sonnet': 3.0,
16857
- 'claude-opus': 15.0,
16858
- 'claude-haiku': 0.8,
16931
+ 'claude-opus': 5.0,
16932
+ 'claude-haiku': 1.0,
16859
16933
  'gpt-4o': 2.5,
16860
16934
  'gpt-4o-mini': 0.15,
16861
16935
  'gemini-1.5-pro': 1.25,
@@ -18084,7 +18158,7 @@ function __tryGit(args, opts = {}) {
18084
18158
  catch (_) { return ''; }
18085
18159
  }
18086
18160
 
18087
- const VERSION = '8.7.0';
18161
+ const VERSION = '8.8.0';
18088
18162
  const MARKER = '\n\n## Auto-generated signatures\n<!-- Updated by gen-context.js -->\n';
18089
18163
 
18090
18164
  function requireSourceOrBundled(key) {
@@ -18197,6 +18271,10 @@ function walkDir(dir, exclude, maxDepth, depth = 0) {
18197
18271
  } catch (_) {
18198
18272
  return [];
18199
18273
  }
18274
+ // Sort by name so the collected file list — and therefore the output section
18275
+ // order — is deterministic. Raw readdir order is filesystem-dependent and can
18276
+ // vary run to run, making the generated context non-byte-stable.
18277
+ entries.sort((a, b) => a.name.localeCompare(b.name));
18200
18278
  for (const entry of entries) {
18201
18279
  if (exclude.includes(entry.name)) continue;
18202
18280
  const full = path.join(dir, entry.name);
@@ -18408,10 +18486,15 @@ function applyTokenBudget(fileEntries, maxTokens) {
18408
18486
  });
18409
18487
 
18410
18488
  // Best-first order: lowest drop-priority, then most-recent, then most-informative.
18489
+ // Final tie-break on filePath keeps the selection deterministic when priority,
18490
+ // mtime, and signalQuality all tie (common for test files sharing a git-checkout
18491
+ // mtime) — otherwise the choice fell through to filesystem readdir order, making
18492
+ // which files are kept vs dropped non-reproducible run to run.
18411
18493
  const bestFirst = withPriority.slice().sort((a, b) => {
18412
18494
  if (a.priority !== b.priority) return a.priority - b.priority;
18413
18495
  if ((b.mtime || 0) !== (a.mtime || 0)) return (b.mtime || 0) - (a.mtime || 0);
18414
- return (b.signalQuality || 0) - (a.signalQuality || 0);
18496
+ if ((b.signalQuality || 0) !== (a.signalQuality || 0)) return (b.signalQuality || 0) - (a.signalQuality || 0);
18497
+ return a.filePath.localeCompare(b.filePath);
18415
18498
  });
18416
18499
 
18417
18500
  const entryCost = (e) => estimateTokens(e.sigs.join('\n')) + sectionOverhead(e);
@@ -19910,6 +19993,7 @@ Usage:
19910
19993
  ${cmd} review-pr --markdown PR Evidence Report — branded Markdown (signatures + blast radius + tests) to post as a PR comment
19911
19994
  ${cmd} create "<task>" Grounded-creation pipeline: scaffold → verify-plan → verify-ai-output → review-pr (--staged)
19912
19995
  ${cmd} squeeze <file|-> Minimize a pasted stacktrace/CI-log/JSON blob (--json for stats)
19996
+ ${cmd} squeeze --response <file|-> Minimize an agent/tool response (same engine; also exposed as the squeeze_output MCP tool)
19913
19997
  ${cmd} ask "<query>" --squeeze Auto-accept input minimization (no prompt; for scripts/CI)
19914
19998
  ${cmd} ask "<query>" --no-squeeze Disable input minimization entirely
19915
19999
  ${cmd} ask "<query>" --squeeze-threshold N Min reduction %% to prompt (default 30)
@@ -21680,7 +21764,20 @@ function main() {
21680
21764
  // v7.0.0: `sigmap squeeze <file|->` — minimize a pasted stacktrace / CI-log / JSON blob
21681
21765
  if (args[0] === 'squeeze') {
21682
21766
  const jsonOut = args.includes('--json');
21683
- const target = args[1] && !args[1].startsWith('--') ? args[1] : '-';
21767
+ // `--response <file|->` names the agent/tool response to squeeze explicitly
21768
+ // (mirrors `sigmap judge --response`); otherwise fall back to the positional arg.
21769
+ const respIdx = args.indexOf('--response');
21770
+ let target;
21771
+ if (respIdx !== -1) {
21772
+ const val = args[respIdx + 1];
21773
+ if (!val || val.startsWith('--')) {
21774
+ console.error('[sigmap] Usage: sigmap squeeze --response <file|-> [--json]');
21775
+ process.exit(1);
21776
+ }
21777
+ target = val;
21778
+ } else {
21779
+ target = args[1] && !args[1].startsWith('--') ? args[1] : '-';
21780
+ }
21684
21781
  let input = '';
21685
21782
  try {
21686
21783
  input = fs.readFileSync(target === '-' ? 0 : path.resolve(cwd, target), 'utf8');
package/llms-full.txt CHANGED
@@ -11,22 +11,22 @@ 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.7.0 | Benchmark: sigmap-v8.7-main (2026-07-05)
14
+ # Version: 8.8.0 | Benchmark: sigmap-v8.8-main (2026-07-05)
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.7-main, 2026-07-05)
20
+ ## Core metrics (benchmark: sigmap-v8.8-main, 2026-07-05)
21
21
 
22
22
  | Metric | Without SigMap | With SigMap |
23
23
  |--------|----------------|-------------|
24
24
  | Retrieval hit@5 | 13.6% (random) | 87.8% (6.5× lift) |
25
25
  | Token reduction | — | 97.0% average |
26
- | Task success proxy | 10% | 68.9% |
27
- | Prompts per task | 2.84 | 1.43 (49.6% fewer) |
26
+ | Task success proxy | 10% | 67.8% |
27
+ | Prompts per task | 2.84 | 1.44 (49.2% fewer) |
28
28
  | Supported languages | — | 33 |
29
- | MCP tools | — | 18 |
29
+ | MCP tools | — | 19 |
30
30
  | npm runtime dependencies | — | 0 |
31
31
 
32
32
  ---
@@ -113,6 +113,7 @@ sigmap review-pr Audit a diff — scope drift, god-node
113
113
  sigmap review-pr --markdown PR Evidence Report — branded Markdown (signatures + blast radius + tests) to post as a PR comment
114
114
  sigmap create "<task>" Grounded-creation pipeline: scaffold → verify-plan → verify-ai-output → review-pr (--staged)
115
115
  sigmap squeeze <file|-> Minimize a pasted stacktrace/CI-log/JSON blob (--json for stats)
116
+ sigmap squeeze --response <file|-> Minimize an agent/tool response (same engine; also exposed as the squeeze_output MCP tool)
116
117
  sigmap ask "<query>" --squeeze Auto-accept input minimization (no prompt; for scripts/CI)
117
118
  sigmap ask "<query>" --no-squeeze Disable input minimization entirely
118
119
  sigmap ask "<query>" --squeeze-threshold N Min reduction %% to prompt (default 30)
@@ -132,7 +133,7 @@ sigmap --version Show version
132
133
 
133
134
  ---
134
135
 
135
- ## MCP server — 18 tools
136
+ ## MCP server — 19 tools
136
137
 
137
138
  Start with `sigmap --mcp` (stdio JSON-RPC). Configure once:
138
139
 
@@ -284,6 +285,14 @@ Ground an AI code suggestion before writing it: verify a snippet or answer again
284
285
  Input: { code: string }
285
286
  ```
286
287
 
288
+ ### squeeze_output
289
+
290
+ Compress noisy tool, command, or agent output before it enters context — a stack trace, CI/build log, or JSON payload. Same deterministic, offline engine as `sigmap squeeze`: keeps the signal (error frames, failing steps, JSON shape), strips the noise, and enriches the top stack frame with its signature. Returns the squeezed text plus token-reduction stats; passes the input through unchanged when no squeezable structure is detected. No LLM, no network.
291
+
292
+ ```
293
+ Input: { content: string }
294
+ ```
295
+
287
296
  ---
288
297
 
289
298
  ## Configuration (gen-context.config.json)
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.7.0 | Benchmark: sigmap-v8.7-main (2026-07-05)
14
+ # Version: 8.8.0 | Benchmark: sigmap-v8.8-main (2026-07-05)
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,13 +23,13 @@ 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.7-main, 2026-07-05)
26
+ ## Core metrics (benchmark: sigmap-v8.8-main, 2026-07-05)
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
30
- - Task success: 68.9% vs 10% without SigMap
31
- - Prompts per task: 1.43 vs 2.84 baseline (49.6% fewer)
32
- - Languages: 33 supported · MCP tools: 18
30
+ - Task success: 67.8% vs 10% without SigMap
31
+ - Prompts per task: 1.44 vs 2.84 baseline (49.2% fewer)
32
+ - Languages: 33 supported · MCP tools: 19
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.7.0",
3
+ "version": "8.8.0",
4
4
  "description": "97% token reduction for AI coding. Extracts function & class signatures with TF-IDF ranking to feed only the right files to Claude, Cursor, Copilot, Aider, Windsurf, local LLMs & MCP. Zero dependencies, runs offline via npx.",
5
5
  "main": "packages/core/index.js",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sigmap-cli",
3
- "version": "8.7.0",
3
+ "version": "8.8.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.7.0",
3
+ "version": "8.8.0",
4
4
  "description": "SigMap core library — zero-dependency code signature extraction, retrieval, and security scanning",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -43,7 +43,11 @@ function resolveSourceRoots(cwd, opts = {}) {
43
43
  score: scoreCandidate(name, full, context),
44
44
  }))
45
45
  .filter(c => c.score > 0)
46
- .sort((a, b) => b.score - a.score);
46
+ // Final tie-break on dir keeps selection deterministic when scores tie — the
47
+ // top-MAX_ROOTS slice below would otherwise admit different dirs run to run
48
+ // (candidate order comes from filesystem readdir), changing which files are
49
+ // collected and making the generated context non-reproducible.
50
+ .sort((a, b) => b.score - a.score || a.dir.localeCompare(b.dir));
47
51
 
48
52
  // Handle special rules
49
53
  let roots = _applySpecialRules(scored, cwd, primaryFw, fwEntry, frameworks);
@@ -86,9 +90,12 @@ function _enumerateCandidates(cwd, isMonorepo, ignorePatterns, excludeList) {
86
90
  const candidates = [];
87
91
  const excSet = new Set(excludeList);
88
92
 
89
- // Root-level dirs
93
+ // Root-level dirs (sorted so candidate order — and downstream dedupe/selection
94
+ // — is deterministic regardless of filesystem readdir order)
90
95
  try {
91
- for (const e of fs.readdirSync(cwd, { withFileTypes: true })) {
96
+ const rootEntries = fs.readdirSync(cwd, { withFileTypes: true })
97
+ .sort((a, b) => a.name.localeCompare(b.name));
98
+ for (const e of rootEntries) {
92
99
  if (!e.isDirectory()) continue;
93
100
  if (excSet.has(e.name)) continue;
94
101
  if (matchesIgnorePattern(e.name, ignorePatterns)) continue;
@@ -157,7 +164,7 @@ function _applySpecialRules(scored, cwd, primaryFw, fwEntry, frameworks) {
157
164
  }
158
165
  }
159
166
  } catch (_) {}
160
- roots.sort((a, b) => b.score - a.score);
167
+ roots.sort((a, b) => b.score - a.score || a.dir.localeCompare(b.dir));
161
168
  }
162
169
 
163
170
  // Swift project dir: dirs with ≥3 .swift files
@@ -172,7 +179,7 @@ function _applySpecialRules(scored, cwd, primaryFw, fwEntry, frameworks) {
172
179
  }
173
180
  }
174
181
  } catch (_) {}
175
- roots.sort((a, b) => b.score - a.score);
182
+ roots.sort((a, b) => b.score - a.score || a.dir.localeCompare(b.dir));
176
183
  }
177
184
 
178
185
  return roots;
@@ -898,4 +898,47 @@ function verifySuggestion(args, cwd) {
898
898
  return out.join('\n');
899
899
  }
900
900
 
901
- module.exports = { readContext, searchSignatures, getMap, createCheckpoint, getRouting, explainFile, listModules, queryContext, getImpact, getLines, readMemory, getCalleeSignatures, notifyFileCreated, notifySymbolAdded, notifyFileDeleted, getDiffContext, getArchitectureOverview, verifySuggestion };
901
+ /**
902
+ * squeeze_output({ content }) → string
903
+ *
904
+ * Compress noisy tool/command/agent output (stack trace, CI/build log, or JSON
905
+ * payload) through the deterministic squeeze engine. Enriches the top stack
906
+ * frame with its signature when the symbol index is available. Passes the input
907
+ * through unchanged when no squeezable structure is detected.
908
+ */
909
+ function squeezeOutput(args, cwd) {
910
+ const content = args && typeof args.content === 'string' ? args.content : '';
911
+ if (!content.trim()) {
912
+ return 'Usage: squeeze_output({ content: "<raw tool/log/JSON output>" }) — provide the output to compress.';
913
+ }
914
+
915
+ let sq;
916
+ try {
917
+ const { squeeze } = require('../squeeze/index');
918
+ let srcDirs;
919
+ let symbolIndex = null;
920
+ try {
921
+ const { loadConfig } = require('../config/loader');
922
+ srcDirs = loadConfig(cwd).srcDirs;
923
+ } catch (_) {}
924
+ try {
925
+ const { buildSigIndex } = require('../retrieval/ranker');
926
+ symbolIndex = buildSigIndex(cwd);
927
+ } catch (_) {}
928
+ sq = squeeze(content, { srcDirs, symbolIndex });
929
+ } catch (err) {
930
+ return `_squeeze_output failed: ${err.message}_`;
931
+ }
932
+
933
+ if (!sq.category || !sq.applies) {
934
+ return `No squeezable structure detected — content unchanged (${sq.rawTokens} tokens).\n\n${content}`;
935
+ }
936
+
937
+ const pct = Math.round(sq.reduction * 100);
938
+ const header =
939
+ `Squeezed ${sq.category} — ${sq.rawTokens} → ${sq.squeezedTokens} tokens ` +
940
+ `(${pct}% smaller)${sq.enriched ? ' · signature-enriched' : ''}\n\n`;
941
+ return header + sq.squeezed;
942
+ }
943
+
944
+ module.exports = { readContext, searchSignatures, getMap, createCheckpoint, getRouting, explainFile, listModules, queryContext, getImpact, getLines, readMemory, getCalleeSignatures, notifyFileCreated, notifySymbolAdded, notifyFileDeleted, getDiffContext, getArchitectureOverview, verifySuggestion, squeezeOutput };
package/src/mcp/server.js CHANGED
@@ -8,17 +8,17 @@
8
8
  *
9
9
  * Supported methods:
10
10
  * initialize → serverInfo + capabilities
11
- * tools/list → 18 tool definitions
11
+ * tools/list → 19 tool definitions
12
12
  * tools/call → dispatch to handler, return result
13
13
  */
14
14
 
15
15
  const readline = require('readline');
16
16
  const { TOOLS } = require('./tools');
17
- const { readContext, searchSignatures, getMap, createCheckpoint, getRouting, explainFile, listModules, queryContext, getImpact, getLines, readMemory, getCalleeSignatures, notifyFileCreated, notifySymbolAdded, notifyFileDeleted, getDiffContext, getArchitectureOverview, verifySuggestion } = require('./handlers');
17
+ const { readContext, searchSignatures, getMap, createCheckpoint, getRouting, explainFile, listModules, queryContext, getImpact, getLines, readMemory, getCalleeSignatures, notifyFileCreated, notifySymbolAdded, notifyFileDeleted, getDiffContext, getArchitectureOverview, verifySuggestion, squeezeOutput } = require('./handlers');
18
18
 
19
19
  const SERVER_INFO = {
20
20
  name: 'sigmap',
21
- version: '8.7.0',
21
+ version: '8.8.0',
22
22
  description: 'SigMap MCP server — code signatures on demand',
23
23
  };
24
24
 
@@ -84,6 +84,7 @@ function dispatch(msg, cwd) {
84
84
  else if (name === 'get_diff_context') text = getDiffContext(args, cwd);
85
85
  else if (name === 'get_architecture_overview') text = getArchitectureOverview(args, cwd);
86
86
  else if (name === 'verify_suggestion') text = verifySuggestion(args, cwd);
87
+ else if (name === 'squeeze_output') text = squeezeOutput(args, cwd);
87
88
  else {
88
89
  respondError(id, -32601, `Unknown tool: ${name}`);
89
90
  return;
package/src/mcp/tools.js CHANGED
@@ -1,11 +1,12 @@
1
1
  'use strict';
2
2
 
3
3
  /**
4
- * MCP tool definitions for SigMap (17 tools).
4
+ * MCP tool definitions for SigMap (19 tools).
5
5
  * read_context, search_signatures, get_map, create_checkpoint, get_routing,
6
6
  * explain_file, list_modules, query_context, get_impact, get_lines, read_memory,
7
7
  * get_callee_signatures, sigmap_notify_file_created, sigmap_notify_symbol_added,
8
- * sigmap_notify_file_deleted, get_diff_context, get_architecture_overview.
8
+ * sigmap_notify_file_deleted, get_diff_context, get_architecture_overview,
9
+ * verify_suggestion, squeeze_output.
9
10
  */
10
11
 
11
12
  const TOOLS = [
@@ -336,6 +337,27 @@ const TOOLS = [
336
337
  required: ['code'],
337
338
  },
338
339
  },
340
+ {
341
+ name: 'squeeze_output',
342
+ description:
343
+ 'Compress noisy tool, command, or agent output before it enters context — a stack ' +
344
+ 'trace, CI/build log, or JSON payload. Same deterministic, offline engine as ' +
345
+ '`sigmap squeeze`: keeps the signal (error frames, failing steps, JSON shape), strips ' +
346
+ 'the noise, and enriches the top stack frame with its signature. Returns the squeezed ' +
347
+ 'text plus token-reduction stats; passes the input through unchanged when no squeezable ' +
348
+ 'structure is detected. No LLM, no network.',
349
+ inputSchema: {
350
+ type: 'object',
351
+ properties: {
352
+ content: {
353
+ type: 'string',
354
+ description:
355
+ 'The raw output to compress (a stack trace, CI/build log, or JSON payload).',
356
+ },
357
+ },
358
+ required: ['content'],
359
+ },
360
+ },
339
361
  ];
340
362
 
341
363
  module.exports = { TOOLS };
@@ -10,11 +10,12 @@
10
10
  * presented as exact. Zero npm dependencies.
11
11
  */
12
12
 
13
- // USD per 1,000,000 input tokens.
13
+ // USD per 1,000,000 input tokens. Claude rates verified 2026-07 against
14
+ // platform.claude.com (Opus 4.8 $5, Sonnet 5/4.6 $3, Haiku 4.5 $1); GPT-4o $2.50.
14
15
  const PRICES = {
15
16
  'claude-sonnet': 3.0,
16
- 'claude-opus': 15.0,
17
- 'claude-haiku': 0.8,
17
+ 'claude-opus': 5.0,
18
+ 'claude-haiku': 1.0,
18
19
  'gpt-4o': 2.5,
19
20
  'gpt-4o-mini': 0.15,
20
21
  'gemini-1.5-pro': 1.25,