@stupidloud/codegraph 0.9.5 → 0.9.9

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 (302) hide show
  1. package/README.md +252 -116
  2. package/dist/bin/codegraph.js +52 -82
  3. package/dist/bin/codegraph.js.map +1 -1
  4. package/dist/context/formatter.d.ts.map +1 -1
  5. package/dist/context/formatter.js +25 -6
  6. package/dist/context/formatter.js.map +1 -1
  7. package/dist/context/index.d.ts +22 -0
  8. package/dist/context/index.d.ts.map +1 -1
  9. package/dist/context/index.js +257 -6
  10. package/dist/context/index.js.map +1 -1
  11. package/dist/context/markers.d.ts +19 -0
  12. package/dist/context/markers.d.ts.map +1 -0
  13. package/dist/context/markers.js +22 -0
  14. package/dist/context/markers.js.map +1 -0
  15. package/dist/db/queries.d.ts +88 -0
  16. package/dist/db/queries.d.ts.map +1 -1
  17. package/dist/db/queries.js +251 -7
  18. package/dist/db/queries.js.map +1 -1
  19. package/dist/db/sqlite-adapter.d.ts +7 -0
  20. package/dist/db/sqlite-adapter.d.ts.map +1 -1
  21. package/dist/db/sqlite-adapter.js +3 -0
  22. package/dist/db/sqlite-adapter.js.map +1 -1
  23. package/dist/directory.d.ts.map +1 -1
  24. package/dist/directory.js +6 -20
  25. package/dist/directory.js.map +1 -1
  26. package/dist/extraction/generated-detection.d.ts +30 -0
  27. package/dist/extraction/generated-detection.d.ts.map +1 -0
  28. package/dist/extraction/generated-detection.js +80 -0
  29. package/dist/extraction/generated-detection.js.map +1 -0
  30. package/dist/extraction/grammars.d.ts +17 -1
  31. package/dist/extraction/grammars.d.ts.map +1 -1
  32. package/dist/extraction/grammars.js +65 -1
  33. package/dist/extraction/grammars.js.map +1 -1
  34. package/dist/extraction/index.d.ts +15 -2
  35. package/dist/extraction/index.d.ts.map +1 -1
  36. package/dist/extraction/index.js +206 -98
  37. package/dist/extraction/index.js.map +1 -1
  38. package/dist/extraction/languages/c-cpp.d.ts.map +1 -1
  39. package/dist/extraction/languages/c-cpp.js +45 -0
  40. package/dist/extraction/languages/c-cpp.js.map +1 -1
  41. package/dist/extraction/languages/csharp.d.ts.map +1 -1
  42. package/dist/extraction/languages/csharp.js +2 -1
  43. package/dist/extraction/languages/csharp.js.map +1 -1
  44. package/dist/extraction/languages/go.d.ts.map +1 -1
  45. package/dist/extraction/languages/go.js +18 -2
  46. package/dist/extraction/languages/go.js.map +1 -1
  47. package/dist/extraction/languages/index.d.ts.map +1 -1
  48. package/dist/extraction/languages/index.js +2 -0
  49. package/dist/extraction/languages/index.js.map +1 -1
  50. package/dist/extraction/languages/java.d.ts.map +1 -1
  51. package/dist/extraction/languages/java.js +6 -0
  52. package/dist/extraction/languages/java.js.map +1 -1
  53. package/dist/extraction/languages/kotlin.d.ts.map +1 -1
  54. package/dist/extraction/languages/kotlin.js +6 -0
  55. package/dist/extraction/languages/kotlin.js.map +1 -1
  56. package/dist/extraction/languages/objc.d.ts +3 -0
  57. package/dist/extraction/languages/objc.d.ts.map +1 -0
  58. package/dist/extraction/languages/objc.js +133 -0
  59. package/dist/extraction/languages/objc.js.map +1 -0
  60. package/dist/extraction/mybatis-extractor.d.ts +48 -0
  61. package/dist/extraction/mybatis-extractor.d.ts.map +1 -0
  62. package/dist/extraction/mybatis-extractor.js +198 -0
  63. package/dist/extraction/mybatis-extractor.js.map +1 -0
  64. package/dist/extraction/tree-sitter-types.d.ts +14 -0
  65. package/dist/extraction/tree-sitter-types.d.ts.map +1 -1
  66. package/dist/extraction/tree-sitter.d.ts +84 -0
  67. package/dist/extraction/tree-sitter.d.ts.map +1 -1
  68. package/dist/extraction/tree-sitter.js +681 -20
  69. package/dist/extraction/tree-sitter.js.map +1 -1
  70. package/dist/extraction/vue-extractor.d.ts +15 -0
  71. package/dist/extraction/vue-extractor.d.ts.map +1 -1
  72. package/dist/extraction/vue-extractor.js +88 -0
  73. package/dist/extraction/vue-extractor.js.map +1 -1
  74. package/dist/extraction/wasm-runtime-flags.d.ts.map +1 -1
  75. package/dist/extraction/wasm-runtime-flags.js +1 -0
  76. package/dist/extraction/wasm-runtime-flags.js.map +1 -1
  77. package/dist/graph/traversal.d.ts.map +1 -1
  78. package/dist/graph/traversal.js +5 -2
  79. package/dist/graph/traversal.js.map +1 -1
  80. package/dist/index.d.ts +66 -3
  81. package/dist/index.d.ts.map +1 -1
  82. package/dist/index.js +105 -1
  83. package/dist/index.js.map +1 -1
  84. package/dist/installer/config-writer.d.ts +7 -8
  85. package/dist/installer/config-writer.d.ts.map +1 -1
  86. package/dist/installer/config-writer.js +7 -27
  87. package/dist/installer/config-writer.js.map +1 -1
  88. package/dist/installer/index.d.ts +3 -20
  89. package/dist/installer/index.d.ts.map +1 -1
  90. package/dist/installer/index.js +8 -39
  91. package/dist/installer/index.js.map +1 -1
  92. package/dist/installer/instructions-template.d.ts +11 -21
  93. package/dist/installer/instructions-template.d.ts.map +1 -1
  94. package/dist/installer/instructions-template.js +12 -56
  95. package/dist/installer/instructions-template.js.map +1 -1
  96. package/dist/installer/targets/antigravity.d.ts +57 -0
  97. package/dist/installer/targets/antigravity.d.ts.map +1 -0
  98. package/dist/installer/targets/antigravity.js +308 -0
  99. package/dist/installer/targets/antigravity.js.map +1 -0
  100. package/dist/installer/targets/claude.d.ts +10 -1
  101. package/dist/installer/targets/claude.d.ts.map +1 -1
  102. package/dist/installer/targets/claude.js +25 -40
  103. package/dist/installer/targets/claude.js.map +1 -1
  104. package/dist/installer/targets/codex.d.ts.map +1 -1
  105. package/dist/installer/targets/codex.js +15 -13
  106. package/dist/installer/targets/codex.js.map +1 -1
  107. package/dist/installer/targets/cursor.d.ts.map +1 -1
  108. package/dist/installer/targets/cursor.js +9 -38
  109. package/dist/installer/targets/cursor.js.map +1 -1
  110. package/dist/installer/targets/gemini.d.ts +26 -0
  111. package/dist/installer/targets/gemini.d.ts.map +1 -0
  112. package/dist/installer/targets/gemini.js +167 -0
  113. package/dist/installer/targets/gemini.js.map +1 -0
  114. package/dist/installer/targets/hermes.d.ts.map +1 -1
  115. package/dist/installer/targets/hermes.js +57 -3
  116. package/dist/installer/targets/hermes.js.map +1 -1
  117. package/dist/installer/targets/kiro.d.ts +27 -0
  118. package/dist/installer/targets/kiro.d.ts.map +1 -0
  119. package/dist/installer/targets/kiro.js +178 -0
  120. package/dist/installer/targets/kiro.js.map +1 -0
  121. package/dist/installer/targets/opencode.d.ts.map +1 -1
  122. package/dist/installer/targets/opencode.js +15 -13
  123. package/dist/installer/targets/opencode.js.map +1 -1
  124. package/dist/installer/targets/registry.d.ts.map +1 -1
  125. package/dist/installer/targets/registry.js +6 -0
  126. package/dist/installer/targets/registry.js.map +1 -1
  127. package/dist/installer/targets/shared.d.ts.map +1 -1
  128. package/dist/installer/targets/shared.js +3 -2
  129. package/dist/installer/targets/shared.js.map +1 -1
  130. package/dist/installer/targets/types.d.ts +1 -16
  131. package/dist/installer/targets/types.d.ts.map +1 -1
  132. package/dist/mcp/daemon-paths.d.ts +46 -0
  133. package/dist/mcp/daemon-paths.d.ts.map +1 -0
  134. package/dist/mcp/daemon-paths.js +125 -0
  135. package/dist/mcp/daemon-paths.js.map +1 -0
  136. package/dist/mcp/daemon.d.ts +161 -0
  137. package/dist/mcp/daemon.d.ts.map +1 -0
  138. package/dist/mcp/daemon.js +403 -0
  139. package/dist/mcp/daemon.js.map +1 -0
  140. package/dist/mcp/engine.d.ts +105 -0
  141. package/dist/mcp/engine.d.ts.map +1 -0
  142. package/dist/mcp/engine.js +270 -0
  143. package/dist/mcp/engine.js.map +1 -0
  144. package/dist/mcp/index.d.ts +67 -53
  145. package/dist/mcp/index.d.ts.map +1 -1
  146. package/dist/mcp/index.js +315 -388
  147. package/dist/mcp/index.js.map +1 -1
  148. package/dist/mcp/proxy.d.ts +81 -0
  149. package/dist/mcp/proxy.d.ts.map +1 -0
  150. package/dist/mcp/proxy.js +510 -0
  151. package/dist/mcp/proxy.js.map +1 -0
  152. package/dist/mcp/server-instructions.d.ts +1 -1
  153. package/dist/mcp/server-instructions.d.ts.map +1 -1
  154. package/dist/mcp/server-instructions.js +21 -21
  155. package/dist/mcp/session.d.ts +77 -0
  156. package/dist/mcp/session.d.ts.map +1 -0
  157. package/dist/mcp/session.js +294 -0
  158. package/dist/mcp/session.js.map +1 -0
  159. package/dist/mcp/tools.d.ts +160 -14
  160. package/dist/mcp/tools.d.ts.map +1 -1
  161. package/dist/mcp/tools.js +1622 -322
  162. package/dist/mcp/tools.js.map +1 -1
  163. package/dist/mcp/transport.d.ts +111 -29
  164. package/dist/mcp/transport.d.ts.map +1 -1
  165. package/dist/mcp/transport.js +181 -71
  166. package/dist/mcp/transport.js.map +1 -1
  167. package/dist/mcp/version.d.ts +19 -0
  168. package/dist/mcp/version.d.ts.map +1 -0
  169. package/dist/mcp/version.js +71 -0
  170. package/dist/mcp/version.js.map +1 -0
  171. package/dist/resolution/callback-synthesizer.d.ts +10 -0
  172. package/dist/resolution/callback-synthesizer.d.ts.map +1 -0
  173. package/dist/resolution/callback-synthesizer.js +1300 -0
  174. package/dist/resolution/callback-synthesizer.js.map +1 -0
  175. package/dist/resolution/frameworks/csharp.d.ts.map +1 -1
  176. package/dist/resolution/frameworks/csharp.js +36 -8
  177. package/dist/resolution/frameworks/csharp.js.map +1 -1
  178. package/dist/resolution/frameworks/drupal.d.ts.map +1 -1
  179. package/dist/resolution/frameworks/drupal.js +44 -12
  180. package/dist/resolution/frameworks/drupal.js.map +1 -1
  181. package/dist/resolution/frameworks/expo-modules.d.ts +3 -0
  182. package/dist/resolution/frameworks/expo-modules.d.ts.map +1 -0
  183. package/dist/resolution/frameworks/expo-modules.js +143 -0
  184. package/dist/resolution/frameworks/expo-modules.js.map +1 -0
  185. package/dist/resolution/frameworks/express.d.ts.map +1 -1
  186. package/dist/resolution/frameworks/express.js +102 -19
  187. package/dist/resolution/frameworks/express.js.map +1 -1
  188. package/dist/resolution/frameworks/fabric.d.ts +3 -0
  189. package/dist/resolution/frameworks/fabric.d.ts.map +1 -0
  190. package/dist/resolution/frameworks/fabric.js +354 -0
  191. package/dist/resolution/frameworks/fabric.js.map +1 -0
  192. package/dist/resolution/frameworks/go.d.ts.map +1 -1
  193. package/dist/resolution/frameworks/go.js +6 -3
  194. package/dist/resolution/frameworks/go.js.map +1 -1
  195. package/dist/resolution/frameworks/index.d.ts +5 -0
  196. package/dist/resolution/frameworks/index.d.ts.map +1 -1
  197. package/dist/resolution/frameworks/index.js +25 -1
  198. package/dist/resolution/frameworks/index.js.map +1 -1
  199. package/dist/resolution/frameworks/java.d.ts.map +1 -1
  200. package/dist/resolution/frameworks/java.js +339 -12
  201. package/dist/resolution/frameworks/java.js.map +1 -1
  202. package/dist/resolution/frameworks/laravel.d.ts.map +1 -1
  203. package/dist/resolution/frameworks/laravel.js +17 -8
  204. package/dist/resolution/frameworks/laravel.js.map +1 -1
  205. package/dist/resolution/frameworks/nestjs.d.ts.map +1 -1
  206. package/dist/resolution/frameworks/nestjs.js +324 -0
  207. package/dist/resolution/frameworks/nestjs.js.map +1 -1
  208. package/dist/resolution/frameworks/play.d.ts +19 -0
  209. package/dist/resolution/frameworks/play.d.ts.map +1 -0
  210. package/dist/resolution/frameworks/play.js +111 -0
  211. package/dist/resolution/frameworks/play.js.map +1 -0
  212. package/dist/resolution/frameworks/python.d.ts.map +1 -1
  213. package/dist/resolution/frameworks/python.js +134 -16
  214. package/dist/resolution/frameworks/python.js.map +1 -1
  215. package/dist/resolution/frameworks/react-native.d.ts +3 -0
  216. package/dist/resolution/frameworks/react-native.d.ts.map +1 -0
  217. package/dist/resolution/frameworks/react-native.js +360 -0
  218. package/dist/resolution/frameworks/react-native.js.map +1 -0
  219. package/dist/resolution/frameworks/react.d.ts.map +1 -1
  220. package/dist/resolution/frameworks/react.js +96 -3
  221. package/dist/resolution/frameworks/react.js.map +1 -1
  222. package/dist/resolution/frameworks/ruby.d.ts.map +1 -1
  223. package/dist/resolution/frameworks/ruby.js +106 -2
  224. package/dist/resolution/frameworks/ruby.js.map +1 -1
  225. package/dist/resolution/frameworks/rust.d.ts.map +1 -1
  226. package/dist/resolution/frameworks/rust.js +102 -5
  227. package/dist/resolution/frameworks/rust.js.map +1 -1
  228. package/dist/resolution/frameworks/swift-objc.d.ts +37 -0
  229. package/dist/resolution/frameworks/swift-objc.d.ts.map +1 -0
  230. package/dist/resolution/frameworks/swift-objc.js +252 -0
  231. package/dist/resolution/frameworks/swift-objc.js.map +1 -0
  232. package/dist/resolution/frameworks/swift.d.ts.map +1 -1
  233. package/dist/resolution/frameworks/swift.js +30 -6
  234. package/dist/resolution/frameworks/swift.js.map +1 -1
  235. package/dist/resolution/go-module.d.ts +26 -0
  236. package/dist/resolution/go-module.d.ts.map +1 -0
  237. package/dist/resolution/go-module.js +78 -0
  238. package/dist/resolution/go-module.js.map +1 -0
  239. package/dist/resolution/import-resolver.d.ts +28 -0
  240. package/dist/resolution/import-resolver.d.ts.map +1 -1
  241. package/dist/resolution/import-resolver.js +617 -5
  242. package/dist/resolution/import-resolver.js.map +1 -1
  243. package/dist/resolution/index.d.ts +11 -0
  244. package/dist/resolution/index.d.ts.map +1 -1
  245. package/dist/resolution/index.js +156 -3
  246. package/dist/resolution/index.js.map +1 -1
  247. package/dist/resolution/name-matcher.d.ts.map +1 -1
  248. package/dist/resolution/name-matcher.js +212 -0
  249. package/dist/resolution/name-matcher.js.map +1 -1
  250. package/dist/resolution/swift-objc-bridge.d.ts +134 -0
  251. package/dist/resolution/swift-objc-bridge.d.ts.map +1 -0
  252. package/dist/resolution/swift-objc-bridge.js +256 -0
  253. package/dist/resolution/swift-objc-bridge.js.map +1 -0
  254. package/dist/resolution/types.d.ts +44 -0
  255. package/dist/resolution/types.d.ts.map +1 -1
  256. package/dist/resolution/workspace-packages.d.ts +48 -0
  257. package/dist/resolution/workspace-packages.d.ts.map +1 -0
  258. package/dist/resolution/workspace-packages.js +208 -0
  259. package/dist/resolution/workspace-packages.js.map +1 -0
  260. package/dist/search/query-utils.d.ts +18 -0
  261. package/dist/search/query-utils.d.ts.map +1 -1
  262. package/dist/search/query-utils.js +30 -0
  263. package/dist/search/query-utils.js.map +1 -1
  264. package/dist/sync/git-hooks.d.ts.map +1 -1
  265. package/dist/sync/git-hooks.js +2 -0
  266. package/dist/sync/git-hooks.js.map +1 -1
  267. package/dist/sync/index.d.ts +3 -1
  268. package/dist/sync/index.d.ts.map +1 -1
  269. package/dist/sync/index.js +8 -1
  270. package/dist/sync/index.js.map +1 -1
  271. package/dist/sync/watcher.d.ts +212 -8
  272. package/dist/sync/watcher.d.ts.map +1 -1
  273. package/dist/sync/watcher.js +465 -51
  274. package/dist/sync/watcher.js.map +1 -1
  275. package/dist/sync/worktree.d.ts +54 -0
  276. package/dist/sync/worktree.d.ts.map +1 -0
  277. package/dist/sync/worktree.js +137 -0
  278. package/dist/sync/worktree.js.map +1 -0
  279. package/dist/types.d.ts +9 -1
  280. package/dist/types.d.ts.map +1 -1
  281. package/dist/types.js +3 -0
  282. package/dist/types.js.map +1 -1
  283. package/package.json +1 -1
  284. package/scripts/agent-eval/arms-F.sh +21 -0
  285. package/scripts/agent-eval/arms-matrix.sh +37 -0
  286. package/scripts/agent-eval/bench-readme.sh +28 -0
  287. package/scripts/agent-eval/bench-why-repo.sh +22 -0
  288. package/scripts/agent-eval/block-read-hook.sh +19 -0
  289. package/scripts/agent-eval/hook-settings.json +15 -0
  290. package/scripts/agent-eval/itrun.sh +24 -11
  291. package/scripts/agent-eval/parse-arms.mjs +116 -0
  292. package/scripts/agent-eval/parse-bench-readme.mjs +84 -0
  293. package/scripts/agent-eval/probe-context.mjs +21 -0
  294. package/scripts/agent-eval/probe-explore.mjs +40 -0
  295. package/scripts/agent-eval/probe-node.mjs +20 -0
  296. package/scripts/agent-eval/probe-sweep.mjs +119 -0
  297. package/scripts/agent-eval/probe-trace.mjs +20 -0
  298. package/scripts/agent-eval/run-arms.sh +56 -0
  299. package/scripts/agent-eval/seq-matrix.mjs +137 -0
  300. package/scripts/npm-sdk.js +75 -0
  301. package/scripts/pack-npm.sh +25 -1
  302. package/scripts/prepare-release.mjs +270 -0
@@ -48,6 +48,7 @@ const liquid_extractor_1 = require("./liquid-extractor");
48
48
  const svelte_extractor_1 = require("./svelte-extractor");
49
49
  const dfm_extractor_1 = require("./dfm-extractor");
50
50
  const vue_extractor_1 = require("./vue-extractor");
51
+ const mybatis_extractor_1 = require("./mybatis-extractor");
51
52
  const frameworks_1 = require("../resolution/frameworks");
52
53
  // Re-export for backward compatibility
53
54
  var tree_sitter_helpers_2 = require("./tree-sitter-helpers");
@@ -56,6 +57,9 @@ Object.defineProperty(exports, "generateNodeId", { enumerable: true, get: functi
56
57
  * Extract the name from a node based on language
57
58
  */
58
59
  function extractName(node, source, extractor) {
60
+ const hookName = extractor.resolveName?.(node, source);
61
+ if (hookName)
62
+ return hookName;
59
63
  // Try field name first
60
64
  const nameNode = (0, tree_sitter_helpers_1.getChildByField)(node, extractor.nameField);
61
65
  if (nameNode) {
@@ -218,7 +222,16 @@ class TreeSitterExtractor {
218
222
  this.nodes.push(fileNode);
219
223
  // Push file node onto stack so top-level declarations get contains edges
220
224
  this.nodeStack.push(fileNode.id);
225
+ // File-level package declaration (Kotlin/Java). Creates an implicit
226
+ // `namespace` node wrapping every top-level declaration so their
227
+ // qualifiedName carries the FQN — required for cross-file import
228
+ // resolution on JVM languages where filename ≠ class name.
229
+ const packageNodeId = this.extractFilePackage(this.tree.rootNode);
230
+ if (packageNodeId)
231
+ this.nodeStack.push(packageNodeId);
221
232
  this.visitNode(this.tree.rootNode);
233
+ if (packageNodeId)
234
+ this.nodeStack.pop();
222
235
  this.nodeStack.pop();
223
236
  }
224
237
  catch (error) {
@@ -383,6 +396,17 @@ class TreeSitterExtractor {
383
396
  // their own `calls` refs.
384
397
  else if (INSTANTIATION_KINDS.has(nodeType)) {
385
398
  this.extractInstantiation(node);
399
+ // Java/C# `new T(...) { ... }` — anonymous class with body. Without
400
+ // extracting it as a class node + its methods, the interface→impl
401
+ // synthesizer (Phase 5.5) can't bridge T's abstract methods to the
402
+ // anonymous overrides, and an agent investigating a call through T
403
+ // (`strategy.iterator(...)` where strategy is a Strategy lambda body)
404
+ // has to Read the file to find the actual implementation.
405
+ const anonBody = this.findAnonymousClassBody(node);
406
+ if (anonBody) {
407
+ this.extractAnonymousClass(node, anonBody);
408
+ skipChildren = true;
409
+ }
386
410
  }
387
411
  // (Decorator handling lives inside the symbol-creating extractors
388
412
  // — extractClass / extractFunction / extractProperty — because the
@@ -392,6 +416,20 @@ class TreeSitterExtractor {
392
416
  else if (nodeType === 'impl_item') {
393
417
  this.extractRustImplItem(node);
394
418
  }
419
+ // TypeScript interface members: property_signature (`foo: T`, `foo?: T`)
420
+ // and method_signature (`foo(arg: A): R`) both carry type annotations the
421
+ // interface walker would otherwise drop. Extract them as `references`
422
+ // edges from the interface so resolvers can wire callers/impact for
423
+ // types that only appear in interface members.
424
+ else if ((nodeType === 'property_signature' || nodeType === 'method_signature') &&
425
+ this.isInsideClassLikeNode() &&
426
+ this.TYPE_ANNOTATION_LANGUAGES.has(this.language)) {
427
+ const parentId = this.nodeStack[this.nodeStack.length - 1];
428
+ if (parentId) {
429
+ this.extractTypeAnnotations(node, parentId);
430
+ }
431
+ // don't skipChildren — nested signatures still need traversal
432
+ }
395
433
  // Visit children (unless the extract method already visited them)
396
434
  if (!skipChildren) {
397
435
  for (let i = 0; i < node.namedChildCount; i++) {
@@ -412,6 +450,19 @@ class TreeSitterExtractor {
412
450
  return null;
413
451
  }
414
452
  const id = (0, tree_sitter_helpers_1.generateNodeId)(this.filePath, kind, name, node.startPosition.row + 1);
453
+ // Some grammars (e.g. Dart) model a function/method body as a *sibling* of
454
+ // the signature node, so the declaration node's own range is just the
455
+ // signature line. Extend endLine to the resolved body when it sits beyond
456
+ // the node so the node spans its body — required for any body-level analysis
457
+ // (callees, the callback synthesizer's body scan, context slices). Guarded to
458
+ // only ever extend: for child-body grammars the body is within range (no-op).
459
+ let endLine = node.endPosition.row + 1;
460
+ if (kind === 'function' || kind === 'method') {
461
+ const body = this.extractor?.resolveBody?.(node, this.extractor.bodyField);
462
+ if (body && body.endPosition.row + 1 > endLine) {
463
+ endLine = body.endPosition.row + 1;
464
+ }
465
+ }
415
466
  const newNode = {
416
467
  id,
417
468
  kind,
@@ -420,7 +471,7 @@ class TreeSitterExtractor {
420
471
  filePath: this.filePath,
421
472
  language: this.language,
422
473
  startLine: node.startPosition.row + 1,
423
- endLine: node.endPosition.row + 1,
474
+ endLine,
424
475
  startColumn: node.startPosition.column,
425
476
  endColumn: node.endPosition.column,
426
477
  updatedAt: Date.now(),
@@ -452,6 +503,32 @@ class TreeSitterExtractor {
452
503
  }
453
504
  return null;
454
505
  }
506
+ /**
507
+ * Find a `packageTypes` child under the root, create a `namespace` node
508
+ * for it, and return its id so the caller can scope top-level
509
+ * declarations underneath. Returns null when no package header is
510
+ * present (script files, .kts without a package).
511
+ */
512
+ extractFilePackage(rootNode) {
513
+ const types = this.extractor?.packageTypes;
514
+ if (!types || types.length === 0 || !this.extractor?.extractPackage)
515
+ return null;
516
+ let pkgNode = null;
517
+ for (let i = 0; i < rootNode.namedChildCount; i++) {
518
+ const child = rootNode.namedChild(i);
519
+ if (child && types.includes(child.type)) {
520
+ pkgNode = child;
521
+ break;
522
+ }
523
+ }
524
+ if (!pkgNode)
525
+ return null;
526
+ const pkgName = this.extractor.extractPackage(pkgNode, this.source);
527
+ if (!pkgName)
528
+ return null;
529
+ const ns = this.createNode('namespace', pkgName, pkgNode);
530
+ return ns?.id ?? null;
531
+ }
455
532
  /**
456
533
  * Build qualified name from node stack
457
534
  */
@@ -510,7 +587,7 @@ class TreeSitterExtractor {
510
587
  /**
511
588
  * Extract a function
512
589
  */
513
- extractFunction(node) {
590
+ extractFunction(node, nameOverride) {
514
591
  if (!this.extractor)
515
592
  return;
516
593
  // If the language provides getReceiverType and this function has a receiver
@@ -519,12 +596,17 @@ class TreeSitterExtractor {
519
596
  this.extractMethod(node);
520
597
  return;
521
598
  }
522
- let name = extractName(node, this.source, this.extractor);
599
+ // nameOverride is supplied only for explicitly-named anonymous functions the
600
+ // caller resolved itself (e.g. arrow values of exported-const object members
601
+ // — SvelteKit actions). Inline-object arrows reached by the general walker
602
+ // get no override, so they still fall through to the <anonymous> skip below.
603
+ let name = nameOverride ?? extractName(node, this.source, this.extractor);
523
604
  // For arrow functions and function expressions assigned to variables,
524
605
  // resolve the name from the parent variable_declarator.
525
606
  // e.g. `export const useAuth = () => { ... }` — the arrow_function node
526
607
  // has no `name` field; the name lives on the variable_declarator.
527
- if (name === '<anonymous>' &&
608
+ if (!nameOverride &&
609
+ name === '<anonymous>' &&
528
610
  (node.type === 'arrow_function' || node.type === 'function_expression')) {
529
611
  const parent = node.parent;
530
612
  if (parent?.type === 'variable_declarator') {
@@ -534,8 +616,19 @@ class TreeSitterExtractor {
534
616
  }
535
617
  }
536
618
  }
537
- if (name === '<anonymous>')
538
- return; // Skip anonymous functions
619
+ if (name === '<anonymous>') {
620
+ // Don't emit a node for the anonymous wrapper itself, but still visit its
621
+ // body: AMD/RequireJS and CommonJS module wrappers (`define([], function(){…})`,
622
+ // `(function(){…})()`) hold named inner functions and calls that would
623
+ // otherwise be lost — the dispatcher set skipChildren, so nothing else
624
+ // descends into this subtree. (#528)
625
+ const body = this.extractor.resolveBody?.(node, this.extractor.bodyField)
626
+ ?? (0, tree_sitter_helpers_1.getChildByField)(node, this.extractor.bodyField);
627
+ if (body) {
628
+ this.visitFunctionBody(body, '');
629
+ }
630
+ return;
631
+ }
539
632
  // Check for misparse artifacts (e.g. C++ macros causing "namespace detail" functions)
540
633
  // Skip the node but still visit the body for calls and structural nodes
541
634
  if (this.extractor.isMisparsedFunction?.(name, node)) {
@@ -630,6 +723,11 @@ class TreeSitterExtractor {
630
723
  // in inline objects). These are ephemeral and create noise (e.g., Svelte context
631
724
  // objects: `ctx.set({ get view() { ... } })`).
632
725
  if (node.parent?.type === 'object' || node.parent?.type === 'object_expression') {
726
+ const body = this.extractor.resolveBody?.(node, this.extractor.bodyField)
727
+ ?? (0, tree_sitter_helpers_1.getChildByField)(node, this.extractor.bodyField);
728
+ if (body) {
729
+ this.visitFunctionBody(body, '');
730
+ }
633
731
  return;
634
732
  }
635
733
  // Not inside a class-like node and no receiver type, treat as function
@@ -831,12 +929,13 @@ class TreeSitterExtractor {
831
929
  const docstring = (0, tree_sitter_helpers_1.getPrecedingDocstring)(node, this.source);
832
930
  const visibility = this.extractor.getVisibility?.(node);
833
931
  const isStatic = this.extractor.isStatic?.(node) ?? false;
834
- // Property name is a direct identifier child
835
- const nameNode = (0, tree_sitter_helpers_1.getChildByField)(node, 'name')
836
- || node.namedChildren.find(c => c.type === 'identifier');
837
- if (!nameNode)
932
+ const hookName = this.extractor.extractPropertyName?.(node, this.source);
933
+ const nameNode = hookName
934
+ ? null
935
+ : (0, tree_sitter_helpers_1.getChildByField)(node, 'name') || node.namedChildren.find(c => c.type === 'identifier');
936
+ const name = hookName ?? (nameNode ? (0, tree_sitter_helpers_1.getNodeText)(nameNode, this.source) : null);
937
+ if (!name)
838
938
  return;
839
- const name = (0, tree_sitter_helpers_1.getNodeText)(nameNode, this.source);
840
939
  // Get property type from the type child (first named child that isn't modifier or identifier)
841
940
  const typeNode = node.namedChildren.find(c => c.type !== 'modifier' && c.type !== 'modifiers'
842
941
  && c.type !== 'identifier' && c.type !== 'accessor_list'
@@ -853,6 +952,10 @@ class TreeSitterExtractor {
853
952
  // decorator->target relationship for class properties too.
854
953
  if (propNode) {
855
954
  this.extractDecoratorsFor(node, propNode.id);
955
+ // Emit `references` edges from the property to types named in its
956
+ // type annotation (#381). The generic walker handles TS-style
957
+ // `type_annotation` children; the C# branch walks the `type` field.
958
+ this.extractTypeAnnotations(node, propNode.id);
856
959
  }
857
960
  }
858
961
  /**
@@ -925,8 +1028,15 @@ class TreeSitterExtractor {
925
1028
  });
926
1029
  // Java/Kotlin annotations / TS field decorators sit on the
927
1030
  // outer field_declaration, not on the individual declarator.
928
- if (fieldNode)
1031
+ if (fieldNode) {
929
1032
  this.extractDecoratorsFor(node, fieldNode.id);
1033
+ // Same as properties: emit `references` to the field's annotated
1034
+ // type. The outer `field_declaration` is the right scope to
1035
+ // search from — C# carries the `type` inside `variable_declaration`
1036
+ // and the language-aware path in `extractTypeAnnotations` descends
1037
+ // into that wrapper (#381).
1038
+ this.extractTypeAnnotations(node, fieldNode.id);
1039
+ }
930
1040
  }
931
1041
  }
932
1042
  else {
@@ -943,6 +1053,114 @@ class TreeSitterExtractor {
943
1053
  }
944
1054
  }
945
1055
  }
1056
+ /**
1057
+ * Extract function-valued properties of an object literal as named function
1058
+ * nodes (named by their property key). Shared by the two object-of-functions
1059
+ * shapes in extractVariable: the object as a direct const value, and the
1060
+ * object returned by a store-initializer call. Handles both `key: () => {}` /
1061
+ * `key: function() {}` pairs and method shorthand `key() {}`.
1062
+ */
1063
+ extractObjectLiteralFunctions(obj) {
1064
+ for (let i = 0; i < obj.namedChildCount; i++) {
1065
+ const member = obj.namedChild(i);
1066
+ if (!member)
1067
+ continue;
1068
+ if (member.type === 'pair') {
1069
+ const key = (0, tree_sitter_helpers_1.getChildByField)(member, 'key');
1070
+ const value = (0, tree_sitter_helpers_1.getChildByField)(member, 'value');
1071
+ if (key && value && (value.type === 'arrow_function' || value.type === 'function_expression')) {
1072
+ this.extractFunction(value, this.objectKeyName(key));
1073
+ }
1074
+ }
1075
+ else if (member.type === 'method_definition') {
1076
+ // Method shorthand: `{ fetchUser() {...} }`. extractMethod deliberately
1077
+ // skips object-literal methods, so route through extractFunction with an
1078
+ // explicit name (method_definition exposes a `body` field, so resolveBody
1079
+ // falls through to it and the node spans the full method).
1080
+ const key = (0, tree_sitter_helpers_1.getChildByField)(member, 'name');
1081
+ if (key)
1082
+ this.extractFunction(member, this.objectKeyName(key));
1083
+ }
1084
+ }
1085
+ }
1086
+ /** Property-key text with surrounding quotes stripped (`'foo'` → `foo`). */
1087
+ objectKeyName(key) {
1088
+ return (0, tree_sitter_helpers_1.getNodeText)(key, this.source).replace(/^['"`]|['"`]$/g, '');
1089
+ }
1090
+ /**
1091
+ * Given a `call_expression` initializer (`create((set, get) => ({...}))`),
1092
+ * find the object literal RETURNED by a function argument — descending through
1093
+ * nested call_expression arguments so middleware wrappers are unwrapped
1094
+ * (`create(persist((set, get) => ({...}), {...}))`, devtools, immer,
1095
+ * subscribeWithSelector). Returns null when no such object is found — the
1096
+ * common case for ordinary call initializers — so this stays cheap and silent
1097
+ * rather than guessing. Keyed purely on AST shape; no library names.
1098
+ */
1099
+ findInitializerReturnedObject(callNode, depth = 0) {
1100
+ if (depth > 4)
1101
+ return null;
1102
+ const args = (0, tree_sitter_helpers_1.getChildByField)(callNode, 'arguments');
1103
+ if (!args)
1104
+ return null;
1105
+ for (let i = 0; i < args.namedChildCount; i++) {
1106
+ const arg = args.namedChild(i);
1107
+ if (!arg)
1108
+ continue;
1109
+ if (arg.type === 'arrow_function' || arg.type === 'function_expression') {
1110
+ const obj = this.functionReturnedObject(arg);
1111
+ if (obj)
1112
+ return obj;
1113
+ }
1114
+ else if (arg.type === 'call_expression') {
1115
+ const obj = this.findInitializerReturnedObject(arg, depth + 1);
1116
+ if (obj)
1117
+ return obj;
1118
+ }
1119
+ }
1120
+ return null;
1121
+ }
1122
+ /**
1123
+ * The object literal a function expression returns — either the `=> ({...})`
1124
+ * arrow form (a parenthesized_expression wrapping an object) or a
1125
+ * `=> { return {...} }` block. Returns null for any other body shape.
1126
+ */
1127
+ functionReturnedObject(fnNode) {
1128
+ const body = (0, tree_sitter_helpers_1.getChildByField)(fnNode, 'body');
1129
+ if (!body)
1130
+ return null;
1131
+ const asObject = (n) => {
1132
+ if (!n)
1133
+ return null;
1134
+ if (n.type === 'object' || n.type === 'object_expression')
1135
+ return n;
1136
+ if (n.type === 'parenthesized_expression') {
1137
+ for (let i = 0; i < n.namedChildCount; i++) {
1138
+ const inner = asObject(n.namedChild(i));
1139
+ if (inner)
1140
+ return inner;
1141
+ }
1142
+ }
1143
+ return null;
1144
+ };
1145
+ // `(set, get) => ({...})` — body is the (parenthesized) object directly.
1146
+ const direct = asObject(body);
1147
+ if (direct)
1148
+ return direct;
1149
+ // `(set, get) => { return {...} }` — scan top-level return statements.
1150
+ if (body.type === 'statement_block') {
1151
+ for (let i = 0; i < body.namedChildCount; i++) {
1152
+ const stmt = body.namedChild(i);
1153
+ if (stmt?.type !== 'return_statement')
1154
+ continue;
1155
+ for (let j = 0; j < stmt.namedChildCount; j++) {
1156
+ const obj = asObject(stmt.namedChild(j));
1157
+ if (obj)
1158
+ return obj;
1159
+ }
1160
+ }
1161
+ }
1162
+ return null;
1163
+ }
946
1164
  /**
947
1165
  * Extract a variable declaration (const, let, var, etc.)
948
1166
  *
@@ -994,6 +1212,42 @@ class TreeSitterExtractor {
994
1212
  if (varNode) {
995
1213
  this.extractVariableTypeAnnotation(child, varNode.id);
996
1214
  }
1215
+ // Exported const object-of-functions — extract each function-valued
1216
+ // property as a function named by its key + walk its body so its
1217
+ // calls are captured. Two shapes, both keyed on AST shape (not on any
1218
+ // library name):
1219
+ // `export const actions = { default: async () => {} }` — object is
1220
+ // the DIRECT value (SvelteKit form actions / handler maps / route
1221
+ // tables).
1222
+ // `export const useStore = create((set, get) => ({ fetchUser:
1223
+ // async () => {} }))` — object is RETURNED by an initializer call,
1224
+ // possibly through middleware wrappers (persist/devtools/immer).
1225
+ // Covers Zustand/Redux/Pinia/MobX stores generically. Without
1226
+ // this, store actions exist only as object-literal properties —
1227
+ // never nodes — so `node`/`callers` on `fetchUser` return "not
1228
+ // found" and the agent Reads the store to reconstruct the flow.
1229
+ // Scoped to EXPORTED consts to exclude inline-object noise
1230
+ // (`ctx.set({...})`) the object-method skip deliberately avoids.
1231
+ const objectOfFns = valueNode && (valueNode.type === 'object' || valueNode.type === 'object_expression')
1232
+ ? valueNode
1233
+ : valueNode?.type === 'call_expression'
1234
+ ? this.findInitializerReturnedObject(valueNode)
1235
+ : null;
1236
+ const extractObjectMethods = isExported && !!objectOfFns;
1237
+ // Visit the initializer body for calls — EXCEPT object literals (their
1238
+ // function-valued properties are extracted below) and the store-factory
1239
+ // call whose returned object we extract method-by-method below (walking
1240
+ // the whole call would re-visit those method arrows and mis-attribute
1241
+ // their inner calls to the file/module scope).
1242
+ if (valueNode &&
1243
+ valueNode.type !== 'object' &&
1244
+ valueNode.type !== 'object_expression' &&
1245
+ !(extractObjectMethods && valueNode.type === 'call_expression')) {
1246
+ this.visitFunctionBody(valueNode, '');
1247
+ }
1248
+ if (extractObjectMethods && objectOfFns) {
1249
+ this.extractObjectLiteralFunctions(objectOfFns);
1250
+ }
997
1251
  }
998
1252
  }
999
1253
  }
@@ -1180,8 +1434,87 @@ class TreeSitterExtractor {
1180
1434
  const value = (0, tree_sitter_helpers_1.getChildByField)(node, 'value');
1181
1435
  if (value) {
1182
1436
  this.extractTypeRefsFromSubtree(value, typeAliasNode.id);
1437
+ // `type X = { foo: T; bar(): T }` — make the members first-class
1438
+ // property/method nodes under the type alias so `recorder.stop()`
1439
+ // can attach the call edge to `RecorderHandle.stop` instead of
1440
+ // an unrelated class method picked by path-proximity (#359).
1441
+ if (this.language === 'typescript' || this.language === 'tsx') {
1442
+ this.extractTsTypeAliasMembers(value, typeAliasNode);
1443
+ }
1444
+ }
1445
+ }
1446
+ return false;
1447
+ }
1448
+ /**
1449
+ * Surface the members of a TypeScript `type X = { ... }` (or intersection
1450
+ * thereof) as `property` / `method` nodes under the type-alias node. Only
1451
+ * walks the immediate object_type / intersection operands so anonymous
1452
+ * nested object types inside generic arguments (`Promise<{ ok: true }>`)
1453
+ * don't produce phantom members.
1454
+ */
1455
+ extractTsTypeAliasMembers(value, typeAliasNode) {
1456
+ const objectTypes = [];
1457
+ if (value.type === 'object_type') {
1458
+ objectTypes.push(value);
1459
+ }
1460
+ else if (value.type === 'intersection_type') {
1461
+ for (let i = 0; i < value.namedChildCount; i++) {
1462
+ const op = value.namedChild(i);
1463
+ if (op && op.type === 'object_type')
1464
+ objectTypes.push(op);
1465
+ }
1466
+ }
1467
+ else {
1468
+ return;
1469
+ }
1470
+ this.nodeStack.push(typeAliasNode.id);
1471
+ for (const objType of objectTypes) {
1472
+ for (let i = 0; i < objType.namedChildCount; i++) {
1473
+ const child = objType.namedChild(i);
1474
+ if (!child)
1475
+ continue;
1476
+ if (child.type !== 'property_signature' && child.type !== 'method_signature')
1477
+ continue;
1478
+ const nameNode = (0, tree_sitter_helpers_1.getChildByField)(child, 'name');
1479
+ const memberName = nameNode ? (0, tree_sitter_helpers_1.getNodeText)(nameNode, this.source) : '';
1480
+ if (!memberName)
1481
+ continue;
1482
+ // `foo: () => T` and `foo(): T` are functionally a method on the
1483
+ // type contract. Treat the property_signature with a function-typed
1484
+ // annotation as a method too so call sites can resolve to it.
1485
+ const memberKind = child.type === 'method_signature'
1486
+ ? 'method'
1487
+ : this.isTsFunctionTypedProperty(child) ? 'method' : 'property';
1488
+ const docstring = (0, tree_sitter_helpers_1.getPrecedingDocstring)(child, this.source);
1489
+ const signature = (0, tree_sitter_helpers_1.getNodeText)(child, this.source);
1490
+ this.createNode(memberKind, memberName, child, {
1491
+ docstring,
1492
+ signature,
1493
+ qualifiedName: `${typeAliasNode.name}::${memberName}`,
1494
+ });
1495
+ // Emit `references` edges from the type alias to types named in the
1496
+ // member's signature, matching the interface-member behavior added in
1497
+ // #432. We attach refs to the type-alias parent (consistent with
1498
+ // interface property_signature treatment).
1499
+ this.extractTypeAnnotations(child, typeAliasNode.id);
1183
1500
  }
1184
1501
  }
1502
+ this.nodeStack.pop();
1503
+ }
1504
+ /**
1505
+ * `foo: () => T` → property_signature whose type_annotation contains a
1506
+ * `function_type`. Treat that as a method-shaped contract member, since
1507
+ * the call site `obj.foo()` has identical semantics to `bar(): T`.
1508
+ */
1509
+ isTsFunctionTypedProperty(propertySignature) {
1510
+ const typeAnno = (0, tree_sitter_helpers_1.getChildByField)(propertySignature, 'type');
1511
+ if (!typeAnno)
1512
+ return false;
1513
+ for (let i = 0; i < typeAnno.namedChildCount; i++) {
1514
+ const inner = typeAnno.namedChild(i);
1515
+ if (inner && inner.type === 'function_type')
1516
+ return true;
1517
+ }
1185
1518
  return false;
1186
1519
  }
1187
1520
  // extractExportedVariables removed — the walker now descends into
@@ -1332,7 +1665,25 @@ class TreeSitterExtractor {
1332
1665
  if (nameField && objectField && (node.type === 'method_invocation' || node.type === 'member_call_expression' || node.type === 'scoped_call_expression')) {
1333
1666
  // Method call with explicit receiver: receiver.method() / $receiver->method() / ClassName::method()
1334
1667
  const methodName = (0, tree_sitter_helpers_1.getNodeText)(nameField, this.source);
1335
- let receiverName = (0, tree_sitter_helpers_1.getNodeText)(objectField, this.source);
1668
+ // Java `this.userbo.toLogin2()` parses as method_invocation(object=field_access(this, userbo)).
1669
+ // Without unwrapping, receiverName is `this.userbo` and the name-matcher's
1670
+ // single-dot receiver regex fails. Pull out the immediate field after `this.`
1671
+ // so the receiver is the field name (`userbo`), which the resolver can then
1672
+ // look up in the enclosing class's field declarations.
1673
+ let receiverName;
1674
+ if (objectField.type === 'field_access') {
1675
+ const inner = (0, tree_sitter_helpers_1.getChildByField)(objectField, 'object');
1676
+ const fld = (0, tree_sitter_helpers_1.getChildByField)(objectField, 'field');
1677
+ if (inner && fld && (inner.type === 'this' || inner.type === 'this_expression')) {
1678
+ receiverName = (0, tree_sitter_helpers_1.getNodeText)(fld, this.source);
1679
+ }
1680
+ else {
1681
+ receiverName = (0, tree_sitter_helpers_1.getNodeText)(objectField, this.source);
1682
+ }
1683
+ }
1684
+ else {
1685
+ receiverName = (0, tree_sitter_helpers_1.getNodeText)(objectField, this.source);
1686
+ }
1336
1687
  // Strip PHP $ prefix from variable names
1337
1688
  receiverName = receiverName.replace(/^\$/, '');
1338
1689
  if (methodName) {
@@ -1346,13 +1697,51 @@ class TreeSitterExtractor {
1346
1697
  }
1347
1698
  }
1348
1699
  }
1700
+ else if (node.type === 'message_expression') {
1701
+ // ObjC message expressions emit one `method` field child per selector
1702
+ // keyword: `[obj a:1 b:2 c:3]` has three `method=identifier` siblings.
1703
+ // Joining them with `:` reconstructs the full selector and matches the
1704
+ // multi-part selector names produced by the ObjC method_definition
1705
+ // extractor (`extractObjcMethodName` in languages/objc.ts). Without this
1706
+ // join, multi-keyword call sites only emitted the first keyword and never
1707
+ // resolved to their target methods (e.g. `GET:parameters:headers:...` had
1708
+ // zero callers despite obviously being called).
1709
+ const methodKeywords = [];
1710
+ for (let i = 0; i < node.namedChildCount; i++) {
1711
+ if (node.fieldNameForNamedChild(i) === 'method') {
1712
+ const kw = node.namedChild(i);
1713
+ if (kw)
1714
+ methodKeywords.push((0, tree_sitter_helpers_1.getNodeText)(kw, this.source));
1715
+ }
1716
+ }
1717
+ if (methodKeywords.length > 0) {
1718
+ const methodName = methodKeywords.length === 1
1719
+ ? methodKeywords[0]
1720
+ : methodKeywords.map((k) => `${k}:`).join('');
1721
+ const receiverField = (0, tree_sitter_helpers_1.getChildByField)(node, 'receiver');
1722
+ const SKIP_RECEIVERS = new Set(['self', 'super']);
1723
+ if (receiverField && receiverField.type !== 'message_expression') {
1724
+ const receiverName = (0, tree_sitter_helpers_1.getNodeText)(receiverField, this.source);
1725
+ if (receiverName && !SKIP_RECEIVERS.has(receiverName)) {
1726
+ calleeName = `${receiverName}.${methodName}`;
1727
+ }
1728
+ else {
1729
+ calleeName = methodName;
1730
+ }
1731
+ }
1732
+ else {
1733
+ calleeName = methodName;
1734
+ }
1735
+ }
1736
+ }
1349
1737
  else {
1350
1738
  const func = (0, tree_sitter_helpers_1.getChildByField)(node, 'function') || node.namedChild(0);
1351
1739
  if (func) {
1352
- if (func.type === 'member_expression' || func.type === 'attribute' || func.type === 'selector_expression' || func.type === 'navigation_expression') {
1740
+ if (func.type === 'member_expression' || func.type === 'attribute' || func.type === 'selector_expression' || func.type === 'navigation_expression' || func.type === 'field_expression') {
1353
1741
  // Method call: obj.method() or obj.field.method()
1354
1742
  // Go uses selector_expression with 'field', JS/TS uses member_expression with 'property'
1355
1743
  // Kotlin uses navigation_expression with navigation_suffix > simple_identifier
1744
+ // C/C++ use field_expression for both `obj.method()` and `ptr->method()`
1356
1745
  let property = (0, tree_sitter_helpers_1.getChildByField)(func, 'property') || (0, tree_sitter_helpers_1.getChildByField)(func, 'field');
1357
1746
  if (!property) {
1358
1747
  const child1 = func.namedChild(1);
@@ -1370,9 +1759,12 @@ class TreeSitterExtractor {
1370
1759
  // This helps the resolver distinguish method calls from bare function calls
1371
1760
  // (e.g., Python's console.print() vs builtin print())
1372
1761
  // Skip self/this/cls as they don't aid resolution
1373
- const receiver = (0, tree_sitter_helpers_1.getChildByField)(func, 'object') || (0, tree_sitter_helpers_1.getChildByField)(func, 'operand') || func.namedChild(0);
1762
+ const receiver = (0, tree_sitter_helpers_1.getChildByField)(func, 'object') ||
1763
+ (0, tree_sitter_helpers_1.getChildByField)(func, 'operand') ||
1764
+ (0, tree_sitter_helpers_1.getChildByField)(func, 'argument') ||
1765
+ func.namedChild(0);
1374
1766
  const SKIP_RECEIVERS = new Set(['self', 'this', 'cls', 'super']);
1375
- if (receiver && (receiver.type === 'identifier' || receiver.type === 'simple_identifier')) {
1767
+ if (receiver && (receiver.type === 'identifier' || receiver.type === 'simple_identifier' || receiver.type === 'field_identifier')) {
1376
1768
  const receiverName = (0, tree_sitter_helpers_1.getNodeText)(receiver, this.source);
1377
1769
  if (!SKIP_RECEIVERS.has(receiverName)) {
1378
1770
  calleeName = `${receiverName}.${methodName}`;
@@ -1453,6 +1845,76 @@ class TreeSitterExtractor {
1453
1845
  });
1454
1846
  }
1455
1847
  }
1848
+ /**
1849
+ * Find a `class_body` child of an `object_creation_expression` — the
1850
+ * marker for an anonymous class (`new T() { ... }`). Returns the body
1851
+ * node so the caller can walk it as the anon class's members.
1852
+ */
1853
+ findAnonymousClassBody(node) {
1854
+ for (let i = 0; i < node.namedChildCount; i++) {
1855
+ const child = node.namedChild(i);
1856
+ // Java: `class_body`. C# uses the same node kind.
1857
+ if (child && (child.type === 'class_body' || child.type === 'declaration_list')) {
1858
+ return child;
1859
+ }
1860
+ }
1861
+ return null;
1862
+ }
1863
+ /**
1864
+ * Extract a Java/C# anonymous class — `new T() { ...members }`. Emits a
1865
+ * `class` node named `<T$anon@line>`, an `extends` reference to T (so
1866
+ * Phase 5.5 interface-impl can bridge), and walks the body so its
1867
+ * `method_declaration` members become method nodes under the anon class.
1868
+ *
1869
+ * Why this matters: without anon-class extraction, the overrides inside
1870
+ * a lambda-returned `new T() { @Override int foo(){...} }` are not nodes,
1871
+ * so a call through T.foo (the abstract parent method) has no static
1872
+ * target — the agent has to Read the file to find the implementation.
1873
+ */
1874
+ extractAnonymousClass(node, body) {
1875
+ if (!this.extractor)
1876
+ return;
1877
+ // The instantiated type sits in the same field/position that
1878
+ // extractInstantiation reads from. Use the same lookup so the anon
1879
+ // class's `extends` target matches the `instantiates` edge.
1880
+ const typeNode = (0, tree_sitter_helpers_1.getChildByField)(node, 'constructor') ||
1881
+ (0, tree_sitter_helpers_1.getChildByField)(node, 'type') ||
1882
+ (0, tree_sitter_helpers_1.getChildByField)(node, 'name') ||
1883
+ node.namedChild(0);
1884
+ let typeName = typeNode ? (0, tree_sitter_helpers_1.getNodeText)(typeNode, this.source) : 'Object';
1885
+ const ltIdx = typeName.indexOf('<');
1886
+ if (ltIdx > 0)
1887
+ typeName = typeName.slice(0, ltIdx);
1888
+ const lastDot = Math.max(typeName.lastIndexOf('.'), typeName.lastIndexOf('::'));
1889
+ if (lastDot >= 0)
1890
+ typeName = typeName.slice(lastDot + 1).replace(/^[:.]/, '');
1891
+ typeName = typeName.trim() || 'Object';
1892
+ const anonName = `<${typeName}$anon@${node.startPosition.row + 1}>`;
1893
+ const classNode = this.createNode('class', anonName, node, {});
1894
+ if (!classNode)
1895
+ return;
1896
+ // The anonymous class implicitly extends/implements the named type.
1897
+ // We can't tell at extraction time whether T is a class or an interface,
1898
+ // so emit `extends`. Resolution will still bind T to whatever it is, and
1899
+ // Phase 5.5 (which already handles both `extends` and `implements`) will
1900
+ // bridge T's methods to the override names found in the anon body.
1901
+ this.unresolvedReferences.push({
1902
+ fromNodeId: classNode.id,
1903
+ referenceName: typeName,
1904
+ referenceKind: 'extends',
1905
+ line: typeNode?.startPosition.row ?? node.startPosition.row,
1906
+ column: typeNode?.startPosition.column ?? node.startPosition.column,
1907
+ });
1908
+ // Walk the body's children so method_declaration nodes inside become
1909
+ // method nodes scoped to the anon class.
1910
+ this.nodeStack.push(classNode.id);
1911
+ for (let i = 0; i < body.namedChildCount; i++) {
1912
+ const child = body.namedChild(i);
1913
+ if (child)
1914
+ this.visitNode(child);
1915
+ }
1916
+ this.nodeStack.pop();
1917
+ }
1456
1918
  /**
1457
1919
  * Scan `declNode` and its preceding siblings (within the parent's
1458
1920
  * named children) for decorator nodes, emitting a `decorates`
@@ -1583,6 +2045,14 @@ class TreeSitterExtractor {
1583
2045
  // about `call_expression`, so constructor invocations
1584
2046
  // produced no graph edges at all.
1585
2047
  this.extractInstantiation(node);
2048
+ // Anonymous class with body: `new T() { ... }` (Java/C#). Extract as
2049
+ // a class so interface-impl synthesis (Phase 5.5) can bridge T's
2050
+ // methods to the overrides — same rationale as in visitNode.
2051
+ const anonBody = this.findAnonymousClassBody(node);
2052
+ if (anonBody) {
2053
+ this.extractAnonymousClass(node, anonBody);
2054
+ return;
2055
+ }
1586
2056
  }
1587
2057
  else if (this.extractor.extractBareCall) {
1588
2058
  const calleeName = this.extractor.extractBareCall(node, this.source);
@@ -1599,6 +2069,20 @@ class TreeSitterExtractor {
1599
2069
  }
1600
2070
  }
1601
2071
  }
2072
+ // Nested NAMED functions inside a body — function declarations and named
2073
+ // function expressions like `.on('mount', function onmount(){})` — become
2074
+ // their own nodes so the graph can link to them (callback handlers, local
2075
+ // helpers). Anonymous arrows/expressions fall through to the default
2076
+ // recursion below, keeping their inner calls attributed to the enclosing
2077
+ // function: this bounds the new nodes to NAMED functions only (no explosion,
2078
+ // no lost edges). extractFunction walks the nested body itself, so we return.
2079
+ if (this.extractor.functionTypes.includes(nodeType)) {
2080
+ const nestedName = extractName(node, this.source, this.extractor);
2081
+ if (nestedName && nestedName !== '<anonymous>') {
2082
+ this.extractFunction(node);
2083
+ return;
2084
+ }
2085
+ }
1602
2086
  // Extract structural nodes found inside function bodies.
1603
2087
  // Each extract method visits its own children, so we return after extracting.
1604
2088
  if (this.extractor.classTypes.includes(nodeType)) {
@@ -1640,6 +2124,42 @@ class TreeSitterExtractor {
1640
2124
  * Extract inheritance relationships
1641
2125
  */
1642
2126
  extractInheritance(node, classId) {
2127
+ // Objective-C @interface MyClass : NSObject <ProtoA, ProtoB>
2128
+ if (node.type === 'class_interface') {
2129
+ const superclass = (0, tree_sitter_helpers_1.getChildByField)(node, 'superclass');
2130
+ if (superclass) {
2131
+ const name = (0, tree_sitter_helpers_1.getNodeText)(superclass, this.source);
2132
+ this.unresolvedReferences.push({
2133
+ fromNodeId: classId,
2134
+ referenceName: name,
2135
+ referenceKind: 'extends',
2136
+ line: superclass.startPosition.row + 1,
2137
+ column: superclass.startPosition.column,
2138
+ });
2139
+ }
2140
+ for (let j = 0; j < node.namedChildCount; j++) {
2141
+ const argList = node.namedChild(j);
2142
+ if (argList?.type !== 'parameterized_arguments')
2143
+ continue;
2144
+ for (let k = 0; k < argList.namedChildCount; k++) {
2145
+ const typeName = argList.namedChild(k);
2146
+ if (!typeName)
2147
+ continue;
2148
+ const typeId = typeName.namedChildren.find((c) => c.type === 'type_identifier' || c.type === 'identifier');
2149
+ if (!typeId)
2150
+ continue;
2151
+ const protocolName = (0, tree_sitter_helpers_1.getNodeText)(typeId, this.source);
2152
+ this.unresolvedReferences.push({
2153
+ fromNodeId: classId,
2154
+ referenceName: protocolName,
2155
+ referenceKind: 'implements',
2156
+ line: typeId.startPosition.row + 1,
2157
+ column: typeId.startPosition.column,
2158
+ });
2159
+ }
2160
+ }
2161
+ return;
2162
+ }
1643
2163
  // Look for extends/implements clauses
1644
2164
  for (let i = 0; i < node.namedChildCount; i++) {
1645
2165
  const child = node.namedChild(i);
@@ -1667,6 +2187,24 @@ class TreeSitterExtractor {
1667
2187
  }
1668
2188
  }
1669
2189
  }
2190
+ // C++ base classes: `class Derived : public Base, private Other` →
2191
+ // base_class_clause holds access specifiers + base type(s). Emit an extends
2192
+ // ref per base type (skip the public/private/protected keywords).
2193
+ if (child.type === 'base_class_clause') {
2194
+ for (const t of child.namedChildren) {
2195
+ if (t.type === 'type_identifier' ||
2196
+ t.type === 'qualified_identifier' ||
2197
+ t.type === 'template_type') {
2198
+ this.unresolvedReferences.push({
2199
+ fromNodeId: classId,
2200
+ referenceName: (0, tree_sitter_helpers_1.getNodeText)(t, this.source),
2201
+ referenceKind: 'extends',
2202
+ line: t.startPosition.row + 1,
2203
+ column: t.startPosition.column,
2204
+ });
2205
+ }
2206
+ }
2207
+ }
1670
2208
  if (child.type === 'implements_clause' ||
1671
2209
  child.type === 'class_interface_clause' ||
1672
2210
  child.type === 'super_interfaces' || // Java class implements
@@ -1937,6 +2475,16 @@ class TreeSitterExtractor {
1937
2475
  return;
1938
2476
  if (!this.TYPE_ANNOTATION_LANGUAGES.has(this.language))
1939
2477
  return;
2478
+ // C# tree-sitter doesn't produce `type_identifier` leaves — it uses
2479
+ // `identifier`, `predefined_type`, `qualified_name`, `generic_name`,
2480
+ // etc. — so the generic walker below emits zero references for it.
2481
+ // Dispatch to a C#-aware path that only walks type-position subtrees
2482
+ // (the `type` field of a parameter/method/property/field), so
2483
+ // parameter NAMES never accidentally surface as type refs (#381).
2484
+ if (this.language === 'csharp') {
2485
+ this.extractCsharpTypeRefs(node, nodeId);
2486
+ return;
2487
+ }
1940
2488
  // Extract parameter type annotations
1941
2489
  const params = (0, tree_sitter_helpers_1.getChildByField)(node, this.extractor.paramsField || 'parameters');
1942
2490
  if (params) {
@@ -1953,6 +2501,112 @@ class TreeSitterExtractor {
1953
2501
  this.extractTypeRefsFromSubtree(typeAnnotation, nodeId);
1954
2502
  }
1955
2503
  }
2504
+ /**
2505
+ * Extract C# type references from a node that owns a type position —
2506
+ * a method/constructor declaration, a property declaration, or a
2507
+ * field declaration (which wraps `variable_declaration → type`).
2508
+ *
2509
+ * Walks ONLY into known type fields, so parameter names like
2510
+ * `request` in `Build(UserDto request)` are never mis-emitted as
2511
+ * type references. Once inside a type subtree, `walkCsharpTypePosition`
2512
+ * recognizes C#'s actual type-leaf node kinds (`identifier`,
2513
+ * `qualified_name`, `generic_name`, `array_type`, `nullable_type`,
2514
+ * `tuple_type`, …) — none of which are `type_identifier`. Closes #381.
2515
+ */
2516
+ extractCsharpTypeRefs(node, nodeId) {
2517
+ // Return type / property type — the field is named `type`.
2518
+ const directType = (0, tree_sitter_helpers_1.getChildByField)(node, 'type');
2519
+ if (directType)
2520
+ this.walkCsharpTypePosition(directType, nodeId);
2521
+ // Field declarations wrap declarators in a `variable_declaration`
2522
+ // whose `type` field carries the type. The outer `field_declaration`
2523
+ // has no `type` field of its own, so the call above is a no-op here
2524
+ // and we descend one level.
2525
+ const varDecl = node.namedChildren.find((c) => c.type === 'variable_declaration');
2526
+ if (varDecl) {
2527
+ const vdType = (0, tree_sitter_helpers_1.getChildByField)(varDecl, 'type');
2528
+ if (vdType)
2529
+ this.walkCsharpTypePosition(vdType, nodeId);
2530
+ }
2531
+ // Method / constructor parameters. The field name on
2532
+ // `method_declaration` is `parameters`; it points at a
2533
+ // `parameter_list` whose `parameter` children each have their own
2534
+ // `type` field. Walking ONLY the type field skips parameter NAMES,
2535
+ // which would otherwise mis-emit as type references.
2536
+ const params = (0, tree_sitter_helpers_1.getChildByField)(node, 'parameters');
2537
+ if (params) {
2538
+ for (let i = 0; i < params.namedChildCount; i++) {
2539
+ const child = params.namedChild(i);
2540
+ if (!child || child.type !== 'parameter')
2541
+ continue;
2542
+ const paramType = (0, tree_sitter_helpers_1.getChildByField)(child, 'type');
2543
+ if (paramType)
2544
+ this.walkCsharpTypePosition(paramType, nodeId);
2545
+ }
2546
+ }
2547
+ }
2548
+ /**
2549
+ * Walk a C# subtree that is KNOWN to be in a type position
2550
+ * (return type, parameter type, property type, field type, generic
2551
+ * argument). Identifiers here are type names, not parameter names.
2552
+ */
2553
+ walkCsharpTypePosition(node, fromNodeId) {
2554
+ // `predefined_type` is int/string/bool/etc. — never a project ref.
2555
+ if (node.type === 'predefined_type')
2556
+ return;
2557
+ // Bare type name: `Foo` in `Foo bar`, or the `Foo` inside `List<Foo>`.
2558
+ if (node.type === 'identifier') {
2559
+ const name = (0, tree_sitter_helpers_1.getNodeText)(node, this.source);
2560
+ if (name && !this.BUILTIN_TYPES.has(name)) {
2561
+ this.unresolvedReferences.push({
2562
+ fromNodeId,
2563
+ referenceName: name,
2564
+ referenceKind: 'references',
2565
+ line: node.startPosition.row + 1,
2566
+ column: node.startPosition.column,
2567
+ });
2568
+ }
2569
+ return;
2570
+ }
2571
+ // `Namespace.Foo` → the rightmost identifier is the type. Emit the
2572
+ // full qualified name as the reference; the resolver can still match
2573
+ // on the trailing simple name when needed.
2574
+ if (node.type === 'qualified_name') {
2575
+ const text = (0, tree_sitter_helpers_1.getNodeText)(node, this.source);
2576
+ const last = text.split('.').pop() ?? text;
2577
+ if (last && !this.BUILTIN_TYPES.has(last)) {
2578
+ this.unresolvedReferences.push({
2579
+ fromNodeId,
2580
+ referenceName: last,
2581
+ referenceKind: 'references',
2582
+ line: node.startPosition.row + 1,
2583
+ column: node.startPosition.column,
2584
+ });
2585
+ }
2586
+ return;
2587
+ }
2588
+ // `(int Code, Foo Payload)` — tuple element has BOTH a `type` and a
2589
+ // `name` field; descending into all named children would mis-emit
2590
+ // the element name (`Code`, `Payload`) as a type ref. Walk only the
2591
+ // type field.
2592
+ if (node.type === 'tuple_element') {
2593
+ const t = (0, tree_sitter_helpers_1.getChildByField)(node, 'type');
2594
+ if (t)
2595
+ this.walkCsharpTypePosition(t, fromNodeId);
2596
+ return;
2597
+ }
2598
+ // Composite type nodes — recurse into named children. Covers
2599
+ // `generic_name` (head identifier + `type_argument_list`),
2600
+ // `nullable_type`, `array_type`, `pointer_type`, `tuple_type`,
2601
+ // `ref_type`, and any newer wrapping shapes the grammar adds.
2602
+ // Identifiers reached here are all type-positional (parameter/field
2603
+ // names are gated out before we descend).
2604
+ for (let i = 0; i < node.namedChildCount; i++) {
2605
+ const child = node.namedChild(i);
2606
+ if (child)
2607
+ this.walkCsharpTypePosition(child, fromNodeId);
2608
+ }
2609
+ }
1956
2610
  /**
1957
2611
  * Extract type references from a variable's type annotation.
1958
2612
  */
@@ -2389,10 +3043,17 @@ function extractFromSource(filePath, source, language, frameworkNames) {
2389
3043
  const extractor = new liquid_extractor_1.LiquidExtractor(filePath, source);
2390
3044
  result = extractor.extract();
2391
3045
  }
2392
- else if (detectedLanguage === 'yaml' || detectedLanguage === 'twig') {
2393
- // No symbol extraction file is tracked at the file-record level only.
2394
- // Framework extractors (e.g. Drupal routing resolver) run below and may
2395
- // add route nodes / references for yaml files such as *.routing.yml.
3046
+ else if (detectedLanguage === 'xml') {
3047
+ // Custom extractor for MyBatis mapper XML. Non-mapper XML returns just a
3048
+ // file node so the watcher tracks it without emitting symbols.
3049
+ const extractor = new mybatis_extractor_1.MyBatisExtractor(filePath, source);
3050
+ result = extractor.extract();
3051
+ }
3052
+ else if ((0, grammars_1.isFileLevelOnlyLanguage)(detectedLanguage)) {
3053
+ // No symbol extraction at this stage — files are tracked at the file-record
3054
+ // level only. Framework extractors (Drupal routing yml, Spring `@Value`
3055
+ // resolution against application.yml/application.properties) run later and
3056
+ // add per-file nodes/references when they apply.
2396
3057
  result = { nodes: [], edges: [], unresolvedReferences: [], errors: [], durationMs: 0 };
2397
3058
  }
2398
3059
  else if (detectedLanguage === 'pascal' &&