graph-indexer 2.1.0 → 3.0.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.
Files changed (129) hide show
  1. package/CHANGELOG.md +260 -1
  2. package/README.md +232 -312
  3. package/SECURITY.md +37 -59
  4. package/bin/graph-indexer.mjs +251 -0
  5. package/integrations/claude-code/.claude-plugin/plugin.json +8 -0
  6. package/integrations/claude-code/.mcp.json +8 -0
  7. package/integrations/claude-code/agents/code-structure.md +22 -0
  8. package/integrations/claude-code/hooks/gi-hook.mjs +99 -0
  9. package/integrations/claude-code/hooks/hooks.json +16 -0
  10. package/integrations/opencode/graph-indexer.js +124 -0
  11. package/package.json +31 -105
  12. package/src/cli/helper.mjs +67 -0
  13. package/src/cli/hook.mjs +356 -0
  14. package/src/cli/init.mjs +255 -0
  15. package/src/cli/resident.mjs +175 -0
  16. package/src/index/discover.mjs +130 -0
  17. package/src/index/fingerprint.mjs +30 -0
  18. package/src/index/graph.mjs +61 -0
  19. package/src/index/indexer.mjs +476 -0
  20. package/src/index/modules.mjs +331 -0
  21. package/src/index/resolver.mjs +954 -0
  22. package/src/mcp/render.mjs +60 -0
  23. package/src/mcp/server.mjs +147 -0
  24. package/src/mcp/tools.mjs +973 -0
  25. package/src/parse/arity.mjs +152 -0
  26. package/src/parse/extract.mjs +1031 -0
  27. package/src/parse/lang/csharp.mjs +106 -0
  28. package/src/parse/lang/ecma.mjs +438 -0
  29. package/src/parse/lang/go.mjs +194 -0
  30. package/src/parse/lang/jvm.mjs +118 -0
  31. package/src/parse/lang/kotlin.mjs +97 -0
  32. package/src/parse/lang/misc.mjs +115 -0
  33. package/src/parse/lang/native.mjs +126 -0
  34. package/src/parse/lang/python.mjs +153 -0
  35. package/src/parse/lang/rust.mjs +157 -0
  36. package/src/parse/lang/scripting.mjs +219 -0
  37. package/src/parse/languages.mjs +48 -0
  38. package/src/parse/runtime.mjs +56 -0
  39. package/src/query/check.mjs +343 -0
  40. package/src/query/facts.mjs +323 -0
  41. package/src/query/intel.mjs +680 -0
  42. package/src/query/read.mjs +81 -0
  43. package/src/query/testcmd.mjs +119 -0
  44. package/src/query/textsearch.mjs +160 -0
  45. package/src/search/search.mjs +210 -0
  46. package/src/search/tokenize.mjs +188 -0
  47. package/src/store/db.mjs +169 -0
  48. package/src/util/paths.mjs +24 -0
  49. package/vendor/grammars/bash.wasm.gz +0 -0
  50. package/vendor/grammars/c.wasm.gz +0 -0
  51. package/vendor/grammars/c_sharp.wasm.gz +0 -0
  52. package/vendor/grammars/cpp.wasm.gz +0 -0
  53. package/vendor/grammars/css.wasm.gz +0 -0
  54. package/vendor/grammars/go.wasm.gz +0 -0
  55. package/vendor/grammars/java.wasm.gz +0 -0
  56. package/vendor/grammars/javascript.wasm.gz +0 -0
  57. package/vendor/grammars/kotlin.wasm.gz +0 -0
  58. package/vendor/grammars/manifest.json +148 -0
  59. package/vendor/grammars/php.wasm.gz +0 -0
  60. package/vendor/grammars/python.wasm.gz +0 -0
  61. package/vendor/grammars/ruby.wasm.gz +0 -0
  62. package/vendor/grammars/rust.wasm.gz +0 -0
  63. package/vendor/grammars/scala.wasm.gz +0 -0
  64. package/vendor/grammars/tsx.wasm.gz +0 -0
  65. package/vendor/grammars/typescript.wasm.gz +0 -0
  66. package/vendor/web-tree-sitter/web-tree-sitter.js +3980 -0
  67. package/vendor/web-tree-sitter/web-tree-sitter.wasm +0 -0
  68. package/cli-ui.mjs +0 -78
  69. package/colbert.mjs +0 -168
  70. package/config.mjs +0 -494
  71. package/daemon-ctl.mjs +0 -175
  72. package/daemon-lock.mjs +0 -69
  73. package/embedders/python/mlx_embed_server.py +0 -61
  74. package/embedders/python/requirements-mlx.txt +0 -5
  75. package/embedders/setup-mlx.mjs +0 -110
  76. package/embeddings.mjs +0 -571
  77. package/engine/binary.mjs +0 -437
  78. package/engine/memory.mjs +0 -1003
  79. package/engine/sqlite.mjs +0 -1136
  80. package/enrichment.mjs +0 -521
  81. package/git-signals.mjs +0 -248
  82. package/indexer.mjs +0 -546
  83. package/init.mjs +0 -1809
  84. package/layout.mjs +0 -123
  85. package/mcp/centrality.mjs +0 -117
  86. package/mcp/format.mjs +0 -61
  87. package/mcp/resolver.mjs +0 -116
  88. package/mcp/symbolgraph.mjs +0 -137
  89. package/mcp/taint.mjs +0 -225
  90. package/mcp/tools.mjs +0 -1582
  91. package/mcp/topology.mjs +0 -676
  92. package/mcp-server.mjs +0 -197
  93. package/parse/extractor.mjs +0 -628
  94. package/parse/imports.mjs +0 -365
  95. package/parse/interprocedural.mjs +0 -134
  96. package/parse/languages.mjs +0 -253
  97. package/parse/metadata.mjs +0 -701
  98. package/parse/routes.mjs +0 -327
  99. package/parse/scip.mjs +0 -391
  100. package/parse/taint-patterns.mjs +0 -119
  101. package/prompts/CORE.md +0 -91
  102. package/prompts/DOMAIN_TEMPLATE.md +0 -46
  103. package/prompts/INTEGRATION.md +0 -123
  104. package/prompts/frameworks/ANDROID.md +0 -17
  105. package/prompts/frameworks/ASPNET_CORE.md +0 -19
  106. package/prompts/frameworks/FASTAPI_DJANGO.md +0 -20
  107. package/prompts/frameworks/LARAVEL_SYMFONY.md +0 -22
  108. package/prompts/frameworks/NODE_EXPRESS_NESTJS.md +0 -19
  109. package/prompts/frameworks/RAILS.md +0 -20
  110. package/prompts/frameworks/REACT.md +0 -20
  111. package/prompts/frameworks/SPRING_BOOT.md +0 -20
  112. package/prompts/languages/BASH.md +0 -22
  113. package/prompts/languages/C.md +0 -21
  114. package/prompts/languages/CSHARP.md +0 -24
  115. package/prompts/languages/CSS_SCSS.md +0 -22
  116. package/prompts/languages/GO.md +0 -24
  117. package/prompts/languages/JAVA.md +0 -23
  118. package/prompts/languages/JAVASCRIPT_TYPESCRIPT.md +0 -24
  119. package/prompts/languages/KOTLIN.md +0 -24
  120. package/prompts/languages/PHP.md +0 -23
  121. package/prompts/languages/PYTHON.md +0 -23
  122. package/prompts/languages/RUBY.md +0 -23
  123. package/prompts/languages/RUST.md +0 -21
  124. package/prompts/languages/SWIFT.md +0 -21
  125. package/seal.mjs +0 -400
  126. package/search-core.mjs +0 -777
  127. package/search-sparse.mjs +0 -183
  128. package/storage.mjs +0 -73
  129. package/watch-daemon.mjs +0 -289
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Compact, agent-oriented text rendering. Conventions:
3
+ * - locations are `path:line` / `path:start-end` (clickable in most agent UIs);
4
+ * - code is shown with right-aligned line numbers, like the agents' own file readers;
5
+ * - every list states its total and what was left out, so truncation is never silent;
6
+ * - confidence words: exact (scope/import/type proven), high, likely, possible (name-only).
7
+ */
8
+ import { confidenceLabel } from '../index/resolver.mjs';
9
+
10
+ const MAX_LINE = 220;
11
+
12
+ export function loc(s) {
13
+ return s.start_line === s.end_line ? `${s.path}:${s.start_line}` : `${s.path}:${s.start_line}-${s.end_line}`;
14
+ }
15
+
16
+ export function clip(text, n = MAX_LINE) {
17
+ if (!text) return '';
18
+ return text.length > n ? text.slice(0, n - 1) + '…' : text;
19
+ }
20
+
21
+ export function codeBlock(lines, from, to, { highlight = null, maxLines = 400 } = {}) {
22
+ if (!lines) return '(file not readable)';
23
+ const a = Math.max(1, from), b = Math.min(lines.length, to);
24
+ const w = String(b).length;
25
+ const out = [];
26
+ let shown = 0;
27
+ for (let i = a; i <= b; i++) {
28
+ if (shown >= maxLines) { out.push(`${' '.repeat(w)} … ${b - i + 1} more lines (${i}-${b})`); break; }
29
+ const mark = highlight?.has(i) ? '›' : ' ';
30
+ out.push(`${String(i).padStart(w)}${mark} ${clip(lines[i - 1].replace(/\t/g, ' '))}`);
31
+ shown++;
32
+ }
33
+ return out.join('\n');
34
+ }
35
+
36
+ export function kindTag(s) {
37
+ const bits = [s.kind];
38
+ if (s.is_test) bits.push('test');
39
+ return bits.join(', ');
40
+ }
41
+
42
+ export function symHeader(s) {
43
+ return `${s.qname} (${kindTag(s)}) ${loc(s)}`;
44
+ }
45
+
46
+ export function confWord(c) { return confidenceLabel(c); }
47
+
48
+ /** Lines of a symbol's body that mention any of the query terms (for search snippets). */
49
+ export function matchingLines(lines, s, terms, max = 2) {
50
+ if (!lines || !terms.length) return [];
51
+ const re = new RegExp(terms.filter(t => t.length >= 3).map(t => t.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|') || '$^', 'i');
52
+ const out = [];
53
+ for (let i = s.start_line + 1; i <= Math.min(s.end_line, lines.length) && out.length < max; i++) {
54
+ const t = lines[i - 1];
55
+ if (t && re.test(t) && t.trim().length > 3) out.push(i);
56
+ }
57
+ return out;
58
+ }
59
+
60
+ export function plural(n, word, pluralWord = word + 's') { return `${n} ${n === 1 ? word : pluralWord}`; }
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Minimal, dependency-free MCP server over stdio (newline-delimited JSON-RPC 2.0).
3
+ *
4
+ * Supports both protocol generations:
5
+ * - handshake-based (2024-11-05 … 2025-11-25): initialize → notifications/initialized → requests
6
+ * - stateless 2026-07-28: every request carries `_meta["io.modelcontextprotocol/protocolVersion"]`
7
+ * and `server/discover` advertises versions, capabilities and instructions.
8
+ * Only tools are exposed (all read-only). stdout carries protocol messages exclusively; logs go to stderr.
9
+ */
10
+ import readline from 'node:readline';
11
+ import { TOOLS, LEGACY_TOOLS, SERVER_INSTRUCTIONS, callTool } from './tools.mjs';
12
+
13
+ export const SUPPORTED_VERSIONS = ['2026-07-28', '2025-11-25', '2025-06-18', '2025-03-26', '2024-11-05'];
14
+ const LATEST_HANDSHAKE = '2025-11-25';
15
+ const META_VERSION = 'io.modelcontextprotocol/protocolVersion';
16
+
17
+ export class McpServer {
18
+ constructor({ intel, name = 'graph-indexer', version = '0.0.0', log = () => {}, ready = Promise.resolve() }) {
19
+ this.intel = intel;
20
+ this.info = { name, version };
21
+ this.log = log;
22
+ this.ready = ready; // resolves when the initial index is usable
23
+ this.negotiated = null;
24
+ this.cancelled = new Set();
25
+ this.inflight = 0;
26
+ this.out = process.stdout;
27
+ }
28
+
29
+ listen(input = process.stdin, output = process.stdout) {
30
+ this.out = output;
31
+ const rl = readline.createInterface({ input, crlfDelay: Infinity });
32
+ rl.on('line', (line) => {
33
+ const text = line.trim();
34
+ if (!text) return;
35
+ let msg;
36
+ try { msg = JSON.parse(text); } catch { this.#send({ jsonrpc: '2.0', id: null, error: { code: -32700, message: 'Parse error' } }); return; }
37
+ if (Array.isArray(msg)) { for (const m of msg) this.#handle(m); } else this.#handle(msg);
38
+ });
39
+ rl.on('close', () => this.onClose?.());
40
+ return this;
41
+ }
42
+
43
+ #send(obj) { this.out.write(JSON.stringify(obj) + '\n'); }
44
+ #result(id, result) { if (id !== undefined && id !== null && !this.cancelled.has(id)) this.#send({ jsonrpc: '2.0', id, result }); this.cancelled.delete(id); }
45
+ #error(id, code, message, data) { if (id !== undefined) this.#send({ jsonrpc: '2.0', id: id ?? null, error: data === undefined ? { code, message } : { code, message, data } }); }
46
+
47
+ #requestVersion(params) {
48
+ return params?._meta?.[META_VERSION] ?? this.negotiated ?? null;
49
+ }
50
+
51
+ async #handle(msg) {
52
+ if (!msg || msg.jsonrpc !== '2.0' || typeof msg.method !== 'string') {
53
+ if (msg && msg.id !== undefined && (msg.result !== undefined || msg.error !== undefined)) return; // response to us (unused)
54
+ this.#error(msg?.id ?? null, -32600, 'Invalid Request');
55
+ return;
56
+ }
57
+ const { id, method, params } = msg;
58
+ const isNotification = id === undefined;
59
+ if (method === 'notifications/cancelled') { if (params?.requestId !== undefined) this.cancelled.add(params.requestId); return; }
60
+ if (isNotification) return; // initialized, progress, roots/list_changed, …
61
+
62
+ const reqVersion = params?._meta?.[META_VERSION];
63
+ if (reqVersion && !SUPPORTED_VERSIONS.includes(reqVersion) && method !== 'initialize') {
64
+ this.#error(id, -32022, `Unsupported protocol version: ${reqVersion}`, { supported: SUPPORTED_VERSIONS, requested: reqVersion });
65
+ return;
66
+ }
67
+ try {
68
+ switch (method) {
69
+ case 'initialize': {
70
+ const requested = params?.protocolVersion;
71
+ const version = SUPPORTED_VERSIONS.includes(requested) && requested !== '2026-07-28' ? requested : LATEST_HANDSHAKE;
72
+ this.negotiated = version;
73
+ this.#result(id, {
74
+ protocolVersion: version,
75
+ capabilities: { tools: { listChanged: false } },
76
+ serverInfo: { ...this.info, title: 'graph-indexer — code graph & search' },
77
+ instructions: SERVER_INSTRUCTIONS,
78
+ });
79
+ return;
80
+ }
81
+ case 'server/discover':
82
+ this.#result(id, {
83
+ supportedVersions: SUPPORTED_VERSIONS,
84
+ capabilities: { tools: { listChanged: false } },
85
+ instructions: SERVER_INSTRUCTIONS,
86
+ ttlMs: 3_600_000,
87
+ cacheScope: 'private',
88
+ _meta: { 'io.modelcontextprotocol/serverInfo': this.info },
89
+ });
90
+ return;
91
+ case 'ping':
92
+ this.#result(id, {});
93
+ return;
94
+ case 'tools/list': {
95
+ const res = { tools: TOOLS.map(({ name, title, description, inputSchema, annotations, _meta }) => ({ name, title, description, inputSchema, annotations, ...(_meta ? { _meta } : {}) })) };
96
+ if (this.#requestVersion(params) === '2026-07-28') Object.assign(res, { ttlMs: 3_600_000, cacheScope: 'private' });
97
+ this.#result(id, res);
98
+ return;
99
+ }
100
+ case 'tools/call': {
101
+ const name = params?.name;
102
+ const tool = TOOLS.find(t => t.name === name) ?? LEGACY_TOOLS.find(t => t.name === name);
103
+ if (!tool) { this.#error(id, -32602, `Unknown tool: ${name}`); return; }
104
+ const args = params?.arguments ?? {};
105
+ const missing = (tool.inputSchema.required ?? []).filter(k => args[k] === undefined || args[k] === null || args[k] === '');
106
+ if (missing.length) {
107
+ this.#result(id, { content: [{ type: 'text', text: `Missing required argument(s): ${missing.join(', ')}.` }], isError: true });
108
+ return;
109
+ }
110
+ this.inflight++;
111
+ try {
112
+ const note = await this.#awaitReady();
113
+ await this.intel.ensureFresh();
114
+ const text = await callTool(this.intel, name, args);
115
+ this.#result(id, { content: [{ type: 'text', text: note ? `${note}\n${text}` : text }] });
116
+ } catch (err) {
117
+ this.log(`tool ${name} failed: ${err.stack || err.message}`);
118
+ this.#result(id, { content: [{ type: 'text', text: `graph-indexer error in ${name}: ${err.message}` }], isError: true });
119
+ } finally { this.inflight--; }
120
+ return;
121
+ }
122
+ case 'resources/list': this.#result(id, { resources: [] }); return;
123
+ case 'resources/templates/list': this.#result(id, { resourceTemplates: [] }); return;
124
+ case 'prompts/list': this.#result(id, { prompts: [] }); return;
125
+ case 'logging/setLevel': this.#result(id, {}); return;
126
+ case 'completion/complete': this.#result(id, { completion: { values: [], hasMore: false } }); return;
127
+ default:
128
+ this.#error(id, -32601, `Method not found: ${method}`);
129
+ }
130
+ } catch (err) {
131
+ this.log(`request ${method} failed: ${err.stack || err.message}`);
132
+ this.#error(id, -32603, `Internal error: ${err.message}`);
133
+ }
134
+ }
135
+
136
+ /** Wait for the initial index; on very large repos answer from the partial index after a while. */
137
+ async #awaitReady() {
138
+ let done = false;
139
+ const t = new Promise(r => setTimeout(() => r('timeout'), 45_000));
140
+ const res = await Promise.race([this.ready.then(() => { done = true; return 'ready'; }), t]);
141
+ if (res === 'timeout' && !done) {
142
+ const p = this.intel.progress;
143
+ return `note: the initial index is still being built${p ? ` (${p.done}/${p.total} files)` : ''}; results may be incomplete.`;
144
+ }
145
+ return null;
146
+ }
147
+ }