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
package/cli-ui.mjs DELETED
@@ -1,78 +0,0 @@
1
- /**
2
- * @file cli-ui.mjs
3
- * @description Tiny, dependency-free console styling shared by the `graph-indexer`
4
- * CLIs (init + daemon control). ANSI colour with graceful degradation
5
- * when stdout is not a TTY or NO_COLOR is set, plus a few drawing
6
- * helpers (boxed banner, rules, status glyphs) for a consistent look.
7
- * @author MaquinaTech <https://github.com/MaquinaTech>
8
- * @copyright (c) 2026 MaquinaTech. All rights reserved.
9
- * @license MIT
10
- */
11
-
12
- export const useColor = process.stdout.isTTY && !process.env.NO_COLOR;
13
-
14
- const paint = (code) => (s) => (useColor ? `\x1b[${code}m${s}\x1b[0m` : `${s}`);
15
-
16
- export const c = {
17
- bold: paint('1'),
18
- dim: paint('2'),
19
- red: paint('31'),
20
- green: paint('32'),
21
- yellow: paint('33'),
22
- blue: paint('34'),
23
- magenta: paint('35'),
24
- cyan: paint('36'),
25
- };
26
-
27
- export const glyph = {
28
- ok: c.green('✓'),
29
- upd: c.cyan('↻'),
30
- keep: c.dim('•'),
31
- move: c.yellow('↪'),
32
- skip: c.dim('–'),
33
- warn: c.yellow('⚠'),
34
- err: c.red('✗'),
35
- run: c.green('●'),
36
- stop: c.dim('○'),
37
- arrow: c.cyan('→'),
38
- };
39
-
40
- export const log = (msg = '') => process.stdout.write(msg + '\n');
41
-
42
- /** A horizontal rule, capped to the terminal width so it never wraps. */
43
- export const rule = (width = Math.min(60, (process.stdout.columns || 80))) => c.dim('─'.repeat(Math.max(1, width)));
44
-
45
- const visibleLen = (s) => s.replace(/\x1b\[[0-9;]*m/g, '').length;
46
-
47
- /** ANSI-aware truncate to `limit` visible columns; closes any open colour + adds an ellipsis. */
48
- function clipVisible(s, limit) {
49
- if (limit < 1) return '';
50
- if (visibleLen(s) <= limit) return s;
51
- let out = '', w = 0, i = 0, ansi = false;
52
- while (i < s.length && w < limit - 1) {
53
- if (s[i] === '\x1b') {
54
- const m = s.slice(i).match(/^\x1b\[[0-9;]*m/);
55
- if (m) { out += m[0]; i += m[0].length; ansi = true; continue; }
56
- }
57
- out += s[i++]; w++;
58
- }
59
- return out + (ansi ? '\x1b[0m' : '') + '…';
60
- }
61
-
62
- /**
63
- * Draws a bordered box. ANSI escapes are stripped before measuring width so colour
64
- * never breaks alignment, and the box (plus each line) is capped to the terminal
65
- * width so the border stays intact on narrow terminals instead of wrapping.
66
- */
67
- export function box(lines, { pad = 1, color = c.cyan } = {}) {
68
- const maxInner = Math.max(1, (process.stdout.columns || 80) - 2); // 2 border columns
69
- const shown = lines.map((line) => clipVisible(line, maxInner - pad * 2));
70
- const inner = Math.min(maxInner, Math.max(...shown.map(visibleLen)) + pad * 2);
71
- const top = color('╭' + '─'.repeat(inner) + '╮');
72
- const bot = color('╰' + '─'.repeat(inner) + '╯');
73
- const body = shown.map((line) => {
74
- const gap = ' '.repeat(Math.max(0, inner - visibleLen(line) - pad));
75
- return color('│') + ' '.repeat(pad) + line + gap + color('│');
76
- });
77
- return [top, ...body, bot].join('\n');
78
- }
package/colbert.mjs DELETED
@@ -1,168 +0,0 @@
1
- /**
2
- * @file colbert.mjs
3
- * @description B4 — ColBERT-style LATE-INTERACTION reranker (multi-vector MaxSim). A third,
4
- * opt-in reranker provider (rerank.provider === 'late-interaction'), sibling to the
5
- * generative judge and the cross-encoder (B2). Where a single-vector dense channel
6
- * pools the query and the document each into ONE vector (losing which part matched
7
- * where), late interaction keeps MULTIPLE vectors per side and scores them with MaxSim:
8
- *
9
- * score(q, d) = Σ_{qi} max_{dj} cos(qi, dj)
10
- *
11
- * i.e. every query sub-vector finds its best-matching document sub-vector and the hits
12
- * sum — the compositional-query win ColBERT is known for. It reuses the document
13
- * sub-vectors the engine ALREADY stores (the base + `|s` summary + `|wN` window vectors
14
- * of each chunk), so there is **no storage blow-up** — the canonical ColBERT cost. It is
15
- * deployed as a RERANKER over the over-fetched candidate pool (the standard
16
- * retrieve-then-late-interact pattern), so it is a post-retrieval re-order — the default
17
- * ranking is untouched and parity is preserved (both backends read the SAME bin → the
18
- * SAME doc vectors → the SAME MaxSim → the SAME order).
19
- *
20
- * AIR-GAPPED / HONEST SCOPE: the query multi-vectors are produced by the SAME local
21
- * embedder the dense channel already uses (no new model, no network). A true ColBERT
22
- * uses a token-level CONTEXTUAL encoder; a general sentence embedder over query
23
- * sub-units only APPROXIMATES that — so this is "late interaction over the existing
24
- * encoder," with a real token-level ColBERT model a clean future drop-in behind
25
- * `encodeMultiVector`. Requires --embeddings (the doc vectors + the embedder); a no-op
26
- * (returns the original order) when either is absent.
27
- * @author MaquinaTech <https://github.com/MaquinaTech>
28
- * @copyright (c) 2026 MaquinaTech. All rights reserved.
29
- * @license MIT
30
- */
31
- import { collectVectorsByKey } from './engine/binary.mjs';
32
- import { embeddingKeyFor, SUMMARY_VEC_SUFFIX, WINDOW_VEC_SUFFIX, EMBEDDING_MAX_WINDOWS } from './search-core.mjs';
33
-
34
- // Query sub-unit extraction is intentionally light (no stemming — surface forms, like the dense
35
- // channel). A tiny stopword guard keeps function words from spending a query vector slot.
36
- const STOP = new Set([
37
- 'the', 'a', 'an', 'and', 'or', 'of', 'to', 'in', 'for', 'on', 'with', 'at', 'by', 'from',
38
- 'that', 'this', 'is', 'are', 'was', 'were', 'be', 'how', 'what', 'when', 'where', 'which',
39
- 'who', 'why', 'does', 'do', 'it', 'its', 'into', 'if', 'then', 'there', 'all', 'any',
40
- ]);
41
-
42
- /** Cosine similarity of two equal-length vectors (0 when either is degenerate). */
43
- export function cosine(a, b) {
44
- if (!a || !b || a.length !== b.length) return 0;
45
- let dot = 0, na = 0, nb = 0;
46
- for (let i = 0; i < a.length; i++) { dot += a[i] * b[i]; na += a[i] * a[i]; nb += b[i] * b[i]; }
47
- if (na === 0 || nb === 0) return 0;
48
- return dot / (Math.sqrt(na) * Math.sqrt(nb));
49
- }
50
-
51
- /**
52
- * MaxSim late-interaction score: Σ over query vectors of the max cosine to any document vector.
53
- * Order-independent (a sum of maxes) → no parity dependence on vector ordering.
54
- * @param {Float32Array[]} qVecs query sub-vectors
55
- * @param {Float32Array[]} dVecs document sub-vectors
56
- * @returns {number} 0 when either side is empty
57
- */
58
- export function maxSimScore(qVecs, dVecs) {
59
- if (!qVecs?.length || !dVecs?.length) return 0;
60
- let total = 0;
61
- for (const q of qVecs) {
62
- let best = -Infinity;
63
- for (const d of dVecs) { const s = cosine(q, d); if (s > best) best = s; }
64
- if (best > -Infinity) total += best;
65
- }
66
- return total;
67
- }
68
-
69
- /** The embedding keys that hold a chunk's stored sub-vectors (base + summary + windows). */
70
- function chunkVectorKeys(chunk) {
71
- const vecKey = embeddingKeyFor(chunk);
72
- const keys = [vecKey, chunk.content_hash, vecKey + SUMMARY_VEC_SUFFIX];
73
- for (let i = 1; i < EMBEDDING_MAX_WINDOWS; i++) keys.push(vecKey + WINDOW_VEC_SUFFIX + i);
74
- return { vecKey, keys };
75
- }
76
-
77
- /**
78
- * Load each chunk's stored sub-vectors from the shared `.embeddings.bin` in a SINGLE pass (one bin
79
- * read per rerank, not per candidate). Identical for both backends (same file, same keys) →
80
- * parity-by-construction. @returns {Map<chunkId, Float32Array[]>}
81
- */
82
- export function loadChunkVectors(binPath, chunks) {
83
- const meta = new Map(); // chunkId → { vecKey, hash }
84
- const allKeys = new Set();
85
- for (const c of chunks) {
86
- if (!c || !c.content_hash) { meta.set(c?.id, null); continue; }
87
- const { vecKey, keys } = chunkVectorKeys(c);
88
- meta.set(c.id, { vecKey, hash: c.content_hash });
89
- for (const k of keys) allKeys.add(k);
90
- }
91
- const found = collectVectorsByKey(binPath, allKeys);
92
- const out = new Map();
93
- for (const [id, m] of meta) {
94
- if (!m) { out.set(id, []); continue; }
95
- const vecs = [];
96
- const base = found.get(m.vecKey) || found.get(m.hash); // deterministic order: base, summary, windows
97
- if (base) vecs.push(base);
98
- const s = found.get(m.vecKey + SUMMARY_VEC_SUFFIX); if (s) vecs.push(s);
99
- for (let i = 1; i < EMBEDDING_MAX_WINDOWS; i++) { const w = found.get(m.vecKey + WINDOW_VEC_SUFFIX + i); if (w) vecs.push(w); }
100
- out.set(id, vecs);
101
- }
102
- return out;
103
- }
104
-
105
- /**
106
- * Encode a query into MULTIPLE vectors (the whole query plus its content sub-units) with the SAME
107
- * local embedder the dense channel uses. Best-effort: returns [] on any failure / no embedder.
108
- * @param {{embedDocuments?:Function, embedQuery?:Function}} embedder
109
- * @param {string} text
110
- * @param {object} [opts]
111
- * @returns {Promise<Float32Array[]>}
112
- */
113
- export async function encodeMultiVector(embedder, text, { maxVecs = 8 } = {}) {
114
- if (!embedder || typeof embedder.embedDocuments !== 'function') return [];
115
- const words = String(text).toLowerCase().split(/[\s\W_]+/).filter(Boolean);
116
- const units = [];
117
- const seen = new Set();
118
- for (const w of words) {
119
- if (w.length < 3 || STOP.has(w) || seen.has(w)) continue;
120
- seen.add(w); units.push(w);
121
- if (units.length >= maxVecs - 1) break;
122
- }
123
- units.unshift(String(text)); // the holistic query vector always leads
124
- let vecs;
125
- try { vecs = await embedder.embedDocuments(units); }
126
- catch { return []; }
127
- return (vecs || []).filter(v => v && v.length).map(v => v instanceof Float32Array ? v : new Float32Array(v));
128
- }
129
-
130
- /**
131
- * Late-interaction RERANK of the over-fetched pool. Re-orders the top-`topM` by MaxSim of the query
132
- * multi-vectors against each candidate's stored doc sub-vectors; the tail is preserved. Best-effort
133
- * and never throws: any shortfall (no embedder, <2 candidates with vectors, empty query vectors)
134
- * returns the ORIGINAL order, so the channel can only help, never corrupt. Never mutates `r.score`
135
- * (protects the downstream git-boost / formatting), exactly like rerankCrossEncoder.
136
- *
137
- * @param {Array<{chunk:object, score:number}>} results
138
- * @param {object} opts
139
- * @param {Float32Array[]} opts.qVecs query multi-vectors (from encodeMultiVector)
140
- * @param {string} opts.binPath the `.embeddings.bin` path (db.embeddingBinPath())
141
- * @param {number} [opts.topM]
142
- * @returns {Array}
143
- */
144
- export function rerankLateInteraction(results, { qVecs, binPath, topM = 12 } = {}) {
145
- const head = results.slice(0, topM);
146
- if (head.length < 2 || !qVecs?.length || !binPath) return results;
147
- const vecsById = loadChunkVectors(binPath, head.map(r => r.chunk));
148
- const scored = head.map((r, i) => {
149
- const dVecs = vecsById.get(r.chunk.id) || [];
150
- return { i, id: r.chunk.id, s: dVecs.length ? maxSimScore(qVecs, dVecs) : NaN };
151
- });
152
- // "Can only help, never corrupt": treat no-signal as a shortfall. Re-order only when at least two
153
- // candidates carry a FINITE and DISTINCT MaxSim — so all-zero scores (e.g. a dim mismatch making
154
- // every cosine 0) or undifferentiated ties fall through to the original order instead of being
155
- // scrambled into id-lexicographic order by the tie-break.
156
- const finite = scored.filter(x => Number.isFinite(x.s));
157
- if (finite.length < 2 || new Set(finite.map(x => x.s)).size < 2) return results;
158
- const order = scored
159
- // finite MaxSim first (desc); candidates without vectors sink but keep a stable id order
160
- .sort((a, b) => {
161
- const af = Number.isFinite(a.s), bf = Number.isFinite(b.s);
162
- if (af && bf) return (b.s - a.s) || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
163
- if (af) return -1; if (bf) return 1;
164
- return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
165
- })
166
- .map(x => x.i);
167
- return [...order.map(i => head[i]), ...results.slice(topM)];
168
- }