sigmap 2.3.0 → 2.4.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 +24 -0
- package/README.md +60 -16
- package/gen-context.js +1 -1
- package/package.json +7 -1
- package/packages/cli/index.js +63 -0
- package/packages/cli/package.json +26 -0
- package/packages/core/README.md +133 -0
- package/packages/core/index.js +215 -0
- package/packages/core/package.json +28 -0
- package/src/mcp/server.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,30 @@ Format: [Semantic Versioning](https://semver.org/)
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## [2.4.0] — 2026-04-05
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- **`packages/core/`** — new `sigmap-core` package exposing a stable programmatic API: `{ extract, rank, buildSigIndex, scan, score }`. Third-party tools can now `require('sigmap')` and use all extraction/retrieval/security/health APIs without spawning a CLI process.
|
|
13
|
+
- **`packages/cli/`** — new `sigmap-cli` thin wrapper that exposes `{ CLI_ENTRY, run }` for programmatic CLI invocation and forward-compat with the v3.0 adapter architecture.
|
|
14
|
+
- **`packages/core/README.md`** — full programmatic API reference with usage examples for all five exported functions.
|
|
15
|
+
- **`exports` field in `package.json`** — `require('sigmap')` resolves to `packages/core/index.js`; `require('sigmap/cli')` resolves to `packages/cli/index.js`.
|
|
16
|
+
- **`test/integration/core-api.test.js`** — 15 integration tests covering: all exports present, `extract` for JS/TS/Python, file-path extension detection, unknown language returns `[]`, never throws on bad input, `rank` with empty map, `rank` sorted shape, `scan` clean/redact, `score` shape, `buildSigIndex` returns Map, CLI `--version` backward compat, CLI `--help` no crash.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- `package.json` `"version"` bumped to `2.4.0`.
|
|
20
|
+
- `package.json` `"files"` — added `"packages/"` so `sigmap-core` and `sigmap-cli` are published with the root package.
|
|
21
|
+
- `gen-context.js` `VERSION` constant bumped to `2.4.0`.
|
|
22
|
+
- `src/mcp/server.js` `SERVER_INFO.version` bumped to `2.4.0`.
|
|
23
|
+
|
|
24
|
+
### Validation gate
|
|
25
|
+
- 21/21 extractor unit tests passed
|
|
26
|
+
- 21/21 integration suites passed (0 failures, including new `core-api.test.js`)
|
|
27
|
+
- `node gen-context.js --version` → `2.4.0`
|
|
28
|
+
- `node -e "const { extract } = require('.'); console.log(extract('function hello(){}', 'javascript').length > 0 ? 'OK' : 'FAIL')"` → `OK`
|
|
29
|
+
- `require('sigmap')` works from any directory
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
9
33
|
## [2.3.0] — 2026-04-07
|
|
10
34
|
|
|
11
35
|
### Added
|
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
<!-- Status -->
|
|
13
13
|
[](https://www.npmjs.com/package/sigmap)
|
|
14
|
-
[](https://github.com/manojmallick/sigmap/tree/main/test)
|
|
15
15
|
[](package.json)
|
|
16
16
|
[](https://github.com/manojmallick/sigmap/commits/main)
|
|
17
17
|
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
| [VS Code extension](#-vs-code-extension) | Status bar, stale alerts, commands |
|
|
42
42
|
| [Languages supported](#-languages-supported) | 21 languages |
|
|
43
43
|
| [Context strategies](#-context-strategies) | full / per-module / hot-cold |
|
|
44
|
-
| [MCP server](#-mcp-server) |
|
|
44
|
+
| [MCP server](#-mcp-server) | 8 on-demand tools |
|
|
45
45
|
| [CLI reference](#-cli-reference) | All flags |
|
|
46
46
|
| [Configuration](#-configuration) | Config file + .contextignore |
|
|
47
47
|
| [Observability](#-observability) | Health score, reports, CI |
|
|
@@ -86,20 +86,18 @@ AI agent session starts with full context
|
|
|
86
86
|
|
|
87
87
|
---
|
|
88
88
|
|
|
89
|
-
## 🆕 What's new in 2.
|
|
89
|
+
## 🆕 What's new in 2.3
|
|
90
90
|
|
|
91
91
|
| Feature | Description |
|
|
92
92
|
|---|---|
|
|
93
|
-
|
|
|
94
|
-
|
|
|
95
|
-
|
|
|
96
|
-
|
|
|
97
|
-
|
|
|
98
|
-
|
|
|
99
|
-
| **Impact radius hints** | Reverse dependency annotations (used by: ...) |
|
|
100
|
-
| **New helper extractors** | `deps.js`, `todos.js`, `coverage.js`, `prdiff.js` |
|
|
93
|
+
| **`--query "<text>"` CLI** | Rank all context files by relevance to a free-text query — scored table + top-3 signature blocks |
|
|
94
|
+
| **`--query --json`** | Machine-readable ranked results (`{ query, results[], totalResults }`) |
|
|
95
|
+
| **`--query --top <n>`** | Limit results (default 10, configurable via `retrieval.topK`) |
|
|
96
|
+
| **`query_context` MCP tool** | 8th MCP tool — `{ query, topK? }` returns ranked file list, usable live in any MCP session |
|
|
97
|
+
| **`--analyze` / `--diagnose-extractors`** | Per-file breakdown of sigs/tokens/extractor/coverage; self-tests all 21 extractors (v2.2) |
|
|
98
|
+
| **`--benchmark` / `--eval`** | Measure hit@5 and MRR retrieval quality against a JSONL task file (v2.1) |
|
|
101
99
|
|
|
102
|
-
|
|
100
|
+
> **Previous v2.0 additions:** enriched signatures, dependency map, TODO/FIXME section, test coverage markers, structural diff mode, impact radius hints. See [CHANGELOG.md](CHANGELOG.md) for the full history.
|
|
103
101
|
|
|
104
102
|
---
|
|
105
103
|
|
|
@@ -258,7 +256,7 @@ Recently committed files are **hot** (auto-injected). Everything else is **cold*
|
|
|
258
256
|
|
|
259
257
|
## 🔌 MCP server
|
|
260
258
|
|
|
261
|
-
> Introduced in v0.3, expanded to
|
|
259
|
+
> Introduced in v0.3, expanded to 8 tools through v2.3.
|
|
262
260
|
|
|
263
261
|
Start the MCP server on stdio:
|
|
264
262
|
|
|
@@ -277,6 +275,7 @@ node gen-context.js --mcp
|
|
|
277
275
|
| `list_modules` | — | Token-count table of all top-level module directories |
|
|
278
276
|
| `create_checkpoint` | `{ summary: string }` | Write a session checkpoint to `.context/` |
|
|
279
277
|
| `get_routing` | — | Full model routing table |
|
|
278
|
+
| `query_context` | `{ query: string, topK?: number }` | Files ranked by relevance to the query (v2.3) |
|
|
280
279
|
|
|
281
280
|
Reads files on every call — no stale state, no restart needed.
|
|
282
281
|
|
|
@@ -296,6 +295,19 @@ node gen-context.js --diff Generate context for git-changed f
|
|
|
296
295
|
node gen-context.js --diff --staged Staged files only (pre-commit check)
|
|
297
296
|
node gen-context.js --mcp Start MCP server on stdio
|
|
298
297
|
|
|
298
|
+
node gen-context.js --query "<text>" Rank files by relevance to a query
|
|
299
|
+
node gen-context.js --query "<text>" --json Ranked results as JSON
|
|
300
|
+
node gen-context.js --query "<text>" --top <n> Limit results to top N files (default 10)
|
|
301
|
+
|
|
302
|
+
node gen-context.js --analyze Per-file breakdown (sigs / tokens / extractor / coverage)
|
|
303
|
+
node gen-context.js --analyze --json Analysis as JSON
|
|
304
|
+
node gen-context.js --analyze --slow Include extraction timing per file
|
|
305
|
+
node gen-context.js --diagnose-extractors Self-test all 21 extractors against fixtures
|
|
306
|
+
|
|
307
|
+
node gen-context.js --benchmark Run retrieval quality benchmark (hit@5 / MRR)
|
|
308
|
+
node gen-context.js --benchmark --json Benchmark results as JSON
|
|
309
|
+
node gen-context.js --eval Alias for --benchmark
|
|
310
|
+
|
|
299
311
|
node gen-context.js --report Token reduction stats
|
|
300
312
|
node gen-context.js --report --json Structured JSON report (exits 1 if over budget)
|
|
301
313
|
node gen-context.js --report --history Usage log summary
|
|
@@ -435,6 +447,31 @@ node gen-context.js --format cache
|
|
|
435
447
|
|
|
436
448
|
---
|
|
437
449
|
|
|
450
|
+
## 📦 Programmatic API (v2.4+)
|
|
451
|
+
|
|
452
|
+
Use SigMap as a library — no CLI subprocess needed:
|
|
453
|
+
|
|
454
|
+
```js
|
|
455
|
+
const { extract, rank, buildSigIndex, scan, score } = require('sigmap');
|
|
456
|
+
|
|
457
|
+
// Extract signatures from source code
|
|
458
|
+
const sigs = extract('function hello() {}', 'javascript');
|
|
459
|
+
|
|
460
|
+
// Build an index and rank files by query
|
|
461
|
+
const index = buildSigIndex('/path/to/project');
|
|
462
|
+
const results = rank('authentication middleware', index);
|
|
463
|
+
|
|
464
|
+
// Scan signatures for secrets before storing
|
|
465
|
+
const { safe, redacted } = scan(sigs, 'src/config.ts');
|
|
466
|
+
|
|
467
|
+
// Get a composite health score for a project
|
|
468
|
+
const health = score('/path/to/project');
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
📖 Full API reference: [packages/core/README.md](packages/core/README.md)
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
438
475
|
## 🧪 Testing
|
|
439
476
|
|
|
440
477
|
```bash
|
|
@@ -464,7 +501,7 @@ grep "require(" gen-context.js | grep -v "^.*//.*require"
|
|
|
464
501
|
|
|
465
502
|
# Gate 3 — MCP server responds correctly
|
|
466
503
|
echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | node gen-context.js --mcp
|
|
467
|
-
# Expected: valid JSON with
|
|
504
|
+
# Expected: valid JSON with 8 tools
|
|
468
505
|
|
|
469
506
|
# Gate 4 — npm artifact is clean
|
|
470
507
|
npm pack --dry-run
|
|
@@ -481,9 +518,16 @@ sigmap/
|
|
|
481
518
|
├── gen-context.js ← PRIMARY ENTRY POINT — single file, zero deps
|
|
482
519
|
├── gen-project-map.js ← import graph, class hierarchy, route table
|
|
483
520
|
│
|
|
521
|
+
├── packages/
|
|
522
|
+
│ ├── core/ ← programmatic API — require('sigmap') (v2.4)
|
|
523
|
+
│ │ └── index.js ← extract, rank, buildSigIndex, scan, score
|
|
524
|
+
│ └── cli/ ← thin CLI wrapper / v3 compat shim (v2.4)
|
|
525
|
+
│
|
|
484
526
|
├── src/
|
|
485
527
|
│ ├── extractors/ ← 21 language extractors (one file per language)
|
|
486
|
-
│ ├──
|
|
528
|
+
│ ├── retrieval/ ← query-aware ranker + tokenizer (v2.3)
|
|
529
|
+
│ ├── eval/ ← benchmark runner + scorer (v2.1), analyzer (v2.2)
|
|
530
|
+
│ ├── mcp/ ← MCP stdio server — 8 tools
|
|
487
531
|
│ ├── security/ ← secret scanner — 10 patterns
|
|
488
532
|
│ ├── routing/ ← model routing hints
|
|
489
533
|
│ ├── tracking/ ← NDJSON usage logger
|
|
@@ -499,7 +543,7 @@ sigmap/
|
|
|
499
543
|
│ ├── fixtures/ ← one source file per language
|
|
500
544
|
│ ├── expected/ ← expected extractor output
|
|
501
545
|
│ ├── run.js ← zero-dep test runner
|
|
502
|
-
│ └── integration/ ←
|
|
546
|
+
│ └── integration/ ← 20 integration test files (304 tests)
|
|
503
547
|
│
|
|
504
548
|
├── docs/ ← documentation site (GitHub Pages)
|
|
505
549
|
│ ├── index.html ← homepage
|
package/gen-context.js
CHANGED
|
@@ -4091,7 +4091,7 @@ const path = require('path');
|
|
|
4091
4091
|
const os = require('os');
|
|
4092
4092
|
const { execSync } = require('child_process');
|
|
4093
4093
|
|
|
4094
|
-
const VERSION = '2.
|
|
4094
|
+
const VERSION = '2.4.0';
|
|
4095
4095
|
const MARKER = '\n\n## Auto-generated signatures\n<!-- Updated by gen-context.js -->\n';
|
|
4096
4096
|
|
|
4097
4097
|
function requireSourceOrBundled(key) {
|
package/package.json
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sigmap",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "Zero-dependency AI context engine — 97% token reduction. No npm install. Runs on Node 18+.",
|
|
5
5
|
"main": "gen-context.js",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./packages/core/index.js",
|
|
8
|
+
"./cli": "./packages/cli/index.js",
|
|
9
|
+
"./core": "./packages/core/index.js"
|
|
10
|
+
},
|
|
6
11
|
"bin": {
|
|
7
12
|
"sigmap": "./gen-context.js",
|
|
8
13
|
"gen-context": "./gen-context.js",
|
|
@@ -26,6 +31,7 @@
|
|
|
26
31
|
"gen-context.js",
|
|
27
32
|
"gen-project-map.js",
|
|
28
33
|
"src/",
|
|
34
|
+
"packages/",
|
|
29
35
|
"README.md",
|
|
30
36
|
"LICENSE",
|
|
31
37
|
"CHANGELOG.md",
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* sigmap-cli — thin CLI wrapper around sigmap-core.
|
|
5
|
+
*
|
|
6
|
+
* This module is required by the root gen-context.js entry point.
|
|
7
|
+
* All --flag handling lives here; business logic lives in src/ or packages/core.
|
|
8
|
+
*
|
|
9
|
+
* NOTE: This file intentionally does NOT duplicate business logic.
|
|
10
|
+
* It re-exports the entry-point function from gen-context.js so that
|
|
11
|
+
* `require('sigmap-cli')` can be used by tooling that wraps SigMap.
|
|
12
|
+
*
|
|
13
|
+
* In v2.4 the root gen-context.js is kept fully intact for backward compat.
|
|
14
|
+
* packages/cli is a forward-compat shim for the v3.0 adapter architecture.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const path = require('path');
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The CLI entry point path.
|
|
21
|
+
* External tools can use this to spawn the CLI as a child process.
|
|
22
|
+
*/
|
|
23
|
+
const CLI_ENTRY = path.resolve(__dirname, '..', '..', 'gen-context.js');
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Run the SigMap CLI programmatically with the given argv array.
|
|
27
|
+
*
|
|
28
|
+
* @param {string[]} [argv] - Arguments to pass (default: process.argv)
|
|
29
|
+
* @param {string} [cwd] - Working directory (default: process.cwd())
|
|
30
|
+
* @returns {void}
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* const { run } = require('sigmap-cli');
|
|
34
|
+
* run(['--report'], '/path/to/project');
|
|
35
|
+
*/
|
|
36
|
+
function run(argv, cwd) {
|
|
37
|
+
const origArgv = process.argv;
|
|
38
|
+
const origCwd = process.cwd();
|
|
39
|
+
|
|
40
|
+
if (cwd) {
|
|
41
|
+
try { process.chdir(cwd); } catch (_) {}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (argv) {
|
|
45
|
+
process.argv = [process.argv[0], CLI_ENTRY, ...argv];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
require(CLI_ENTRY);
|
|
50
|
+
} finally {
|
|
51
|
+
process.argv = origArgv;
|
|
52
|
+
if (cwd) {
|
|
53
|
+
try { process.chdir(origCwd); } catch (_) {}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
module.exports = {
|
|
59
|
+
/** Absolute path to the gen-context.js entry point */
|
|
60
|
+
CLI_ENTRY,
|
|
61
|
+
/** Run the SigMap CLI programmatically */
|
|
62
|
+
run,
|
|
63
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sigmap-cli",
|
|
3
|
+
"version": "2.4.0",
|
|
4
|
+
"description": "SigMap CLI wrapper — thin adapter for programmatic CLI invocation",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"sigmap",
|
|
8
|
+
"cli",
|
|
9
|
+
"ai-context",
|
|
10
|
+
"code-signatures"
|
|
11
|
+
],
|
|
12
|
+
"author": {
|
|
13
|
+
"name": "Manoj Mallick",
|
|
14
|
+
"url": "https://github.com/manojmallick"
|
|
15
|
+
},
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/manojmallick/sigmap.git",
|
|
19
|
+
"directory": "packages/cli"
|
|
20
|
+
},
|
|
21
|
+
"homepage": "https://manojmallick.github.io/sigmap/",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=18.0.0"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# sigmap-core
|
|
2
|
+
|
|
3
|
+
Programmatic API for [SigMap](https://manojmallick.github.io/sigmap/) — zero-dependency code signature extraction, ranked retrieval, secret scanning, and project health scoring.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install sigmap # installs the full package (CLI + core)
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
`require('sigmap')` resolves to this library via the root `exports` field.
|
|
12
|
+
|
|
13
|
+
## Quick start
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
const { extract, rank, buildSigIndex, scan, score } = require('sigmap');
|
|
17
|
+
|
|
18
|
+
// 1. Extract signatures from any source file
|
|
19
|
+
const sigs = extract('function hello() { return "world"; }', 'javascript');
|
|
20
|
+
// → ['function hello()']
|
|
21
|
+
|
|
22
|
+
// 2. Scan for secrets before storing signatures
|
|
23
|
+
const { safe, redacted } = scan(sigs, 'src/utils.js');
|
|
24
|
+
|
|
25
|
+
// 3. Build an index from the generated context file
|
|
26
|
+
const index = buildSigIndex('/path/to/your/project');
|
|
27
|
+
|
|
28
|
+
// 4. Rank files against a query
|
|
29
|
+
const results = rank('add a new language extractor', index, { topK: 5 });
|
|
30
|
+
// → [{ file: 'src/extractors/python.js', score: 3.5, sigs: [...], tokens: 42 }, ...]
|
|
31
|
+
|
|
32
|
+
// 5. Check project health
|
|
33
|
+
const health = score('/path/to/your/project');
|
|
34
|
+
// → { score: 92, grade: 'A', strategy: 'full', ... }
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## API reference
|
|
38
|
+
|
|
39
|
+
### `extract(src, language)` → `string[]`
|
|
40
|
+
|
|
41
|
+
Extract code signatures from source text.
|
|
42
|
+
|
|
43
|
+
| Param | Type | Description |
|
|
44
|
+
|---|---|---|
|
|
45
|
+
| `src` | `string` | Raw file content |
|
|
46
|
+
| `language` | `string` | Language name (`'typescript'`, `'python'`, etc.) **or** a file path/name with a recognised extension |
|
|
47
|
+
|
|
48
|
+
Returns an array of signature strings. Never throws — returns `[]` on any error.
|
|
49
|
+
|
|
50
|
+
**Supported languages:** typescript, javascript, python, java, kotlin, go, rust, csharp, cpp, ruby, php, swift, dart, scala, vue, svelte, html, css, yaml, shell, dockerfile (21 total)
|
|
51
|
+
|
|
52
|
+
```js
|
|
53
|
+
// By language name
|
|
54
|
+
extract(src, 'python');
|
|
55
|
+
|
|
56
|
+
// By file path (extension is used to detect language)
|
|
57
|
+
extract(src, 'src/server.ts');
|
|
58
|
+
extract(src, 'Dockerfile');
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
### `rank(query, sigIndex, opts?)` → `Result[]`
|
|
64
|
+
|
|
65
|
+
Rank all files in a signature index against a natural-language query.
|
|
66
|
+
|
|
67
|
+
| Param | Type | Description |
|
|
68
|
+
|---|---|---|
|
|
69
|
+
| `query` | `string` | Natural language or keyword query |
|
|
70
|
+
| `sigIndex` | `Map<string, string[]>` | File → signatures map (from `buildSigIndex`) |
|
|
71
|
+
| `opts.topK` | `number` | Max files to return (default: `10`) |
|
|
72
|
+
| `opts.weights` | `object` | Override default scoring weights |
|
|
73
|
+
| `opts.recencySet` | `Set<string>` | Files to boost with `recencyBoost` multiplier |
|
|
74
|
+
|
|
75
|
+
Each result: `{ file: string, score: number, sigs: string[], tokens: number }`
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
### `buildSigIndex(cwd)` → `Map<string, string[]>`
|
|
80
|
+
|
|
81
|
+
Build a file→signatures map from the generated `.github/copilot-instructions.md`.
|
|
82
|
+
Requires `node gen-context.js` to have been run first.
|
|
83
|
+
|
|
84
|
+
```js
|
|
85
|
+
const index = buildSigIndex('/path/to/project');
|
|
86
|
+
// → Map { 'src/extractors/python.js' => ['class Extractor', ' def extract(src)'], ... }
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
### `scan(sigs, filePath)` → `{ safe: string[], redacted: boolean }`
|
|
92
|
+
|
|
93
|
+
Scan signature strings for secrets (AWS keys, GitHub tokens, DB connection strings, etc.) and redact any matches.
|
|
94
|
+
|
|
95
|
+
```js
|
|
96
|
+
const { safe, redacted } = scan(
|
|
97
|
+
['const SECRET = "ghp_abc123xyz..."'],
|
|
98
|
+
'src/config.ts'
|
|
99
|
+
);
|
|
100
|
+
// safe → ['[REDACTED — GitHub Token detected in src/config.ts]']
|
|
101
|
+
// redacted → true
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**Detected patterns:** AWS Access Key, AWS Secret Key, GCP API Key, GitHub Token, JWT Token, DB Connection String, SSH Private Key, Stripe Key, Twilio Key, Generic Secret
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
### `score(cwd)` → `HealthResult`
|
|
109
|
+
|
|
110
|
+
Compute a composite health score for the SigMap installation in a project.
|
|
111
|
+
|
|
112
|
+
```js
|
|
113
|
+
const health = score('/path/to/project');
|
|
114
|
+
// {
|
|
115
|
+
// score: 92,
|
|
116
|
+
// grade: 'A', // A ≥90 | B ≥75 | C ≥60 | D <60
|
|
117
|
+
// strategy: 'full',
|
|
118
|
+
// tokenReductionPct: 97.2,
|
|
119
|
+
// daysSinceRegen: 0.1,
|
|
120
|
+
// totalRuns: 48,
|
|
121
|
+
// overBudgetRuns: 0,
|
|
122
|
+
// }
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Migration from v2.3 and earlier
|
|
126
|
+
|
|
127
|
+
`require('sigmap')` was not available before v2.4. The programmatic API is new — no migration needed for CLI usage.
|
|
128
|
+
|
|
129
|
+
All existing CLI flags (`--generate`, `--watch`, `--mcp`, `--query`, `--analyze`, `--benchmark`, `--health`, …) are unchanged.
|
|
130
|
+
|
|
131
|
+
## Zero dependencies
|
|
132
|
+
|
|
133
|
+
This package has zero runtime npm dependencies. It uses only Node.js built-ins: `fs`, `path`.
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* sigmap-core — public programmatic API
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* const { extract, rank, scan, score } = require('sigmap');
|
|
8
|
+
*
|
|
9
|
+
* All functions are zero-dependency and never throw.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const path = require('path');
|
|
13
|
+
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Language extractor registry
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
const EXT_MAP = {
|
|
18
|
+
'.ts': 'typescript', '.tsx': 'typescript',
|
|
19
|
+
'.js': 'javascript', '.jsx': 'javascript', '.mjs': 'javascript', '.cjs': 'javascript',
|
|
20
|
+
'.py': 'python', '.pyw': 'python',
|
|
21
|
+
'.java': 'java',
|
|
22
|
+
'.kt': 'kotlin', '.kts': 'kotlin',
|
|
23
|
+
'.go': 'go',
|
|
24
|
+
'.rs': 'rust',
|
|
25
|
+
'.cs': 'csharp',
|
|
26
|
+
'.cpp': 'cpp', '.c': 'cpp', '.h': 'cpp', '.hpp': 'cpp', '.cc': 'cpp',
|
|
27
|
+
'.rb': 'ruby', '.rake': 'ruby',
|
|
28
|
+
'.php': 'php',
|
|
29
|
+
'.swift': 'swift',
|
|
30
|
+
'.dart': 'dart',
|
|
31
|
+
'.scala': 'scala', '.sc': 'scala',
|
|
32
|
+
'.vue': 'vue',
|
|
33
|
+
'.svelte': 'svelte',
|
|
34
|
+
'.html': 'html', '.htm': 'html',
|
|
35
|
+
'.css': 'css', '.scss': 'css', '.sass': 'css', '.less': 'css',
|
|
36
|
+
'.yml': 'yaml', '.yaml': 'yaml',
|
|
37
|
+
'.sh': 'shell', '.bash': 'shell', '.zsh': 'shell', '.fish': 'shell',
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const SRC_ROOT = path.resolve(__dirname, '..', '..', 'src');
|
|
41
|
+
|
|
42
|
+
function _resolveExtractor(language) {
|
|
43
|
+
const extPath = path.join(SRC_ROOT, 'extractors', language + '.js');
|
|
44
|
+
try {
|
|
45
|
+
return require(extPath);
|
|
46
|
+
} catch (_) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
// extract(src, language) → string[]
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
/**
|
|
55
|
+
* Extract code signatures from source text for the given language.
|
|
56
|
+
*
|
|
57
|
+
* @param {string} src - Raw source file content
|
|
58
|
+
* @param {string} language - Language name (e.g. 'typescript', 'python')
|
|
59
|
+
* OR a file path/name with a recognised extension
|
|
60
|
+
* @returns {string[]} Array of signature strings (never throws)
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* const sigs = extract('function hello() {}', 'javascript');
|
|
64
|
+
* // → ['function hello()']
|
|
65
|
+
*
|
|
66
|
+
* const sigs2 = extract(src, 'src/server.ts');
|
|
67
|
+
* // → detected as typescript via extension
|
|
68
|
+
*/
|
|
69
|
+
function extract(src, language) {
|
|
70
|
+
if (!src || typeof src !== 'string') return [];
|
|
71
|
+
if (!language || typeof language !== 'string') return [];
|
|
72
|
+
|
|
73
|
+
// If language looks like a file path, derive language from extension
|
|
74
|
+
let lang = language;
|
|
75
|
+
if (language.includes('.') || language.includes('/') || language.includes('\\')) {
|
|
76
|
+
const ext = path.extname(language).toLowerCase();
|
|
77
|
+
const base = path.basename(language);
|
|
78
|
+
if (base === 'Dockerfile' || base.startsWith('Dockerfile.')) {
|
|
79
|
+
lang = 'dockerfile';
|
|
80
|
+
} else {
|
|
81
|
+
lang = EXT_MAP[ext] || null;
|
|
82
|
+
}
|
|
83
|
+
if (!lang) return [];
|
|
84
|
+
} else {
|
|
85
|
+
// Normalise e.g. 'JavaScript' → 'javascript'
|
|
86
|
+
lang = language.toLowerCase();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const mod = _resolveExtractor(lang);
|
|
90
|
+
if (!mod || typeof mod.extract !== 'function') return [];
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
const result = mod.extract(src);
|
|
94
|
+
return Array.isArray(result) ? result : [];
|
|
95
|
+
} catch (_) {
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
// rank(query, sigIndex, opts?) → { file, score, sigs, tokens }[]
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
103
|
+
/**
|
|
104
|
+
* Rank files in a signature index against a natural-language query.
|
|
105
|
+
*
|
|
106
|
+
* @param {string} query - Natural language or keyword query
|
|
107
|
+
* @param {Map<string, string[]>} sigIndex - File → signatures map
|
|
108
|
+
* @param {object} [opts]
|
|
109
|
+
* @param {number} [opts.topK=10] - Maximum results to return
|
|
110
|
+
* @param {number} [opts.recencyBoost] - Score multiplier for recent files
|
|
111
|
+
* @param {Set<string>} [opts.recencySet] - Set of file paths considered recent
|
|
112
|
+
* @param {object} [opts.weights] - Override default scoring weights
|
|
113
|
+
* @returns {{ file: string, score: number, sigs: string[], tokens: number }[]}
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* const { rank, buildSigIndex } = require('sigmap');
|
|
117
|
+
* const index = buildSigIndex('/path/to/project');
|
|
118
|
+
* const results = rank('add a new language extractor', index, { topK: 5 });
|
|
119
|
+
*/
|
|
120
|
+
function rank(query, sigIndex, opts) {
|
|
121
|
+
try {
|
|
122
|
+
const { rank: _rank } = require(path.join(SRC_ROOT, 'retrieval', 'ranker.js'));
|
|
123
|
+
return _rank(query, sigIndex, opts);
|
|
124
|
+
} catch (_) {
|
|
125
|
+
return [];
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ---------------------------------------------------------------------------
|
|
130
|
+
// buildSigIndex(cwd) → Map<string, string[]>
|
|
131
|
+
// ---------------------------------------------------------------------------
|
|
132
|
+
/**
|
|
133
|
+
* Build a file→signatures index from the generated context file.
|
|
134
|
+
* Requires gen-context.js to have been run first.
|
|
135
|
+
*
|
|
136
|
+
* @param {string} cwd - Project root directory
|
|
137
|
+
* @returns {Map<string, string[]>}
|
|
138
|
+
*/
|
|
139
|
+
function buildSigIndex(cwd) {
|
|
140
|
+
try {
|
|
141
|
+
const { buildSigIndex: _build } = require(path.join(SRC_ROOT, 'retrieval', 'ranker.js'));
|
|
142
|
+
return _build(cwd);
|
|
143
|
+
} catch (_) {
|
|
144
|
+
return new Map();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ---------------------------------------------------------------------------
|
|
149
|
+
// scan(sigs, filePath) → { safe: string[], redacted: boolean }
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
/**
|
|
152
|
+
* Scan an array of signature strings for secrets and redact any matches.
|
|
153
|
+
*
|
|
154
|
+
* @param {string[]} sigs - Signature strings to scan
|
|
155
|
+
* @param {string} filePath - Source file path (used in redaction message)
|
|
156
|
+
* @returns {{ safe: string[], redacted: boolean }}
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* const { safe, redacted } = scan(['const KEY = "AKIAEXAMPLE123..."'], 'config.js');
|
|
160
|
+
* // redacted === true — key was replaced with [REDACTED — AWS Access Key ...]
|
|
161
|
+
*/
|
|
162
|
+
function scan(sigs, filePath) {
|
|
163
|
+
try {
|
|
164
|
+
const { scan: _scan } = require(path.join(SRC_ROOT, 'security', 'scanner.js'));
|
|
165
|
+
return _scan(sigs, filePath);
|
|
166
|
+
} catch (_) {
|
|
167
|
+
return { safe: Array.isArray(sigs) ? sigs : [], redacted: false };
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ---------------------------------------------------------------------------
|
|
172
|
+
// score(cwd) → { score, grade, ... }
|
|
173
|
+
// ---------------------------------------------------------------------------
|
|
174
|
+
/**
|
|
175
|
+
* Compute a composite health score for the project at cwd.
|
|
176
|
+
*
|
|
177
|
+
* @param {string} cwd - Project root directory
|
|
178
|
+
* @returns {{
|
|
179
|
+
* score: number,
|
|
180
|
+
* grade: 'A'|'B'|'C'|'D',
|
|
181
|
+
* strategy: string,
|
|
182
|
+
* tokenReductionPct: number|null,
|
|
183
|
+
* daysSinceRegen: number|null,
|
|
184
|
+
* totalRuns: number,
|
|
185
|
+
* overBudgetRuns: number,
|
|
186
|
+
* }}
|
|
187
|
+
*
|
|
188
|
+
* @example
|
|
189
|
+
* const health = score('/path/to/project');
|
|
190
|
+
* console.log(health.grade); // 'A'
|
|
191
|
+
*/
|
|
192
|
+
function score(cwd) {
|
|
193
|
+
try {
|
|
194
|
+
const { score: _score } = require(path.join(SRC_ROOT, 'health', 'scorer.js'));
|
|
195
|
+
return _score(cwd);
|
|
196
|
+
} catch (_) {
|
|
197
|
+
return { score: 0, grade: 'D', strategy: 'full', tokenReductionPct: null, daysSinceRegen: null, totalRuns: 0, overBudgetRuns: 0 };
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// ---------------------------------------------------------------------------
|
|
202
|
+
// Exports
|
|
203
|
+
// ---------------------------------------------------------------------------
|
|
204
|
+
module.exports = {
|
|
205
|
+
/** Extract signatures from source text */
|
|
206
|
+
extract,
|
|
207
|
+
/** Rank project files against a query */
|
|
208
|
+
rank,
|
|
209
|
+
/** Build a signature index from the generated context file */
|
|
210
|
+
buildSigIndex,
|
|
211
|
+
/** Scan signatures for secrets (redacts matches) */
|
|
212
|
+
scan,
|
|
213
|
+
/** Compute project health score */
|
|
214
|
+
score,
|
|
215
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sigmap-core",
|
|
3
|
+
"version": "2.4.0",
|
|
4
|
+
"description": "SigMap core library — zero-dependency code signature extraction, retrieval, and security scanning",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"sigmap",
|
|
8
|
+
"ai-context",
|
|
9
|
+
"code-signatures",
|
|
10
|
+
"extraction",
|
|
11
|
+
"retrieval",
|
|
12
|
+
"zero-dependency"
|
|
13
|
+
],
|
|
14
|
+
"author": {
|
|
15
|
+
"name": "Manoj Mallick",
|
|
16
|
+
"url": "https://github.com/manojmallick"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/manojmallick/sigmap.git",
|
|
21
|
+
"directory": "packages/core"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://manojmallick.github.io/sigmap/",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=18.0.0"
|
|
27
|
+
}
|
|
28
|
+
}
|
package/src/mcp/server.js
CHANGED