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 +12 -0
- package/README.md +1 -1
- package/gen-context.js +99 -2
- package/llms-full.txt +3 -2
- package/llms.txt +2 -2
- package/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/core/package.json +1 -1
- package/src/mcp/server.js +1 -1
- package/src/security/redact.js +56 -0
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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": {
|
package/src/mcp/server.js
CHANGED
|
@@ -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 };
|