seer-mcp 0.1.0

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 (371) hide show
  1. package/.vscode/settings.json +3 -0
  2. package/LICENSE +176 -0
  3. package/README.md +272 -0
  4. package/README_dev.md +199 -0
  5. package/dist/bundle/ci.d.ts +47 -0
  6. package/dist/bundle/ci.d.ts.map +1 -0
  7. package/dist/bundle/ci.js +113 -0
  8. package/dist/bundle/ci.js.map +1 -0
  9. package/dist/bundle/contract.d.ts +111 -0
  10. package/dist/bundle/contract.d.ts.map +1 -0
  11. package/dist/bundle/contract.js +352 -0
  12. package/dist/bundle/contract.js.map +1 -0
  13. package/dist/bundle/export.d.ts +36 -0
  14. package/dist/bundle/export.d.ts.map +1 -0
  15. package/dist/bundle/export.js +152 -0
  16. package/dist/bundle/export.js.map +1 -0
  17. package/dist/bundle/external.d.ts +66 -0
  18. package/dist/bundle/external.d.ts.map +1 -0
  19. package/dist/bundle/external.js +238 -0
  20. package/dist/bundle/external.js.map +1 -0
  21. package/dist/bundle/format.d.ts +94 -0
  22. package/dist/bundle/format.d.ts.map +1 -0
  23. package/dist/bundle/format.js +42 -0
  24. package/dist/bundle/format.js.map +1 -0
  25. package/dist/bundle/import.d.ts +49 -0
  26. package/dist/bundle/import.d.ts.map +1 -0
  27. package/dist/bundle/import.js +116 -0
  28. package/dist/bundle/import.js.map +1 -0
  29. package/dist/cli/index.d.ts +3 -0
  30. package/dist/cli/index.d.ts.map +1 -0
  31. package/dist/cli/index.js +1402 -0
  32. package/dist/cli/index.js.map +1 -0
  33. package/dist/cli/init.d.ts +48 -0
  34. package/dist/cli/init.d.ts.map +1 -0
  35. package/dist/cli/init.js +284 -0
  36. package/dist/cli/init.js.map +1 -0
  37. package/dist/db/schema.d.ts +3 -0
  38. package/dist/db/schema.d.ts.map +1 -0
  39. package/dist/db/schema.js +616 -0
  40. package/dist/db/schema.js.map +1 -0
  41. package/dist/db/store.d.ts +1011 -0
  42. package/dist/db/store.d.ts.map +1 -0
  43. package/dist/db/store.js +3888 -0
  44. package/dist/db/store.js.map +1 -0
  45. package/dist/graph/pagerank.d.ts +9 -0
  46. package/dist/graph/pagerank.d.ts.map +1 -0
  47. package/dist/graph/pagerank.js +47 -0
  48. package/dist/graph/pagerank.js.map +1 -0
  49. package/dist/indexer/architecture.d.ts +72 -0
  50. package/dist/indexer/architecture.d.ts.map +1 -0
  51. package/dist/indexer/architecture.js +112 -0
  52. package/dist/indexer/architecture.js.map +1 -0
  53. package/dist/indexer/behavior.d.ts +75 -0
  54. package/dist/indexer/behavior.d.ts.map +1 -0
  55. package/dist/indexer/behavior.js +395 -0
  56. package/dist/indexer/behavior.js.map +1 -0
  57. package/dist/indexer/boundaries.d.ts +60 -0
  58. package/dist/indexer/boundaries.d.ts.map +1 -0
  59. package/dist/indexer/boundaries.js +366 -0
  60. package/dist/indexer/boundaries.js.map +1 -0
  61. package/dist/indexer/churn.d.ts +15 -0
  62. package/dist/indexer/churn.d.ts.map +1 -0
  63. package/dist/indexer/churn.js +49 -0
  64. package/dist/indexer/churn.js.map +1 -0
  65. package/dist/indexer/classify.d.ts +9 -0
  66. package/dist/indexer/classify.d.ts.map +1 -0
  67. package/dist/indexer/classify.js +90 -0
  68. package/dist/indexer/classify.js.map +1 -0
  69. package/dist/indexer/context.d.ts +176 -0
  70. package/dist/indexer/context.d.ts.map +1 -0
  71. package/dist/indexer/context.js +193 -0
  72. package/dist/indexer/context.js.map +1 -0
  73. package/dist/indexer/continuity.d.ts +67 -0
  74. package/dist/indexer/continuity.d.ts.map +1 -0
  75. package/dist/indexer/continuity.js +288 -0
  76. package/dist/indexer/continuity.js.map +1 -0
  77. package/dist/indexer/detectchanges.d.ts +32 -0
  78. package/dist/indexer/detectchanges.d.ts.map +1 -0
  79. package/dist/indexer/detectchanges.js +74 -0
  80. package/dist/indexer/detectchanges.js.map +1 -0
  81. package/dist/indexer/discovery.d.ts +37 -0
  82. package/dist/indexer/discovery.d.ts.map +1 -0
  83. package/dist/indexer/discovery.js +136 -0
  84. package/dist/indexer/discovery.js.map +1 -0
  85. package/dist/indexer/externaldeps.d.ts +18 -0
  86. package/dist/indexer/externaldeps.d.ts.map +1 -0
  87. package/dist/indexer/externaldeps.js +288 -0
  88. package/dist/indexer/externaldeps.js.map +1 -0
  89. package/dist/indexer/freshness.d.ts +48 -0
  90. package/dist/indexer/freshness.d.ts.map +1 -0
  91. package/dist/indexer/freshness.js +128 -0
  92. package/dist/indexer/freshness.js.map +1 -0
  93. package/dist/indexer/git.d.ts +144 -0
  94. package/dist/indexer/git.d.ts.map +1 -0
  95. package/dist/indexer/git.js +444 -0
  96. package/dist/indexer/git.js.map +1 -0
  97. package/dist/indexer/index.d.ts +145 -0
  98. package/dist/indexer/index.d.ts.map +1 -0
  99. package/dist/indexer/index.js +930 -0
  100. package/dist/indexer/index.js.map +1 -0
  101. package/dist/indexer/modules.d.ts +62 -0
  102. package/dist/indexer/modules.d.ts.map +1 -0
  103. package/dist/indexer/modules.js +293 -0
  104. package/dist/indexer/modules.js.map +1 -0
  105. package/dist/indexer/preflight.d.ts +154 -0
  106. package/dist/indexer/preflight.d.ts.map +1 -0
  107. package/dist/indexer/preflight.js +399 -0
  108. package/dist/indexer/preflight.js.map +1 -0
  109. package/dist/indexer/protoScanner.d.ts +34 -0
  110. package/dist/indexer/protoScanner.d.ts.map +1 -0
  111. package/dist/indexer/protoScanner.js +133 -0
  112. package/dist/indexer/protoScanner.js.map +1 -0
  113. package/dist/indexer/risk.d.ts +115 -0
  114. package/dist/indexer/risk.d.ts.map +1 -0
  115. package/dist/indexer/risk.js +194 -0
  116. package/dist/indexer/risk.js.map +1 -0
  117. package/dist/indexer/serviceHostScanner.d.ts +25 -0
  118. package/dist/indexer/serviceHostScanner.d.ts.map +1 -0
  119. package/dist/indexer/serviceHostScanner.js +95 -0
  120. package/dist/indexer/serviceHostScanner.js.map +1 -0
  121. package/dist/indexer/serviceLinks.d.ts +105 -0
  122. package/dist/indexer/serviceLinks.d.ts.map +1 -0
  123. package/dist/indexer/serviceLinks.js +509 -0
  124. package/dist/indexer/serviceLinks.js.map +1 -0
  125. package/dist/indexer/shapehash.d.ts +98 -0
  126. package/dist/indexer/shapehash.d.ts.map +1 -0
  127. package/dist/indexer/shapehash.js +354 -0
  128. package/dist/indexer/shapehash.js.map +1 -0
  129. package/dist/indexer/skeleton.d.ts +15 -0
  130. package/dist/indexer/skeleton.d.ts.map +1 -0
  131. package/dist/indexer/skeleton.js +136 -0
  132. package/dist/indexer/skeleton.js.map +1 -0
  133. package/dist/indexer/symbolhistory.d.ts +41 -0
  134. package/dist/indexer/symbolhistory.d.ts.map +1 -0
  135. package/dist/indexer/symbolhistory.js +124 -0
  136. package/dist/indexer/symbolhistory.js.map +1 -0
  137. package/dist/indexer/watcher.d.ts +68 -0
  138. package/dist/indexer/watcher.d.ts.map +1 -0
  139. package/dist/indexer/watcher.js +179 -0
  140. package/dist/indexer/watcher.js.map +1 -0
  141. package/dist/mcp/server.d.ts +80 -0
  142. package/dist/mcp/server.d.ts.map +1 -0
  143. package/dist/mcp/server.js +1610 -0
  144. package/dist/mcp/server.js.map +1 -0
  145. package/dist/parser/index.d.ts +8 -0
  146. package/dist/parser/index.d.ts.map +1 -0
  147. package/dist/parser/index.js +33 -0
  148. package/dist/parser/index.js.map +1 -0
  149. package/dist/parser/languages/cpp.d.ts +3 -0
  150. package/dist/parser/languages/cpp.d.ts.map +1 -0
  151. package/dist/parser/languages/cpp.js +350 -0
  152. package/dist/parser/languages/cpp.js.map +1 -0
  153. package/dist/parser/languages/csharp.d.ts +3 -0
  154. package/dist/parser/languages/csharp.d.ts.map +1 -0
  155. package/dist/parser/languages/csharp.js +239 -0
  156. package/dist/parser/languages/csharp.js.map +1 -0
  157. package/dist/parser/languages/go.d.ts +3 -0
  158. package/dist/parser/languages/go.d.ts.map +1 -0
  159. package/dist/parser/languages/go.js +259 -0
  160. package/dist/parser/languages/go.js.map +1 -0
  161. package/dist/parser/languages/java.d.ts +3 -0
  162. package/dist/parser/languages/java.d.ts.map +1 -0
  163. package/dist/parser/languages/java.js +391 -0
  164. package/dist/parser/languages/java.js.map +1 -0
  165. package/dist/parser/languages/python.d.ts +3 -0
  166. package/dist/parser/languages/python.d.ts.map +1 -0
  167. package/dist/parser/languages/python.js +396 -0
  168. package/dist/parser/languages/python.js.map +1 -0
  169. package/dist/parser/languages/rust.d.ts +3 -0
  170. package/dist/parser/languages/rust.d.ts.map +1 -0
  171. package/dist/parser/languages/rust.js +159 -0
  172. package/dist/parser/languages/rust.js.map +1 -0
  173. package/dist/parser/languages/typescript.d.ts +3 -0
  174. package/dist/parser/languages/typescript.d.ts.map +1 -0
  175. package/dist/parser/languages/typescript.js +1442 -0
  176. package/dist/parser/languages/typescript.js.map +1 -0
  177. package/dist/parser/parserContext.d.ts +77 -0
  178. package/dist/parser/parserContext.d.ts.map +1 -0
  179. package/dist/parser/parserContext.js +354 -0
  180. package/dist/parser/parserContext.js.map +1 -0
  181. package/dist/parser/walker.d.ts +81 -0
  182. package/dist/parser/walker.d.ts.map +1 -0
  183. package/dist/parser/walker.js +217 -0
  184. package/dist/parser/walker.js.map +1 -0
  185. package/dist/parser/worker.d.ts +66 -0
  186. package/dist/parser/worker.d.ts.map +1 -0
  187. package/dist/parser/worker.js +129 -0
  188. package/dist/parser/worker.js.map +1 -0
  189. package/dist/parser/workerpool.d.ts +107 -0
  190. package/dist/parser/workerpool.d.ts.map +1 -0
  191. package/dist/parser/workerpool.js +383 -0
  192. package/dist/parser/workerpool.js.map +1 -0
  193. package/dist/scip/format.d.ts +87 -0
  194. package/dist/scip/format.d.ts.map +1 -0
  195. package/dist/scip/format.js +31 -0
  196. package/dist/scip/format.js.map +1 -0
  197. package/dist/scip/import.d.ts +37 -0
  198. package/dist/scip/import.d.ts.map +1 -0
  199. package/dist/scip/import.js +180 -0
  200. package/dist/scip/import.js.map +1 -0
  201. package/dist/types.d.ts +392 -0
  202. package/dist/types.d.ts.map +1 -0
  203. package/dist/types.js +4 -0
  204. package/dist/types.js.map +1 -0
  205. package/docs/architecture.md +105 -0
  206. package/docs/benchmarks/methodology.md +134 -0
  207. package/docs/benchmarks/raw-results.md +71 -0
  208. package/docs/benchmarks.md +74 -0
  209. package/docs/cli.md +148 -0
  210. package/docs/examples/behavior-tests.md +70 -0
  211. package/docs/examples/change-history.md +85 -0
  212. package/docs/examples/pre-edit-context.md +81 -0
  213. package/docs/examples/service-links.md +88 -0
  214. package/docs/examples.md +80 -0
  215. package/docs/faq.md +70 -0
  216. package/docs/internals.md +104 -0
  217. package/docs/languages.md +70 -0
  218. package/docs/limits.md +52 -0
  219. package/docs/mcp.md +199 -0
  220. package/docs/quickstart.md +119 -0
  221. package/docs/testing.md +123 -0
  222. package/docs/tools.md +115 -0
  223. package/package.json +52 -0
  224. package/research-codebase.md +578 -0
  225. package/seer-cli-docs.md +326 -0
  226. package/seer-master-guide.md +246 -0
  227. package/src/bundle/ci.ts +141 -0
  228. package/src/bundle/contract.ts +387 -0
  229. package/src/bundle/export.ts +175 -0
  230. package/src/bundle/external.ts +285 -0
  231. package/src/bundle/format.ts +92 -0
  232. package/src/bundle/import.ts +157 -0
  233. package/src/cli/index.ts +1249 -0
  234. package/src/cli/init.ts +389 -0
  235. package/src/db/schema.ts +614 -0
  236. package/src/db/store.ts +4306 -0
  237. package/src/graph/pagerank.ts +53 -0
  238. package/src/indexer/architecture.ts +148 -0
  239. package/src/indexer/behavior.ts +466 -0
  240. package/src/indexer/boundaries.ts +374 -0
  241. package/src/indexer/churn.ts +58 -0
  242. package/src/indexer/classify.ts +96 -0
  243. package/src/indexer/context.ts +340 -0
  244. package/src/indexer/continuity.ts +322 -0
  245. package/src/indexer/detectchanges.ts +94 -0
  246. package/src/indexer/discovery.ts +176 -0
  247. package/src/indexer/externaldeps.ts +243 -0
  248. package/src/indexer/freshness.ts +166 -0
  249. package/src/indexer/git.ts +453 -0
  250. package/src/indexer/index.ts +1092 -0
  251. package/src/indexer/modules.ts +358 -0
  252. package/src/indexer/preflight.ts +548 -0
  253. package/src/indexer/protoScanner.ts +147 -0
  254. package/src/indexer/risk.ts +304 -0
  255. package/src/indexer/serviceHostScanner.ts +92 -0
  256. package/src/indexer/serviceLinks.ts +543 -0
  257. package/src/indexer/shapehash.ts +370 -0
  258. package/src/indexer/skeleton.ts +169 -0
  259. package/src/indexer/symbolhistory.ts +172 -0
  260. package/src/indexer/watcher.ts +206 -0
  261. package/src/mcp/server.ts +1659 -0
  262. package/src/parser/index.ts +37 -0
  263. package/src/parser/languages/cpp.ts +361 -0
  264. package/src/parser/languages/csharp.ts +235 -0
  265. package/src/parser/languages/go.ts +259 -0
  266. package/src/parser/languages/java.ts +382 -0
  267. package/src/parser/languages/python.ts +370 -0
  268. package/src/parser/languages/rust.ts +164 -0
  269. package/src/parser/languages/typescript.ts +1435 -0
  270. package/src/parser/parserContext.ts +392 -0
  271. package/src/parser/walker.ts +306 -0
  272. package/src/parser/worker.ts +181 -0
  273. package/src/parser/workerpool.ts +448 -0
  274. package/src/scip/format.ts +83 -0
  275. package/src/scip/import.ts +216 -0
  276. package/src/types.ts +457 -0
  277. package/tests/benchmark-service-links.ts +244 -0
  278. package/tests/bug-regressions.ts +626 -0
  279. package/tests/filters.ts +264 -0
  280. package/tests/fixtures/Counter.tsx +38 -0
  281. package/tests/fixtures/caller.ts +7 -0
  282. package/tests/fixtures/collisions.ts +23 -0
  283. package/tests/fixtures/local_helper.ts +5 -0
  284. package/tests/fixtures/overloads.java +17 -0
  285. package/tests/fixtures/remote_helper.ts +4 -0
  286. package/tests/fixtures/sample.c +15 -0
  287. package/tests/fixtures/sample.cpp +47 -0
  288. package/tests/fixtures/sample.cs +62 -0
  289. package/tests/fixtures/sample.go +68 -0
  290. package/tests/fixtures/sample.h +30 -0
  291. package/tests/fixtures/sample.java +85 -0
  292. package/tests/fixtures/sample.py +46 -0
  293. package/tests/fixtures/sample.rs +78 -0
  294. package/tests/fixtures/sample.ts +76 -0
  295. package/tests/fixtures-service/HttpClients.cs +30 -0
  296. package/tests/fixtures-service/HttpClients.java +24 -0
  297. package/tests/fixtures-service/billing.ts +15 -0
  298. package/tests/fixtures-service/docker-compose.yml +15 -0
  299. package/tests/fixtures-service/gateway.ts +10 -0
  300. package/tests/fixtures-service/get_user.ts +11 -0
  301. package/tests/fixtures-service/graphql_client.ts +63 -0
  302. package/tests/fixtures-service/graphql_server.ts +30 -0
  303. package/tests/fixtures-service/grpc_client.go +30 -0
  304. package/tests/fixtures-service/http_clients.go +23 -0
  305. package/tests/fixtures-service/http_clients.py +38 -0
  306. package/tests/fixtures-service/http_clients.ts +49 -0
  307. package/tests/fixtures-service/k8s/payment-service.yaml +22 -0
  308. package/tests/fixtures-service/k8s_calls.ts +20 -0
  309. package/tests/fixtures-service/messaging.ts +87 -0
  310. package/tests/fixtures-service/trpc_client.ts +39 -0
  311. package/tests/fixtures-service/trpc_server.ts +39 -0
  312. package/tests/fixtures-service/user_service.proto +33 -0
  313. package/tests/fixtures-trackcd/Cargo.toml +11 -0
  314. package/tests/fixtures-trackcd/SpringController.java +36 -0
  315. package/tests/fixtures-trackcd/auth_service.ts +19 -0
  316. package/tests/fixtures-trackcd/complex_module.py +50 -0
  317. package/tests/fixtures-trackcd/express_app.js +30 -0
  318. package/tests/fixtures-trackcd/fastapi_app.py +49 -0
  319. package/tests/fixtures-trackcd/fastify_object_routes.js +32 -0
  320. package/tests/fixtures-trackcd/go.mod +8 -0
  321. package/tests/fixtures-trackcd/package.json +15 -0
  322. package/tests/fixtures-trackcd/requirements.txt +4 -0
  323. package/tests/fixtures-trackcd/tests/auth_service.test.ts +13 -0
  324. package/tests/fixtures-tracke/auth/AuthService.ts +23 -0
  325. package/tests/fixtures-tracke/auth/crypto.ts +7 -0
  326. package/tests/fixtures-tracke/billing/Billing.ts +20 -0
  327. package/tests/fixtures-tracke/billing/Invoice.ts +10 -0
  328. package/tests/fixtures-tracke/billing/server.ts +17 -0
  329. package/tests/fixtures-tracke/package.json +7 -0
  330. package/tests/fixtures-tracke/tests/auth.test.ts +23 -0
  331. package/tests/fixtures-tracke/tests/billing.test.ts +14 -0
  332. package/tests/fixtures-trackf/package.json +5 -0
  333. package/tests/fixtures-trackf/src/auth.ts +26 -0
  334. package/tests/fixtures-trackf/src/handlers.ts +35 -0
  335. package/tests/fixtures-tracki/billing/routes.ts +12 -0
  336. package/tests/fixtures-tracki/gateway/client.ts +13 -0
  337. package/tests/git-features.ts +267 -0
  338. package/tests/init.ts +141 -0
  339. package/tests/mcp-jit.ts +130 -0
  340. package/tests/mcp-smoke.ts +191 -0
  341. package/tests/mcp-trackcd.ts +169 -0
  342. package/tests/mcp-tracke.ts +229 -0
  343. package/tests/mcp-trackf.ts +330 -0
  344. package/tests/mcp-trackg.ts +219 -0
  345. package/tests/mcp-tracki.ts +174 -0
  346. package/tests/mcp-watcher.ts +126 -0
  347. package/tests/optspec.ts +194 -0
  348. package/tests/parallel-index.ts +333 -0
  349. package/tests/parallel-read.ts +125 -0
  350. package/tests/parallel-recovery.ts +241 -0
  351. package/tests/perf-callers.ts +145 -0
  352. package/tests/query-parity.ts +184 -0
  353. package/tests/query-perf.ts +55 -0
  354. package/tests/scale-parallel-parity.ts +225 -0
  355. package/tests/scale-test.ts +523 -0
  356. package/tests/smoke.ts +396 -0
  357. package/tests/trackcd.ts +325 -0
  358. package/tests/tracke-collisions.ts +255 -0
  359. package/tests/tracke.ts +314 -0
  360. package/tests/trackf-bugs.ts +406 -0
  361. package/tests/trackf.ts +390 -0
  362. package/tests/trackg.ts +1372 -0
  363. package/tests/tracki-boundaries.ts +202 -0
  364. package/tests/tracki-continuity.ts +253 -0
  365. package/tests/tracki-contract-diff.ts +249 -0
  366. package/tests/tracki-external-bundles.ts +341 -0
  367. package/tests/tracki-preflight.ts +251 -0
  368. package/tests/verify-roles.ts +51 -0
  369. package/tests/worker-parity.ts +286 -0
  370. package/tests/worker-pool.ts +262 -0
  371. package/tsconfig.json +20 -0
@@ -0,0 +1,259 @@
1
+ import type Parser from 'web-tree-sitter';
2
+ import type { SymbolDef, ServiceCallDef } from '../../types.js';
3
+ import type { LanguageExtractor } from '../walker.js';
4
+ import { firstLine } from '../walker.js';
5
+
6
+ // Go HTTP-client method names. The receiver may be `http`, `http.DefaultClient`,
7
+ // or any user client (e.g. `myClient`).
8
+ const GO_HTTP_VERBS = new Set([
9
+ 'Get', 'Post', 'PostForm', 'Head', 'Do', 'Patch', 'Put', 'Delete', 'NewRequest',
10
+ ]);
11
+
12
+ const GO_BRANCH_NODES = new Set<string>([
13
+ 'if_statement', 'for_statement', 'expression_case', 'default_case',
14
+ 'type_case', 'communication_case', 'select_statement',
15
+ ]);
16
+
17
+ const GO_NESTING_NODES = new Set<string>([
18
+ 'if_statement', 'for_statement', 'expression_switch_statement',
19
+ 'type_switch_statement', 'select_statement',
20
+ ]);
21
+
22
+ const GO_CANDIDATE_NODE_TYPES = [
23
+ // tryExtractDefinition
24
+ 'function_declaration',
25
+ 'method_declaration',
26
+ 'type_declaration',
27
+ // tryExtractCallName
28
+ 'call_expression',
29
+ // tryExtractImport
30
+ 'import_spec',
31
+ ] as const;
32
+
33
+ export const goExtractor: LanguageExtractor = {
34
+ languageName: 'go',
35
+ extensions: ['.go'],
36
+ branchNodeTypes: GO_BRANCH_NODES,
37
+ nestingNodeTypes: GO_NESTING_NODES,
38
+ candidateNodeTypes: GO_CANDIDATE_NODE_TYPES,
39
+
40
+ tryExtractDefinition(node: Parser.SyntaxNode): SymbolDef | null {
41
+ switch (node.type) {
42
+ case 'function_declaration': {
43
+ const nameNode = node.childForFieldName('name');
44
+ if (!nameNode) return null;
45
+ return {
46
+ name: nameNode.text,
47
+ kind: 'function',
48
+ lineStart: node.startPosition.row,
49
+ lineEnd: node.endPosition.row,
50
+ colStart: node.startPosition.column,
51
+ colEnd: node.endPosition.column,
52
+ signature: firstLine(node),
53
+ };
54
+ }
55
+
56
+ case 'method_declaration': {
57
+ const nameNode = node.childForFieldName('name');
58
+ if (!nameNode) return null;
59
+ return {
60
+ name: nameNode.text,
61
+ kind: 'method',
62
+ lineStart: node.startPosition.row,
63
+ lineEnd: node.endPosition.row,
64
+ colStart: node.startPosition.column,
65
+ colEnd: node.endPosition.column,
66
+ signature: firstLine(node),
67
+ };
68
+ }
69
+
70
+ // type Foo struct {} or type Foo interface {}
71
+ case 'type_declaration': {
72
+ // type_declaration contains one or more type_spec children
73
+ for (const child of node.children) {
74
+ if (child.type === 'type_spec') {
75
+ const nameNode = child.childForFieldName('name');
76
+ if (!nameNode) continue;
77
+ const typeNode = child.childForFieldName('type');
78
+ const kind = typeNode?.type === 'interface_type' ? 'interface'
79
+ : typeNode?.type === 'struct_type' ? 'struct'
80
+ : 'type';
81
+ return {
82
+ name: nameNode.text,
83
+ kind,
84
+ lineStart: node.startPosition.row,
85
+ lineEnd: node.endPosition.row,
86
+ colStart: node.startPosition.column,
87
+ colEnd: node.endPosition.column,
88
+ signature: firstLine(node),
89
+ };
90
+ }
91
+ }
92
+ return null;
93
+ }
94
+
95
+ default:
96
+ return null;
97
+ }
98
+ },
99
+
100
+ tryExtractCallName(node: Parser.SyntaxNode): string | null {
101
+ if (node.type !== 'call_expression') return null;
102
+ const funcNode = node.childForFieldName('function');
103
+ if (!funcNode) return null;
104
+
105
+ // foo()
106
+ if (funcNode.type === 'identifier') return funcNode.text;
107
+
108
+ // pkg.Func() or receiver.Method()
109
+ if (funcNode.type === 'selector_expression') {
110
+ return funcNode.childForFieldName('field')?.text ?? null;
111
+ }
112
+
113
+ return null;
114
+ },
115
+
116
+ tryExtractImport(node: Parser.SyntaxNode): string | null {
117
+ // import_spec contains a "path" field (interpreted_string_literal)
118
+ if (node.type === 'import_spec') {
119
+ const pathNode = node.childForFieldName('path');
120
+ return pathNode?.text?.replace(/['"]/g, '') ?? null;
121
+ }
122
+ return null;
123
+ },
124
+
125
+ /**
126
+ * Go HTTP-client calls:
127
+ * http.Get("https://x/y") ← yes
128
+ * http.Post("/api", "json", body) ← yes
129
+ * client.Get("/api/users") ← yes (any receiver, capital verb)
130
+ * http.NewRequest("GET", "/x", …) ← yes (method = first string arg)
131
+ */
132
+ tryExtractServiceCalls(node: Parser.SyntaxNode): ServiceCallDef[] | null {
133
+ if (node.type !== 'call_expression') return null;
134
+ const fn = node.childForFieldName('function');
135
+ if (!fn || fn.type !== 'selector_expression') return null;
136
+ const recv = fn.childForFieldName('operand');
137
+ const field = fn.childForFieldName('field');
138
+ if (!recv || !field) return null;
139
+ const verb = field.text;
140
+
141
+ // v9 Track-H — gRPC client call:
142
+ // pb.NewUserServiceClient(conn).GetUser(ctx, &req)
143
+ // recv is a call_expression to pb.New<X>ServiceClient(...) or pb.New<X>Client(...);
144
+ // verb is the method (capitalized). We emit a service_call with operation
145
+ // = "Service/Method" matching the .proto resolver.
146
+ if (recv.type === 'call_expression' && verb && /^[A-Z]/.test(verb)) {
147
+ const grpc = tryExtractGoGrpcCall(node, recv, verb);
148
+ if (grpc) return [grpc];
149
+ }
150
+
151
+ if (!GO_HTTP_VERBS.has(verb)) return null;
152
+
153
+ let framework: string;
154
+ if (recv.text === 'http') framework = 'http';
155
+ else if (recv.text === 'httputil') framework = 'httputil';
156
+ else framework = 'http-client';
157
+
158
+ const args = node.childForFieldName('arguments');
159
+ if (!args) return null;
160
+ const named = args.namedChildren;
161
+
162
+ let method: string | undefined;
163
+ let urlIdx = 0;
164
+ if (verb === 'NewRequest' || verb === 'NewRequestWithContext') {
165
+ const ctxOffset = verb === 'NewRequestWithContext' ? 1 : 0;
166
+ const methodNode = named[ctxOffset];
167
+ if (methodNode && methodNode.type === 'interpreted_string_literal') {
168
+ method = methodNode.text.replace(/['"`]/g, '').toUpperCase();
169
+ }
170
+ urlIdx = ctxOffset + 1;
171
+ } else if (verb === 'Do') {
172
+ // http.Client.Do(req) doesn't expose the URL here; skip.
173
+ return null;
174
+ } else {
175
+ method = verb === 'Get' ? 'GET'
176
+ : verb === 'Post' ? 'POST'
177
+ : verb === 'PostForm' ? 'POST'
178
+ : verb === 'Head' ? 'HEAD'
179
+ : verb === 'Patch' ? 'PATCH'
180
+ : verb === 'Put' ? 'PUT'
181
+ : verb === 'Delete' ? 'DELETE'
182
+ : 'ANY';
183
+ }
184
+
185
+ const urlNode = named[urlIdx];
186
+ if (!urlNode) return null;
187
+ let raw: string | null = null;
188
+ if (urlNode.type === 'interpreted_string_literal' || urlNode.type === 'raw_string_literal') {
189
+ raw = urlNode.text.replace(/^[`"']|[`"']$/g, '');
190
+ } else {
191
+ return null;
192
+ }
193
+ if (!raw || !goLooksLikeHttpTarget(raw)) return null;
194
+
195
+ return [{
196
+ protocol: 'http',
197
+ method: method ?? 'ANY',
198
+ rawTarget: raw.slice(0, 240),
199
+ framework,
200
+ line: node.startPosition.row,
201
+ confidence: 0.85,
202
+ }];
203
+ },
204
+ };
205
+
206
+ /**
207
+ * v9 Track-H — detect a gRPC client stub call in Go.
208
+ *
209
+ * Pattern: `pb.NewUserServiceClient(conn).GetUser(ctx, &req)`
210
+ *
211
+ * `recv` is the inner call expression (`pb.NewUserServiceClient(conn)`); we
212
+ * look at its callee to harvest the service name from `NewXServiceClient` or
213
+ * `NewXClient`. The outer method (`GetUser`) becomes the rpc method.
214
+ *
215
+ * Returns a ServiceCallDef with:
216
+ * - protocol = 'grpc'
217
+ * - operation = 'Service/Method' (matches .proto-derived routes)
218
+ * - service = 'UserService'
219
+ * - method = 'GetUser'
220
+ *
221
+ * Returns null if the inner callee does not parse as a NewXClient
222
+ * constructor — keeps determinism high (no false-positive on a chained
223
+ * method call against an unrelated builder pattern).
224
+ */
225
+ function tryExtractGoGrpcCall(
226
+ outer: Parser.SyntaxNode,
227
+ recv: Parser.SyntaxNode,
228
+ rpcMethod: string,
229
+ ): ServiceCallDef | null {
230
+ const innerFn = recv.childForFieldName('function');
231
+ if (!innerFn || innerFn.type !== 'selector_expression') return null;
232
+ const innerField = innerFn.childForFieldName('field');
233
+ if (!innerField) return null;
234
+ const ctor = innerField.text;
235
+ // Match `New(.*)(?:Service)?Client$`. Service is the middle capture if it ends
236
+ // in "ServiceClient"; otherwise strip just "Client".
237
+ const m = ctor.match(/^New([A-Z][A-Za-z0-9_]*?)(Service)?Client$/);
238
+ if (!m) return null;
239
+ const serviceName = m[1] + (m[2] ? 'Service' : '');
240
+ const operation = `${serviceName}/${rpcMethod}`;
241
+ return {
242
+ protocol: 'grpc',
243
+ method: rpcMethod.toUpperCase(),
244
+ rawTarget: `${ctor}.${rpcMethod}`,
245
+ framework: 'grpc-go',
246
+ line: outer.startPosition.row,
247
+ confidence: 0.9,
248
+ operation,
249
+ service: serviceName,
250
+ };
251
+ }
252
+
253
+ function goLooksLikeHttpTarget(s: string): boolean {
254
+ if (!s) return false;
255
+ if (s.startsWith('/')) return true;
256
+ if (/^https?:\/\//i.test(s)) return true;
257
+ if (/^[a-zA-Z0-9._-]+\/[a-zA-Z0-9_-]/.test(s)) return true;
258
+ return false;
259
+ }
@@ -0,0 +1,382 @@
1
+ import type Parser from 'web-tree-sitter';
2
+ import type { SymbolDef, RouteDef, ConfigKeyRead, ServiceCallDef } from '../../types.js';
3
+ import type { LanguageExtractor } from '../walker.js';
4
+ import { firstLine } from '../walker.js';
5
+
6
+ const JAVA_BRANCH_NODES = new Set<string>([
7
+ 'if_statement', 'while_statement', 'do_statement', 'for_statement', 'enhanced_for_statement',
8
+ 'switch_label', 'catch_clause', 'ternary_expression',
9
+ ]);
10
+
11
+ const JAVA_NESTING_NODES = new Set<string>([
12
+ 'if_statement', 'while_statement', 'do_statement', 'for_statement', 'enhanced_for_statement',
13
+ 'switch_block', 'catch_clause',
14
+ ]);
15
+
16
+ const SPRING_REQUEST_ANNOTATIONS: Record<string, string> = {
17
+ GetMapping: 'GET',
18
+ PostMapping: 'POST',
19
+ PutMapping: 'PUT',
20
+ PatchMapping: 'PATCH',
21
+ DeleteMapping: 'DELETE',
22
+ };
23
+
24
+ // Java HTTP CLIENT method names → HTTP verb. RestTemplate exposes a separate
25
+ // method per verb; java.net.http builds the request via .GET()/.POST(…).
26
+ const JAVA_HTTP_CLIENT_METHODS = new Map<string, string>([
27
+ ['getForObject', 'GET'], ['getForEntity', 'GET'],
28
+ ['postForObject', 'POST'], ['postForEntity', 'POST'], ['postForLocation', 'POST'],
29
+ ['put', 'PUT'], ['delete', 'DELETE'],
30
+ ['exchange', 'ANY'],
31
+ ['newBuilder', 'ANY'], // HttpRequest.newBuilder(URI.create("..."))
32
+ ]);
33
+
34
+ function javaLooksLikeHttpTarget(s: string): boolean {
35
+ if (!s) return false;
36
+ if (s.startsWith('/')) return true;
37
+ if (/^https?:\/\//i.test(s)) return true;
38
+ return false;
39
+ }
40
+
41
+ const JAVA_CANDIDATE_NODE_TYPES = [
42
+ // tryExtractDefinition
43
+ 'method_declaration',
44
+ 'class_declaration',
45
+ 'interface_declaration',
46
+ 'constructor_declaration',
47
+ 'enum_declaration',
48
+ // tryExtractCallName + tryExtractConfigKey
49
+ 'method_invocation',
50
+ // tryExtractImport
51
+ 'import_declaration',
52
+ // tryExtractRoute (Spring annotations on methods)
53
+ 'annotation',
54
+ 'marker_annotation',
55
+ ] as const;
56
+
57
+ export const javaExtractor: LanguageExtractor = {
58
+ languageName: 'java',
59
+ extensions: ['.java'],
60
+ branchNodeTypes: JAVA_BRANCH_NODES,
61
+ nestingNodeTypes: JAVA_NESTING_NODES,
62
+ candidateNodeTypes: JAVA_CANDIDATE_NODE_TYPES,
63
+
64
+ tryExtractDefinition(node: Parser.SyntaxNode): SymbolDef | null {
65
+ switch (node.type) {
66
+ case 'method_declaration': {
67
+ const nameNode = node.childForFieldName('name');
68
+ if (!nameNode) return null;
69
+ return {
70
+ name: nameNode.text,
71
+ kind: 'method',
72
+ lineStart: node.startPosition.row,
73
+ lineEnd: node.endPosition.row,
74
+ colStart: node.startPosition.column,
75
+ colEnd: node.endPosition.column,
76
+ signature: firstLine(node),
77
+ };
78
+ }
79
+
80
+ case 'class_declaration': {
81
+ const nameNode = node.childForFieldName('name');
82
+ if (!nameNode) return null;
83
+ return {
84
+ name: nameNode.text,
85
+ kind: 'class',
86
+ lineStart: node.startPosition.row,
87
+ lineEnd: node.endPosition.row,
88
+ colStart: node.startPosition.column,
89
+ colEnd: node.endPosition.column,
90
+ signature: firstLine(node),
91
+ };
92
+ }
93
+
94
+ case 'interface_declaration': {
95
+ const nameNode = node.childForFieldName('name');
96
+ if (!nameNode) return null;
97
+ return {
98
+ name: nameNode.text,
99
+ kind: 'interface',
100
+ lineStart: node.startPosition.row,
101
+ lineEnd: node.endPosition.row,
102
+ colStart: node.startPosition.column,
103
+ colEnd: node.endPosition.column,
104
+ signature: firstLine(node),
105
+ };
106
+ }
107
+
108
+ case 'constructor_declaration': {
109
+ const nameNode = node.childForFieldName('name');
110
+ if (!nameNode) return null;
111
+ return {
112
+ name: nameNode.text,
113
+ kind: 'constructor',
114
+ lineStart: node.startPosition.row,
115
+ lineEnd: node.endPosition.row,
116
+ colStart: node.startPosition.column,
117
+ colEnd: node.endPosition.column,
118
+ signature: firstLine(node),
119
+ };
120
+ }
121
+
122
+ case 'enum_declaration': {
123
+ const nameNode = node.childForFieldName('name');
124
+ if (!nameNode) return null;
125
+ return {
126
+ name: nameNode.text,
127
+ kind: 'enum',
128
+ lineStart: node.startPosition.row,
129
+ lineEnd: node.endPosition.row,
130
+ colStart: node.startPosition.column,
131
+ colEnd: node.endPosition.column,
132
+ signature: firstLine(node),
133
+ };
134
+ }
135
+
136
+ default:
137
+ return null;
138
+ }
139
+ },
140
+
141
+ tryExtractCallName(node: Parser.SyntaxNode): string | null {
142
+ if (node.type === 'method_invocation') {
143
+ return node.childForFieldName('name')?.text ?? null;
144
+ }
145
+ return null;
146
+ },
147
+
148
+ tryExtractImport(node: Parser.SyntaxNode): string | null {
149
+ if (node.type === 'import_declaration') {
150
+ for (const child of node.children) {
151
+ if (child.type === 'scoped_identifier' || child.type === 'identifier') {
152
+ return child.text;
153
+ }
154
+ }
155
+ }
156
+ return null;
157
+ },
158
+
159
+ /**
160
+ * Spring Boot mapping annotations on methods:
161
+ * @GetMapping("/users")
162
+ * @PostMapping(value = "/users")
163
+ * @RequestMapping(value = "/x", method = RequestMethod.GET)
164
+ *
165
+ * Class-level @RequestMapping("/api") is treated as a PREFIX for every
166
+ * mapping annotation on that class's methods, not as a route on its own.
167
+ * We never emit a route from a class-level annotation directly — that
168
+ * was a pre-existing bug that produced bogus entries like `GET /api`.
169
+ *
170
+ * The annotation node sits above the method_declaration; we extract on
171
+ * the annotation and walk up to the method for the handler name, then
172
+ * walk further up to find the enclosing class to pick up any class-level
173
+ * @RequestMapping prefix.
174
+ */
175
+ tryExtractRoute(node: Parser.SyntaxNode): RouteDef[] | null {
176
+ if (node.type !== 'annotation' && node.type !== 'marker_annotation') return null;
177
+ const nameNode = node.childForFieldName('name');
178
+ if (!nameNode) return null;
179
+ const annName = nameNode.text;
180
+ const method = SPRING_REQUEST_ANNOTATIONS[annName];
181
+
182
+ // The enclosing declaration (method vs class) decides whether this
183
+ // annotation is a route or a prefix. modifiers are the syntactic parent
184
+ // of an annotation in tree-sitter-java; the method/class is one more
185
+ // level up.
186
+ let enclosing: Parser.SyntaxNode | null = node.parent;
187
+ while (enclosing && enclosing.type === 'modifiers') enclosing = enclosing.parent;
188
+ if (!enclosing) return null;
189
+ // Class-level annotations: not a route on their own. Their path becomes
190
+ // a prefix for method-level routes inside the class; the method-level
191
+ // pass below walks up to find this annotation and prepends its path.
192
+ if (enclosing.type === 'class_declaration' || enclosing.type === 'interface_declaration') {
193
+ return null;
194
+ }
195
+ // Anything other than a method/constructor: not a route.
196
+ if (enclosing.type !== 'method_declaration' && enclosing.type !== 'constructor_declaration') {
197
+ return null;
198
+ }
199
+
200
+ const routePath = readSpringPath(node);
201
+
202
+ let detectedMethod = method;
203
+ const args = node.childForFieldName('arguments');
204
+ if (annName === 'RequestMapping' && args) {
205
+ for (const child of args.namedChildren) {
206
+ if (child.type === 'element_value_pair') {
207
+ const kn = child.childForFieldName('key');
208
+ const vn = child.childForFieldName('value');
209
+ if (kn?.text === 'method' && vn) {
210
+ const txt = vn.text.replace(/.*RequestMethod\./, '').replace(/[,}\s]/g, '');
211
+ if (txt) detectedMethod = txt;
212
+ }
213
+ }
214
+ }
215
+ if (!detectedMethod) detectedMethod = 'GET';
216
+ }
217
+
218
+ if (!detectedMethod) return null;
219
+
220
+ // Walk up to the enclosing class to pick up a class-level @RequestMapping
221
+ // prefix (if any). Concatenate with care so we don't end up with double
222
+ // slashes or missing slashes.
223
+ const prefix = findSpringClassPrefix(enclosing);
224
+ const fullPath = joinSpringPaths(prefix, routePath);
225
+ if (!fullPath) return null;
226
+
227
+ const handlerName = enclosing.childForFieldName('name')?.text;
228
+
229
+ return [{
230
+ method: detectedMethod,
231
+ path: fullPath,
232
+ framework: 'spring',
233
+ handlerName,
234
+ line: node.startPosition.row,
235
+ }];
236
+ },
237
+
238
+ /**
239
+ * Java HTTP client calls — minimal initial coverage:
240
+ * HttpRequest.newBuilder(URI.create("https://x/y"))
241
+ * .GET().build() ← yes (path lifted; method=GET)
242
+ * restTemplate.getForObject("/api/x", String.class) ← yes (Spring RestTemplate)
243
+ * webClient.get().uri("/api/x") ← future
244
+ *
245
+ * The recognizer is conservative: we accept any method_invocation whose
246
+ * name is in JAVA_HTTP_CLIENT_METHODS and whose first argument is a string
247
+ * literal that looks like an HTTP target.
248
+ */
249
+ tryExtractServiceCalls(node: Parser.SyntaxNode): ServiceCallDef[] | null {
250
+ if (node.type !== 'method_invocation') return null;
251
+ const name = node.childForFieldName('name')?.text;
252
+ if (!name) return null;
253
+ const verb = JAVA_HTTP_CLIENT_METHODS.get(name);
254
+ if (!verb) return null;
255
+
256
+ const args = node.childForFieldName('arguments');
257
+ if (!args) return null;
258
+ const first = args.namedChildren[0];
259
+ if (!first) return null;
260
+ let raw: string | null = null;
261
+ if (first.type === 'string_literal') {
262
+ raw = stripJavaQuotes(first.text);
263
+ } else if (first.type === 'method_invocation') {
264
+ // URI.create("https://x/y") inside HttpRequest.newBuilder(...)
265
+ const innerName = first.childForFieldName('name')?.text;
266
+ const innerObj = first.childForFieldName('object')?.text;
267
+ if (innerObj === 'URI' && innerName === 'create') {
268
+ const ia = first.childForFieldName('arguments');
269
+ const first2 = ia?.namedChildren[0];
270
+ if (first2?.type === 'string_literal') raw = stripJavaQuotes(first2.text);
271
+ }
272
+ }
273
+ if (!raw || !javaLooksLikeHttpTarget(raw)) return null;
274
+
275
+ let framework = 'http-client';
276
+ const obj = node.childForFieldName('object')?.text;
277
+ if (obj === 'HttpRequest') framework = 'java.net.http';
278
+ else if (obj && /(restTemplate|RestTemplate)/.test(obj)) framework = 'spring-rest';
279
+
280
+ return [{
281
+ protocol: 'http',
282
+ method: verb,
283
+ rawTarget: raw.slice(0, 240),
284
+ framework,
285
+ line: node.startPosition.row,
286
+ confidence: 0.8,
287
+ }];
288
+ },
289
+
290
+ /**
291
+ * Java env reads: `System.getenv("NAME")`.
292
+ */
293
+ tryExtractConfigKey(node: Parser.SyntaxNode): ConfigKeyRead | null {
294
+ if (node.type !== 'method_invocation') return null;
295
+ const obj = node.childForFieldName('object');
296
+ const name = node.childForFieldName('name');
297
+ if (!obj || !name) return null;
298
+ if (obj.text === 'System' && name.text === 'getenv') {
299
+ const args = node.childForFieldName('arguments');
300
+ if (!args) return null;
301
+ for (const child of args.namedChildren) {
302
+ if (child.type === 'string_literal') {
303
+ const key = stripJavaQuotes(child.text);
304
+ if (key) return { key, source: 'env', line: node.startPosition.row };
305
+ break;
306
+ }
307
+ }
308
+ }
309
+ return null;
310
+ },
311
+ };
312
+
313
+ function stripJavaQuotes(s: string): string {
314
+ return s.replace(/^"|"$/g, '');
315
+ }
316
+
317
+ /**
318
+ * Read the `value=`/`path=` (or first positional string-literal) from a
319
+ * Spring mapping annotation. Returns '' when no path is given — which is
320
+ * a perfectly valid mapping (`@GetMapping` with class-level prefix only).
321
+ */
322
+ function readSpringPath(annNode: Parser.SyntaxNode): string {
323
+ const args = annNode.childForFieldName('arguments');
324
+ if (!args) return '';
325
+ for (const child of args.namedChildren) {
326
+ if (child.type === 'string_literal') return stripJavaQuotes(child.text);
327
+ if (child.type === 'element_value_pair') {
328
+ const kn = child.childForFieldName('key');
329
+ const vn = child.childForFieldName('value');
330
+ if ((kn?.text === 'value' || kn?.text === 'path') && vn?.type === 'string_literal') {
331
+ return stripJavaQuotes(vn.text);
332
+ }
333
+ }
334
+ }
335
+ return '';
336
+ }
337
+
338
+ /**
339
+ * Walk up from a method_declaration to its enclosing class_declaration and
340
+ * return the path component of a class-level @RequestMapping(...) — or '' if
341
+ * the class has no such annotation. Bare class-level @RestController without
342
+ * a @RequestMapping yields ''.
343
+ */
344
+ function findSpringClassPrefix(methodOrCtor: Parser.SyntaxNode): string {
345
+ let n: Parser.SyntaxNode | null = methodOrCtor.parent;
346
+ while (n) {
347
+ if (n.type === 'class_declaration' || n.type === 'interface_declaration') break;
348
+ n = n.parent;
349
+ }
350
+ if (!n) return '';
351
+ // Class-level annotations sit in a `modifiers` block as the first child of
352
+ // the class_declaration in tree-sitter-java.
353
+ for (const child of n.children) {
354
+ if (child.type !== 'modifiers') continue;
355
+ for (const m of child.namedChildren) {
356
+ if (m.type !== 'annotation' && m.type !== 'marker_annotation') continue;
357
+ const nm = m.childForFieldName('name');
358
+ if (nm?.text !== 'RequestMapping') continue;
359
+ return readSpringPath(m);
360
+ }
361
+ }
362
+ return '';
363
+ }
364
+
365
+ /**
366
+ * Join a Spring class-level prefix and method-level path. Empty strings are
367
+ * dropped; consecutive slashes are collapsed. Returns '' when both inputs
368
+ * are empty.
369
+ * ('/api', '/users') → '/api/users'
370
+ * ('/api', '') → '/api'
371
+ * ('', '/users') → '/users'
372
+ * ('/api/', '/users') → '/api/users'
373
+ * ('', '') → ''
374
+ */
375
+ function joinSpringPaths(prefix: string, route: string): string {
376
+ if (!prefix && !route) return '';
377
+ if (!prefix) return route;
378
+ if (!route) return prefix;
379
+ const a = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;
380
+ const b = route.startsWith('/') ? route : '/' + route;
381
+ return a + b;
382
+ }