codesift-mcp 0.2.18 → 0.4.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 (461) hide show
  1. package/README.md +143 -20
  2. package/dist/cache/hono-cache.d.ts +50 -0
  3. package/dist/cache/hono-cache.d.ts.map +1 -0
  4. package/dist/cache/hono-cache.js +132 -0
  5. package/dist/cache/hono-cache.js.map +1 -0
  6. package/dist/cli/setup.d.ts.map +1 -1
  7. package/dist/cli/setup.js +17 -2
  8. package/dist/cli/setup.js.map +1 -1
  9. package/dist/formatters-shortening.d.ts +13 -0
  10. package/dist/formatters-shortening.d.ts.map +1 -1
  11. package/dist/formatters-shortening.js +131 -0
  12. package/dist/formatters-shortening.js.map +1 -1
  13. package/dist/formatters.d.ts +38 -0
  14. package/dist/formatters.d.ts.map +1 -1
  15. package/dist/formatters.js +498 -0
  16. package/dist/formatters.js.map +1 -1
  17. package/dist/instructions.d.ts +1 -1
  18. package/dist/instructions.d.ts.map +1 -1
  19. package/dist/instructions.js +27 -26
  20. package/dist/instructions.js.map +1 -1
  21. package/dist/lsp/lsp-servers.d.ts.map +1 -1
  22. package/dist/lsp/lsp-servers.js +5 -0
  23. package/dist/lsp/lsp-servers.js.map +1 -1
  24. package/dist/lsp/lsp-tools.d.ts.map +1 -1
  25. package/dist/lsp/lsp-tools.js +1 -0
  26. package/dist/lsp/lsp-tools.js.map +1 -1
  27. package/dist/parser/astro-template.d.ts +47 -0
  28. package/dist/parser/astro-template.d.ts.map +1 -0
  29. package/dist/parser/astro-template.js +171 -0
  30. package/dist/parser/astro-template.js.map +1 -0
  31. package/dist/parser/extractors/_shared.d.ts +4 -0
  32. package/dist/parser/extractors/_shared.d.ts.map +1 -1
  33. package/dist/parser/extractors/_shared.js +8 -0
  34. package/dist/parser/extractors/_shared.js.map +1 -1
  35. package/dist/parser/extractors/astro.d.ts +4 -5
  36. package/dist/parser/extractors/astro.d.ts.map +1 -1
  37. package/dist/parser/extractors/astro.js +102 -26
  38. package/dist/parser/extractors/astro.js.map +1 -1
  39. package/dist/parser/extractors/gradle-kts.d.ts +4 -0
  40. package/dist/parser/extractors/gradle-kts.d.ts.map +1 -0
  41. package/dist/parser/extractors/gradle-kts.js +246 -0
  42. package/dist/parser/extractors/gradle-kts.js.map +1 -0
  43. package/dist/parser/extractors/hono-inline-analyzer.d.ts +34 -0
  44. package/dist/parser/extractors/hono-inline-analyzer.d.ts.map +1 -0
  45. package/dist/parser/extractors/hono-inline-analyzer.js +465 -0
  46. package/dist/parser/extractors/hono-inline-analyzer.js.map +1 -0
  47. package/dist/parser/extractors/hono-model.d.ts +196 -0
  48. package/dist/parser/extractors/hono-model.d.ts.map +1 -0
  49. package/dist/parser/extractors/hono-model.js +10 -0
  50. package/dist/parser/extractors/hono-model.js.map +1 -0
  51. package/dist/parser/extractors/hono.d.ts +118 -0
  52. package/dist/parser/extractors/hono.d.ts.map +1 -0
  53. package/dist/parser/extractors/hono.js +1527 -0
  54. package/dist/parser/extractors/hono.js.map +1 -0
  55. package/dist/parser/extractors/kotlin.d.ts +4 -0
  56. package/dist/parser/extractors/kotlin.d.ts.map +1 -0
  57. package/dist/parser/extractors/kotlin.js +521 -0
  58. package/dist/parser/extractors/kotlin.js.map +1 -0
  59. package/dist/parser/extractors/php.d.ts +22 -0
  60. package/dist/parser/extractors/php.d.ts.map +1 -0
  61. package/dist/parser/extractors/php.js +326 -0
  62. package/dist/parser/extractors/php.js.map +1 -0
  63. package/dist/parser/extractors/python.d.ts.map +1 -1
  64. package/dist/parser/extractors/python.js +234 -11
  65. package/dist/parser/extractors/python.js.map +1 -1
  66. package/dist/parser/extractors/sql.d.ts +33 -0
  67. package/dist/parser/extractors/sql.d.ts.map +1 -0
  68. package/dist/parser/extractors/sql.js +506 -0
  69. package/dist/parser/extractors/sql.js.map +1 -0
  70. package/dist/parser/extractors/typescript.d.ts.map +1 -1
  71. package/dist/parser/extractors/typescript.js +166 -3
  72. package/dist/parser/extractors/typescript.js.map +1 -1
  73. package/dist/parser/languages/tree-sitter-javascript.wasm +0 -0
  74. package/dist/parser/languages/tree-sitter-kotlin.wasm +0 -0
  75. package/dist/parser/languages/tree-sitter-php.wasm +0 -0
  76. package/dist/parser/languages/tree-sitter-php_only.wasm +0 -0
  77. package/dist/parser/languages/tree-sitter-python.wasm +0 -0
  78. package/dist/parser/parser-manager.d.ts +32 -0
  79. package/dist/parser/parser-manager.d.ts.map +1 -1
  80. package/dist/parser/parser-manager.js +82 -3
  81. package/dist/parser/parser-manager.js.map +1 -1
  82. package/dist/parser/symbol-extractor.d.ts.map +1 -1
  83. package/dist/parser/symbol-extractor.js +16 -0
  84. package/dist/parser/symbol-extractor.js.map +1 -1
  85. package/dist/register-tools.d.ts +37 -1
  86. package/dist/register-tools.d.ts.map +1 -1
  87. package/dist/register-tools.js +2657 -191
  88. package/dist/register-tools.js.map +1 -1
  89. package/dist/search/reranker.js +1 -1
  90. package/dist/search/reranker.js.map +1 -1
  91. package/dist/server-helpers.d.ts.map +1 -1
  92. package/dist/server-helpers.js +11 -0
  93. package/dist/server-helpers.js.map +1 -1
  94. package/dist/server.js +28 -1
  95. package/dist/server.js.map +1 -1
  96. package/dist/storage/index-store.d.ts +15 -1
  97. package/dist/storage/index-store.d.ts.map +1 -1
  98. package/dist/storage/index-store.js +27 -1
  99. package/dist/storage/index-store.js.map +1 -1
  100. package/dist/storage/session-state.d.ts +1 -1
  101. package/dist/storage/session-state.d.ts.map +1 -1
  102. package/dist/storage/session-state.js +6 -4
  103. package/dist/storage/session-state.js.map +1 -1
  104. package/dist/tools/agent-config-tools.d.ts +24 -0
  105. package/dist/tools/agent-config-tools.d.ts.map +1 -0
  106. package/dist/tools/agent-config-tools.js +119 -0
  107. package/dist/tools/agent-config-tools.js.map +1 -0
  108. package/dist/tools/architecture-tools.d.ts +23 -0
  109. package/dist/tools/architecture-tools.d.ts.map +1 -0
  110. package/dist/tools/architecture-tools.js +140 -0
  111. package/dist/tools/architecture-tools.js.map +1 -0
  112. package/dist/tools/astro-config.d.ts +33 -0
  113. package/dist/tools/astro-config.d.ts.map +1 -0
  114. package/dist/tools/astro-config.js +260 -0
  115. package/dist/tools/astro-config.js.map +1 -0
  116. package/dist/tools/astro-islands.d.ts +61 -0
  117. package/dist/tools/astro-islands.d.ts.map +1 -0
  118. package/dist/tools/astro-islands.js +240 -0
  119. package/dist/tools/astro-islands.js.map +1 -0
  120. package/dist/tools/astro-routes.d.ts +49 -0
  121. package/dist/tools/astro-routes.d.ts.map +1 -0
  122. package/dist/tools/astro-routes.js +119 -0
  123. package/dist/tools/astro-routes.js.map +1 -0
  124. package/dist/tools/audit-tools.d.ts +38 -0
  125. package/dist/tools/audit-tools.d.ts.map +1 -0
  126. package/dist/tools/audit-tools.js +248 -0
  127. package/dist/tools/audit-tools.js.map +1 -0
  128. package/dist/tools/celery-tools.d.ts +38 -0
  129. package/dist/tools/celery-tools.d.ts.map +1 -0
  130. package/dist/tools/celery-tools.js +154 -0
  131. package/dist/tools/celery-tools.js.map +1 -0
  132. package/dist/tools/clone-tools.js +1 -1
  133. package/dist/tools/clone-tools.js.map +1 -1
  134. package/dist/tools/complexity-tools.d.ts +4 -0
  135. package/dist/tools/complexity-tools.d.ts.map +1 -1
  136. package/dist/tools/complexity-tools.js +78 -4
  137. package/dist/tools/complexity-tools.js.map +1 -1
  138. package/dist/tools/compose-tools.d.ts +60 -0
  139. package/dist/tools/compose-tools.d.ts.map +1 -0
  140. package/dist/tools/compose-tools.js +203 -0
  141. package/dist/tools/compose-tools.js.map +1 -0
  142. package/dist/tools/coupling-tools.d.ts +50 -0
  143. package/dist/tools/coupling-tools.d.ts.map +1 -0
  144. package/dist/tools/coupling-tools.js +262 -0
  145. package/dist/tools/coupling-tools.js.map +1 -0
  146. package/dist/tools/dependency-audit-tools.d.ts +65 -0
  147. package/dist/tools/dependency-audit-tools.d.ts.map +1 -0
  148. package/dist/tools/dependency-audit-tools.js +553 -0
  149. package/dist/tools/dependency-audit-tools.js.map +1 -0
  150. package/dist/tools/django-settings.d.ts +22 -0
  151. package/dist/tools/django-settings.d.ts.map +1 -0
  152. package/dist/tools/django-settings.js +301 -0
  153. package/dist/tools/django-settings.js.map +1 -0
  154. package/dist/tools/frequency-tools.js +1 -1
  155. package/dist/tools/frequency-tools.js.map +1 -1
  156. package/dist/tools/graph-tools.d.ts +8 -2
  157. package/dist/tools/graph-tools.d.ts.map +1 -1
  158. package/dist/tools/graph-tools.js +44 -3
  159. package/dist/tools/graph-tools.js.map +1 -1
  160. package/dist/tools/hilt-tools.d.ts +55 -0
  161. package/dist/tools/hilt-tools.d.ts.map +1 -0
  162. package/dist/tools/hilt-tools.js +258 -0
  163. package/dist/tools/hilt-tools.js.map +1 -0
  164. package/dist/tools/hono-analyze-app.d.ts +48 -0
  165. package/dist/tools/hono-analyze-app.d.ts.map +1 -0
  166. package/dist/tools/hono-analyze-app.js +102 -0
  167. package/dist/tools/hono-analyze-app.js.map +1 -0
  168. package/dist/tools/hono-api-contract.d.ts +22 -0
  169. package/dist/tools/hono-api-contract.d.ts.map +1 -0
  170. package/dist/tools/hono-api-contract.js +80 -0
  171. package/dist/tools/hono-api-contract.js.map +1 -0
  172. package/dist/tools/hono-conditional-middleware.d.ts +27 -0
  173. package/dist/tools/hono-conditional-middleware.d.ts.map +1 -0
  174. package/dist/tools/hono-conditional-middleware.js +62 -0
  175. package/dist/tools/hono-conditional-middleware.js.map +1 -0
  176. package/dist/tools/hono-context-flow.d.ts +24 -0
  177. package/dist/tools/hono-context-flow.d.ts.map +1 -0
  178. package/dist/tools/hono-context-flow.js +78 -0
  179. package/dist/tools/hono-context-flow.js.map +1 -0
  180. package/dist/tools/hono-dead-routes.d.ts +26 -0
  181. package/dist/tools/hono-dead-routes.d.ts.map +1 -0
  182. package/dist/tools/hono-dead-routes.js +109 -0
  183. package/dist/tools/hono-dead-routes.js.map +1 -0
  184. package/dist/tools/hono-env-regression.d.ts +29 -0
  185. package/dist/tools/hono-env-regression.d.ts.map +1 -0
  186. package/dist/tools/hono-env-regression.js +157 -0
  187. package/dist/tools/hono-env-regression.js.map +1 -0
  188. package/dist/tools/hono-inline-analyze.d.ts +31 -0
  189. package/dist/tools/hono-inline-analyze.d.ts.map +1 -0
  190. package/dist/tools/hono-inline-analyze.js +67 -0
  191. package/dist/tools/hono-inline-analyze.js.map +1 -0
  192. package/dist/tools/hono-middleware-chain.d.ts +22 -0
  193. package/dist/tools/hono-middleware-chain.d.ts.map +1 -0
  194. package/dist/tools/hono-middleware-chain.js +84 -0
  195. package/dist/tools/hono-middleware-chain.js.map +1 -0
  196. package/dist/tools/hono-modules.d.ts +22 -0
  197. package/dist/tools/hono-modules.d.ts.map +1 -0
  198. package/dist/tools/hono-modules.js +126 -0
  199. package/dist/tools/hono-modules.js.map +1 -0
  200. package/dist/tools/hono-response-types.d.ts +37 -0
  201. package/dist/tools/hono-response-types.d.ts.map +1 -0
  202. package/dist/tools/hono-response-types.js +84 -0
  203. package/dist/tools/hono-response-types.js.map +1 -0
  204. package/dist/tools/hono-rpc-types.d.ts +21 -0
  205. package/dist/tools/hono-rpc-types.d.ts.map +1 -0
  206. package/dist/tools/hono-rpc-types.js +57 -0
  207. package/dist/tools/hono-rpc-types.js.map +1 -0
  208. package/dist/tools/hono-security.d.ts +21 -0
  209. package/dist/tools/hono-security.d.ts.map +1 -0
  210. package/dist/tools/hono-security.js +98 -0
  211. package/dist/tools/hono-security.js.map +1 -0
  212. package/dist/tools/hono-visualize.d.ts +13 -0
  213. package/dist/tools/hono-visualize.d.ts.map +1 -0
  214. package/dist/tools/hono-visualize.js +72 -0
  215. package/dist/tools/hono-visualize.js.map +1 -0
  216. package/dist/tools/hotspot-tools.d.ts.map +1 -1
  217. package/dist/tools/hotspot-tools.js +9 -7
  218. package/dist/tools/hotspot-tools.js.map +1 -1
  219. package/dist/tools/index-tools.d.ts +17 -0
  220. package/dist/tools/index-tools.d.ts.map +1 -1
  221. package/dist/tools/index-tools.js +210 -10
  222. package/dist/tools/index-tools.js.map +1 -1
  223. package/dist/tools/kotlin-tools.d.ts +142 -0
  224. package/dist/tools/kotlin-tools.d.ts.map +1 -0
  225. package/dist/tools/kotlin-tools.js +572 -0
  226. package/dist/tools/kotlin-tools.js.map +1 -0
  227. package/dist/tools/legacy-hono-conventions.d.ts +14 -0
  228. package/dist/tools/legacy-hono-conventions.d.ts.map +1 -0
  229. package/dist/tools/legacy-hono-conventions.js +152 -0
  230. package/dist/tools/legacy-hono-conventions.js.map +1 -0
  231. package/dist/tools/migration-lint-tools.d.ts +26 -0
  232. package/dist/tools/migration-lint-tools.d.ts.map +1 -0
  233. package/dist/tools/migration-lint-tools.js +247 -0
  234. package/dist/tools/migration-lint-tools.js.map +1 -0
  235. package/dist/tools/model-tools.d.ts +30 -0
  236. package/dist/tools/model-tools.d.ts.map +1 -0
  237. package/dist/tools/model-tools.js +145 -0
  238. package/dist/tools/model-tools.js.map +1 -0
  239. package/dist/tools/nest-ext-tools.d.ts +92 -0
  240. package/dist/tools/nest-ext-tools.d.ts.map +1 -0
  241. package/dist/tools/nest-ext-tools.js +359 -0
  242. package/dist/tools/nest-ext-tools.js.map +1 -0
  243. package/dist/tools/nest-tools.d.ts +171 -0
  244. package/dist/tools/nest-tools.d.ts.map +1 -0
  245. package/dist/tools/nest-tools.js +1042 -0
  246. package/dist/tools/nest-tools.js.map +1 -0
  247. package/dist/tools/nextjs-api-contract-readers.d.ts +14 -0
  248. package/dist/tools/nextjs-api-contract-readers.d.ts.map +1 -0
  249. package/dist/tools/nextjs-api-contract-readers.js +204 -0
  250. package/dist/tools/nextjs-api-contract-readers.js.map +1 -0
  251. package/dist/tools/nextjs-api-contract-tools.d.ts +57 -0
  252. package/dist/tools/nextjs-api-contract-tools.d.ts.map +1 -0
  253. package/dist/tools/nextjs-api-contract-tools.js +144 -0
  254. package/dist/tools/nextjs-api-contract-tools.js.map +1 -0
  255. package/dist/tools/nextjs-boundary-tools.d.ts +39 -0
  256. package/dist/tools/nextjs-boundary-tools.d.ts.map +1 -0
  257. package/dist/tools/nextjs-boundary-tools.js +152 -0
  258. package/dist/tools/nextjs-boundary-tools.js.map +1 -0
  259. package/dist/tools/nextjs-component-tools.d.ts +121 -0
  260. package/dist/tools/nextjs-component-tools.d.ts.map +1 -0
  261. package/dist/tools/nextjs-component-tools.js +460 -0
  262. package/dist/tools/nextjs-component-tools.js.map +1 -0
  263. package/dist/tools/nextjs-data-flow-tools.d.ts +42 -0
  264. package/dist/tools/nextjs-data-flow-tools.d.ts.map +1 -0
  265. package/dist/tools/nextjs-data-flow-tools.js +158 -0
  266. package/dist/tools/nextjs-data-flow-tools.js.map +1 -0
  267. package/dist/tools/nextjs-framework-audit-tools.d.ts +37 -0
  268. package/dist/tools/nextjs-framework-audit-tools.d.ts.map +1 -0
  269. package/dist/tools/nextjs-framework-audit-tools.js +211 -0
  270. package/dist/tools/nextjs-framework-audit-tools.js.map +1 -0
  271. package/dist/tools/nextjs-link-tools.d.ts +41 -0
  272. package/dist/tools/nextjs-link-tools.d.ts.map +1 -0
  273. package/dist/tools/nextjs-link-tools.js +157 -0
  274. package/dist/tools/nextjs-link-tools.js.map +1 -0
  275. package/dist/tools/nextjs-metadata-tools.d.ts +74 -0
  276. package/dist/tools/nextjs-metadata-tools.d.ts.map +1 -0
  277. package/dist/tools/nextjs-metadata-tools.js +252 -0
  278. package/dist/tools/nextjs-metadata-tools.js.map +1 -0
  279. package/dist/tools/nextjs-middleware-coverage-tools.d.ts +41 -0
  280. package/dist/tools/nextjs-middleware-coverage-tools.d.ts.map +1 -0
  281. package/dist/tools/nextjs-middleware-coverage-tools.js +88 -0
  282. package/dist/tools/nextjs-middleware-coverage-tools.js.map +1 -0
  283. package/dist/tools/nextjs-route-tools.d.ts +100 -0
  284. package/dist/tools/nextjs-route-tools.d.ts.map +1 -0
  285. package/dist/tools/nextjs-route-tools.js +493 -0
  286. package/dist/tools/nextjs-route-tools.js.map +1 -0
  287. package/dist/tools/nextjs-security-readers.d.ts +22 -0
  288. package/dist/tools/nextjs-security-readers.d.ts.map +1 -0
  289. package/dist/tools/nextjs-security-readers.js +318 -0
  290. package/dist/tools/nextjs-security-readers.js.map +1 -0
  291. package/dist/tools/nextjs-security-scoring.d.ts +15 -0
  292. package/dist/tools/nextjs-security-scoring.d.ts.map +1 -0
  293. package/dist/tools/nextjs-security-scoring.js +65 -0
  294. package/dist/tools/nextjs-security-scoring.js.map +1 -0
  295. package/dist/tools/nextjs-security-tools.d.ts +75 -0
  296. package/dist/tools/nextjs-security-tools.d.ts.map +1 -0
  297. package/dist/tools/nextjs-security-tools.js +153 -0
  298. package/dist/tools/nextjs-security-tools.js.map +1 -0
  299. package/dist/tools/nextjs-tools.d.ts +15 -0
  300. package/dist/tools/nextjs-tools.d.ts.map +1 -0
  301. package/dist/tools/nextjs-tools.js +15 -0
  302. package/dist/tools/nextjs-tools.js.map +1 -0
  303. package/dist/tools/outline-tools.d.ts.map +1 -1
  304. package/dist/tools/outline-tools.js +20 -0
  305. package/dist/tools/outline-tools.js.map +1 -1
  306. package/dist/tools/pattern-tools.d.ts +8 -0
  307. package/dist/tools/pattern-tools.d.ts.map +1 -1
  308. package/dist/tools/pattern-tools.js +561 -3
  309. package/dist/tools/pattern-tools.js.map +1 -1
  310. package/dist/tools/perf-tools.d.ts +32 -0
  311. package/dist/tools/perf-tools.d.ts.map +1 -0
  312. package/dist/tools/perf-tools.js +227 -0
  313. package/dist/tools/perf-tools.js.map +1 -0
  314. package/dist/tools/php-tools.d.ts +176 -0
  315. package/dist/tools/php-tools.d.ts.map +1 -0
  316. package/dist/tools/php-tools.js +543 -0
  317. package/dist/tools/php-tools.js.map +1 -0
  318. package/dist/tools/prisma-schema-tools.d.ts +44 -0
  319. package/dist/tools/prisma-schema-tools.d.ts.map +1 -0
  320. package/dist/tools/prisma-schema-tools.js +358 -0
  321. package/dist/tools/prisma-schema-tools.js.map +1 -0
  322. package/dist/tools/project-tools.d.ts +115 -6
  323. package/dist/tools/project-tools.d.ts.map +1 -1
  324. package/dist/tools/project-tools.js +594 -217
  325. package/dist/tools/project-tools.js.map +1 -1
  326. package/dist/tools/pyproject-tools.d.ts +23 -0
  327. package/dist/tools/pyproject-tools.d.ts.map +1 -0
  328. package/dist/tools/pyproject-tools.js +133 -0
  329. package/dist/tools/pyproject-tools.js.map +1 -0
  330. package/dist/tools/pytest-tools.d.ts +20 -0
  331. package/dist/tools/pytest-tools.d.ts.map +1 -0
  332. package/dist/tools/pytest-tools.js +106 -0
  333. package/dist/tools/pytest-tools.js.map +1 -0
  334. package/dist/tools/python-callers.d.ts +28 -0
  335. package/dist/tools/python-callers.d.ts.map +1 -0
  336. package/dist/tools/python-callers.js +110 -0
  337. package/dist/tools/python-callers.js.map +1 -0
  338. package/dist/tools/python-circular-imports.d.ts +19 -0
  339. package/dist/tools/python-circular-imports.d.ts.map +1 -0
  340. package/dist/tools/python-circular-imports.js +126 -0
  341. package/dist/tools/python-circular-imports.js.map +1 -0
  342. package/dist/tools/python-deps-analyzer.d.ts +46 -0
  343. package/dist/tools/python-deps-analyzer.d.ts.map +1 -0
  344. package/dist/tools/python-deps-analyzer.js +227 -0
  345. package/dist/tools/python-deps-analyzer.js.map +1 -0
  346. package/dist/tools/query-tools.d.ts +23 -0
  347. package/dist/tools/query-tools.d.ts.map +1 -0
  348. package/dist/tools/query-tools.js +256 -0
  349. package/dist/tools/query-tools.js.map +1 -0
  350. package/dist/tools/react-tools.d.ts +218 -0
  351. package/dist/tools/react-tools.d.ts.map +1 -0
  352. package/dist/tools/react-tools.js +714 -0
  353. package/dist/tools/react-tools.js.map +1 -0
  354. package/dist/tools/report-tools.js +47 -0
  355. package/dist/tools/report-tools.js.map +1 -1
  356. package/dist/tools/review-diff-tools.d.ts +2 -6
  357. package/dist/tools/review-diff-tools.d.ts.map +1 -1
  358. package/dist/tools/review-diff-tools.js +51 -66
  359. package/dist/tools/review-diff-tools.js.map +1 -1
  360. package/dist/tools/room-tools.d.ts +36 -0
  361. package/dist/tools/room-tools.d.ts.map +1 -0
  362. package/dist/tools/room-tools.js +147 -0
  363. package/dist/tools/room-tools.js.map +1 -0
  364. package/dist/tools/route-tools.d.ts +27 -1
  365. package/dist/tools/route-tools.d.ts.map +1 -1
  366. package/dist/tools/route-tools.js +744 -18
  367. package/dist/tools/route-tools.js.map +1 -1
  368. package/dist/tools/ruff-tools.d.ts +32 -0
  369. package/dist/tools/ruff-tools.d.ts.map +1 -0
  370. package/dist/tools/ruff-tools.js +114 -0
  371. package/dist/tools/ruff-tools.js.map +1 -0
  372. package/dist/tools/search-ranker.d.ts.map +1 -1
  373. package/dist/tools/search-ranker.js +7 -0
  374. package/dist/tools/search-ranker.js.map +1 -1
  375. package/dist/tools/serialization-tools.d.ts +24 -0
  376. package/dist/tools/serialization-tools.d.ts.map +1 -0
  377. package/dist/tools/serialization-tools.js +156 -0
  378. package/dist/tools/serialization-tools.js.map +1 -0
  379. package/dist/tools/sql-tools.d.ts +234 -0
  380. package/dist/tools/sql-tools.d.ts.map +1 -0
  381. package/dist/tools/sql-tools.js +1037 -0
  382. package/dist/tools/sql-tools.js.map +1 -0
  383. package/dist/tools/status-tools.d.ts +10 -0
  384. package/dist/tools/status-tools.d.ts.map +1 -0
  385. package/dist/tools/status-tools.js +32 -0
  386. package/dist/tools/status-tools.js.map +1 -0
  387. package/dist/tools/symbol-tools.d.ts +19 -0
  388. package/dist/tools/symbol-tools.d.ts.map +1 -1
  389. package/dist/tools/symbol-tools.js +78 -4
  390. package/dist/tools/symbol-tools.js.map +1 -1
  391. package/dist/tools/test-impact-tools.d.ts +29 -0
  392. package/dist/tools/test-impact-tools.d.ts.map +1 -0
  393. package/dist/tools/test-impact-tools.js +156 -0
  394. package/dist/tools/test-impact-tools.js.map +1 -0
  395. package/dist/tools/typecheck-tools.d.ts +39 -0
  396. package/dist/tools/typecheck-tools.d.ts.map +1 -0
  397. package/dist/tools/typecheck-tools.js +191 -0
  398. package/dist/tools/typecheck-tools.js.map +1 -0
  399. package/dist/tools/wiring-tools.d.ts +19 -0
  400. package/dist/tools/wiring-tools.d.ts.map +1 -0
  401. package/dist/tools/wiring-tools.js +147 -0
  402. package/dist/tools/wiring-tools.js.map +1 -0
  403. package/dist/types.d.ts +9 -1
  404. package/dist/types.d.ts.map +1 -1
  405. package/dist/utils/framework-detect.d.ts +18 -2
  406. package/dist/utils/framework-detect.d.ts.map +1 -1
  407. package/dist/utils/framework-detect.js +150 -3
  408. package/dist/utils/framework-detect.js.map +1 -1
  409. package/dist/utils/import-graph.d.ts +36 -0
  410. package/dist/utils/import-graph.d.ts.map +1 -1
  411. package/dist/utils/import-graph.js +212 -9
  412. package/dist/utils/import-graph.js.map +1 -1
  413. package/dist/utils/language-detect.d.ts +21 -0
  414. package/dist/utils/language-detect.d.ts.map +1 -0
  415. package/dist/utils/language-detect.js +183 -0
  416. package/dist/utils/language-detect.js.map +1 -0
  417. package/dist/utils/nextjs-ast-readers.d.ts +44 -0
  418. package/dist/utils/nextjs-ast-readers.d.ts.map +1 -0
  419. package/dist/utils/nextjs-ast-readers.js +341 -0
  420. package/dist/utils/nextjs-ast-readers.js.map +1 -0
  421. package/dist/utils/nextjs-audit-cache.d.ts +51 -0
  422. package/dist/utils/nextjs-audit-cache.d.ts.map +1 -0
  423. package/dist/utils/nextjs-audit-cache.js +116 -0
  424. package/dist/utils/nextjs-audit-cache.js.map +1 -0
  425. package/dist/utils/nextjs-metadata-readers.d.ts +65 -0
  426. package/dist/utils/nextjs-metadata-readers.d.ts.map +1 -0
  427. package/dist/utils/nextjs-metadata-readers.js +447 -0
  428. package/dist/utils/nextjs-metadata-readers.js.map +1 -0
  429. package/dist/utils/nextjs.d.ts +42 -0
  430. package/dist/utils/nextjs.d.ts.map +1 -0
  431. package/dist/utils/nextjs.js +284 -0
  432. package/dist/utils/nextjs.js.map +1 -0
  433. package/dist/utils/python-import-resolver.d.ts +42 -0
  434. package/dist/utils/python-import-resolver.d.ts.map +1 -0
  435. package/dist/utils/python-import-resolver.js +101 -0
  436. package/dist/utils/python-import-resolver.js.map +1 -0
  437. package/dist/utils/python-imports.d.ts +28 -0
  438. package/dist/utils/python-imports.d.ts.map +1 -0
  439. package/dist/utils/python-imports.js +117 -0
  440. package/dist/utils/python-imports.js.map +1 -0
  441. package/dist/utils/react-alias.d.ts +15 -0
  442. package/dist/utils/react-alias.d.ts.map +1 -0
  443. package/dist/utils/react-alias.js +31 -0
  444. package/dist/utils/react-alias.js.map +1 -0
  445. package/dist/utils/test-file.d.ts.map +1 -1
  446. package/dist/utils/test-file.js +7 -0
  447. package/dist/utils/test-file.js.map +1 -1
  448. package/dist/utils/walk.d.ts +22 -0
  449. package/dist/utils/walk.d.ts.map +1 -1
  450. package/dist/utils/walk.js +70 -2
  451. package/dist/utils/walk.js.map +1 -1
  452. package/package.json +3 -2
  453. package/rules/codesift.md +34 -5
  454. package/rules/codesift.mdc +34 -5
  455. package/rules/codex.md +34 -5
  456. package/rules/gemini.md +34 -5
  457. package/src/parser/languages/tree-sitter-javascript.wasm +0 -0
  458. package/src/parser/languages/tree-sitter-kotlin.wasm +0 -0
  459. package/src/parser/languages/tree-sitter-php.wasm +0 -0
  460. package/src/parser/languages/tree-sitter-php_only.wasm +0 -0
  461. package/src/parser/languages/tree-sitter-python.wasm +0 -0
@@ -1,20 +1,37 @@
1
1
  /**
2
2
  * HTTP route tracing — given a URL path, find handler → service → DB calls.
3
- * Supports NestJS decorators, Next.js App Router, and Express patterns.
3
+ * Supports NestJS decorators, Next.js App Router, Express, Yii2 conventions, and Laravel routes.
4
4
  */
5
5
  import { getCodeIndex } from "./index-tools.js";
6
6
  import { buildAdjacencyIndex, buildCallTree, stripSource } from "./graph-tools.js";
7
+ import { findAstroHandlers } from "./astro-routes.js";
8
+ import { deriveUrlPath, computeLayoutChain, traceMiddleware, scanDirective } from "../utils/nextjs.js";
9
+ import { join } from "node:path";
7
10
  const DB_PATTERNS = [
8
11
  /prisma\.\w+\.(findMany|findFirst|findUnique|create|update|delete|upsert|count|aggregate|groupBy)/,
9
12
  /\.\$(transaction|queryRaw|executeRaw)/,
10
13
  /getRepository|\.query\(|\.execute\(/,
11
14
  /knex\.|\.raw\(/,
15
+ // PHP / Yii2 ActiveRecord
16
+ /->find\(\)|->findOne\(|->findAll\(|->findBySql\(/,
17
+ /->createCommand\(|Yii::\$app->db/,
18
+ /::find\(\)->where\(|->andWhere\(|->orWhere\(/,
19
+ // Kotlin — Exposed ORM, Spring Data, Ktor
20
+ /transaction\s*\{[\s\S]*?\.(select|insert|update|delete)/,
21
+ /\.(findById|findAll|save|deleteById|findBy\w+)\s*\(/,
22
+ /\bSchemaUtils\.(create|drop)/,
23
+ // Python — Django ORM, SQLAlchemy
24
+ /\.objects\.(get|filter|all|exclude|create|update|delete|aggregate|annotate|values|values_list|count|exists|first|last|bulk_create|bulk_update|get_or_create|update_or_create)\s*\(/,
25
+ /\.query\.(filter|filter_by|get|all|first|one|one_or_none|join|outerjoin|subquery)\s*\(/,
26
+ /session\.(add|delete|commit|rollback|flush|execute|query)\s*\(/,
27
+ /\.select_related\(|\.prefetch_related\(/,
12
28
  ];
13
29
  /**
14
30
  * Match a URL path pattern against a route definition.
15
- * Handles :param, [param], [...param], [[...param]] as wildcards.
31
+ * Handles :param, [param], [...param], [[...param]], <type:name> (Flask/Django),
32
+ * {name} (FastAPI) as wildcards.
16
33
  */
17
- function matchPath(routePath, searchPath) {
34
+ export function matchPath(routePath, searchPath) {
18
35
  const normalize = (p) => p.replace(/^\/|\/$/g, "").toLowerCase();
19
36
  const routeParts = normalize(routePath).split("/");
20
37
  const searchParts = normalize(searchPath).split("/");
@@ -23,8 +40,8 @@ function matchPath(routePath, searchPath) {
23
40
  for (let i = 0; i < routeParts.length; i++) {
24
41
  const rp = routeParts[i];
25
42
  const sp = searchParts[i];
26
- // Dynamic segments: :id, [id], [...slug], [[...slug]]
27
- if (rp.startsWith(":") || rp.startsWith("["))
43
+ // Dynamic segments: :id, [id], [...slug], [[...slug]], <type:name>, {name}
44
+ if (rp.startsWith(":") || rp.startsWith("[") || rp.startsWith("<") || rp.startsWith("{"))
28
45
  continue;
29
46
  if (rp !== sp)
30
47
  return false;
@@ -35,25 +52,27 @@ function matchPath(routePath, searchPath) {
35
52
  * Find NestJS route handlers via @Controller + @Get/@Post/etc. decorators.
36
53
  * Reads raw file content because tree-sitter symbol source may not include decorators.
37
54
  */
38
- async function findNestJSHandlers(index, searchPath) {
55
+ export async function findNestJSHandlers(index, searchPath) {
39
56
  const handlers = [];
40
57
  const methods = ["Get", "Post", "Put", "Delete", "Patch"];
41
58
  // Find controller files
42
59
  const controllerFiles = index.files.filter((f) => f.path.endsWith(".controller.ts") || f.path.endsWith(".controller.js"));
43
60
  const { readFile } = await import("node:fs/promises");
44
- const { join } = await import("node:path");
61
+ const { join: joinPath } = await import("node:path");
45
62
  for (const file of controllerFiles) {
46
63
  let source;
47
64
  try {
48
- source = await readFile(join(index.root, file.path), "utf-8");
65
+ source = await readFile(joinPath(index.root, file.path), "utf-8");
49
66
  }
50
67
  catch {
51
68
  continue;
52
69
  }
53
- // Extract controller prefix
54
- const ctrlMatch = /@Controller\s*\(\s*['"`]([^'"`]*)['"`]/.exec(source);
55
- const controllerPrefix = ctrlMatch?.[1] ?? "";
70
+ // Extract controller prefix — supports both @Controller('prefix') and @Controller()
71
+ const ctrlMatchStr = /@Controller\s*\(\s*['"`]([^'"`]*)['"`]/.exec(source);
72
+ const ctrlMatchEmpty = !ctrlMatchStr ? /@Controller\s*\(\s*\)/.exec(source) : null;
73
+ const controllerPrefix = ctrlMatchStr?.[1] ?? (ctrlMatchEmpty ? "" : "");
56
74
  for (const method of methods) {
75
+ // Pass 1: string-literal paths — @Get('path')
57
76
  const re = new RegExp(`@${method}\\s*\\(\\s*['"\`]([^'"\`]*)['"\`]\\s*\\)\\s*\\n\\s*(?:async\\s+)?(\\w+)`, "g");
58
77
  let match;
59
78
  while ((match = re.exec(source)) !== null) {
@@ -70,6 +89,24 @@ async function findNestJSHandlers(index, searchPath) {
70
89
  });
71
90
  }
72
91
  }
92
+ // Pass 2: empty decorator — @Get() with no path argument
93
+ const reEmpty = new RegExp(`@${method}\\s*\\(\\s*\\)\\s*\\n\\s*(?:async\\s+)?(\\w+)`, "g");
94
+ let emptyMatch;
95
+ while ((emptyMatch = reEmpty.exec(source)) !== null) {
96
+ const funcName = emptyMatch[1] ?? "";
97
+ const fullPath = `/${controllerPrefix}`.replace(/\/+/g, "/") || "/";
98
+ if (matchPath(fullPath, searchPath)) {
99
+ if (handlers.some((h) => h.file === file.path && h.symbol.name === funcName && h.method === method.toUpperCase()))
100
+ continue;
101
+ const sym = index.symbols.find((s) => s.file === file.path && s.name === funcName);
102
+ handlers.push({
103
+ symbol: sym ? stripSource(sym) : { id: `${file.path}:${funcName}`, name: funcName, kind: "method", file: file.path, start_line: 1, end_line: 1 },
104
+ file: file.path,
105
+ method: method.toUpperCase(),
106
+ framework: "nestjs",
107
+ });
108
+ }
109
+ }
73
110
  }
74
111
  }
75
112
  return handlers;
@@ -81,14 +118,15 @@ function findNextJSHandlers(index, searchPath) {
81
118
  const handlers = [];
82
119
  const normalized = searchPath.replace(/^\/|\/$/g, "");
83
120
  for (const file of index.files) {
84
- // Match app/api/...route.ts or app/...route.ts
85
- if (!file.path.endsWith("/route.ts") && !file.path.endsWith("/route.js"))
121
+ // Match app/api/...route.{ts,tsx,js,jsx} or app/...route.{ts,tsx,js,jsx}
122
+ if (!/\/route\.[jt]sx?$/.test(file.path))
86
123
  continue;
87
124
  // Extract route path from file path: app/api/users/[id]/route.ts → /api/users/[id]
88
- const routeMatch = file.path.match(/app\/(.*?)\/route\.\w+$/);
125
+ const routeMatch = file.path.match(/app\/(.*?)\/route\.[jt]sx?$/);
89
126
  if (!routeMatch)
90
127
  continue;
91
- const filePath = routeMatch[1];
128
+ // Strip route groups: (auth)/login → login
129
+ const filePath = routeMatch[1].replace(/\([^)]+\)\/?/g, "");
92
130
  if (matchPath(filePath, normalized)) {
93
131
  // Find exported handler functions (GET, POST, etc.)
94
132
  const fileSymbols = index.symbols.filter((s) => s.file === file.path && /^(GET|POST|PUT|DELETE|PATCH)$/.test(s.name));
@@ -98,6 +136,7 @@ function findNextJSHandlers(index, searchPath) {
98
136
  file: sym.file,
99
137
  method: sym.name,
100
138
  framework: "nextjs",
139
+ router: "app",
101
140
  });
102
141
  }
103
142
  // If no named exports found, add the file itself
@@ -106,21 +145,162 @@ function findNextJSHandlers(index, searchPath) {
106
145
  symbol: { id: file.path, name: "route", kind: "function", file: file.path, start_line: 1, end_line: 1 },
107
146
  file: file.path,
108
147
  framework: "nextjs",
148
+ router: "app",
149
+ });
150
+ }
151
+ }
152
+ }
153
+ return handlers;
154
+ }
155
+ /**
156
+ * Find Pages Router API route handlers via default exports in pages/api/.
157
+ * @internal exported for unit testing
158
+ */
159
+ function findPagesRouterHandlers(index, searchPath) {
160
+ const handlers = [];
161
+ // Require Next.js project signal to disambiguate from Astro's src/pages/api/*.
162
+ // Accept next.config.* at root/src OR an App Router convention file.
163
+ const hasNextSignal = index.files.some((f) => /^(src\/)?next\.config\.[mc]?[jt]sx?$/.test(f.path) ||
164
+ /(^|\/)app\/.*\/(page|layout|route)\.[jt]sx?$/.test(f.path));
165
+ if (!hasNextSignal)
166
+ return handlers;
167
+ for (const file of index.files) {
168
+ // Only match files under pages/api/ (not src/pages/api/ which is Astro convention)
169
+ if (!/^(\.\/)?pages\/api\//.test(file.path))
170
+ continue;
171
+ // Derive URL path from file path
172
+ const urlPath = deriveUrlPath(file.path, "pages");
173
+ const normalizedSearch = searchPath.replace(/^\/|\/$/g, "");
174
+ const normalizedUrl = urlPath.replace(/^\/|\/$/g, "");
175
+ if (normalizedUrl !== normalizedSearch)
176
+ continue;
177
+ // Find default export or named handler in the file
178
+ const fileSymbols = index.symbols.filter((s) => s.file === file.path);
179
+ // Look for default export
180
+ const defaultExport = fileSymbols.find((s) => s.name === "default" || s.name === "handler");
181
+ if (defaultExport) {
182
+ handlers.push({
183
+ symbol: stripSource(defaultExport),
184
+ file: file.path,
185
+ framework: "nextjs",
186
+ router: "pages",
187
+ });
188
+ }
189
+ else if (fileSymbols.length > 0) {
190
+ // Try variable indirection: find any exported function
191
+ const exported = fileSymbols.find((s) => s.kind === "function" || s.kind === "variable");
192
+ if (exported) {
193
+ handlers.push({
194
+ symbol: stripSource(exported),
195
+ file: file.path,
196
+ framework: "nextjs",
197
+ router: "pages",
109
198
  });
110
199
  }
111
200
  }
201
+ // Fallback: at least mark the file as having a handler
202
+ if (handlers.filter((h) => h.file === file.path).length === 0) {
203
+ handlers.push({
204
+ symbol: {
205
+ id: file.path, name: "handler", kind: "function",
206
+ file: file.path, start_line: 1, end_line: 1,
207
+ },
208
+ file: file.path,
209
+ framework: "nextjs",
210
+ router: "pages",
211
+ });
212
+ }
112
213
  }
113
214
  return handlers;
114
215
  }
115
216
  /**
116
217
  * Find Express-style route handlers via router.get/app.post patterns.
218
+ * Restricted to JS/TS files to avoid false positives from Python test
219
+ * code like `client.get('/')` or Ruby/PHP method calls.
220
+ */
221
+ /**
222
+ * Find Hono route handlers by consulting the HonoExtractor-produced model.
223
+ * Uses HonoCache when available, falls back to on-demand parse.
224
+ *
225
+ * The model has pre-resolved paths (basePath + mount prefix + route path),
226
+ * so path matching is a direct matchPath() call against each route.
117
227
  */
228
+ async function findHonoHandlers(repo, index, searchPath) {
229
+ // Only run if Hono is detected
230
+ const { detectFrameworks } = await import("../utils/framework-detect.js");
231
+ const frameworks = detectFrameworks(index);
232
+ if (!frameworks.has("hono"))
233
+ return [];
234
+ // Resolve entry file: prefer orchestrator, else first file containing `new Hono()`
235
+ const entryFile = resolveHonoEntryFile(index);
236
+ if (!entryFile)
237
+ return [];
238
+ // Get model (via cache)
239
+ let model;
240
+ try {
241
+ const { honoCache } = await import("../cache/hono-cache.js");
242
+ const { HonoExtractor } = await import("../parser/extractors/hono.js");
243
+ const extractor = new HonoExtractor();
244
+ model = await honoCache.get(repo, entryFile, extractor);
245
+ }
246
+ catch {
247
+ return [];
248
+ }
249
+ const handlers = [];
250
+ for (const route of model.routes) {
251
+ if (!matchPath(route.path, searchPath))
252
+ continue;
253
+ // Find the handler symbol in the index by file + name + line
254
+ let handlerSym = index.symbols.find((s) => s.file === route.handler.file.replace(index.root + "/", "") &&
255
+ s.name === route.handler.name &&
256
+ Math.abs(s.start_line - route.handler.line) <= 2);
257
+ // Fallback: synthetic stub for inline handlers
258
+ if (!handlerSym) {
259
+ handlerSym = {
260
+ id: `hono:${route.file}:${route.line}`,
261
+ repo,
262
+ name: route.handler.name,
263
+ kind: "function",
264
+ file: route.handler.file.replace(index.root + "/", ""),
265
+ start_line: route.handler.line,
266
+ end_line: route.handler.line,
267
+ start_byte: 0,
268
+ end_byte: 0,
269
+ source: "",
270
+ tokens: [route.handler.name],
271
+ };
272
+ }
273
+ handlers.push({
274
+ symbol: stripSource(handlerSym),
275
+ file: handlerSym.file,
276
+ method: route.method,
277
+ framework: "hono",
278
+ });
279
+ }
280
+ return handlers;
281
+ }
282
+ /** Find the Hono entry file for a repo — ORCHESTRATOR or first new Hono() file. */
283
+ function resolveHonoEntryFile(index) {
284
+ for (const sym of index.symbols) {
285
+ if (sym.source && /new\s+(?:Hono|OpenAPIHono)\s*(?:<[^>]*>)?\s*\(/.test(sym.source)) {
286
+ // Return absolute path
287
+ return join(index.root, sym.file);
288
+ }
289
+ }
290
+ return null;
291
+ }
118
292
  function findExpressHandlers(index, searchPath) {
119
293
  const handlers = [];
120
294
  const methods = ["get", "post", "put", "delete", "patch"];
121
295
  for (const sym of index.symbols) {
122
296
  if (!sym.source)
123
297
  continue;
298
+ // Only scan JS/TS files — .get()/.post() is ambiguous across languages
299
+ if (!/\.(ts|tsx|js|jsx|mjs|cjs)$/.test(sym.file))
300
+ continue;
301
+ // Skip test files to avoid matching test harness client calls
302
+ if (/\.(test|spec)\.(ts|tsx|js|jsx)$/.test(sym.file))
303
+ continue;
124
304
  for (const method of methods) {
125
305
  const re = new RegExp(`\\.(${method})\\s*\\(\\s*['"\`]([^'"\`]+)['"\`]`);
126
306
  const match = re.exec(sym.source);
@@ -142,6 +322,37 @@ function findExpressHandlers(index, searchPath) {
142
322
  /**
143
323
  * Detect DB operations in a symbol's call chain.
144
324
  */
325
+ /**
326
+ * Detect server actions in the call chain by checking for "use server" directive
327
+ * at the file level (not function-body level).
328
+ */
329
+ async function findServerActions(repoRoot, calleeSymbols, callChain) {
330
+ const actions = [];
331
+ const checkedFiles = new Map();
332
+ for (const sym of calleeSymbols) {
333
+ const absPath = join(repoRoot, sym.file);
334
+ let hasDirective;
335
+ if (checkedFiles.has(sym.file)) {
336
+ hasDirective = checkedFiles.get(sym.file);
337
+ }
338
+ else {
339
+ const directive = await scanDirective(absPath);
340
+ hasDirective = directive === "use server";
341
+ checkedFiles.set(sym.file, hasDirective);
342
+ }
343
+ if (hasDirective) {
344
+ // Find who called this symbol
345
+ const callerIdx = callChain.findIndex((c) => c.file === sym.file && c.name === sym.name);
346
+ const calledFrom = callerIdx > 0 ? callChain[callerIdx - 1]?.name : undefined;
347
+ actions.push({
348
+ name: sym.name,
349
+ file: sym.file,
350
+ called_from: calledFrom,
351
+ });
352
+ }
353
+ }
354
+ return actions;
355
+ }
145
356
  function findDbCalls(symbols) {
146
357
  const calls = [];
147
358
  for (const sym of symbols) {
@@ -184,8 +395,9 @@ function appendDbCalls(lines, dbCalls, node, actor) {
184
395
  }
185
396
  /**
186
397
  * Render a RouteTraceResult as a Mermaid sequence diagram.
398
+ * @internal exported for unit testing
187
399
  */
188
- function routeToMermaid(result) {
400
+ export function routeToMermaid(result) {
189
401
  if (result.handlers.length === 0) {
190
402
  return "sequenceDiagram\n Note over Client: No handler found for " + result.path;
191
403
  }
@@ -193,7 +405,26 @@ function routeToMermaid(result) {
193
405
  const handler = result.handlers[0];
194
406
  const method = handler.method ?? "REQUEST";
195
407
  const aliases = new Map();
196
- lines.push(` Client->>+Controller: ${method} ${result.path}`);
408
+ // Add Middleware participant if middleware applies
409
+ if (result.middleware?.applies) {
410
+ lines.push(` participant Middleware`);
411
+ lines.push(` Client->>+Middleware: ${method} ${result.path}`);
412
+ lines.push(` Middleware->>+Controller: continue`);
413
+ }
414
+ else {
415
+ lines.push(` Client->>+Controller: ${method} ${result.path}`);
416
+ }
417
+ // Add Layout chain rendering
418
+ if (result.layout_chain && result.layout_chain.length > 0) {
419
+ let prev = "Controller";
420
+ for (let i = 0; i < result.layout_chain.length; i++) {
421
+ const layoutName = `Layout${i + 1}`;
422
+ const layoutFile = result.layout_chain[i];
423
+ lines.push(` participant ${layoutName}`);
424
+ lines.push(` ${prev}->>+${layoutName}: render (${layoutFile})`);
425
+ prev = layoutName;
426
+ }
427
+ }
197
428
  const root = result.call_chain[0];
198
429
  if (root) {
199
430
  appendDbCalls(lines, result.db_calls, root, "Controller");
@@ -223,9 +454,463 @@ function routeToMermaid(result) {
223
454
  closeUntilDepth(nextDepth);
224
455
  }
225
456
  closeUntilDepth(0);
226
- lines.push(` Controller-->>-Client: response`);
457
+ // Close layout chain
458
+ if (result.layout_chain && result.layout_chain.length > 0) {
459
+ for (let i = result.layout_chain.length - 1; i >= 0; i--) {
460
+ const layoutName = `Layout${i + 1}`;
461
+ const returnTo = i > 0 ? `Layout${i}` : "Controller";
462
+ lines.push(` ${layoutName}-->>-${returnTo}: rendered`);
463
+ }
464
+ }
465
+ if (result.middleware?.applies) {
466
+ lines.push(` Controller-->>-Middleware: response`);
467
+ lines.push(` Middleware-->>-Client: response`);
468
+ }
469
+ else {
470
+ lines.push(` Controller-->>-Client: response`);
471
+ }
227
472
  return lines.join("\n");
228
473
  }
474
+ /**
475
+ * Find Yii2 route handlers via convention: controller-id/action-id → ControllerIdController::actionActionId().
476
+ * Supports modules: module-id/controller-id/action-id.
477
+ */
478
+ async function findYii2Handlers(index, searchPath) {
479
+ const handlers = [];
480
+ const normalized = searchPath.replace(/^\/|\/$/g, "").toLowerCase();
481
+ const segments = normalized.split("/").filter(Boolean);
482
+ if (segments.length === 0)
483
+ return handlers;
484
+ // Determine controller ID and action ID
485
+ // Patterns: "controller/action", "module/controller/action", "controller" (default action=index)
486
+ let controllerId;
487
+ let actionId;
488
+ if (segments.length === 1) {
489
+ controllerId = segments[0];
490
+ actionId = "index";
491
+ }
492
+ else if (segments.length === 2) {
493
+ controllerId = segments[0];
494
+ actionId = segments[1];
495
+ }
496
+ else {
497
+ // Module routing: take last two segments as controller/action
498
+ controllerId = segments[segments.length - 2];
499
+ actionId = segments[segments.length - 1];
500
+ }
501
+ // Convert kebab-case to PascalCase for class name: "site" → "Site", "user-comment" → "UserComment"
502
+ const toPascal = (s) => s.split("-").map(p => p.charAt(0).toUpperCase() + p.slice(1)).join("");
503
+ // Convert kebab-case to camelCase for action method: "hello-world" → "HelloWorld"
504
+ const toCamelAction = (s) => s.split("-").map(p => p.charAt(0).toUpperCase() + p.slice(1)).join("");
505
+ const controllerName = toPascal(controllerId) + "Controller";
506
+ const actionMethod = "action" + toCamelAction(actionId);
507
+ // Find controller class in index
508
+ const controllerSymbol = index.symbols.find((s) => s.name === controllerName && s.kind === "class");
509
+ if (!controllerSymbol) {
510
+ // Fallback: try urlManager rules from config/web.php
511
+ return findYii2HandlersFromConfig(index, searchPath);
512
+ }
513
+ // Find action method within the controller
514
+ const actionSymbol = index.symbols.find((s) => s.name === actionMethod && s.parent === controllerSymbol.id);
515
+ if (actionSymbol) {
516
+ handlers.push({
517
+ symbol: stripSource(actionSymbol),
518
+ file: actionSymbol.file,
519
+ method: "GET",
520
+ framework: "yii2",
521
+ });
522
+ }
523
+ else {
524
+ // Fallback: controller found but action method not indexed — report controller
525
+ handlers.push({
526
+ symbol: stripSource(controllerSymbol),
527
+ file: controllerSymbol.file,
528
+ framework: "yii2",
529
+ });
530
+ }
531
+ return handlers;
532
+ }
533
+ /**
534
+ * Fallback: parse Yii2 urlManager rules from config/web.php.
535
+ * Matches patterns like: 'GET api/users/<id>' => 'user/view'
536
+ */
537
+ async function findYii2HandlersFromConfig(index, searchPath) {
538
+ const handlers = [];
539
+ const configFile = index.files.find((f) => /config\/web\.php$/.test(f.path));
540
+ if (!configFile)
541
+ return handlers;
542
+ const { readFile: rf } = await import("node:fs/promises");
543
+ const { join: j } = await import("node:path");
544
+ let source;
545
+ try {
546
+ source = await rf(j(index.root, configFile.path), "utf-8");
547
+ }
548
+ catch {
549
+ return handlers;
550
+ }
551
+ const normalized = searchPath.replace(/^\/|\/$/g, "").toLowerCase();
552
+ // Match: 'route/pattern' => 'controller/action' or ['GET method/pattern'] => 'controller/action'
553
+ const ruleRe = /['"](?:(?:GET|POST|PUT|DELETE|PATCH)\s+)?([^'"]+)['"]\s*=>\s*['"]([^'"]+)['"]/g;
554
+ let match;
555
+ while ((match = ruleRe.exec(source)) !== null) {
556
+ const rulePattern = match[1].replace(/<\w+(?::[^>]+)?>/g, "[param]").toLowerCase();
557
+ if (!matchPath(rulePattern, normalized))
558
+ continue;
559
+ const route = match[2]; // e.g. "user/view"
560
+ const parts = route.split("/");
561
+ if (parts.length < 2)
562
+ continue;
563
+ const controllerId = parts[parts.length - 2];
564
+ const actionId = parts[parts.length - 1];
565
+ const toPascal = (s) => s.split("-").map(p => p.charAt(0).toUpperCase() + p.slice(1)).join("");
566
+ const controllerName = toPascal(controllerId) + "Controller";
567
+ const actionMethod = "action" + toPascal(actionId);
568
+ const ctrlSym = index.symbols.find(s => s.name === controllerName && s.kind === "class");
569
+ if (!ctrlSym)
570
+ continue;
571
+ const actionSym = index.symbols.find(s => s.name === actionMethod && s.parent === ctrlSym.id);
572
+ handlers.push({
573
+ symbol: stripSource(actionSym ?? ctrlSym),
574
+ file: (actionSym ?? ctrlSym).file,
575
+ method: "GET",
576
+ framework: "yii2",
577
+ });
578
+ }
579
+ return handlers;
580
+ }
581
+ /**
582
+ * Find Laravel route handlers by scanning route files for Route::method() patterns.
583
+ */
584
+ async function findLaravelHandlers(index, searchPath) {
585
+ const handlers = [];
586
+ const routeFiles = index.files.filter((f) => /routes\/(web|api)\.php$/.test(f.path));
587
+ if (routeFiles.length === 0)
588
+ return handlers;
589
+ const { readFile } = await import("node:fs/promises");
590
+ const { join } = await import("node:path");
591
+ const methods = ["get", "post", "put", "delete", "patch"];
592
+ for (const file of routeFiles) {
593
+ let source;
594
+ try {
595
+ source = await readFile(join(index.root, file.path), "utf-8");
596
+ }
597
+ catch {
598
+ continue;
599
+ }
600
+ for (const method of methods) {
601
+ // Match: Route::get('/path', [Controller::class, 'method']) or Route::get('/path', 'Controller@method')
602
+ const re = new RegExp(`Route::${method}\\s*\\(\\s*['"\`]([^'"\`]+)['"\`]\\s*,\\s*(?:\\[([\\w\\\\]+)::class\\s*,\\s*['"\`](\\w+)['"\`]\\]|['"\`](\\w+)@(\\w+)['"\`])`, "gi");
603
+ let match;
604
+ while ((match = re.exec(source)) !== null) {
605
+ const routePath = match[1] ?? "";
606
+ const controllerClass = match[2] ?? match[4] ?? "";
607
+ const methodName = match[3] ?? match[5] ?? "";
608
+ if (!matchPath(routePath, searchPath))
609
+ continue;
610
+ // Find the controller method in the index
611
+ const controllerName = controllerClass.split("\\").pop() ?? controllerClass;
612
+ const sym = index.symbols.find((s) => s.name === methodName && s.kind === "method" &&
613
+ index.symbols.some((c) => c.id === s.parent && c.name === controllerName));
614
+ handlers.push({
615
+ symbol: sym
616
+ ? stripSource(sym)
617
+ : { id: `${controllerName}::${methodName}`, name: methodName, kind: "method", file: file.path, start_line: 0, end_line: 0 },
618
+ file: sym?.file ?? file.path,
619
+ method: method.toUpperCase(),
620
+ framework: "laravel",
621
+ });
622
+ }
623
+ }
624
+ }
625
+ return handlers;
626
+ }
627
+ /**
628
+ * Find Ktor route handlers via `routing { get("/path") { ... } }` DSL.
629
+ * Supports nested `route("/prefix") { get("/sub") { } }` patterns.
630
+ */
631
+ async function findKtorHandlers(index, searchPath) {
632
+ const handlers = [];
633
+ const methods = ["get", "post", "put", "delete", "patch", "head", "options"];
634
+ // Ktor handlers are in .kt files, typically in files containing "routing {" or with "Route" in name
635
+ const kotlinFiles = index.files.filter((f) => /\.kts?$/.test(f.path));
636
+ if (kotlinFiles.length === 0)
637
+ return handlers;
638
+ const { readFile } = await import("node:fs/promises");
639
+ const { join } = await import("node:path");
640
+ for (const file of kotlinFiles) {
641
+ let source;
642
+ try {
643
+ source = await readFile(join(index.root, file.path), "utf-8");
644
+ }
645
+ catch {
646
+ continue;
647
+ }
648
+ // Skip files without routing DSL
649
+ if (!/\b(routing|route)\s*[({]/.test(source))
650
+ continue;
651
+ // Extract route("/prefix") blocks to support nested prefixes
652
+ // Simple approach: find all method calls with path args, combine with enclosing route() prefix via line scan
653
+ const lines = source.split("\n");
654
+ const prefixStack = [];
655
+ let braceDepth = 0;
656
+ for (let i = 0; i < lines.length; i++) {
657
+ const line = lines[i];
658
+ // Track route("/prefix") { ... } blocks
659
+ const routeMatch = /\broute\s*\(\s*["']([^"']+)["']\s*\)\s*\{/.exec(line);
660
+ if (routeMatch) {
661
+ prefixStack.push({ prefix: routeMatch[1], braceDepth });
662
+ }
663
+ // Count braces to detect when route() scope closes
664
+ for (const ch of line) {
665
+ if (ch === "{")
666
+ braceDepth++;
667
+ else if (ch === "}") {
668
+ braceDepth--;
669
+ // Pop route prefixes whose scope ended
670
+ while (prefixStack.length > 0 &&
671
+ prefixStack[prefixStack.length - 1].braceDepth >= braceDepth) {
672
+ prefixStack.pop();
673
+ }
674
+ }
675
+ }
676
+ // Match method handlers: get("/path") { ... } or post("/path") { ... }
677
+ for (const method of methods) {
678
+ const re = new RegExp(`\\b${method}\\s*\\(\\s*["']([^"']+)["']\\s*\\)\\s*\\{`);
679
+ const match = re.exec(line);
680
+ if (!match)
681
+ continue;
682
+ const methodPath = match[1];
683
+ const prefix = prefixStack.map((p) => p.prefix).join("");
684
+ const fullPath = `${prefix}/${methodPath}`.replace(/\/+/g, "/");
685
+ if (!matchPath(fullPath, searchPath))
686
+ continue;
687
+ // Find enclosing function symbol (if any) for this line
688
+ const lineNum = i + 1;
689
+ const sym = index.symbols.find((s) => s.file === file.path && s.start_line <= lineNum && s.end_line >= lineNum);
690
+ handlers.push({
691
+ symbol: sym
692
+ ? stripSource(sym)
693
+ : {
694
+ id: `${file.path}:${method}:${methodPath}`,
695
+ name: `${method} ${methodPath}`,
696
+ kind: "function",
697
+ file: file.path,
698
+ start_line: lineNum,
699
+ end_line: lineNum,
700
+ },
701
+ file: file.path,
702
+ method: method.toUpperCase(),
703
+ framework: "ktor",
704
+ });
705
+ }
706
+ }
707
+ }
708
+ return handlers;
709
+ }
710
+ /**
711
+ * Find Spring Boot Kotlin route handlers via @RestController/@Controller + @GetMapping/etc.
712
+ */
713
+ async function findSpringBootKotlinHandlers(index, searchPath) {
714
+ const handlers = [];
715
+ const mappingAnnotations = [
716
+ { ann: "GetMapping", method: "GET" },
717
+ { ann: "PostMapping", method: "POST" },
718
+ { ann: "PutMapping", method: "PUT" },
719
+ { ann: "DeleteMapping", method: "DELETE" },
720
+ { ann: "PatchMapping", method: "PATCH" },
721
+ ];
722
+ const kotlinFiles = index.files.filter((f) => /\.kts?$/.test(f.path));
723
+ if (kotlinFiles.length === 0)
724
+ return handlers;
725
+ const { readFile } = await import("node:fs/promises");
726
+ const { join } = await import("node:path");
727
+ for (const file of kotlinFiles) {
728
+ let source;
729
+ try {
730
+ source = await readFile(join(index.root, file.path), "utf-8");
731
+ }
732
+ catch {
733
+ continue;
734
+ }
735
+ // Must have @RestController or @Controller annotation
736
+ if (!/@(?:RestController|Controller)\b/.test(source))
737
+ continue;
738
+ // Extract class-level @RequestMapping prefix (optional)
739
+ const classRequestMatch = /@RequestMapping\s*\(\s*(?:value\s*=\s*)?["']([^"']*)["']/.exec(source);
740
+ const classPrefix = classRequestMatch?.[1] ?? "";
741
+ for (const { ann, method } of mappingAnnotations) {
742
+ // Match: @GetMapping("/path") fun funcName(...)
743
+ // Or: @GetMapping(value = "/path") fun funcName(...)
744
+ const re = new RegExp(`@${ann}\\s*\\(\\s*(?:value\\s*=\\s*)?["']([^"']*)["'](?:[^)]*)?\\)\\s*(?:fun|\\n\\s*fun)\\s+(\\w+)`, "g");
745
+ let match;
746
+ while ((match = re.exec(source)) !== null) {
747
+ const routePath = match[1] ?? "";
748
+ const funcName = match[2] ?? "";
749
+ const fullPath = `${classPrefix}/${routePath}`.replace(/\/+/g, "/");
750
+ if (!matchPath(fullPath, searchPath))
751
+ continue;
752
+ const sym = index.symbols.find((s) => s.file === file.path && s.name === funcName);
753
+ handlers.push({
754
+ symbol: sym
755
+ ? stripSource(sym)
756
+ : {
757
+ id: `${file.path}:${funcName}`,
758
+ name: funcName,
759
+ kind: "method",
760
+ file: file.path,
761
+ start_line: 1,
762
+ end_line: 1,
763
+ },
764
+ file: file.path,
765
+ method,
766
+ framework: "spring-kotlin",
767
+ });
768
+ }
769
+ }
770
+ }
771
+ return handlers;
772
+ }
773
+ // --- Python frameworks ---
774
+ /**
775
+ * Detect Python test files by pytest naming conventions.
776
+ * Matches: test_*.py, *_test.py, conftest.py, and tests/ subdirectories.
777
+ */
778
+ function isPythonTestFile(path) {
779
+ const basename = path.split("/").pop() ?? path;
780
+ if (basename === "conftest.py")
781
+ return true;
782
+ if (/^test_.*\.py$/.test(basename))
783
+ return true;
784
+ if (/_test\.py$/.test(basename))
785
+ return true;
786
+ if (/\/tests?\//.test(path))
787
+ return true;
788
+ return false;
789
+ }
790
+ /**
791
+ * Find Flask route handlers via @app.route() and @bp.route() decorators.
792
+ * Also handles @app.get/post/put/delete() (Flask 2.0+ shorthand).
793
+ */
794
+ function findFlaskHandlers(index, searchPath) {
795
+ const handlers = [];
796
+ // Exclude test files and conftest — users tracing a production route rarely
797
+ // want test fixture routes like `@app.route('/')` inside test_*.py
798
+ const pyFiles = index.files.filter((f) => f.path.endsWith(".py")
799
+ && !isPythonTestFile(f.path));
800
+ for (const file of pyFiles) {
801
+ const fileSymbols = index.symbols.filter((s) => s.file === file.path);
802
+ for (const sym of fileSymbols) {
803
+ if (!sym.decorators || sym.decorators.length === 0)
804
+ continue;
805
+ for (const dec of sym.decorators) {
806
+ // Match @app.route('/path') or @bp.route('/path') — Flask-specific.
807
+ // For HTTP verb shortcuts (@app.get, @app.post), prefer FastAPI handler
808
+ // since the syntax is ambiguous between Flask 2.0+ and FastAPI.
809
+ const routeMatch = dec.match(/@\w+\.route\s*\(\s*['"]([^'"]*)['"]/);
810
+ if (!routeMatch)
811
+ continue;
812
+ const routePath = routeMatch[1] ?? "";
813
+ if (!matchPath(routePath, searchPath))
814
+ continue;
815
+ // @app.route can handle any method — omit method field
816
+ handlers.push({
817
+ symbol: stripSource(sym),
818
+ file: file.path,
819
+ framework: "flask",
820
+ });
821
+ }
822
+ }
823
+ }
824
+ return handlers;
825
+ }
826
+ /**
827
+ * Find FastAPI route handlers via @app.get/post/put/delete() and @router.get() decorators.
828
+ * Handles APIRouter prefix extraction.
829
+ */
830
+ function findFastAPIHandlers(index, searchPath) {
831
+ const handlers = [];
832
+ const pyFiles = index.files.filter((f) => f.path.endsWith(".py")
833
+ && !isPythonTestFile(f.path));
834
+ for (const file of pyFiles) {
835
+ const fileSymbols = index.symbols.filter((s) => s.file === file.path);
836
+ for (const sym of fileSymbols) {
837
+ if (!sym.decorators || sym.decorators.length === 0)
838
+ continue;
839
+ for (const dec of sym.decorators) {
840
+ // Match @app.get('/path') or @router.get('/path') etc.
841
+ const routeMatch = dec.match(/@\w+\.(get|post|put|delete|patch|options|head)\s*\(\s*['"]([^'"]*)['"]/);
842
+ if (!routeMatch)
843
+ continue;
844
+ const method = routeMatch[1].toUpperCase();
845
+ const routePath = routeMatch[2] ?? "";
846
+ if (!matchPath(routePath, searchPath))
847
+ continue;
848
+ handlers.push({
849
+ symbol: stripSource(sym),
850
+ file: file.path,
851
+ method,
852
+ framework: "fastapi",
853
+ });
854
+ }
855
+ }
856
+ }
857
+ return handlers;
858
+ }
859
+ /**
860
+ * Find Django route handlers by parsing urlpatterns in urls.py files.
861
+ * Handles path(), re_path(), and include() chains.
862
+ */
863
+ async function findDjangoHandlers(index, searchPath) {
864
+ const handlers = [];
865
+ const { readFile } = await import("node:fs/promises");
866
+ const { join } = await import("node:path");
867
+ // Find urls.py files
868
+ const urlFiles = index.files.filter((f) => f.path.endsWith("urls.py"));
869
+ for (const file of urlFiles) {
870
+ let source;
871
+ try {
872
+ source = await readFile(join(index.root, file.path), "utf-8");
873
+ }
874
+ catch {
875
+ continue;
876
+ }
877
+ // Extract path() patterns: path('users/', views.user_list, name='...')
878
+ // and path('users/<int:pk>/', views.user_detail)
879
+ const pathRe = /path\s*\(\s*['"]([^'"]*)['"]\s*,\s*([\w.]+)/g;
880
+ let match;
881
+ // Get the URL prefix from the file's directory context
882
+ // e.g., if this urls.py is included from a parent with prefix 'api/'
883
+ while ((match = pathRe.exec(source)) !== null) {
884
+ const routePath = match[1] ?? "";
885
+ const viewRef = match[2] ?? "";
886
+ // Skip include() references
887
+ if (viewRef === "include")
888
+ continue;
889
+ // Convert Django <type:name> to :name for matchPath
890
+ const normalizedPath = `/${routePath}`.replace(/<\w+:(\w+)>/g, ":$1").replace(/\/+/g, "/");
891
+ if (!matchPath(normalizedPath, searchPath))
892
+ continue;
893
+ // Resolve view reference to a symbol
894
+ const viewName = viewRef.split(".").pop() ?? viewRef;
895
+ const sym = index.symbols.find((s) => s.name === viewName && s.file.endsWith(".py"));
896
+ handlers.push({
897
+ symbol: sym
898
+ ? stripSource(sym)
899
+ : {
900
+ id: `${file.path}:${viewName}`,
901
+ name: viewName,
902
+ kind: "function",
903
+ file: file.path,
904
+ start_line: 1,
905
+ end_line: 1,
906
+ },
907
+ file: sym?.file ?? file.path,
908
+ framework: "django",
909
+ });
910
+ }
911
+ }
912
+ return handlers;
913
+ }
229
914
  /**
230
915
  * Trace an HTTP route: find handler, trace callees, identify DB calls.
231
916
  */
@@ -234,10 +919,21 @@ export async function traceRoute(repo, path, outputFormat) {
234
919
  if (!index)
235
920
  throw new Error(`Repository "${repo}" not found.`);
236
921
  // Try all frameworks
922
+ const astroHandlers = findAstroHandlers(index, path);
237
923
  const handlers = [
238
924
  ...(await findNestJSHandlers(index, path)),
239
925
  ...findNextJSHandlers(index, path),
926
+ ...findPagesRouterHandlers(index, path),
240
927
  ...findExpressHandlers(index, path),
928
+ ...(await findHonoHandlers(repo, index, path)),
929
+ ...(await findYii2Handlers(index, path)),
930
+ ...(await findLaravelHandlers(index, path)),
931
+ ...(await findKtorHandlers(index, path)),
932
+ ...(await findSpringBootKotlinHandlers(index, path)),
933
+ ...astroHandlers,
934
+ ...findFastAPIHandlers(index, path),
935
+ ...findFlaskHandlers(index, path),
936
+ ...(await findDjangoHandlers(index, path)),
241
937
  ];
242
938
  if (handlers.length === 0) {
243
939
  return { path, handlers: [], call_chain: [], db_calls: [] };
@@ -268,6 +964,36 @@ export async function traceRoute(repo, path, outputFormat) {
268
964
  }
269
965
  const dbCalls = findDbCalls(allCalleeSymbols);
270
966
  const result = { path, handlers, call_chain: callChain, db_calls: dbCalls };
967
+ // Next.js-specific: layout chain, middleware, and server actions tracing
968
+ const hasNextjsHandler = handlers.some((h) => h.framework === "nextjs");
969
+ if (hasNextjsHandler) {
970
+ const repoRoot = index.root;
971
+ // Layout chain from the first handler's file
972
+ const firstFile = handlers[0]?.file;
973
+ if (firstFile) {
974
+ try {
975
+ result.layout_chain = await computeLayoutChain(firstFile, repoRoot);
976
+ }
977
+ catch {
978
+ result.layout_chain = [];
979
+ }
980
+ }
981
+ else {
982
+ result.layout_chain = [];
983
+ }
984
+ // Middleware tracing
985
+ try {
986
+ const mw = await traceMiddleware(repoRoot, path);
987
+ if (mw) {
988
+ result.middleware = mw;
989
+ }
990
+ }
991
+ catch {
992
+ // Middleware tracing failed — skip
993
+ }
994
+ // Server actions detection
995
+ result.server_actions = await findServerActions(repoRoot, allCalleeSymbols, callChain);
996
+ }
271
997
  if (outputFormat === "mermaid") {
272
998
  return { mermaid: routeToMermaid(result) };
273
999
  }