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 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
  [![npm version](https://img.shields.io/npm/v/sigmap?color=7c6af7&label=latest&logo=npm)](https://www.npmjs.com/package/sigmap)
14
- [![Tests](https://img.shields.io/badge/tests-262%20passing-22c55e)](https://github.com/manojmallick/sigmap/tree/main/test)
14
+ [![Tests](https://img.shields.io/badge/tests-325%20passing-22c55e)](https://github.com/manojmallick/sigmap/tree/main/test)
15
15
  [![Zero deps](https://img.shields.io/badge/dependencies-zero-22c55e)](package.json)
16
16
  [![Last commit](https://img.shields.io/github/last-commit/manojmallick/sigmap?color=7c6af7)](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) | 7 on-demand tools |
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.0
89
+ ## 🆕 What's new in 2.3
90
90
 
91
91
  | Feature | Description |
92
92
  |---|---|
93
- | **Enriched signatures** | Return types, type hints, and schema field collapse (Python `@dataclass` / `BaseModel`) |
94
- | **Dependency map** | Compact import dependency section at the top of output (~50–100 extra tokens) |
95
- | **TODO/FIXME section** | Auto-harvested TODO/FIXME/HACK/XXX comments (max 20 entries) |
96
- | **Recent changes section** | Git-based recent changes summary in output |
97
- | **Test coverage markers** | Per-function `✓`/`✗` hints by scanning test directories |
98
- | **Structural diff mode** | `--diff <base-ref>` writes a signature-level diff section |
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
- Several v2 enhancements (deps map, TODOs, recent changes) are enabled by default. All v2 sections can be tuned or disabled via `gen-context.config.json`.
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 7 tools through v1.4.
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 7 tools
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
- │ ├── mcp/ MCP stdio server 7 tools
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/ ← 17 integration test files (241 tests)
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.3.0';
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.0",
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
@@ -18,7 +18,7 @@ const { readContext, searchSignatures, getMap, createCheckpoint, getRouting, exp
18
18
 
19
19
  const SERVER_INFO = {
20
20
  name: 'sigmap',
21
- version: '2.3.0',
21
+ version: '2.4.0',
22
22
  description: 'SigMap MCP server — code signatures on demand',
23
23
  };
24
24