graph-indexer 3.0.1 → 3.1.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 (58) hide show
  1. package/CHANGELOG.md +456 -0
  2. package/README.md +61 -8
  3. package/SECURITY.md +35 -20
  4. package/bin/graph-indexer.mjs +46 -5
  5. package/integrations/claude-code/.claude-plugin/plugin.json +1 -1
  6. package/integrations/claude-code/agents/code-structure.md +0 -3
  7. package/integrations/claude-code/skills/setup/SKILL.md +26 -0
  8. package/integrations/claude-code-local/.claude-plugin/plugin.json +9 -0
  9. package/integrations/{claude-code → claude-code-local}/.mcp.json +1 -1
  10. package/integrations/claude-code-local/README.md +10 -0
  11. package/integrations/claude-code-local/agents/code-structure.md +22 -0
  12. package/integrations/{claude-code → claude-code-local}/hooks/gi-hook.mjs +15 -4
  13. package/integrations/opencode/graph-indexer.js +10 -3
  14. package/package.json +1 -1
  15. package/src/cli/helper.mjs +15 -1
  16. package/src/cli/hook.mjs +124 -40
  17. package/src/cli/init.mjs +32 -5
  18. package/src/cli/resident.mjs +19 -4
  19. package/src/index/discover.mjs +52 -30
  20. package/src/index/indexer.mjs +78 -42
  21. package/src/index/modules.mjs +75 -2
  22. package/src/index/parse-worker.mjs +12 -0
  23. package/src/index/parsepool.mjs +73 -0
  24. package/src/index/resolver.mjs +362 -23
  25. package/src/mcp/server.mjs +43 -13
  26. package/src/mcp/tools.mjs +418 -26
  27. package/src/parse/arity.mjs +39 -2
  28. package/src/parse/extract.mjs +269 -45
  29. package/src/parse/lang/ecma.mjs +2 -0
  30. package/src/parse/lang/go.mjs +2 -0
  31. package/src/parse/lang/jvm.mjs +33 -0
  32. package/src/parse/lang/markdown.mjs +92 -0
  33. package/src/parse/lang/python.mjs +43 -0
  34. package/src/parse/lang/rust.mjs +215 -15
  35. package/src/parse/lang/sfc.mjs +126 -0
  36. package/src/parse/languages.mjs +9 -2
  37. package/src/query/check.mjs +49 -13
  38. package/src/query/facts.mjs +3 -2
  39. package/src/query/injection-spring.mjs +406 -0
  40. package/src/query/injection.mjs +174 -0
  41. package/src/query/intel.mjs +85 -15
  42. package/src/query/issue.mjs +156 -0
  43. package/src/query/orm.mjs +288 -0
  44. package/src/query/read.mjs +6 -1
  45. package/src/query/report.mjs +53 -0
  46. package/src/query/routes.mjs +142 -0
  47. package/src/query/showcase.mjs +59 -0
  48. package/src/query/textsearch.mjs +30 -9
  49. package/src/search/search.mjs +85 -17
  50. package/src/search/semantic.mjs +201 -0
  51. package/src/search/vectors.mjs +122 -0
  52. package/src/store/db.mjs +33 -4
  53. package/src/util/git.mjs +60 -11
  54. package/src/util/paths.mjs +10 -3
  55. package/vendor/grammars/README.md +47 -0
  56. package/vendor/grammars/kotlin.wasm.gz +0 -0
  57. package/vendor/grammars/manifest.json +4 -4
  58. /package/integrations/{claude-code → claude-code-local}/hooks/hooks.json +0 -0
@@ -10,6 +10,32 @@
10
10
  import readline from 'node:readline';
11
11
  import { TOOLS, LEGACY_TOOLS, SERVER_INSTRUCTIONS, callTool } from './tools.mjs';
12
12
 
13
+ const MAX_ITEMS = 100; // an array argument with no maxItems of its own
14
+ const MAX_STRING = 200_000; // a string argument (an issue pasted as a query is long, not this long)
15
+
16
+ /**
17
+ * The arguments as the tool's schema bounds them: numbers clamped to minimum/maximum, arrays cut
18
+ * to maxItems (else MAX_ITEMS), strings to MAX_STRING. A client is not trusted to respect the schema,
19
+ * and `depth: 1e9` or ten thousand queries would otherwise hold the server.
20
+ */
21
+ export function boundArgs(schema, raw) {
22
+ const args = raw && typeof raw === 'object' && !Array.isArray(raw) ? { ...raw } : {};
23
+ const props = schema?.properties ?? {};
24
+ const str = (v) => (typeof v === 'string' && v.length > MAX_STRING ? v.slice(0, MAX_STRING) : v);
25
+ for (const [k, v] of Object.entries(args)) {
26
+ const p = props[k];
27
+ if (typeof v === 'number' && p && (p.type === 'integer' || p.type === 'number')) {
28
+ let n = Number.isFinite(v) ? v : (p.default ?? p.minimum ?? 0);
29
+ if (p.type === 'integer') n = Math.trunc(n);
30
+ if (p.minimum != null) n = Math.max(p.minimum, n);
31
+ if (p.maximum != null) n = Math.min(p.maximum, n);
32
+ args[k] = n;
33
+ } else if (Array.isArray(v)) args[k] = v.slice(0, p?.maxItems ?? MAX_ITEMS).map(str);
34
+ else args[k] = str(v);
35
+ }
36
+ return args;
37
+ }
38
+
13
39
  export const SUPPORTED_VERSIONS = ['2026-07-28', '2025-11-25', '2025-06-18', '2025-03-26', '2024-11-05'];
14
40
  const LATEST_HANDSHAKE = '2025-11-25';
15
41
  const META_VERSION = 'io.modelcontextprotocol/protocolVersion';
@@ -64,13 +90,17 @@ export class McpServer {
64
90
  this.#error(id, -32022, `Unsupported protocol version: ${reqVersion}`, { supported: SUPPORTED_VERSIONS, requested: reqVersion });
65
91
  return;
66
92
  }
93
+ // 2026-07-28 results say whether they are complete (or need more input from the client);
94
+ // a client of that version ignores a result without it
95
+ const stateless = this.#requestVersion(params) === '2026-07-28' || method === 'server/discover';
96
+ const done = (res) => this.#result(id, stateless ? { resultType: 'complete', ...res } : res);
67
97
  try {
68
98
  switch (method) {
69
99
  case 'initialize': {
70
100
  const requested = params?.protocolVersion;
71
101
  const version = SUPPORTED_VERSIONS.includes(requested) && requested !== '2026-07-28' ? requested : LATEST_HANDSHAKE;
72
102
  this.negotiated = version;
73
- this.#result(id, {
103
+ done({
74
104
  protocolVersion: version,
75
105
  capabilities: { tools: { listChanged: false } },
76
106
  serverInfo: { ...this.info, title: 'graph-indexer — code graph & search' },
@@ -79,7 +109,7 @@ export class McpServer {
79
109
  return;
80
110
  }
81
111
  case 'server/discover':
82
- this.#result(id, {
112
+ done({
83
113
  supportedVersions: SUPPORTED_VERSIONS,
84
114
  capabilities: { tools: { listChanged: false } },
85
115
  instructions: SERVER_INSTRUCTIONS,
@@ -89,22 +119,22 @@ export class McpServer {
89
119
  });
90
120
  return;
91
121
  case 'ping':
92
- this.#result(id, {});
122
+ done({});
93
123
  return;
94
124
  case 'tools/list': {
95
125
  const res = { tools: TOOLS.map(({ name, title, description, inputSchema, annotations, _meta }) => ({ name, title, description, inputSchema, annotations, ...(_meta ? { _meta } : {}) })) };
96
126
  if (this.#requestVersion(params) === '2026-07-28') Object.assign(res, { ttlMs: 3_600_000, cacheScope: 'private' });
97
- this.#result(id, res);
127
+ done(res);
98
128
  return;
99
129
  }
100
130
  case 'tools/call': {
101
131
  const name = params?.name;
102
132
  const tool = TOOLS.find(t => t.name === name) ?? LEGACY_TOOLS.find(t => t.name === name);
103
133
  if (!tool) { this.#error(id, -32602, `Unknown tool: ${name}`); return; }
104
- const args = params?.arguments ?? {};
134
+ const args = boundArgs(tool.inputSchema, params?.arguments);
105
135
  const missing = (tool.inputSchema.required ?? []).filter(k => args[k] === undefined || args[k] === null || args[k] === '');
106
136
  if (missing.length) {
107
- this.#result(id, { content: [{ type: 'text', text: `Missing required argument(s): ${missing.join(', ')}.` }], isError: true });
137
+ done({ content: [{ type: 'text', text: `Missing required argument(s): ${missing.join(', ')}.` }], isError: true });
108
138
  return;
109
139
  }
110
140
  this.inflight++;
@@ -112,18 +142,18 @@ export class McpServer {
112
142
  const note = await this.#awaitReady();
113
143
  await this.intel.ensureFresh();
114
144
  const text = await callTool(this.intel, name, args);
115
- this.#result(id, { content: [{ type: 'text', text: note ? `${note}\n${text}` : text }] });
145
+ done({ content: [{ type: 'text', text: note ? `${note}\n${text}` : text }] });
116
146
  } catch (err) {
117
147
  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 });
148
+ done({ content: [{ type: 'text', text: `graph-indexer error in ${name}: ${err.message}` }], isError: true });
119
149
  } finally { this.inflight--; }
120
150
  return;
121
151
  }
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;
152
+ case 'resources/list': done({ resources: [] }); return;
153
+ case 'resources/templates/list': done({ resourceTemplates: [] }); return;
154
+ case 'prompts/list': done({ prompts: [] }); return;
155
+ case 'logging/setLevel': done({}); return;
156
+ case 'completion/complete': done({ completion: { values: [], hasMore: false } }); return;
127
157
  default:
128
158
  this.#error(id, -32601, `Method not found: ${method}`);
129
159
  }