sigmap 8.23.0 → 8.24.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,18 @@ Format: [Semantic Versioning](https://semver.org/)
10
10
 
11
11
  ---
12
12
 
13
+ ## [8.24.0] — 2026-07-28
14
+
15
+ Minor release — **"Trust Quick Wins I" (v8.24, G3a)**: the secret-redaction engine that already protects signatures, `get_lines`, and evidence packs becomes a standalone command for arbitrary text.
16
+
17
+ ### Added
18
+ - **`sigmap redact` (#511, PR #512):** new `src/security/redact.js` — `redactText()` masks only the matched secret substring (`[REDACTED:<pattern name>]`), preserving surrounding text (unlike the generation-time scanner's whole-line replacement, which is unchanged). Findings carry 1-based line numbers with per-pattern counts. CLI: `sigmap redact [file] [--json]` — file argument or stdin; redacted text on stdout (pipe-clean), summary on stderr. Reuses the existing 10-pattern bank verbatim; zero new dependencies; never throws. 6 new integration tests including an every-pattern mask sweep (130 test files).
19
+
20
+ ### Changed
21
+ - Test fixtures for secret patterns are assembled at runtime (no secret-shaped literals in committed blobs) — GitHub Push Protection rejected the first push over the fake samples, which validated the detection class this command implements.
22
+
23
+ ---
24
+
13
25
  ## [8.23.0] — 2026-07-28
14
26
 
15
27
  Minor release — **"Agent Economy I" (v8.23, F1)**: SigMap's token savings become queryable *during* a session. A spend ledger over the existing gain log, an optional budget threshold, and context-freshness age — as a CLI command and the 21st MCP tool.
package/README.md CHANGED
@@ -122,7 +122,7 @@ Ask → Rank → Context → Validate → Judge → Learn
122
122
 
123
123
  <!--SM:benchmarkBlock-->
124
124
  ```
125
- Benchmark : sigmap-v8.23-main (21 repositories, including R language)
125
+ Benchmark : sigmap-v8.24-main (21 repositories, including R language)
126
126
  Date : 2026-07-28
127
127
 
128
128
  Hit@5 : 82.2% (grep-agent baseline 44.8% — 1.59× lift)
package/gen-context.js CHANGED
@@ -14981,7 +14981,7 @@ __factories["./src/mcp/server"] = function(module, exports) {
14981
14981
 
14982
14982
  const SERVER_INFO = {
14983
14983
  name: 'sigmap',
14984
- version: '8.23.0',
14984
+ version: '8.24.0',
14985
14985
  description: 'SigMap MCP server — code signatures on demand',
14986
14986
  };
14987
14987
 
@@ -17528,6 +17528,66 @@ __factories["./src/security/patterns"] = function(module, exports) {
17528
17528
 
17529
17529
  };
17530
17530
 
17531
+ // ── ./src/security/redact ──
17532
+ __factories["./src/security/redact"] = function(module, exports) {
17533
+
17534
+ /**
17535
+ * Standalone text redaction (v8.24 G3a) — the same pattern bank the
17536
+ * generation-time scanner uses (security/patterns.js), applied to arbitrary
17537
+ * text. Unlike scanner.js (which replaces whole signature lines), this masks
17538
+ * only the matched secret substring so surrounding text stays readable.
17539
+ *
17540
+ * Zero dependencies; never throws — on any error the original text is
17541
+ * returned unredacted.
17542
+ */
17543
+
17544
+ const { PATTERNS } = __require('./src/security/patterns');
17545
+
17546
+ // Global variants of the pattern regexes (needed for replace-all per line).
17547
+ const GLOBAL_PATTERNS = PATTERNS.map((p) => ({
17548
+ name: p.name,
17549
+ regex: new RegExp(p.regex.source, p.regex.flags.includes('g') ? p.regex.flags : p.regex.flags + 'g'),
17550
+ }));
17551
+
17552
+ /**
17553
+ * Redact secrets in arbitrary text.
17554
+ * @param {string} text
17555
+ * @returns {{
17556
+ * text: string, redacted: boolean,
17557
+ * findings: Array<{ line: number, pattern: string }>,
17558
+ * counts: Object<string, number>
17559
+ * }}
17560
+ */
17561
+ function redactText(text) {
17562
+ if (typeof text !== 'string' || text.length === 0) {
17563
+ return { text: typeof text === 'string' ? text : '', redacted: false, findings: [], counts: {} };
17564
+ }
17565
+ try {
17566
+ const findings = [];
17567
+ const counts = {};
17568
+ const lines = text.split('\n');
17569
+ const out = lines.map((line, i) => {
17570
+ let masked = line;
17571
+ for (const p of GLOBAL_PATTERNS) {
17572
+ p.regex.lastIndex = 0;
17573
+ if (!p.regex.test(masked)) continue;
17574
+ p.regex.lastIndex = 0;
17575
+ masked = masked.replace(p.regex, `[REDACTED:${p.name}]`);
17576
+ findings.push({ line: i + 1, pattern: p.name });
17577
+ counts[p.name] = (counts[p.name] || 0) + 1;
17578
+ }
17579
+ return masked;
17580
+ });
17581
+ return { text: out.join('\n'), redacted: findings.length > 0, findings, counts };
17582
+ } catch (_) {
17583
+ return { text, redacted: false, findings: [], counts: {} };
17584
+ }
17585
+ }
17586
+
17587
+ module.exports = { redactText };
17588
+
17589
+ };
17590
+
17531
17591
  // ── ./src/security/scanner ──
17532
17592
  __factories["./src/security/scanner"] = function(module, exports) {
17533
17593
 
@@ -20347,7 +20407,7 @@ function __tryGit(args, opts = {}) {
20347
20407
  catch (_) { return ''; }
20348
20408
  }
20349
20409
 
20350
- const VERSION = '8.23.0';
20410
+ const VERSION = '8.24.0';
20351
20411
  const MARKER = '\n\n## Auto-generated signatures\n<!-- Updated by gen-context.js -->\n';
20352
20412
 
20353
20413
  function requireSourceOrBundled(key) {
@@ -22234,6 +22294,7 @@ Usage:
22234
22294
  ${cmd} memory --clear <store> Clear one store: session|notes|weights|evidence|all (--json supported)
22235
22295
  ${cmd} budget Session spend ledger — estimated SigMap-emitted tokens, budget, context age (--json)
22236
22296
  ${cmd} budget --budget <tokens> One-off budget override (config: sessionBudgetTokens, contextTtlDays)
22297
+ ${cmd} redact [file] Mask secrets in a file or stdin (10-pattern bank); redacted text to stdout (--json)
22237
22298
  ${cmd} note "<text>" Append a note to the cross-session decision log
22238
22299
  ${cmd} note List recent notes (also: note --list <N>)
22239
22300
  ${cmd} status Show repo state — branch, dirty files, index freshness, notes
@@ -23715,6 +23776,42 @@ function main() {
23715
23776
  process.exit(0);
23716
23777
  }
23717
23778
 
23779
+ if (args[0] === 'redact') {
23780
+ const jsonOut = args.includes('--json');
23781
+ const { redactText } = requireSourceOrBundled('./src/security/redact');
23782
+ const fileArg = args.slice(1).find((a) => !a.startsWith('--'));
23783
+ let input;
23784
+ if (fileArg) {
23785
+ try {
23786
+ input = fs.readFileSync(path.resolve(cwd, fileArg), 'utf8');
23787
+ } catch (err) {
23788
+ console.error(`[sigmap] redact: cannot read ${fileArg}: ${err.message}`);
23789
+ process.exit(1);
23790
+ }
23791
+ } else if (!process.stdin.isTTY) {
23792
+ try {
23793
+ input = fs.readFileSync(0, 'utf8');
23794
+ } catch (err) {
23795
+ console.error(`[sigmap] redact: cannot read stdin: ${err.message}`);
23796
+ process.exit(1);
23797
+ }
23798
+ } else {
23799
+ console.error('[sigmap] usage: sigmap redact <file> or <cmd> | sigmap redact');
23800
+ process.exit(1);
23801
+ }
23802
+ const r = redactText(input);
23803
+ if (jsonOut) {
23804
+ process.stdout.write(JSON.stringify(r) + '\n');
23805
+ process.exit(0);
23806
+ }
23807
+ process.stdout.write(r.text);
23808
+ const parts = Object.entries(r.counts).map(([k, v]) => `${k}×${v}`);
23809
+ console.error(r.redacted
23810
+ ? `[sigmap] redact: masked ${r.findings.length} secret(s) — ${parts.join(', ')}`
23811
+ : '[sigmap] redact: no secrets found');
23812
+ process.exit(0);
23813
+ }
23814
+
23718
23815
  if (args[0] === 'note') {
23719
23816
  const jsonOut = args.includes('--json');
23720
23817
  const { addNote, readNotes, formatNotes } = requireSourceOrBundled('./src/session/notes');
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.23.0 | Benchmark: sigmap-v8.23-main (2026-07-28)
14
+ # Version: 8.24.0 | Benchmark: sigmap-v8.24-main (2026-07-28)
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.23-main, 2026-07-28)
20
+ ## Core metrics (benchmark: sigmap-v8.24-main, 2026-07-28)
21
21
 
22
22
  | Metric | Without SigMap | With SigMap |
23
23
  |--------|----------------|-------------|
@@ -127,6 +127,7 @@ sigmap memory List cross-session stores (.context/)
127
127
  sigmap memory --clear <store> Clear one store: session|notes|weights|evidence|all (--json supported)
128
128
  sigmap budget Session spend ledger — estimated SigMap-emitted tokens, budget, context age (--json)
129
129
  sigmap budget --budget <tokens> One-off budget override (config: sessionBudgetTokens, contextTtlDays)
130
+ sigmap redact [file] Mask secrets in a file or stdin (10-pattern bank); redacted text to stdout (--json)
130
131
  sigmap note "<text>" Append a note to the cross-session decision log
131
132
  sigmap note List recent notes (also: note --list <N>)
132
133
  sigmap status Show repo state — branch, dirty files, index freshness, notes
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.23.0 | Benchmark: sigmap-v8.23-main (2026-07-28)
14
+ # Version: 8.24.0 | Benchmark: sigmap-v8.24-main (2026-07-28)
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.23-main, 2026-07-28)
26
+ ## Core metrics (benchmark: sigmap-v8.24-main, 2026-07-28)
27
27
 
28
28
  - hit@5 retrieval: 82.2% vs 44.8% single-shot grep baseline (1.59× lift)
29
29
  - Token reduction: 96.8% average across benchmark repos
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sigmap",
3
- "version": "8.23.0",
3
+ "version": "8.24.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": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sigmap-cli",
3
- "version": "8.23.0",
3
+ "version": "8.24.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.23.0",
3
+ "version": "8.24.0",
4
4
  "description": "SigMap core library — zero-dependency code signature extraction, retrieval, and security scanning",
5
5
  "main": "index.js",
6
6
  "keywords": [
package/src/mcp/server.js CHANGED
@@ -18,7 +18,7 @@ const { readContext, searchSignatures, getMap, createCheckpoint, getRouting, exp
18
18
 
19
19
  const SERVER_INFO = {
20
20
  name: 'sigmap',
21
- version: '8.23.0',
21
+ version: '8.24.0',
22
22
  description: 'SigMap MCP server — code signatures on demand',
23
23
  };
24
24
 
@@ -0,0 +1,56 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Standalone text redaction (v8.24 G3a) — the same pattern bank the
5
+ * generation-time scanner uses (security/patterns.js), applied to arbitrary
6
+ * text. Unlike scanner.js (which replaces whole signature lines), this masks
7
+ * only the matched secret substring so surrounding text stays readable.
8
+ *
9
+ * Zero dependencies; never throws — on any error the original text is
10
+ * returned unredacted.
11
+ */
12
+
13
+ const { PATTERNS } = require('./patterns');
14
+
15
+ // Global variants of the pattern regexes (needed for replace-all per line).
16
+ const GLOBAL_PATTERNS = PATTERNS.map((p) => ({
17
+ name: p.name,
18
+ regex: new RegExp(p.regex.source, p.regex.flags.includes('g') ? p.regex.flags : p.regex.flags + 'g'),
19
+ }));
20
+
21
+ /**
22
+ * Redact secrets in arbitrary text.
23
+ * @param {string} text
24
+ * @returns {{
25
+ * text: string, redacted: boolean,
26
+ * findings: Array<{ line: number, pattern: string }>,
27
+ * counts: Object<string, number>
28
+ * }}
29
+ */
30
+ function redactText(text) {
31
+ if (typeof text !== 'string' || text.length === 0) {
32
+ return { text: typeof text === 'string' ? text : '', redacted: false, findings: [], counts: {} };
33
+ }
34
+ try {
35
+ const findings = [];
36
+ const counts = {};
37
+ const lines = text.split('\n');
38
+ const out = lines.map((line, i) => {
39
+ let masked = line;
40
+ for (const p of GLOBAL_PATTERNS) {
41
+ p.regex.lastIndex = 0;
42
+ if (!p.regex.test(masked)) continue;
43
+ p.regex.lastIndex = 0;
44
+ masked = masked.replace(p.regex, `[REDACTED:${p.name}]`);
45
+ findings.push({ line: i + 1, pattern: p.name });
46
+ counts[p.name] = (counts[p.name] || 0) + 1;
47
+ }
48
+ return masked;
49
+ });
50
+ return { text: out.join('\n'), redacted: findings.length > 0, findings, counts };
51
+ } catch (_) {
52
+ return { text, redacted: false, findings: [], counts: {} };
53
+ }
54
+ }
55
+
56
+ module.exports = { redactText };