graph-indexer 2.1.1 → 3.0.1

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 (131) hide show
  1. package/CHANGELOG.md +312 -1
  2. package/README.md +236 -312
  3. package/SECURITY.md +53 -58
  4. package/bin/graph-indexer.mjs +251 -0
  5. package/integrations/claude-code/.claude-plugin/plugin.json +9 -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 +103 -0
  9. package/integrations/claude-code/hooks/hooks.json +16 -0
  10. package/integrations/opencode/graph-indexer.js +129 -0
  11. package/package.json +32 -109
  12. package/src/cli/helper.mjs +67 -0
  13. package/src/cli/hook.mjs +366 -0
  14. package/src/cli/init.mjs +272 -0
  15. package/src/cli/resident.mjs +185 -0
  16. package/src/index/discover.mjs +128 -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 +332 -0
  21. package/src/index/resolver.mjs +961 -0
  22. package/src/mcp/render.mjs +60 -0
  23. package/src/mcp/server.mjs +147 -0
  24. package/src/mcp/tools.mjs +974 -0
  25. package/src/parse/arity.mjs +153 -0
  26. package/src/parse/extract.mjs +1038 -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 +345 -0
  40. package/src/query/facts.mjs +323 -0
  41. package/src/query/intel.mjs +683 -0
  42. package/src/query/read.mjs +81 -0
  43. package/src/query/testcmd.mjs +130 -0
  44. package/src/query/textsearch.mjs +178 -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/git.mjs +30 -0
  49. package/src/util/paths.mjs +63 -0
  50. package/src/util/regex.mjs +2 -0
  51. package/vendor/grammars/bash.wasm.gz +0 -0
  52. package/vendor/grammars/c.wasm.gz +0 -0
  53. package/vendor/grammars/c_sharp.wasm.gz +0 -0
  54. package/vendor/grammars/cpp.wasm.gz +0 -0
  55. package/vendor/grammars/css.wasm.gz +0 -0
  56. package/vendor/grammars/go.wasm.gz +0 -0
  57. package/vendor/grammars/java.wasm.gz +0 -0
  58. package/vendor/grammars/javascript.wasm.gz +0 -0
  59. package/vendor/grammars/kotlin.wasm.gz +0 -0
  60. package/vendor/grammars/manifest.json +148 -0
  61. package/vendor/grammars/php.wasm.gz +0 -0
  62. package/vendor/grammars/python.wasm.gz +0 -0
  63. package/vendor/grammars/ruby.wasm.gz +0 -0
  64. package/vendor/grammars/rust.wasm.gz +0 -0
  65. package/vendor/grammars/scala.wasm.gz +0 -0
  66. package/vendor/grammars/tsx.wasm.gz +0 -0
  67. package/vendor/grammars/typescript.wasm.gz +0 -0
  68. package/vendor/web-tree-sitter/web-tree-sitter.js +3980 -0
  69. package/vendor/web-tree-sitter/web-tree-sitter.wasm +0 -0
  70. package/cli-ui.mjs +0 -78
  71. package/colbert.mjs +0 -168
  72. package/config.mjs +0 -494
  73. package/daemon-ctl.mjs +0 -175
  74. package/daemon-lock.mjs +0 -69
  75. package/embedders/python/mlx_embed_server.py +0 -61
  76. package/embedders/python/requirements-mlx.txt +0 -5
  77. package/embedders/setup-mlx.mjs +0 -110
  78. package/embeddings.mjs +0 -571
  79. package/engine/binary.mjs +0 -437
  80. package/engine/memory.mjs +0 -1003
  81. package/engine/sqlite.mjs +0 -1136
  82. package/enrichment.mjs +0 -521
  83. package/git-signals.mjs +0 -248
  84. package/indexer.mjs +0 -546
  85. package/init.mjs +0 -1809
  86. package/layout.mjs +0 -123
  87. package/mcp/centrality.mjs +0 -117
  88. package/mcp/format.mjs +0 -61
  89. package/mcp/resolver.mjs +0 -116
  90. package/mcp/symbolgraph.mjs +0 -137
  91. package/mcp/taint.mjs +0 -225
  92. package/mcp/tools.mjs +0 -1582
  93. package/mcp/topology.mjs +0 -676
  94. package/mcp-server.mjs +0 -197
  95. package/parse/extractor.mjs +0 -628
  96. package/parse/imports.mjs +0 -365
  97. package/parse/interprocedural.mjs +0 -134
  98. package/parse/languages.mjs +0 -253
  99. package/parse/metadata.mjs +0 -701
  100. package/parse/routes.mjs +0 -327
  101. package/parse/scip.mjs +0 -391
  102. package/parse/taint-patterns.mjs +0 -119
  103. package/prompts/CORE.md +0 -91
  104. package/prompts/DOMAIN_TEMPLATE.md +0 -46
  105. package/prompts/INTEGRATION.md +0 -123
  106. package/prompts/frameworks/ANDROID.md +0 -17
  107. package/prompts/frameworks/ASPNET_CORE.md +0 -19
  108. package/prompts/frameworks/FASTAPI_DJANGO.md +0 -20
  109. package/prompts/frameworks/LARAVEL_SYMFONY.md +0 -22
  110. package/prompts/frameworks/NODE_EXPRESS_NESTJS.md +0 -19
  111. package/prompts/frameworks/RAILS.md +0 -20
  112. package/prompts/frameworks/REACT.md +0 -20
  113. package/prompts/frameworks/SPRING_BOOT.md +0 -20
  114. package/prompts/languages/BASH.md +0 -22
  115. package/prompts/languages/C.md +0 -21
  116. package/prompts/languages/CSHARP.md +0 -24
  117. package/prompts/languages/CSS_SCSS.md +0 -22
  118. package/prompts/languages/GO.md +0 -24
  119. package/prompts/languages/JAVA.md +0 -23
  120. package/prompts/languages/JAVASCRIPT_TYPESCRIPT.md +0 -24
  121. package/prompts/languages/KOTLIN.md +0 -24
  122. package/prompts/languages/PHP.md +0 -23
  123. package/prompts/languages/PYTHON.md +0 -23
  124. package/prompts/languages/RUBY.md +0 -23
  125. package/prompts/languages/RUST.md +0 -21
  126. package/prompts/languages/SWIFT.md +0 -21
  127. package/seal.mjs +0 -400
  128. package/search-core.mjs +0 -777
  129. package/search-sparse.mjs +0 -183
  130. package/storage.mjs +0 -73
  131. 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
+ }