gitnexus 1.4.5 → 1.4.7

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 (49) hide show
  1. package/dist/cli/eval-server.js +13 -5
  2. package/dist/cli/index.js +0 -0
  3. package/dist/cli/tool.d.ts +3 -2
  4. package/dist/cli/tool.js +48 -13
  5. package/dist/core/graph/types.d.ts +2 -2
  6. package/dist/core/ingestion/call-processor.d.ts +7 -2
  7. package/dist/core/ingestion/call-processor.js +308 -235
  8. package/dist/core/ingestion/call-routing.d.ts +17 -2
  9. package/dist/core/ingestion/call-routing.js +21 -0
  10. package/dist/core/ingestion/parsing-processor.d.ts +2 -1
  11. package/dist/core/ingestion/parsing-processor.js +37 -8
  12. package/dist/core/ingestion/pipeline.js +5 -1
  13. package/dist/core/ingestion/symbol-table.d.ts +19 -3
  14. package/dist/core/ingestion/symbol-table.js +41 -2
  15. package/dist/core/ingestion/tree-sitter-queries.d.ts +12 -12
  16. package/dist/core/ingestion/tree-sitter-queries.js +200 -0
  17. package/dist/core/ingestion/type-env.js +126 -18
  18. package/dist/core/ingestion/type-extractors/c-cpp.js +28 -3
  19. package/dist/core/ingestion/type-extractors/csharp.js +61 -7
  20. package/dist/core/ingestion/type-extractors/go.js +86 -10
  21. package/dist/core/ingestion/type-extractors/jvm.js +122 -23
  22. package/dist/core/ingestion/type-extractors/php.js +172 -7
  23. package/dist/core/ingestion/type-extractors/python.js +107 -21
  24. package/dist/core/ingestion/type-extractors/ruby.js +18 -3
  25. package/dist/core/ingestion/type-extractors/rust.js +61 -14
  26. package/dist/core/ingestion/type-extractors/shared.d.ts +13 -0
  27. package/dist/core/ingestion/type-extractors/shared.js +243 -4
  28. package/dist/core/ingestion/type-extractors/types.d.ts +57 -12
  29. package/dist/core/ingestion/type-extractors/typescript.js +52 -8
  30. package/dist/core/ingestion/utils.d.ts +25 -0
  31. package/dist/core/ingestion/utils.js +160 -1
  32. package/dist/core/ingestion/workers/parse-worker.d.ts +23 -7
  33. package/dist/core/ingestion/workers/parse-worker.js +73 -28
  34. package/dist/core/lbug/lbug-adapter.d.ts +2 -0
  35. package/dist/core/lbug/lbug-adapter.js +2 -0
  36. package/dist/core/lbug/schema.d.ts +1 -1
  37. package/dist/core/lbug/schema.js +1 -1
  38. package/dist/mcp/core/lbug-adapter.d.ts +22 -0
  39. package/dist/mcp/core/lbug-adapter.js +167 -23
  40. package/dist/mcp/local/local-backend.d.ts +1 -0
  41. package/dist/mcp/local/local-backend.js +25 -3
  42. package/dist/mcp/resources.js +11 -0
  43. package/dist/mcp/server.js +26 -4
  44. package/dist/mcp/tools.js +15 -5
  45. package/hooks/claude/gitnexus-hook.cjs +0 -0
  46. package/hooks/claude/pre-tool-use.sh +0 -0
  47. package/hooks/claude/session-start.sh +0 -0
  48. package/package.json +6 -5
  49. package/scripts/patch-tree-sitter-swift.cjs +0 -0
@@ -37,7 +37,7 @@ export const VALID_NODE_LABELS = new Set([
37
37
  'Record', 'Delegate', 'Annotation', 'Constructor', 'Template', 'Module',
38
38
  ]);
39
39
  /** Valid relation types for impact analysis filtering */
40
- export const VALID_RELATION_TYPES = new Set(['CALLS', 'IMPORTS', 'EXTENDS', 'IMPLEMENTS']);
40
+ export const VALID_RELATION_TYPES = new Set(['CALLS', 'IMPORTS', 'EXTENDS', 'IMPLEMENTS', 'HAS_METHOD', 'HAS_PROPERTY', 'OVERRIDES', 'ACCESSES']);
41
41
  /** Regex to detect write operations in user-supplied Cypher queries */
42
42
  export const CYPHER_WRITE_RE = /\b(CREATE|DELETE|SET|MERGE|REMOVE|DROP|ALTER|COPY|DETACH)\b/i;
43
43
  /** Check if a Cypher query contains write operations */
@@ -788,14 +788,14 @@ export class LocalBackend {
788
788
  // Categorized incoming refs
789
789
  const incomingRows = await executeParameterized(repo.id, `
790
790
  MATCH (caller)-[r:CodeRelation]->(n {id: $symId})
791
- WHERE r.type IN ['CALLS', 'IMPORTS', 'EXTENDS', 'IMPLEMENTS']
791
+ WHERE r.type IN ['CALLS', 'IMPORTS', 'EXTENDS', 'IMPLEMENTS', 'HAS_METHOD', 'HAS_PROPERTY', 'OVERRIDES', 'ACCESSES']
792
792
  RETURN r.type AS relType, caller.id AS uid, caller.name AS name, caller.filePath AS filePath, labels(caller)[0] AS kind
793
793
  LIMIT 30
794
794
  `, { symId });
795
795
  // Categorized outgoing refs
796
796
  const outgoingRows = await executeParameterized(repo.id, `
797
797
  MATCH (n {id: $symId})-[r:CodeRelation]->(target)
798
- WHERE r.type IN ['CALLS', 'IMPORTS', 'EXTENDS', 'IMPLEMENTS']
798
+ WHERE r.type IN ['CALLS', 'IMPORTS', 'EXTENDS', 'IMPLEMENTS', 'HAS_METHOD', 'HAS_PROPERTY', 'OVERRIDES', 'ACCESSES']
799
799
  RETURN r.type AS relType, target.id AS uid, target.name AS name, target.filePath AS filePath, labels(target)[0] AS kind
800
800
  LIMIT 30
801
801
  `, { symId });
@@ -1189,6 +1189,22 @@ export class LocalBackend {
1189
1189
  };
1190
1190
  }
1191
1191
  async impact(repo, params) {
1192
+ try {
1193
+ return await this._impactImpl(repo, params);
1194
+ }
1195
+ catch (err) {
1196
+ // Return structured error instead of crashing (#321)
1197
+ return {
1198
+ error: (err instanceof Error ? err.message : String(err)) || 'Impact analysis failed',
1199
+ target: { name: params.target },
1200
+ direction: params.direction,
1201
+ impactedCount: 0,
1202
+ risk: 'UNKNOWN',
1203
+ suggestion: 'The graph query failed — try gitnexus context <symbol> as a fallback',
1204
+ };
1205
+ }
1206
+ }
1207
+ async _impactImpl(repo, params) {
1192
1208
  await this.ensureInitialized(repo.id);
1193
1209
  const { target, direction } = params;
1194
1210
  const maxDepth = params.maxDepth || 3;
@@ -1213,6 +1229,7 @@ export class LocalBackend {
1213
1229
  const impacted = [];
1214
1230
  const visited = new Set([symId]);
1215
1231
  let frontier = [symId];
1232
+ let traversalComplete = true;
1216
1233
  for (let depth = 1; depth <= maxDepth && frontier.length > 0; depth++) {
1217
1234
  const nextFrontier = [];
1218
1235
  // Batch frontier nodes into a single Cypher query per depth level
@@ -1244,6 +1261,10 @@ export class LocalBackend {
1244
1261
  }
1245
1262
  catch (e) {
1246
1263
  logQueryError('impact:depth-traversal', e);
1264
+ // Break out of depth loop on query failure but return partial results
1265
+ // collected so far, rather than silently swallowing the error (#321)
1266
+ traversalComplete = false;
1267
+ break;
1247
1268
  }
1248
1269
  frontier = nextFrontier;
1249
1270
  }
@@ -1321,6 +1342,7 @@ export class LocalBackend {
1321
1342
  direction,
1322
1343
  impactedCount: impacted.length,
1323
1344
  risk,
1345
+ ...(!traversalComplete && { partial: true }),
1324
1346
  summary: {
1325
1347
  direct: directCount,
1326
1348
  processes_affected: processCount,
@@ -271,6 +271,13 @@ nodes:
271
271
 
272
272
  additional_node_types: "Multi-language: Struct, Enum, Macro, Typedef, Union, Namespace, Trait, Impl, TypeAlias, Const, Static, Property, Record, Delegate, Annotation, Constructor, Template, Module (use backticks in queries: \`Struct\`, \`Enum\`, etc.)"
273
273
 
274
+ node_properties:
275
+ common: "name (STRING), filePath (STRING), startLine (INT32), endLine (INT32)"
276
+ Method: "parameterCount (INT32), returnType (STRING), isVariadic (BOOL)"
277
+ Function: "parameterCount (INT32), returnType (STRING), isVariadic (BOOL)"
278
+ Property: "declaredType (STRING) — the field's type annotation (e.g., 'Address', 'City'). Used for field-access chain resolution."
279
+ Constructor: "parameterCount (INT32)"
280
+
274
281
  relationships:
275
282
  - CONTAINS: File/Folder contains child
276
283
  - DEFINES: File defines a symbol
@@ -278,6 +285,10 @@ relationships:
278
285
  - IMPORTS: Module imports
279
286
  - EXTENDS: Class inheritance
280
287
  - IMPLEMENTS: Interface implementation
288
+ - HAS_METHOD: Class/Struct/Interface owns a Method
289
+ - HAS_PROPERTY: Class/Struct/Interface owns a Property (field)
290
+ - ACCESSES: Function/Method reads or writes a Property (reason: 'read' or 'write')
291
+ - OVERRIDES: Method overrides another Method (MRO)
281
292
  - MEMBER_OF: Symbol belongs to community
282
293
  - STEP_IN_PROCESS: Symbol is step N in process
283
294
 
@@ -15,6 +15,7 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
15
15
  import { CompatibleStdioServerTransport } from './compatible-stdio-transport.js';
16
16
  import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListResourceTemplatesRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
17
17
  import { GITNEXUS_TOOLS } from './tools.js';
18
+ import { realStdoutWrite } from './core/lbug-adapter.js';
18
19
  import { getResourceDefinitions, getResourceTemplates, readResource } from './resources.js';
19
20
  /**
20
21
  * Next-step hints appended to tool responses.
@@ -237,12 +238,22 @@ Follow these steps:
237
238
  */
238
239
  export async function startMCPServer(backend) {
239
240
  const server = createMCPServer(backend);
240
- // Connect to stdio transport
241
- const transport = new CompatibleStdioServerTransport();
241
+ // Use the shared stdout reference captured at module-load time by the
242
+ // lbug-adapter. Avoids divergence if anything patches stdout between
243
+ // module load and server start.
244
+ const _safeStdout = new Proxy(process.stdout, {
245
+ get(target, prop, receiver) {
246
+ if (prop === 'write')
247
+ return realStdoutWrite;
248
+ const val = Reflect.get(target, prop, receiver);
249
+ return typeof val === 'function' ? val.bind(target) : val;
250
+ }
251
+ });
252
+ const transport = new CompatibleStdioServerTransport(process.stdin, _safeStdout);
242
253
  await server.connect(transport);
243
254
  // Graceful shutdown helper
244
255
  let shuttingDown = false;
245
- const shutdown = async () => {
256
+ const shutdown = async (exitCode = 0) => {
246
257
  if (shuttingDown)
247
258
  return;
248
259
  shuttingDown = true;
@@ -254,11 +265,22 @@ export async function startMCPServer(backend) {
254
265
  await server.close();
255
266
  }
256
267
  catch { }
257
- process.exit(0);
268
+ process.exit(exitCode);
258
269
  };
259
270
  // Handle graceful shutdown
260
271
  process.on('SIGINT', shutdown);
261
272
  process.on('SIGTERM', shutdown);
273
+ // Log crashes to stderr so they aren't silently lost.
274
+ // uncaughtException is fatal — shut down.
275
+ // unhandledRejection is logged but kept non-fatal (availability-first):
276
+ // killing the server for one missed catch would be worse than logging it.
277
+ process.on('uncaughtException', (err) => {
278
+ process.stderr.write(`GitNexus MCP uncaughtException: ${err?.stack || err}\n`);
279
+ shutdown(1);
280
+ });
281
+ process.on('unhandledRejection', (reason) => {
282
+ process.stderr.write(`GitNexus MCP unhandledRejection: ${reason?.stack || reason}\n`);
283
+ });
262
284
  // Handle stdio errors — stdin close means the parent process is gone
263
285
  process.stdin.on('end', shutdown);
264
286
  process.stdin.on('error', () => shutdown());
package/dist/mcp/tools.js CHANGED
@@ -61,7 +61,7 @@ SCHEMA:
61
61
  - Nodes: File, Folder, Function, Class, Interface, Method, CodeElement, Community, Process
62
62
  - Multi-language nodes (use backticks): \`Struct\`, \`Enum\`, \`Trait\`, \`Impl\`, etc.
63
63
  - All edges via single CodeRelation table with 'type' property
64
- - Edge types: CONTAINS, DEFINES, CALLS, IMPORTS, EXTENDS, IMPLEMENTS, HAS_METHOD, OVERRIDES, MEMBER_OF, STEP_IN_PROCESS
64
+ - Edge types: CONTAINS, DEFINES, CALLS, IMPORTS, EXTENDS, IMPLEMENTS, HAS_METHOD, HAS_PROPERTY, ACCESSES, OVERRIDES, MEMBER_OF, STEP_IN_PROCESS
65
65
  - Edge properties: type (STRING), confidence (DOUBLE), reason (STRING), step (INT32)
66
66
 
67
67
  EXAMPLES:
@@ -77,6 +77,12 @@ EXAMPLES:
77
77
  • Find all methods of a class:
78
78
  MATCH (c:Class {name: "UserService"})-[r:CodeRelation {type: 'HAS_METHOD'}]->(m:Method) RETURN m.name, m.parameterCount, m.returnType
79
79
 
80
+ • Find all properties of a class:
81
+ MATCH (c:Class {name: "User"})-[r:CodeRelation {type: 'HAS_PROPERTY'}]->(p:Property) RETURN p.name, p.declaredType
82
+
83
+ • Find all writers of a field:
84
+ MATCH (f:Function)-[r:CodeRelation {type: 'ACCESSES', reason: 'write'}]->(p:Property) WHERE p.name = "address" RETURN f.name, f.filePath
85
+
80
86
  • Find method overrides (MRO resolution):
81
87
  MATCH (winner:Method)-[r:CodeRelation {type: 'OVERRIDES'}]->(loser:Method) RETURN winner.name, winner.filePath, loser.filePath, r.reason
82
88
 
@@ -102,12 +108,14 @@ TIPS:
102
108
  {
103
109
  name: 'context',
104
110
  description: `360-degree view of a single code symbol.
105
- Shows categorized incoming/outgoing references (calls, imports, extends, implements), process participation, and file location.
111
+ Shows categorized incoming/outgoing references (calls, imports, extends, implements, methods, properties, overrides), process participation, and file location.
106
112
 
107
113
  WHEN TO USE: After query() to understand a specific symbol in depth. When you need to know all callers, callees, and what execution flows a symbol participates in.
108
114
  AFTER THIS: Use impact() if planning changes, or READ gitnexus://repo/{name}/process/{processName} for full execution trace.
109
115
 
110
- Handles disambiguation: if multiple symbols share the same name, returns candidates for you to pick from. Use uid param for zero-ambiguity lookup from prior results.`,
116
+ Handles disambiguation: if multiple symbols share the same name, returns candidates for you to pick from. Use uid param for zero-ambiguity lookup from prior results.
117
+
118
+ NOTE: ACCESSES edges (field read/write tracking) are included in context results with reason 'read' or 'write'. CALLS edges resolve through field access chains and method-call chains (e.g., user.address.getCity().save() produces CALLS edges at each step).`,
111
119
  inputSchema: {
112
120
  type: 'object',
113
121
  properties: {
@@ -183,7 +191,9 @@ Depth groups:
183
191
  - d=2: LIKELY AFFECTED (indirect)
184
192
  - d=3: MAY NEED TESTING (transitive)
185
193
 
186
- EdgeType: CALLS, IMPORTS, EXTENDS, IMPLEMENTS, HAS_METHOD, OVERRIDES
194
+ TIP: Default traversal uses CALLS/IMPORTS/EXTENDS/IMPLEMENTS. For class members, include HAS_METHOD and HAS_PROPERTY in relationTypes. For field access analysis, include ACCESSES in relationTypes.
195
+
196
+ EdgeType: CALLS, IMPORTS, EXTENDS, IMPLEMENTS, HAS_METHOD, HAS_PROPERTY, OVERRIDES, ACCESSES
187
197
  Confidence: 1.0 = certain, <0.8 = fuzzy match`,
188
198
  inputSchema: {
189
199
  type: 'object',
@@ -191,7 +201,7 @@ Confidence: 1.0 = certain, <0.8 = fuzzy match`,
191
201
  target: { type: 'string', description: 'Name of function, class, or file to analyze' },
192
202
  direction: { type: 'string', description: 'upstream (what depends on this) or downstream (what this depends on)' },
193
203
  maxDepth: { type: 'number', description: 'Max relationship depth (default: 3)', default: 3 },
194
- relationTypes: { type: 'array', items: { type: 'string' }, description: 'Filter: CALLS, IMPORTS, EXTENDS, IMPLEMENTS, HAS_METHOD, OVERRIDES (default: usage-based)' },
204
+ relationTypes: { type: 'array', items: { type: 'string' }, description: 'Filter: CALLS, IMPORTS, EXTENDS, IMPLEMENTS, HAS_METHOD, HAS_PROPERTY, OVERRIDES, ACCESSES (default: usage-based, ACCESSES excluded by default)' },
195
205
  includeTests: { type: 'boolean', description: 'Include test files (default: false)' },
196
206
  minConfidence: { type: 'number', description: 'Minimum confidence 0-1 (default: 0.7)' },
197
207
  repo: { type: 'string', description: 'Repository name or path. Omit if only one repo is indexed.' },
File without changes
File without changes
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.4.5",
3
+ "version": "1.4.7",
4
4
  "description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.",
5
5
  "author": "Abhigyan Patwari",
6
6
  "license": "PolyForm-Noncommercial-1.0.0",
@@ -39,13 +39,14 @@
39
39
  "scripts": {
40
40
  "build": "tsc",
41
41
  "dev": "tsx watch src/cli/index.ts",
42
- "test": "vitest run test/unit",
42
+ "test": "vitest run",
43
+ "test:unit": "vitest run test/unit",
43
44
  "test:integration": "vitest run test/integration",
44
- "test:all": "vitest run",
45
45
  "test:watch": "vitest",
46
46
  "test:coverage": "vitest run --coverage",
47
47
  "prepare": "npm run build",
48
- "postinstall": "node scripts/patch-tree-sitter-swift.cjs"
48
+ "postinstall": "node scripts/patch-tree-sitter-swift.cjs",
49
+ "prepack": "npm run build && chmod +x dist/cli/index.js"
49
50
  },
50
51
  "dependencies": {
51
52
  "@huggingface/transformers": "^3.0.0",
@@ -58,7 +59,7 @@
58
59
  "graphology": "^0.25.4",
59
60
  "graphology-indices": "^0.17.0",
60
61
  "graphology-utils": "^2.3.0",
61
- "@ladybugdb/core": "^0.15.1",
62
+ "@ladybugdb/core": "^0.15.2",
62
63
  "ignore": "^7.0.5",
63
64
  "lru-cache": "^11.0.0",
64
65
  "mnemonist": "^0.39.0",
File without changes