sigmap 7.3.0 → 7.5.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 +21 -0
- package/gen-context.js +784 -51
- package/llms-full.txt +29 -5
- 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/cache/freshen.js +123 -0
- package/src/extractors/dispatch.js +112 -0
- package/src/mcp/handlers.js +79 -1
- package/src/mcp/server.js +5 -2
- package/src/mcp/tools.js +45 -2
package/llms-full.txt
CHANGED
|
@@ -9,7 +9,7 @@ the files relevant to the task — cutting tokens ~97% while keeping answers
|
|
|
9
9
|
grounded. Deterministic, offline, no embeddings or vector database. Works with
|
|
10
10
|
Claude, Cursor, GitHub Copilot, Aider, Windsurf, local LLMs, and MCP.
|
|
11
11
|
|
|
12
|
-
# Version: 7.
|
|
12
|
+
# Version: 7.5.0 | Benchmark: sigmap-v7.0-main (2026-06-14)
|
|
13
13
|
# Source: auto-generated from package.json, version.json, src/mcp/tools.js, src/config/defaults.js
|
|
14
14
|
# Regenerate: npm run generate:llms | Validate: npm run validate:llms
|
|
15
15
|
|
|
@@ -24,7 +24,7 @@ Claude, Cursor, GitHub Copilot, Aider, Windsurf, local LLMs, and MCP.
|
|
|
24
24
|
| Task success proxy | 10% | 52.2% |
|
|
25
25
|
| Prompts per task | 2.84 | 1.72 (39.4% fewer) |
|
|
26
26
|
| Supported languages | — | 31 |
|
|
27
|
-
| MCP tools | — |
|
|
27
|
+
| MCP tools | — | 15 |
|
|
28
28
|
| npm runtime dependencies | — | 0 |
|
|
29
29
|
|
|
30
30
|
---
|
|
@@ -114,7 +114,7 @@ sigmap --version Show version
|
|
|
114
114
|
|
|
115
115
|
---
|
|
116
116
|
|
|
117
|
-
## MCP server —
|
|
117
|
+
## MCP server — 15 tools
|
|
118
118
|
|
|
119
119
|
Start with `sigmap --mcp` (stdio JSON-RPC). Configure once:
|
|
120
120
|
|
|
@@ -218,6 +218,30 @@ Return the EXACT current signature(s) of named symbols (functions, classes, meth
|
|
|
218
218
|
Input: { symbols: array }
|
|
219
219
|
```
|
|
220
220
|
|
|
221
|
+
### sigmap_notify_file_created
|
|
222
|
+
|
|
223
|
+
Tell SigMap a file was created or modified so its signatures are indexed live for the rest of the session. Call this after writing a file — the new symbols become resolvable by search_signatures / get_callee_signatures.
|
|
224
|
+
|
|
225
|
+
```
|
|
226
|
+
Input: { path: string, content?: string }
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### sigmap_notify_symbol_added
|
|
230
|
+
|
|
231
|
+
Fast path: register a single new symbol signature directly in the live index without re-reading the whole file.
|
|
232
|
+
|
|
233
|
+
```
|
|
234
|
+
Input: { signature: string, file: string, line?: number }
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### sigmap_notify_file_deleted
|
|
238
|
+
|
|
239
|
+
Tell SigMap a file was deleted so its symbols are dropped from the live index.
|
|
240
|
+
|
|
241
|
+
```
|
|
242
|
+
Input: { path: string }
|
|
243
|
+
```
|
|
244
|
+
|
|
221
245
|
---
|
|
222
246
|
|
|
223
247
|
## Configuration (gen-context.config.json)
|
|
@@ -261,9 +285,9 @@ impact = {"depth":3,"includeSigs":true}
|
|
|
261
285
|
|
|
262
286
|
---
|
|
263
287
|
|
|
264
|
-
## Supported languages (
|
|
288
|
+
## Supported languages (35 extractors)
|
|
265
289
|
|
|
266
|
-
cpp, csharp, css, dart, dockerfile, gdscript, generic, go, graphql, html, java, javascript, kotlin, markdown, php, properties, protobuf, python, r, ruby, rust, scala, shell, sql, svelte, swift, terraform, toml, typescript, typescript_react, vue, vue_sfc, xml, yaml
|
|
290
|
+
cpp, csharp, css, dart, dispatch, dockerfile, gdscript, generic, go, graphql, html, java, javascript, kotlin, markdown, php, properties, protobuf, python, r, ruby, rust, scala, shell, sql, svelte, swift, terraform, toml, typescript, typescript_react, vue, vue_sfc, xml, yaml
|
|
267
291
|
|
|
268
292
|
---
|
|
269
293
|
|
package/llms.txt
CHANGED
|
@@ -9,7 +9,7 @@ the files relevant to the task — cutting tokens ~97% while keeping answers
|
|
|
9
9
|
grounded. Deterministic, offline, no embeddings or vector database. Works with
|
|
10
10
|
Claude, Cursor, GitHub Copilot, Aider, Windsurf, local LLMs, and MCP.
|
|
11
11
|
|
|
12
|
-
# Version: 7.
|
|
12
|
+
# Version: 7.5.0 | Benchmark: sigmap-v7.0-main (2026-06-14)
|
|
13
13
|
# Source: auto-generated from package.json, version.json, src/mcp/tools.js, src/config/defaults.js
|
|
14
14
|
# Regenerate: npm run generate:llms | Validate: npm run validate:llms
|
|
15
15
|
|
|
@@ -27,7 +27,7 @@ Claude, Cursor, GitHub Copilot, Aider, Windsurf, local LLMs, and MCP.
|
|
|
27
27
|
- Token reduction: 97.0% average across benchmark repos
|
|
28
28
|
- Task success: 52.2% vs 10% without SigMap
|
|
29
29
|
- Prompts per task: 1.72 vs 2.84 baseline (39.4% fewer)
|
|
30
|
-
- Languages: 31 supported · MCP tools:
|
|
30
|
+
- Languages: 31 supported · MCP tools: 15
|
|
31
31
|
- Dependencies: zero npm runtime dependencies · fully offline
|
|
32
32
|
|
|
33
33
|
## Quick start
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sigmap",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.5.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": {
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Read-time self-heal (IMPL.md Layer 1, "safety net" tier).
|
|
5
|
+
*
|
|
6
|
+
* Keeps the sig-cache in line with the current source tree so the index reflects
|
|
7
|
+
* on-disk reality even when no write hook was called. Re-extracts files modified
|
|
8
|
+
* since the context file was generated (bounded to actual session edits, not the
|
|
9
|
+
* whole tree), drops cache entries for deleted files, and persists. buildSigIndex
|
|
10
|
+
* already merges the cache, so the next read is fresh.
|
|
11
|
+
*
|
|
12
|
+
* Throttled per cwd. Skips entirely when there is no generated index to heal
|
|
13
|
+
* (a cold repo should run `generate` or use the notify hooks).
|
|
14
|
+
*
|
|
15
|
+
* Zero-dependency, bundle-safe (fs + dispatch + sig-cache).
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
const { loadCache, saveCache, getChangedFiles } = require('./sig-cache');
|
|
21
|
+
const { extractFile, langFor } = require('../extractors/dispatch');
|
|
22
|
+
|
|
23
|
+
const DEFAULT_SRC_DIRS = ['src', 'app', 'lib', 'packages', 'services', 'api'];
|
|
24
|
+
const DEFAULT_EXCLUDE = [
|
|
25
|
+
'node_modules', '.git', 'dist', 'build', 'out', '__pycache__',
|
|
26
|
+
'.next', 'coverage', 'target', 'vendor', '.context',
|
|
27
|
+
];
|
|
28
|
+
const CONTEXT_PATHS = [
|
|
29
|
+
['.github', 'copilot-instructions.md'],
|
|
30
|
+
['CLAUDE.md'], ['AGENTS.md'], ['.github', 'context-cold.md'],
|
|
31
|
+
];
|
|
32
|
+
const THROTTLE_MS = 1500;
|
|
33
|
+
const _lastRun = new Map();
|
|
34
|
+
|
|
35
|
+
function _readConfig(cwd) {
|
|
36
|
+
try {
|
|
37
|
+
const cfg = JSON.parse(fs.readFileSync(path.join(cwd, 'gen-context.config.json'), 'utf8'));
|
|
38
|
+
return cfg && typeof cfg === 'object' ? cfg : {};
|
|
39
|
+
} catch (_) { return {}; }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function _pkgVersion(cwd) {
|
|
43
|
+
try { return JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8')).version || '0.0.0'; }
|
|
44
|
+
catch (_) { return '0.0.0'; }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Newest mtime among existing generated context files, or 0 if none. */
|
|
48
|
+
function _contextMtime(cwd) {
|
|
49
|
+
let newest = 0;
|
|
50
|
+
for (const parts of CONTEXT_PATHS) {
|
|
51
|
+
try { newest = Math.max(newest, fs.statSync(path.join(cwd, ...parts)).mtimeMs); } catch (_) {}
|
|
52
|
+
}
|
|
53
|
+
return newest;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function _walk(dir, exclude, out, depth, maxDepth) {
|
|
57
|
+
if (depth > maxDepth) return;
|
|
58
|
+
let entries;
|
|
59
|
+
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch (_) { return; }
|
|
60
|
+
for (const e of entries) {
|
|
61
|
+
if (exclude.has(e.name)) continue;
|
|
62
|
+
const full = path.join(dir, e.name);
|
|
63
|
+
if (e.isDirectory()) _walk(full, exclude, out, depth + 1, maxDepth);
|
|
64
|
+
else if (e.isFile() && langFor(e.name)) out.push(full);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Re-extract source files changed since the last generate; drop deleted files.
|
|
70
|
+
* @param {string} cwd
|
|
71
|
+
* @param {{force?:boolean, now?:number}} [opts]
|
|
72
|
+
* @returns {number} cache entries touched
|
|
73
|
+
*/
|
|
74
|
+
function freshen(cwd, opts = {}) {
|
|
75
|
+
const now = opts.now != null ? opts.now : Date.now();
|
|
76
|
+
if (!opts.force) {
|
|
77
|
+
if (now - (_lastRun.get(cwd) || 0) < THROTTLE_MS) return 0;
|
|
78
|
+
}
|
|
79
|
+
_lastRun.set(cwd, now);
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
const version = _pkgVersion(cwd);
|
|
83
|
+
const cache = loadCache(cwd, version);
|
|
84
|
+
const ctxMtime = _contextMtime(cwd);
|
|
85
|
+
// Nothing to heal: no generated context AND no live cache overlay.
|
|
86
|
+
if (ctxMtime === 0 && cache.size === 0) return 0;
|
|
87
|
+
|
|
88
|
+
const cfg = _readConfig(cwd);
|
|
89
|
+
const srcDirs = Array.isArray(cfg.srcDirs) && cfg.srcDirs.length ? cfg.srcDirs : DEFAULT_SRC_DIRS;
|
|
90
|
+
const exclude = new Set([...DEFAULT_EXCLUDE, ...(Array.isArray(cfg.exclude) ? cfg.exclude : [])]);
|
|
91
|
+
const maxDepth = Number.isFinite(cfg.maxDepth) ? cfg.maxDepth : 8;
|
|
92
|
+
|
|
93
|
+
const files = [];
|
|
94
|
+
for (const d of srcDirs) {
|
|
95
|
+
const abs = path.isAbsolute(d) ? d : path.join(cwd, d);
|
|
96
|
+
if (fs.existsSync(abs)) _walk(abs, exclude, files, 0, maxDepth);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Candidates = files modified since the context was generated, or not yet cached.
|
|
100
|
+
const candidates = files.filter((f) => {
|
|
101
|
+
try { return fs.statSync(f).mtimeMs > ctxMtime || !cache.has(f); } catch (_) { return false; }
|
|
102
|
+
});
|
|
103
|
+
const { changed } = getChangedFiles(candidates, cache);
|
|
104
|
+
|
|
105
|
+
let touched = 0;
|
|
106
|
+
for (const f of changed) {
|
|
107
|
+
try {
|
|
108
|
+
const sigs = extractFile(f, fs.readFileSync(f, 'utf8'));
|
|
109
|
+
cache.set(f, { mtime: fs.statSync(f).mtimeMs, sigs });
|
|
110
|
+
touched++;
|
|
111
|
+
} catch (_) {}
|
|
112
|
+
}
|
|
113
|
+
// Note: deletions are NOT swept here — a cache entry may be a `notify`
|
|
114
|
+
// overlay for a file not yet on disk. Explicit removal is `notify_file_deleted`.
|
|
115
|
+
|
|
116
|
+
if (touched > 0) saveCache(cwd, version, cache);
|
|
117
|
+
return touched;
|
|
118
|
+
} catch (_) {
|
|
119
|
+
return 0;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
module.exports = { freshen };
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Bundle-safe extractor dispatch.
|
|
5
|
+
*
|
|
6
|
+
* `packages/core`'s extract() resolves extractors via dynamic `require(path.join(…))`,
|
|
7
|
+
* which cannot run inside the standalone bundle (no filesystem `src/`). This module
|
|
8
|
+
* uses STATIC requires so the bundler rewrites them to `__require` and the extractors
|
|
9
|
+
* resolve from the bundled factories. Used by the live-index MCP write hooks.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const path = require('path');
|
|
13
|
+
|
|
14
|
+
// Static language → extractor map (every entry is a bundled factory).
|
|
15
|
+
const EXTRACTORS = {
|
|
16
|
+
typescript: require('./typescript'),
|
|
17
|
+
typescript_react: require('./typescript_react'),
|
|
18
|
+
javascript: require('./javascript'),
|
|
19
|
+
python: require('./python'),
|
|
20
|
+
java: require('./java'),
|
|
21
|
+
kotlin: require('./kotlin'),
|
|
22
|
+
go: require('./go'),
|
|
23
|
+
rust: require('./rust'),
|
|
24
|
+
csharp: require('./csharp'),
|
|
25
|
+
cpp: require('./cpp'),
|
|
26
|
+
ruby: require('./ruby'),
|
|
27
|
+
php: require('./php'),
|
|
28
|
+
swift: require('./swift'),
|
|
29
|
+
dart: require('./dart'),
|
|
30
|
+
scala: require('./scala'),
|
|
31
|
+
gdscript: require('./gdscript'),
|
|
32
|
+
r: require('./r'),
|
|
33
|
+
vue: require('./vue'),
|
|
34
|
+
vue_sfc: require('./vue_sfc'),
|
|
35
|
+
svelte: require('./svelte'),
|
|
36
|
+
html: require('./html'),
|
|
37
|
+
css: require('./css'),
|
|
38
|
+
yaml: require('./yaml'),
|
|
39
|
+
shell: require('./shell'),
|
|
40
|
+
sql: require('./sql'),
|
|
41
|
+
graphql: require('./graphql'),
|
|
42
|
+
terraform: require('./terraform'),
|
|
43
|
+
protobuf: require('./protobuf'),
|
|
44
|
+
toml: require('./toml'),
|
|
45
|
+
properties: require('./properties'),
|
|
46
|
+
xml: require('./xml'),
|
|
47
|
+
markdown: require('./markdown'),
|
|
48
|
+
dockerfile: require('./dockerfile'),
|
|
49
|
+
generic: require('./generic'),
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const EXT_MAP = {
|
|
53
|
+
'.ts': 'typescript', '.tsx': 'typescript_react',
|
|
54
|
+
'.js': 'javascript', '.jsx': 'javascript', '.mjs': 'javascript', '.cjs': 'javascript',
|
|
55
|
+
'.py': 'python', '.pyw': 'python',
|
|
56
|
+
'.java': 'java',
|
|
57
|
+
'.kt': 'kotlin', '.kts': 'kotlin',
|
|
58
|
+
'.go': 'go',
|
|
59
|
+
'.rs': 'rust',
|
|
60
|
+
'.cs': 'csharp',
|
|
61
|
+
'.cpp': 'cpp', '.c': 'cpp', '.h': 'cpp', '.hpp': 'cpp', '.cc': 'cpp',
|
|
62
|
+
'.rb': 'ruby', '.rake': 'ruby',
|
|
63
|
+
'.php': 'php',
|
|
64
|
+
'.swift': 'swift',
|
|
65
|
+
'.dart': 'dart',
|
|
66
|
+
'.scala': 'scala', '.sc': 'scala',
|
|
67
|
+
'.gd': 'gdscript',
|
|
68
|
+
'.r': 'r', '.R': 'r',
|
|
69
|
+
'.vue': 'vue_sfc',
|
|
70
|
+
'.svelte': 'svelte',
|
|
71
|
+
'.html': 'html', '.htm': 'html',
|
|
72
|
+
'.css': 'css', '.scss': 'css', '.sass': 'css', '.less': 'css',
|
|
73
|
+
'.yml': 'yaml', '.yaml': 'yaml',
|
|
74
|
+
'.sh': 'shell', '.bash': 'shell', '.zsh': 'shell', '.fish': 'shell',
|
|
75
|
+
'.sql': 'sql',
|
|
76
|
+
'.graphql': 'graphql', '.gql': 'graphql',
|
|
77
|
+
'.tf': 'terraform', '.tfvars': 'terraform',
|
|
78
|
+
'.proto': 'protobuf',
|
|
79
|
+
'.toml': 'toml',
|
|
80
|
+
'.properties': 'properties',
|
|
81
|
+
'.xml': 'xml',
|
|
82
|
+
'.md': 'markdown',
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/** Resolve a language key from a file path/name. */
|
|
86
|
+
function langFor(filePathOrName) {
|
|
87
|
+
const base = path.basename(String(filePathOrName || ''));
|
|
88
|
+
if (base === 'Dockerfile' || base.startsWith('Dockerfile.')) return 'dockerfile';
|
|
89
|
+
const ext = path.extname(base).toLowerCase();
|
|
90
|
+
return EXT_MAP[ext] || null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Extract signatures from a file's content using the right extractor.
|
|
95
|
+
* @param {string} filePathOrName - path or name (extension drives the extractor)
|
|
96
|
+
* @param {string} src - file content
|
|
97
|
+
* @returns {string[]}
|
|
98
|
+
*/
|
|
99
|
+
function extractFile(filePathOrName, src) {
|
|
100
|
+
if (!src || typeof src !== 'string') return [];
|
|
101
|
+
const lang = langFor(filePathOrName);
|
|
102
|
+
const mod = lang ? EXTRACTORS[lang] : null;
|
|
103
|
+
if (!mod || typeof mod.extract !== 'function') return [];
|
|
104
|
+
try {
|
|
105
|
+
const out = mod.extract(src);
|
|
106
|
+
return Array.isArray(out) ? out : [];
|
|
107
|
+
} catch (_) {
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
module.exports = { extractFile, langFor };
|
package/src/mcp/handlers.js
CHANGED
|
@@ -72,6 +72,7 @@ function searchSignatures(args, cwd) {
|
|
|
72
72
|
|
|
73
73
|
const query = args.query.toLowerCase();
|
|
74
74
|
try {
|
|
75
|
+
try { require('../cache/freshen').freshen(cwd); } catch (_) {}
|
|
75
76
|
const { buildSigIndex } = require('../retrieval/ranker');
|
|
76
77
|
const index = buildSigIndex(cwd);
|
|
77
78
|
if (index.size === 0) {
|
|
@@ -559,6 +560,7 @@ function getCalleeSignatures(args, cwd) {
|
|
|
559
560
|
}
|
|
560
561
|
|
|
561
562
|
try {
|
|
563
|
+
try { require('../cache/freshen').freshen(cwd); } catch (_) {}
|
|
562
564
|
const { buildSigIndex } = require('../retrieval/ranker');
|
|
563
565
|
const { buildSymbolCandidates, closestMatch, formatSuggestion } = require('../verify/closest-match');
|
|
564
566
|
const index = buildSigIndex(cwd);
|
|
@@ -596,4 +598,80 @@ function getCalleeSignatures(args, cwd) {
|
|
|
596
598
|
}
|
|
597
599
|
}
|
|
598
600
|
|
|
599
|
-
|
|
601
|
+
// ── Layer 1: live-index write hooks ────────────────────────────────────────
|
|
602
|
+
// Keep the sig-cache fresh while an agent creates/modifies/deletes files, so
|
|
603
|
+
// new code is discoverable in the same session. buildSigIndex already merges
|
|
604
|
+
// the cache (_buildSigIndexFromCache), so updates are live on the next read.
|
|
605
|
+
|
|
606
|
+
function _pkgVersion(cwd) {
|
|
607
|
+
try { return JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8')).version || '0.0.0'; }
|
|
608
|
+
catch (_) { return '0.0.0'; }
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
|
|
612
|
+
/** notify_file_created — extract a file's signatures and index it live. */
|
|
613
|
+
function notifyFileCreated(args, cwd) {
|
|
614
|
+
const rel = args && args.path;
|
|
615
|
+
if (!rel) return 'Missing required argument: path';
|
|
616
|
+
try {
|
|
617
|
+
const { extractFile } = require('../extractors/dispatch');
|
|
618
|
+
const { loadCache, saveCache } = require('../cache/sig-cache');
|
|
619
|
+
const abs = path.resolve(cwd, rel);
|
|
620
|
+
let content = args.content;
|
|
621
|
+
if (typeof content !== 'string') {
|
|
622
|
+
try { content = fs.readFileSync(abs, 'utf8'); } catch (_) { content = ''; }
|
|
623
|
+
}
|
|
624
|
+
const sigs = extractFile(abs, content);
|
|
625
|
+
const version = _pkgVersion(cwd);
|
|
626
|
+
const cache = loadCache(cwd, version);
|
|
627
|
+
if (sigs.length > 0) {
|
|
628
|
+
cache.set(abs, { mtime: Date.now(), sigs });
|
|
629
|
+
}
|
|
630
|
+
saveCache(cwd, version, cache);
|
|
631
|
+
return `Indexed ${rel}: ${sigs.length} signature(s) now live.`;
|
|
632
|
+
} catch (err) {
|
|
633
|
+
return `_notify_file_created failed: ${err.message}_`;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
/** notify_symbol_added — append one signature to a file's live cache entry. */
|
|
638
|
+
function notifySymbolAdded(args, cwd) {
|
|
639
|
+
if (!args || !args.signature || !args.file) {
|
|
640
|
+
return 'Missing required arguments: signature, file';
|
|
641
|
+
}
|
|
642
|
+
try {
|
|
643
|
+
const { loadCache, saveCache } = require('../cache/sig-cache');
|
|
644
|
+
const abs = path.resolve(cwd, args.file);
|
|
645
|
+
const version = _pkgVersion(cwd);
|
|
646
|
+
const cache = loadCache(cwd, version);
|
|
647
|
+
const entry = cache.get(abs) || { mtime: Date.now(), sigs: [] };
|
|
648
|
+
const line = Number.isFinite(Number(args.line)) ? ` :${args.line}` : '';
|
|
649
|
+
const sig = String(args.signature) + line;
|
|
650
|
+
if (!entry.sigs.includes(sig)) entry.sigs.push(sig);
|
|
651
|
+
entry.mtime = Date.now();
|
|
652
|
+
cache.set(abs, entry);
|
|
653
|
+
saveCache(cwd, version, cache);
|
|
654
|
+
return `Added signature to ${args.file} (${entry.sigs.length} total).`;
|
|
655
|
+
} catch (err) {
|
|
656
|
+
return `_notify_symbol_added failed: ${err.message}_`;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/** notify_file_deleted — drop a file's cache-overlay entry. */
|
|
661
|
+
function notifyFileDeleted(args, cwd) {
|
|
662
|
+
const rel = args && args.path;
|
|
663
|
+
if (!rel) return 'Missing required argument: path';
|
|
664
|
+
try {
|
|
665
|
+
const { loadCache, saveCache } = require('../cache/sig-cache');
|
|
666
|
+
const abs = path.resolve(cwd, rel);
|
|
667
|
+
const version = _pkgVersion(cwd);
|
|
668
|
+
const cache = loadCache(cwd, version);
|
|
669
|
+
const had = cache.delete(abs);
|
|
670
|
+
saveCache(cwd, version, cache);
|
|
671
|
+
return had ? `Removed ${rel} from the live index.` : `${rel} was not in the live cache.`;
|
|
672
|
+
} catch (err) {
|
|
673
|
+
return `_notify_file_deleted failed: ${err.message}_`;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
module.exports = { readContext, searchSignatures, getMap, createCheckpoint, getRouting, explainFile, listModules, queryContext, getImpact, getLines, readMemory, getCalleeSignatures, notifyFileCreated, notifySymbolAdded, notifyFileDeleted };
|
package/src/mcp/server.js
CHANGED
|
@@ -14,11 +14,11 @@
|
|
|
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 } = require('./handlers');
|
|
17
|
+
const { readContext, searchSignatures, getMap, createCheckpoint, getRouting, explainFile, listModules, queryContext, getImpact, getLines, readMemory, getCalleeSignatures, notifyFileCreated, notifySymbolAdded, notifyFileDeleted } = require('./handlers');
|
|
18
18
|
|
|
19
19
|
const SERVER_INFO = {
|
|
20
20
|
name: 'sigmap',
|
|
21
|
-
version: '7.
|
|
21
|
+
version: '7.5.0',
|
|
22
22
|
description: 'SigMap MCP server — code signatures on demand',
|
|
23
23
|
};
|
|
24
24
|
|
|
@@ -78,6 +78,9 @@ function dispatch(msg, cwd) {
|
|
|
78
78
|
else if (name === 'get_lines') text = getLines(args, cwd);
|
|
79
79
|
else if (name === 'read_memory') text = readMemory(args, cwd);
|
|
80
80
|
else if (name === 'get_callee_signatures') text = getCalleeSignatures(args, cwd);
|
|
81
|
+
else if (name === 'sigmap_notify_file_created') text = notifyFileCreated(args, cwd);
|
|
82
|
+
else if (name === 'sigmap_notify_symbol_added') text = notifySymbolAdded(args, cwd);
|
|
83
|
+
else if (name === 'sigmap_notify_file_deleted') text = notifyFileDeleted(args, cwd);
|
|
81
84
|
else {
|
|
82
85
|
respondError(id, -32601, `Unknown tool: ${name}`);
|
|
83
86
|
return;
|
package/src/mcp/tools.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* MCP tool definitions for SigMap (
|
|
4
|
+
* MCP tool definitions for SigMap (15 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
|
-
* get_callee_signatures
|
|
7
|
+
* get_callee_signatures, sigmap_notify_file_created, sigmap_notify_symbol_added,
|
|
8
|
+
* sigmap_notify_file_deleted.
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
11
|
const TOOLS = [
|
|
@@ -235,6 +236,48 @@ const TOOLS = [
|
|
|
235
236
|
required: ['symbols'],
|
|
236
237
|
},
|
|
237
238
|
},
|
|
239
|
+
{
|
|
240
|
+
name: 'sigmap_notify_file_created',
|
|
241
|
+
description:
|
|
242
|
+
'Tell SigMap a file was created or modified so its signatures are indexed ' +
|
|
243
|
+
'live for the rest of the session. Call this after writing a file — the new ' +
|
|
244
|
+
'symbols become resolvable by search_signatures / get_callee_signatures.',
|
|
245
|
+
inputSchema: {
|
|
246
|
+
type: 'object',
|
|
247
|
+
properties: {
|
|
248
|
+
path: { type: 'string', description: 'File path relative to the project root.' },
|
|
249
|
+
content: { type: 'string', description: 'Optional file content; read from disk if omitted.' },
|
|
250
|
+
},
|
|
251
|
+
required: ['path'],
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
name: 'sigmap_notify_symbol_added',
|
|
256
|
+
description:
|
|
257
|
+
'Fast path: register a single new symbol signature directly in the live ' +
|
|
258
|
+
'index without re-reading the whole file.',
|
|
259
|
+
inputSchema: {
|
|
260
|
+
type: 'object',
|
|
261
|
+
properties: {
|
|
262
|
+
signature: { type: 'string', description: 'The signature line (e.g. "function check(key)").' },
|
|
263
|
+
file: { type: 'string', description: 'File path the symbol belongs to (relative to root).' },
|
|
264
|
+
line: { type: 'number', description: 'Optional 1-based line number.' },
|
|
265
|
+
},
|
|
266
|
+
required: ['signature', 'file'],
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
name: 'sigmap_notify_file_deleted',
|
|
271
|
+
description:
|
|
272
|
+
'Tell SigMap a file was deleted so its symbols are dropped from the live index.',
|
|
273
|
+
inputSchema: {
|
|
274
|
+
type: 'object',
|
|
275
|
+
properties: {
|
|
276
|
+
path: { type: 'string', description: 'Deleted file path relative to the project root.' },
|
|
277
|
+
},
|
|
278
|
+
required: ['path'],
|
|
279
|
+
},
|
|
280
|
+
},
|
|
238
281
|
];
|
|
239
282
|
|
|
240
283
|
module.exports = { TOOLS };
|