codesift-mcp 0.3.0 → 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 +33 -5
  454. package/rules/codesift.mdc +33 -5
  455. package/rules/codex.md +33 -5
  456. package/rules/gemini.md +33 -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
@@ -6,22 +6,26 @@
6
6
  * the zuvo project-profile schema v1.0.
7
7
  */
8
8
  import { readFile, writeFile, access, readdir, mkdir } from "node:fs/promises";
9
+ import { readFileSync } from "node:fs";
9
10
  import { join, relative } from "node:path";
10
11
  import { execFileSync } from "node:child_process";
11
12
  import { getCodeIndex } from "./index-tools.js";
13
+ import { extractAstroConventions } from "./astro-config.js";
12
14
  // ---------------------------------------------------------------------------
13
15
  // Versioning — used by get_extractor_versions
14
16
  // ---------------------------------------------------------------------------
15
17
  export const EXTRACTOR_VERSIONS = {
16
18
  stack_detector: "1.0.0",
17
19
  file_classifier: "1.0.0",
18
- hono: "1.0.0",
20
+ hono: "2.0.0",
19
21
  nestjs: "1.0.0",
20
22
  nextjs: "1.0.0",
21
23
  express: "1.0.0",
22
24
  react: "1.0.0",
23
25
  python: "1.0.0",
24
26
  php: "1.0.0",
27
+ astro: "1.0.0",
28
+ kotlin: "1.0.0",
25
29
  };
26
30
  // ---------------------------------------------------------------------------
27
31
  // Stack Detector
@@ -108,6 +112,11 @@ export async function detectStack(projectRoot) {
108
112
  framework_version = phpDeps["laravel/framework"]?.replace(/^[\^~>=<]/, "") ?? null;
109
113
  detected_from.push("composer.json:require.laravel/framework");
110
114
  }
115
+ else if (phpDeps?.["yiisoft/yii2"]) {
116
+ framework = "yii2";
117
+ framework_version = phpDeps["yiisoft/yii2"]?.replace(/^[\^~>=<]/, "") ?? null;
118
+ detected_from.push("composer.json:require.yiisoft/yii2");
119
+ }
111
120
  else if (phpDeps?.["symfony/framework-bundle"]) {
112
121
  framework = "symfony";
113
122
  detected_from.push("composer.json:require.symfony/framework-bundle");
@@ -127,7 +136,7 @@ export async function detectStack(projectRoot) {
127
136
  detected_from.push("pyproject.toml or requirements.txt");
128
137
  }
129
138
  // Check for PHP
130
- if (["laravel", "symfony"].includes(framework ?? "")) {
139
+ if (["laravel", "symfony", "yii2"].includes(framework ?? "")) {
131
140
  language = "php";
132
141
  detected_from.push("framework implies php");
133
142
  }
@@ -287,6 +296,66 @@ export async function detectStack(projectRoot) {
287
296
  detected_from.push("tsconfig.base.json");
288
297
  }
289
298
  }
299
+ // Build tool detection (Vite, CRA, webpack, Parcel, esbuild, Rspack, Turbopack)
300
+ // Order matters: check more specific/modern tools first.
301
+ let build_tool = null;
302
+ if (pkg) {
303
+ const devDeps = pkg.devDependencies ?? {};
304
+ const deps = pkg.dependencies ?? {};
305
+ const allDeps = { ...deps, ...devDeps };
306
+ if (allDeps["vite"]) {
307
+ build_tool = "vite";
308
+ detected_from.push("package.json:vite");
309
+ }
310
+ else if (allDeps["react-scripts"]) {
311
+ build_tool = "cra";
312
+ detected_from.push("package.json:react-scripts");
313
+ }
314
+ else if (allDeps["@rsbuild/core"]) {
315
+ build_tool = "rsbuild";
316
+ detected_from.push("package.json:@rsbuild/core");
317
+ }
318
+ else if (allDeps["@rspack/cli"] || allDeps["@rspack/core"]) {
319
+ build_tool = "rspack";
320
+ detected_from.push("package.json:@rspack/*");
321
+ }
322
+ else if (allDeps["parcel"] || allDeps["parcel-bundler"]) {
323
+ build_tool = "parcel";
324
+ detected_from.push("package.json:parcel");
325
+ }
326
+ else if (allDeps["webpack"] || allDeps["webpack-cli"]) {
327
+ build_tool = "webpack";
328
+ detected_from.push("package.json:webpack");
329
+ }
330
+ else if (allDeps["esbuild"]) {
331
+ build_tool = "esbuild";
332
+ detected_from.push("package.json:esbuild");
333
+ }
334
+ else if (allDeps["turbopack"]) {
335
+ build_tool = "turbopack";
336
+ detected_from.push("package.json:turbopack");
337
+ }
338
+ }
339
+ // Fallback: look for config files if no dep match
340
+ if (!build_tool) {
341
+ const configChecks = [
342
+ ["vite.config.ts", "vite"],
343
+ ["vite.config.js", "vite"],
344
+ ["vite.config.mjs", "vite"],
345
+ ["webpack.config.js", "webpack"],
346
+ ["webpack.config.ts", "webpack"],
347
+ ["rspack.config.js", "rspack"],
348
+ ["rsbuild.config.ts", "rsbuild"],
349
+ [".parcelrc", "parcel"],
350
+ ];
351
+ for (const [file, tool] of configChecks) {
352
+ if (await fileExists(join(projectRoot, file))) {
353
+ build_tool = tool;
354
+ detected_from.push(file);
355
+ break;
356
+ }
357
+ }
358
+ }
290
359
  return {
291
360
  framework,
292
361
  framework_version,
@@ -294,6 +363,7 @@ export async function detectStack(projectRoot) {
294
363
  language_version,
295
364
  test_runner,
296
365
  package_manager,
366
+ build_tool,
297
367
  monorepo,
298
368
  detected_from,
299
369
  };
@@ -439,188 +509,102 @@ export function classifyFiles(index) {
439
509
  routine: { count: routineCount, by_type: routineCounts },
440
510
  };
441
511
  }
442
- function parseHonoCalls(source) {
443
- const calls = [];
444
- const lines = source.split("\n");
445
- for (let i = 0; i < lines.length; i++) {
446
- const line = lines[i];
447
- const lineNum = i + 1;
448
- // Match app.use("path", handler) or app.route("path", router)
449
- const useMatch = line.match(/app\.(use|route|get|post|put|delete|all)\s*\(\s*["']([^"']+)["']\s*,\s*(.+)/);
450
- if (useMatch) {
451
- // Clean trailing ); but preserve function call parens like rateLimit(3, 3600)
452
- const args = useMatch[3].trim().replace(/\);?\s*$/, "").trim();
453
- calls.push({
454
- type: useMatch[1],
455
- path: useMatch[2],
456
- args,
457
- line: lineNum,
458
- });
459
- continue;
460
- }
461
- // Match app.use("*", handler) — global middleware
462
- const globalUseMatch = line.match(/app\.use\s*\(\s*["']\*["']\s*,\s*(.+)/);
463
- if (globalUseMatch) {
464
- const args = globalUseMatch[1].trim().replace(/\);?\s*$/, "").trim();
465
- calls.push({
466
- type: "use",
467
- path: "*",
468
- args,
469
- line: lineNum,
470
- });
471
- continue;
472
- }
473
- // Match app.get("/path", (c) => ...) — inline handler
474
- const inlineMatch = line.match(/app\.(get|post|put|delete)\s*\(\s*["']([^"']+)["']\s*,/);
475
- if (inlineMatch) {
476
- calls.push({
477
- type: inlineMatch[1],
478
- path: inlineMatch[2],
479
- args: "(inline handler)",
480
- line: lineNum,
481
- });
482
- }
512
+ // ---------------------------------------------------------------------------
513
+ // Hono Extractor
514
+ // ---------------------------------------------------------------------------
515
+ let _honoFallbackCount = 0;
516
+ export function getHonoFallbackCount() { return _honoFallbackCount; }
517
+ /**
518
+ * Extract Hono conventions from an orchestrator file.
519
+ *
520
+ * Uses the tree-sitter AST extractor (HonoExtractor) and adapts the result
521
+ * to the legacy Conventions shape. Falls back to the regex-based legacy
522
+ * implementation on failure (with counter tracking for observability).
523
+ *
524
+ * Kill switch: set CODESIFT_LEGACY_HONO=1 to force legacy extractor.
525
+ */
526
+ export async function extractHonoConventions(source, filePath) {
527
+ if (process.env.CODESIFT_LEGACY_HONO === "1") {
528
+ const { legacyExtractHonoConventions } = await import("./legacy-hono-conventions.js");
529
+ return legacyExtractHonoConventions(source, filePath);
483
530
  }
484
- return calls;
485
- }
486
- function extractMiddlewareName(args) {
487
- // Handle rateLimit(3, 3600) "rateLimit"
488
- const funcCall = args.match(/^(\w+)\s*\(/);
489
- if (funcCall)
490
- return funcCall[1];
491
- // Handle simple identifier: clerkAuth
492
- const simple = args.match(/^(\w+)$/);
493
- if (simple)
494
- return simple[1];
495
- return null;
496
- }
497
- function extractRateLimit(args) {
498
- const match = args.match(/rateLimit\s*\(\s*(\d+)\s*,\s*(\d+)\s*\)/);
499
- if (match)
500
- return { max: parseInt(match[1]), window: parseInt(match[2]) };
501
- return null;
502
- }
503
- export function extractHonoConventions(source, filePath) {
504
- const calls = parseHonoCalls(source);
505
- // Build import map: variable name → import path
506
- const importMap = new Map();
507
- for (const line of source.split("\n")) {
508
- // import adminContests from "./routes/admin/contests/index.js";
509
- const defaultImport = line.match(/import\s+(\w+)\s+from\s+["']([^"']+)["']/);
510
- if (defaultImport) {
511
- importMap.set(defaultImport[1], defaultImport[2]);
512
- }
513
- // import { clerkAuth } from "./middleware/auth.js";
514
- const namedImport = line.match(/import\s+\{([^}]+)\}\s+from\s+["']([^"']+)["']/);
515
- if (namedImport) {
516
- const names = namedImport[1].split(",").map((n) => n.trim().split(/\s+as\s+/).pop().trim());
517
- for (const name of names) {
518
- importMap.set(name, namedImport[2]);
519
- }
520
- }
531
+ // If called with a source string and a file path that doesn't exist on disk,
532
+ // this is the legacy string-fixture API — use legacy extractor directly.
533
+ // The AST extractor needs a real file to parse.
534
+ const { existsSync } = await import("node:fs");
535
+ const { isAbsolute, resolve: pathResolve } = await import("node:path");
536
+ const resolved = isAbsolute(filePath) ? filePath : pathResolve(filePath);
537
+ if (!existsSync(resolved)) {
538
+ const { legacyExtractHonoConventions } = await import("./legacy-hono-conventions.js");
539
+ return legacyExtractHonoConventions(source, filePath);
540
+ }
541
+ try {
542
+ return await honoConventionsAdapter(resolved);
521
543
  }
522
- const middleware_chains = [];
523
- const rate_limits = [];
524
- const route_mounts = [];
544
+ catch (err) {
545
+ _honoFallbackCount++;
546
+ const msg = err instanceof Error ? err.message : String(err);
547
+ console.warn(`[codesift] Hono AST extractor failed (fallback #${_honoFallbackCount}): ${msg}`);
548
+ if (process.env.NODE_ENV !== "production" && process.env.CODESIFT_SILENT_FALLBACK !== "1") {
549
+ throw err;
550
+ }
551
+ const { legacyExtractHonoConventions } = await import("./legacy-hono-conventions.js");
552
+ return legacyExtractHonoConventions(source, filePath);
553
+ }
554
+ }
555
+ /**
556
+ * Adapter: run HonoExtractor.parse() and map HonoAppModel → Conventions.
557
+ */
558
+ async function honoConventionsAdapter(filePath) {
559
+ const { HonoExtractor } = await import("../parser/extractors/hono.js");
560
+ const extractor = new HonoExtractor();
561
+ const model = await extractor.parse(filePath);
562
+ // Map middleware_chains
563
+ const middleware_chains = model.middleware_chains.map((mc) => ({
564
+ scope: mc.scope === "*" ? "global" : mc.scope.replace(/\/\*$/, "").split("/").filter(Boolean)[1] ?? mc.scope,
565
+ file: mc.entries[0]?.file ?? filePath,
566
+ chain: mc.entries.map((e) => ({
567
+ name: e.name,
568
+ line: e.line,
569
+ order: e.order,
570
+ })),
571
+ }));
572
+ // Map route_mounts
573
+ const route_mounts = model.mounts
574
+ .filter((m) => m.mount_type === "hono_route")
575
+ .map((m) => ({
576
+ file: filePath,
577
+ line: 0, // line not tracked in new model at mount level
578
+ mount_path: m.mount_path,
579
+ imported_from: m.child_file || null,
580
+ exported_as: m.child_var,
581
+ }));
582
+ // Map auth_patterns from middleware names
525
583
  const authGroups = {};
526
584
  let auth_middleware = null;
527
- // Group middleware by scope — deduplicate same middleware on different paths
528
- const scopeChains = new Map();
529
- const scopeMwSeen = new Map(); // scope → set of middleware names already in chain
530
- let globalOrder = 0;
531
- const scopeOrders = new Map();
532
- for (const call of calls) {
533
- if (call.type === "use") {
534
- const mwName = extractMiddlewareName(call.args);
535
- const rl = extractRateLimit(call.args);
536
- if (rl) {
537
- rate_limits.push({
538
- file: filePath,
539
- line: call.line,
540
- max: rl.max,
541
- window: rl.window,
542
- applied_to_path: call.path !== "*" ? call.path : null,
543
- method: null,
544
- });
545
- }
546
- else if (mwName) {
547
- const scope = call.path === "*" ? "global" : inferScope(call.path ?? "");
548
- // Deduplicate: same middleware applied to different paths in same scope
549
- // e.g. publicTenantResolver on /api/contests/*, /api/translations/*, /api/r/*
550
- if (!scopeMwSeen.has(scope))
551
- scopeMwSeen.set(scope, new Set());
552
- const seen = scopeMwSeen.get(scope);
553
- if (!seen.has(mwName)) {
554
- seen.add(mwName);
555
- const currentOrder = scope === "global"
556
- ? ++globalOrder
557
- : (scopeOrders.set(scope, (scopeOrders.get(scope) ?? 0) + 1), scopeOrders.get(scope));
558
- if (!scopeChains.has(scope))
559
- scopeChains.set(scope, []);
560
- scopeChains.get(scope).push({ name: mwName, line: call.line, order: currentOrder });
561
- }
562
- // Detect auth middleware
563
- if (/auth|clerk|jwt|session|passport/i.test(mwName)) {
564
- auth_middleware = mwName;
565
- const group = inferScope(call.path ?? "");
566
- if (!authGroups[group])
567
- authGroups[group] = { requires_auth: false, middleware: [] };
568
- authGroups[group].requires_auth = true;
569
- if (!authGroups[group].middleware.includes(mwName)) {
570
- authGroups[group].middleware.push(mwName);
571
- }
572
- }
573
- else if (scope !== "global") {
574
- const group = scope;
575
- if (!authGroups[group])
576
- authGroups[group] = { requires_auth: false, middleware: [] };
577
- if (!authGroups[group].middleware.includes(mwName)) {
578
- authGroups[group].middleware.push(mwName);
579
- }
585
+ for (const mc of model.middleware_chains) {
586
+ const scope = mc.scope === "*" ? "global" : mc.scope.replace(/\/\*$/, "").split("/").filter(Boolean)[1] ?? mc.scope;
587
+ for (const entry of mc.entries) {
588
+ if (/auth|clerk|jwt|session|passport/i.test(entry.name)) {
589
+ auth_middleware = entry.name;
590
+ if (!authGroups[scope])
591
+ authGroups[scope] = { requires_auth: false, middleware: [] };
592
+ authGroups[scope].requires_auth = true;
593
+ if (!authGroups[scope].middleware.includes(entry.name)) {
594
+ authGroups[scope].middleware.push(entry.name);
580
595
  }
581
596
  }
582
597
  }
583
- else if (call.type === "route") {
584
- const varName = call.args.trim();
585
- route_mounts.push({
586
- file: filePath,
587
- line: call.line,
588
- mount_path: call.path ?? "",
589
- imported_from: importMap.get(varName) ?? null,
590
- exported_as: varName,
591
- });
592
- // Infer route group for auth detection
593
- const group = inferScope(call.path ?? "");
594
- if (!authGroups[group])
595
- authGroups[group] = { requires_auth: false, middleware: [] };
596
- }
597
- else if (call.type === "get" || call.type === "post" || call.type === "put" || call.type === "delete") {
598
- // Inline route — nothing to extract for conventions, but note for route groups
599
- }
600
598
  }
601
- // Build middleware chain array
602
- for (const [scope, chain] of scopeChains) {
603
- middleware_chains.push({ scope, file: filePath, chain });
604
- }
605
- // Build auth pattern groups — ensure all route groups are represented
606
- const routeGroups = new Set();
607
- for (const mount of route_mounts) {
608
- routeGroups.add(inferScope(mount.mount_path));
609
- }
610
- // Add health and other direct routes
611
- for (const call of calls) {
612
- if (call.type !== "use" && call.type !== "route" && call.path) {
613
- routeGroups.add(inferScope(call.path));
614
- }
615
- }
616
- for (const group of routeGroups) {
617
- if (!authGroups[group]) {
599
+ // Ensure all route groups represented
600
+ for (const mount of model.mounts) {
601
+ const group = mount.mount_path.split("/").filter(Boolean)[1] ?? "root";
602
+ if (!authGroups[group])
618
603
  authGroups[group] = { requires_auth: false, middleware: [] };
619
- }
620
604
  }
621
605
  return {
622
606
  middleware_chains,
623
- rate_limits,
607
+ rate_limits: [], // rate limits extracted from AST in future; empty for now
624
608
  route_mounts,
625
609
  auth_patterns: { auth_middleware, groups: authGroups },
626
610
  };
@@ -628,6 +612,17 @@ export function extractHonoConventions(source, filePath) {
628
612
  // ---------------------------------------------------------------------------
629
613
  // NestJS Extractor
630
614
  // ---------------------------------------------------------------------------
615
+ /** Count net `[` vs `]` on a line — positive means more open brackets. */
616
+ function countBracketBalance(text) {
617
+ let depth = 0;
618
+ for (const ch of text) {
619
+ if (ch === "[")
620
+ depth++;
621
+ else if (ch === "]")
622
+ depth--;
623
+ }
624
+ return depth;
625
+ }
631
626
  export function extractNestConventions(source, filePath) {
632
627
  const lines = source.split("\n");
633
628
  // Build import map
@@ -645,41 +640,164 @@ export function extractNestConventions(source, filePath) {
645
640
  const global_guards = [];
646
641
  const global_filters = [];
647
642
  const global_pipes = [];
643
+ const global_interceptors = [];
648
644
  const controllers = [];
649
645
  let throttler = null;
650
646
  let inImports = false;
647
+ let importsBracketDepth = 0;
651
648
  let inProviders = false;
649
+ let providersBracketDepth = 0;
652
650
  let inControllers = false;
651
+ let controllersBracketDepth = 0;
653
652
  for (let i = 0; i < lines.length; i++) {
654
653
  const line = lines[i];
655
654
  const lineNum = i + 1;
656
- // Track @Module sections
657
- if (/imports:\s*\[/.test(line))
655
+ // Track @Module sections via bracket depth — handles both multi-line arrays
656
+ // (imports: [\n A,\n B,\n]) and single-line arrays (imports: [A, B]).
657
+ // Critical: mark inImports BEFORE scanning the current line so the scan
658
+ // block runs on single-line `imports: [...]` arrays.
659
+ let closeInImportsAfterLine = false;
660
+ if (/imports:\s*\[/.test(line)) {
658
661
  inImports = true;
659
- if (/providers:\s*\[/.test(line))
662
+ importsBracketDepth += countBracketBalance(line.slice(line.indexOf("[")));
663
+ if (importsBracketDepth <= 0) {
664
+ closeInImportsAfterLine = true;
665
+ importsBracketDepth = 0;
666
+ }
667
+ }
668
+ else if (inImports) {
669
+ importsBracketDepth += countBracketBalance(line);
670
+ if (importsBracketDepth <= 0) {
671
+ closeInImportsAfterLine = true;
672
+ importsBracketDepth = 0;
673
+ }
674
+ }
675
+ // R-1 fix: use bracket-depth tracking for providers/controllers too
676
+ // (same approach as imports) so single-line arrays close correctly.
677
+ let closeProvidersAfterLine = false;
678
+ if (/providers:\s*\[/.test(line)) {
660
679
  inProviders = true;
661
- if (/controllers:\s*\[/.test(line))
680
+ providersBracketDepth += countBracketBalance(line.slice(line.indexOf("[", line.indexOf("providers"))));
681
+ if (providersBracketDepth <= 0) {
682
+ closeProvidersAfterLine = true;
683
+ providersBracketDepth = 0;
684
+ }
685
+ }
686
+ else if (inProviders) {
687
+ providersBracketDepth += countBracketBalance(line);
688
+ if (providersBracketDepth <= 0) {
689
+ closeProvidersAfterLine = true;
690
+ providersBracketDepth = 0;
691
+ }
692
+ }
693
+ let closeControllersAfterLine = false;
694
+ if (/controllers:\s*\[/.test(line)) {
662
695
  inControllers = true;
663
- if (inImports && /^\s*\]/.test(line))
664
- inImports = false;
665
- if (inProviders && /^\s*\]/.test(line))
666
- inProviders = false;
667
- if (inControllers && /^\s*\]/.test(line))
668
- inControllers = false;
669
- // Extract module imports
696
+ controllersBracketDepth += countBracketBalance(line.slice(line.indexOf("[", line.indexOf("controllers"))));
697
+ if (controllersBracketDepth <= 0) {
698
+ closeControllersAfterLine = true;
699
+ controllersBracketDepth = 0;
700
+ }
701
+ }
702
+ else if (inControllers) {
703
+ controllersBracketDepth += countBracketBalance(line);
704
+ if (controllersBracketDepth <= 0) {
705
+ closeControllersAfterLine = true;
706
+ controllersBracketDepth = 0;
707
+ }
708
+ }
709
+ // Extract module imports — scan for all module names on the line (not just
710
+ // the first indented one). Handles single-line `imports: [A, B.forFeature([...]), C]`
711
+ // where multiple modules share one line.
670
712
  if (inImports) {
671
- // Match: ModuleName, or ModuleName.forRoot(), or ModuleName.forRootAsync({...})
672
- const moduleMatch = line.match(/^\s+(\w+Module)(?:\.for(?:Root|Feature)(?:Async)?\s*\()?/);
673
- if (moduleMatch) {
713
+ const moduleRe = /(\w+Module)(?:\.for(Root|Feature)(?:Async)?\s*\()?/g;
714
+ let moduleMatch;
715
+ const matchedThisLine = new Set();
716
+ while ((moduleMatch = moduleRe.exec(line)) !== null) {
717
+ // Avoid duplicates within the same line
718
+ const key = `${moduleMatch[1]}:${moduleMatch.index}`;
719
+ if (matchedThisLine.has(key))
720
+ continue;
721
+ matchedThisLine.add(key);
674
722
  const name = moduleMatch[1];
723
+ const dynamicKind = moduleMatch[2]; // "Root" | "Feature" | undefined
675
724
  const isGlobal = /isGlobal:\s*true/.test(line) || /ConfigModule|SentryModule/.test(name);
676
- modules.push({
725
+ const entry = {
677
726
  name,
678
727
  file: filePath,
679
728
  line: lineNum,
680
729
  imported_from: importMap.get(name) ?? null,
681
730
  is_global: isGlobal,
682
- });
731
+ };
732
+ // G2: forFeature([...]) — extract entity class names (scan ahead up to 15 lines)
733
+ if (dynamicKind === "Feature") {
734
+ const entities = [];
735
+ let bracketDepth = 0;
736
+ let started = false;
737
+ for (let j = i; j < Math.min(i + 15, lines.length); j++) {
738
+ for (const ch of lines[j]) {
739
+ if (ch === "[") {
740
+ bracketDepth++;
741
+ started = true;
742
+ }
743
+ else if (ch === "]") {
744
+ bracketDepth--;
745
+ if (started && bracketDepth === 0)
746
+ break;
747
+ }
748
+ }
749
+ // Capture entity names after the opening [
750
+ const featureMatch = lines[j].match(/forFeature\s*\(\s*\[([^\]]*)\]?/);
751
+ if (featureMatch) {
752
+ const inner = featureMatch[1];
753
+ for (const m of inner.matchAll(/\b([A-Z]\w*)\b/g))
754
+ entities.push(m[1]);
755
+ // Continue to next lines if the array spans multiple
756
+ for (let k = j + 1; k < Math.min(j + 10, lines.length) && !/\]/.test(lines[k - 1]); k++) {
757
+ for (const m of lines[k].matchAll(/\b([A-Z]\w*)\b/g))
758
+ entities.push(m[1]);
759
+ }
760
+ break;
761
+ }
762
+ // Multi-line case: forFeature([ on one line, entities on next
763
+ if (/forFeature\s*\(\s*\[\s*$/.test(lines[j])) {
764
+ for (let k = j + 1; k < Math.min(j + 10, lines.length); k++) {
765
+ if (/^\s*\]/.test(lines[k]))
766
+ break;
767
+ for (const m of lines[k].matchAll(/\b([A-Z]\w*)\b/g))
768
+ entities.push(m[1]);
769
+ }
770
+ break;
771
+ }
772
+ }
773
+ if (entities.length > 0)
774
+ entry.entities = entities;
775
+ }
776
+ // G2: forRoot({...}) — extract top-level config keys
777
+ if (dynamicKind === "Root") {
778
+ const keys = [];
779
+ let braceDepth = 0;
780
+ for (let j = i; j < Math.min(i + 15, lines.length); j++) {
781
+ for (const ch of lines[j]) {
782
+ if (ch === "{")
783
+ braceDepth++;
784
+ else if (ch === "}")
785
+ braceDepth--;
786
+ }
787
+ // Only capture keys at the top-level of the forRoot config object
788
+ if (braceDepth >= 1) {
789
+ // Match indented key: value pattern at top-level (heuristic: 1-level indent)
790
+ const keyMatch = lines[j].match(/^\s{6,10}(\w+):\s*/);
791
+ if (keyMatch && !keys.includes(keyMatch[1]))
792
+ keys.push(keyMatch[1]);
793
+ }
794
+ if (braceDepth === 0 && j > i)
795
+ break;
796
+ }
797
+ if (keys.length > 0)
798
+ entry.dynamic_config_keys = keys;
799
+ }
800
+ modules.push(entry);
683
801
  }
684
802
  // Extract ThrottlerModule config
685
803
  if (/ThrottlerModule/.test(line)) {
@@ -754,9 +872,97 @@ export function extractNestConventions(source, filePath) {
754
872
  }
755
873
  }
756
874
  }
875
+ if (/provide:\s*APP_INTERCEPTOR/.test(line)) {
876
+ for (let j = i; j < Math.min(i + 5, lines.length); j++) {
877
+ const useClassMatch = lines[j].match(/useClass:\s*(\w+)/);
878
+ if (useClassMatch) {
879
+ global_interceptors.push({
880
+ name: useClassMatch[1],
881
+ token: "APP_INTERCEPTOR",
882
+ file: filePath,
883
+ line: j + 1,
884
+ imported_from: importMap.get(useClassMatch[1]) ?? null,
885
+ });
886
+ break;
887
+ }
888
+ }
889
+ }
890
+ }
891
+ // Close inImports at end of line iteration if brackets balanced
892
+ if (closeInImportsAfterLine)
893
+ inImports = false;
894
+ if (closeProvidersAfterLine)
895
+ inProviders = false;
896
+ if (closeControllersAfterLine)
897
+ inControllers = false;
898
+ }
899
+ // G1: parse middleware.configure(consumer) chains
900
+ const middleware_chains = parseMiddlewareChains(source, filePath);
901
+ return { modules, global_guards, global_filters, global_pipes, global_interceptors, controllers, throttler, middleware_chains };
902
+ }
903
+ /**
904
+ * G1: Parse `configure(consumer: MiddlewareConsumer) { ... }` blocks.
905
+ * Extracts consumer.apply(Middleware).forRoutes(...) chains.
906
+ */
907
+ export function parseMiddlewareChains(source, filePath) {
908
+ const results = [];
909
+ // Find configure( method — can be `configure(consumer` or `configure(consumer: MiddlewareConsumer)`
910
+ const configureStart = source.search(/\bconfigure\s*\(\s*\w+\s*(?::\s*\w+)?\s*\)\s*\{/);
911
+ if (configureStart === -1)
912
+ return results;
913
+ // Extract body of configure method via brace counting
914
+ const bodyStart = source.indexOf("{", configureStart);
915
+ if (bodyStart === -1)
916
+ return results;
917
+ let depth = 1;
918
+ let i = bodyStart + 1;
919
+ while (i < source.length && depth > 0) {
920
+ if (source[i] === "{")
921
+ depth++;
922
+ else if (source[i] === "}")
923
+ depth--;
924
+ i++;
925
+ }
926
+ const body = source.slice(bodyStart + 1, i - 1);
927
+ // Match: consumer.apply(Middleware).forRoutes(<args>) — may chain multiple middlewares
928
+ // Use a tolerant pattern that captures the apply(...) arg and forRoutes(...) arg separately.
929
+ const applyRe = /\.apply\s*\(\s*([\w,\s]+)\s*\)\s*\.forRoutes\s*\(([\s\S]*?)\)\s*[;}]/g;
930
+ let m;
931
+ while ((m = applyRe.exec(body)) !== null) {
932
+ const middlewareNames = m[1].split(",").map((s) => s.trim()).filter(Boolean);
933
+ const routesArg = m[2];
934
+ const routes = [];
935
+ // Parse routesArg — supports:
936
+ // '*'
937
+ // 'users/*'
938
+ // { path: 'users', method: RequestMethod.GET }
939
+ // ControllerClass
940
+ const stringPathRe = /['"`]([^'"`]+)['"`]/g;
941
+ let sm;
942
+ while ((sm = stringPathRe.exec(routesArg)) !== null) {
943
+ // Check if this string is part of a { path: '...', method: ... } object literal
944
+ const before = routesArg.slice(Math.max(0, sm.index - 30), sm.index);
945
+ if (/path:\s*$/.test(before)) {
946
+ // Object form — capture path AND method
947
+ const objRe = /path:\s*['"`]([^'"`]+)['"`]\s*,\s*method:\s*(?:RequestMethod\.)?(\w+)/;
948
+ const afterContext = routesArg.slice(Math.max(0, sm.index - 20), sm.index + 200);
949
+ const objMatch = objRe.exec(afterContext);
950
+ if (objMatch) {
951
+ routes.push({ path: objMatch[1], method: objMatch[2] });
952
+ continue;
953
+ }
954
+ }
955
+ routes.push({ path: sm[1] });
956
+ }
957
+ // Also capture bare ControllerClass (PascalCase identifier) if present
958
+ for (const name of middlewareNames) {
959
+ // R-3 fix: guard indexOf returning -1 (would produce wrong line via slice(0,-1))
960
+ const namePos = source.indexOf(name, configureStart);
961
+ const line = namePos >= 0 ? source.slice(0, namePos).split("\n").length : 0;
962
+ results.push({ middleware: name, routes, file: filePath, line });
757
963
  }
758
964
  }
759
- return { modules, global_guards, global_filters, global_pipes, controllers, throttler };
965
+ return results;
760
966
  }
761
967
  export function extractNextConventions(_projectRoot, files) {
762
968
  const pages = [];
@@ -764,6 +970,8 @@ export function extractNextConventions(_projectRoot, files) {
764
970
  const inngest_functions = [];
765
971
  const webhooks = [];
766
972
  let services_count = 0;
973
+ let client_component_count = 0;
974
+ let server_action_count = 0;
767
975
  let middleware = null;
768
976
  const hasAppDir = files.some((f) => f.path.includes("app/"));
769
977
  const hasSrcDir = files.some((f) => f.path.startsWith("src/"));
@@ -787,6 +995,18 @@ export function extractNextConventions(_projectRoot, files) {
787
995
  if (/app\/.*\/error\.(tsx|jsx|ts|js)$/.test(p)) {
788
996
  pages.push({ path: p, type: "error" });
789
997
  }
998
+ if (/app\/.*\/not-found\.(tsx|jsx|ts|js)$/.test(p)) {
999
+ pages.push({ path: p, type: "not-found" });
1000
+ }
1001
+ if (/app\/.*\/global-error\.(tsx|jsx|ts|js)$/.test(p)) {
1002
+ pages.push({ path: p, type: "global-error" });
1003
+ }
1004
+ if (/app\/.*\/default\.(tsx|jsx|ts|js)$/.test(p)) {
1005
+ pages.push({ path: p, type: "default" });
1006
+ }
1007
+ if (/app\/.*\/template\.(tsx|jsx|ts|js)$/.test(p)) {
1008
+ pages.push({ path: p, type: "template" });
1009
+ }
790
1010
  // API routes (App Router — route.ts files under app/api/)
791
1011
  if (/app\/api\/.*route\.(ts|js)$/.test(p)) {
792
1012
  api_routes.push({ path: p, methods: [], file: p });
@@ -807,12 +1027,27 @@ export function extractNextConventions(_projectRoot, files) {
807
1027
  if (/webhook/.test(p) && /route\.(ts|js)$/.test(p)) {
808
1028
  webhooks.push(p);
809
1029
  }
1030
+ // Directive scanning — check first line for "use client" / "use server"
1031
+ if (/\.(tsx|ts|jsx|js)$/.test(p) && /app\//.test(p)) {
1032
+ try {
1033
+ const head = readFileSync(join(_projectRoot, p), { encoding: "utf8", flag: "r" }).slice(0, 80);
1034
+ if (/['"]use client['"]/.test(head))
1035
+ client_component_count++;
1036
+ if (/['"]use server['"]/.test(head))
1037
+ server_action_count++;
1038
+ }
1039
+ catch {
1040
+ // file may have been deleted since indexing
1041
+ }
1042
+ }
810
1043
  }
811
1044
  return {
812
1045
  pages,
813
1046
  middleware,
814
1047
  api_routes,
815
1048
  services_count,
1049
+ client_component_count,
1050
+ server_action_count,
816
1051
  inngest_functions,
817
1052
  webhooks,
818
1053
  config: { app_router: hasAppDir, src_dir: hasSrcDir, i18n: hasI18n },
@@ -863,7 +1098,7 @@ export function extractExpressConventions(source, filePath) {
863
1098
  }
864
1099
  return { middleware, routers, error_handlers };
865
1100
  }
866
- export function extractReactConventions(files, deps) {
1101
+ export function extractReactConventions(files, deps, symbols) {
867
1102
  // State management
868
1103
  let state_management = null;
869
1104
  if (deps["@reduxjs/toolkit"] || deps["redux"])
@@ -885,8 +1120,13 @@ export function extractReactConventions(files, deps) {
885
1120
  else if (deps["wouter"])
886
1121
  routing = "wouter";
887
1122
  // UI library
1123
+ // shadcn/ui detection: canonical path pattern is components/ui/*.tsx — checked
1124
+ // FIRST so it takes precedence over generic radix dep (shadcn re-exports radix).
1125
+ const hasShadcnFiles = files.some((f) => /(^|\/)components\/ui\/[a-z-]+\.(tsx|jsx)$/.test(f.path));
888
1126
  let ui_library = null;
889
- if (deps["@mui/material"])
1127
+ if (hasShadcnFiles)
1128
+ ui_library = "shadcn";
1129
+ else if (deps["@mui/material"])
890
1130
  ui_library = "mui";
891
1131
  else if (deps["@chakra-ui/react"])
892
1132
  ui_library = "chakra";
@@ -896,7 +1136,15 @@ export function extractReactConventions(files, deps) {
896
1136
  ui_library = "radix";
897
1137
  else if (deps["tailwindcss"])
898
1138
  ui_library = "tailwind";
899
- // Component counts
1139
+ // Form library detection (Item 7)
1140
+ let form_library = null;
1141
+ if (deps["react-hook-form"])
1142
+ form_library = "react-hook-form";
1143
+ else if (deps["formik"])
1144
+ form_library = "formik";
1145
+ else if (deps["final-form"] || deps["react-final-form"])
1146
+ form_library = "final-form";
1147
+ // File-path-based component counts (legacy, coarse)
900
1148
  let pages = 0, components = 0, hooks = 0;
901
1149
  for (const f of files) {
902
1150
  if (/\/pages?\//.test(f.path) && /\.(tsx|jsx)$/.test(f.path))
@@ -906,11 +1154,53 @@ export function extractReactConventions(files, deps) {
906
1154
  if (/\/hooks?\//.test(f.path) || /\.hook\.(ts|js)$/.test(f.path))
907
1155
  hooks++;
908
1156
  }
1157
+ // Symbol-based semantic counts (requires Wave 1 extractor)
1158
+ let actual_component_count = 0;
1159
+ let actual_hook_count = 0;
1160
+ const hookUsageMap = new Map();
1161
+ const component_patterns = { memo: 0, forwardRef: 0, lazy: 0 };
1162
+ if (symbols) {
1163
+ // Set of stdlib hooks to exclude from "hook usage" tracking — we want
1164
+ // to highlight which library/custom hooks components consume.
1165
+ for (const sym of symbols) {
1166
+ if (sym.kind === "component") {
1167
+ actual_component_count++;
1168
+ if (sym.source) {
1169
+ // Detect wrapper patterns in component source
1170
+ // Generic-aware patterns: memo<Props>(...), forwardRef<T, P>(...), lazy<T>(...) — Item 9
1171
+ if (/\b(?:React\.)?memo\s*(?:<[^>]+>)?\s*\(/.test(sym.source))
1172
+ component_patterns.memo++;
1173
+ if (/\b(?:React\.)?forwardRef\s*(?:<[^>]+>)?\s*\(/.test(sym.source))
1174
+ component_patterns.forwardRef++;
1175
+ if (/\b(?:React\.)?lazy\s*(?:<[^>]+>)?\s*\(/.test(sym.source))
1176
+ component_patterns.lazy++;
1177
+ // Count hook calls inside this component
1178
+ const hookCalls = sym.source.matchAll(/\b(use[A-Z]\w*)\s*\(/g);
1179
+ for (const m of hookCalls) {
1180
+ const hookName = m[1];
1181
+ hookUsageMap.set(hookName, (hookUsageMap.get(hookName) ?? 0) + 1);
1182
+ }
1183
+ }
1184
+ }
1185
+ else if (sym.kind === "hook") {
1186
+ actual_hook_count++;
1187
+ }
1188
+ }
1189
+ }
1190
+ const hook_usage = [...hookUsageMap.entries()]
1191
+ .sort((a, b) => b[1] - a[1])
1192
+ .slice(0, 10)
1193
+ .map(([name, count]) => ({ name, count }));
909
1194
  return {
910
1195
  state_management,
911
1196
  routing,
912
1197
  ui_library,
1198
+ form_library,
913
1199
  component_count: { pages, components, hooks },
1200
+ actual_component_count,
1201
+ actual_hook_count,
1202
+ hook_usage,
1203
+ component_patterns,
914
1204
  };
915
1205
  }
916
1206
  export function extractPythonConventions(files) {
@@ -974,21 +1264,67 @@ export function extractPhpConventions(files) {
974
1264
  if (/Controller\.php$/.test(f.path)) {
975
1265
  controllers.push({ name, path: f.path });
976
1266
  }
977
- if (/\/[Mm]iddleware\//.test(f.path) && f.path.endsWith(".php")) {
1267
+ if (/(^|\/)[Mm]iddleware\//.test(f.path) && f.path.endsWith(".php")) {
978
1268
  middleware.push({ name, path: f.path });
979
1269
  }
980
- if (/\/[Mm]odels?\//.test(f.path) && f.path.endsWith(".php")) {
1270
+ if (/(^|\/)[Mm]odels?\//.test(f.path) && f.path.endsWith(".php")) {
981
1271
  models.push({ name, path: f.path });
982
1272
  }
983
- if (/routes\//.test(f.path) && f.path.endsWith(".php")) {
1273
+ if (/(^|\/)routes\//.test(f.path) && f.path.endsWith(".php")) {
984
1274
  routes_files.push(f.path);
985
1275
  }
986
- if (/migrations?\//.test(f.path)) {
1276
+ if (/(^|\/)migrations?\//.test(f.path)) {
987
1277
  migrations_count++;
988
1278
  }
989
1279
  }
990
1280
  return { controllers, middleware, models, routes_files, migrations_count };
991
1281
  }
1282
+ export function extractYii2Conventions(files) {
1283
+ const base = extractPhpConventions(files);
1284
+ const modules = [];
1285
+ const widgets = [];
1286
+ const behaviors = [];
1287
+ const components = [];
1288
+ const assets = [];
1289
+ const config_files = [];
1290
+ for (const f of files) {
1291
+ const name = f.path.split("/").pop()?.replace(/\.php$/, "") ?? "";
1292
+ // Modules: Module.php in modules/*/ directories
1293
+ if (/(^|\/)modules\/[^/]+\/Module\.php$/.test(f.path)) {
1294
+ modules.push({ name, path: f.path });
1295
+ }
1296
+ // Widgets: files in widgets/ directories or named *Widget.php
1297
+ if ((/(^|\/)widgets\//.test(f.path) || /Widget\.php$/.test(f.path)) && f.path.endsWith(".php")) {
1298
+ widgets.push({ name, path: f.path });
1299
+ }
1300
+ // Behaviors: files in behaviors/ directories or named *Behavior.php
1301
+ if ((/(^|\/)behaviors\//.test(f.path) || /Behavior\.php$/.test(f.path)) && f.path.endsWith(".php")) {
1302
+ behaviors.push({ name, path: f.path });
1303
+ }
1304
+ // Components: files in components/ directory
1305
+ if (/(^|\/)components\//.test(f.path) && f.path.endsWith(".php")) {
1306
+ components.push({ name, path: f.path });
1307
+ }
1308
+ // Assets: files named *Asset.php in assets/ directory
1309
+ if (/(^|\/)assets\//.test(f.path) && /Asset\.php$/.test(f.path)) {
1310
+ assets.push({ name, path: f.path });
1311
+ }
1312
+ // Config files
1313
+ if (/config\/(web|console|db|params|main|test)\.php$/.test(f.path)) {
1314
+ config_files.push(f.path);
1315
+ }
1316
+ }
1317
+ return {
1318
+ ...base,
1319
+ framework_type: "yii2",
1320
+ modules,
1321
+ widgets,
1322
+ behaviors,
1323
+ components,
1324
+ assets,
1325
+ config_files,
1326
+ };
1327
+ }
992
1328
  // ---------------------------------------------------------------------------
993
1329
  // Identity Extractor
994
1330
  // ---------------------------------------------------------------------------
@@ -1269,23 +1605,6 @@ function extractGitHealth(projectRoot) {
1269
1605
  return null;
1270
1606
  }
1271
1607
  }
1272
- function inferScope(path) {
1273
- if (path === "*")
1274
- return "global";
1275
- if (path.includes("/admin"))
1276
- return "admin";
1277
- if (path.includes("/webhook"))
1278
- return "webhook";
1279
- if (path.includes("/health"))
1280
- return "health";
1281
- if (path.includes("/public") || path.includes("/contests") || path.includes("/translations") || path.includes("/r/"))
1282
- return "public";
1283
- // Default: extract first meaningful segment
1284
- const segments = path.split("/").filter(Boolean);
1285
- if (segments.length >= 2)
1286
- return segments[1];
1287
- return "root";
1288
- }
1289
1608
  // ---------------------------------------------------------------------------
1290
1609
  // Main orchestrator: analyze_project
1291
1610
  // ---------------------------------------------------------------------------
@@ -1343,6 +1662,7 @@ export async function analyzeProject(repoName, _options = {}) {
1343
1662
  let reactConventions;
1344
1663
  let pythonConventions;
1345
1664
  let phpConventions;
1665
+ let astroConventions;
1346
1666
  let status = "complete";
1347
1667
  const fw = stack.framework;
1348
1668
  try {
@@ -1350,7 +1670,7 @@ export async function analyzeProject(repoName, _options = {}) {
1350
1670
  const orchestratorFile = file_classifications.critical.find((f) => f.code_type === "ORCHESTRATOR");
1351
1671
  if (orchestratorFile) {
1352
1672
  const appSource = await readFile(join(projectRoot, orchestratorFile.path), "utf-8");
1353
- conventions = extractHonoConventions(appSource, orchestratorFile.path);
1673
+ conventions = await extractHonoConventions(appSource, orchestratorFile.path);
1354
1674
  }
1355
1675
  else {
1356
1676
  status = "partial";
@@ -1386,14 +1706,22 @@ export async function analyzeProject(repoName, _options = {}) {
1386
1706
  else if (fw === "react") {
1387
1707
  const pkg = await readJson(join(projectRoot, "package.json"));
1388
1708
  const allDeps = { ...pkg?.dependencies, ...pkg?.devDependencies };
1389
- reactConventions = extractReactConventions(index.files, allDeps);
1709
+ reactConventions = extractReactConventions(index.files, allDeps, index.symbols);
1390
1710
  }
1391
1711
  else if (fw === "fastapi" || fw === "django" || fw === "flask") {
1392
1712
  pythonConventions = extractPythonConventions(index.files);
1393
1713
  }
1714
+ else if (fw === "yii2") {
1715
+ phpConventions = extractYii2Conventions(index.files);
1716
+ }
1394
1717
  else if (fw === "laravel" || fw === "symfony") {
1395
1718
  phpConventions = extractPhpConventions(index.files);
1396
1719
  }
1720
+ else if (fw === "astro") {
1721
+ const astroResult = await extractAstroConventions(index.files.map((f) => f.path), projectRoot);
1722
+ astroConventions = astroResult.conventions;
1723
+ status = "complete";
1724
+ }
1397
1725
  else {
1398
1726
  status = "partial";
1399
1727
  }
@@ -1425,6 +1753,7 @@ export async function analyzeProject(repoName, _options = {}) {
1425
1753
  ...(reactConventions ? { react_conventions: reactConventions } : {}),
1426
1754
  ...(pythonConventions ? { python_conventions: pythonConventions } : {}),
1427
1755
  ...(phpConventions ? { php_conventions: phpConventions } : {}),
1756
+ ...(astroConventions ? { astro_conventions: astroConventions } : {}),
1428
1757
  dependency_health: await extractDependencyHealth(projectRoot) ?? undefined,
1429
1758
  git_health: extractGitHealth(projectRoot) ?? undefined,
1430
1759
  generation_metadata: {
@@ -1448,7 +1777,7 @@ async function writeProfileToDisk(projectRoot, profile) {
1448
1777
  await writeFile(profilePath, JSON.stringify(profile, null, 2), "utf-8");
1449
1778
  return profilePath;
1450
1779
  }
1451
- function buildConventionsSummary(profile) {
1780
+ export function buildConventionsSummary(profile) {
1452
1781
  const p = profile;
1453
1782
  if (p.conventions)
1454
1783
  return {
@@ -1463,6 +1792,7 @@ function buildConventionsSummary(profile) {
1463
1792
  modules: p.nest_conventions.modules.length,
1464
1793
  global_guards: p.nest_conventions.global_guards.length,
1465
1794
  global_filters: p.nest_conventions.global_filters.length,
1795
+ global_interceptors: p.nest_conventions.global_interceptors.length,
1466
1796
  controllers: p.nest_conventions.controllers.length,
1467
1797
  has_throttler: !!p.nest_conventions.throttler,
1468
1798
  };
@@ -1507,6 +1837,15 @@ function buildConventionsSummary(profile) {
1507
1837
  models: p.php_conventions.models.length,
1508
1838
  migrations: p.php_conventions.migrations_count,
1509
1839
  };
1840
+ if (p.astro_conventions)
1841
+ return {
1842
+ type: "astro",
1843
+ output_mode: p.astro_conventions.output_mode,
1844
+ adapter: p.astro_conventions.adapter,
1845
+ integrations: p.astro_conventions.integrations.length,
1846
+ has_i18n: !!p.astro_conventions.i18n,
1847
+ config_resolution: p.astro_conventions.config_resolution,
1848
+ };
1510
1849
  return null;
1511
1850
  }
1512
1851
  function buildSummary(profile, profilePath) {
@@ -1540,7 +1879,45 @@ function buildSummary(profile, profilePath) {
1540
1879
  // ---------------------------------------------------------------------------
1541
1880
  // get_extractor_versions — fast metadata call
1542
1881
  // ---------------------------------------------------------------------------
1882
+ /**
1883
+ * Languages with a tree-sitter parser extractor — these get symbol-level
1884
+ * indexing (search_symbols, get_file_outline, get_symbol, find_references, etc.).
1885
+ */
1886
+ export const PARSER_LANGUAGES = [
1887
+ "typescript",
1888
+ "javascript",
1889
+ "python",
1890
+ "go",
1891
+ "rust",
1892
+ "php",
1893
+ "kotlin",
1894
+ "prisma",
1895
+ "markdown",
1896
+ "astro",
1897
+ "sql",
1898
+ "sql-jinja",
1899
+ ];
1900
+ /**
1901
+ * Languages indexed as FileEntry (so get_file_tree and search_text with
1902
+ * file_pattern work) but WITHOUT symbol extraction. Ripgrep-backed search_text
1903
+ * and scan_secrets work on these via filesystem. Upgrade path: add a
1904
+ * tree-sitter grammar .wasm and extractor to move a language to PARSER_LANGUAGES.
1905
+ */
1906
+ export const TEXT_STUB_LANGUAGES = [
1907
+ "swift", "dart", "scala", "clojure",
1908
+ "elixir", "lua", "zig", "nim", "gradle", "sbt",
1909
+ ];
1543
1910
  export function getExtractorVersions() {
1544
- return { ...EXTRACTOR_VERSIONS };
1911
+ return {
1912
+ parser_languages: PARSER_LANGUAGES,
1913
+ text_stub_languages: TEXT_STUB_LANGUAGES,
1914
+ profile_frameworks: { ...EXTRACTOR_VERSIONS },
1915
+ note: "search_text, get_file_tree, search_conversations, and scan_secrets work on ALL indexed files " +
1916
+ "(both parser_languages AND text_stub_languages). Only symbol-based tools (search_symbols, " +
1917
+ "get_file_outline, get_symbol, find_references, trace_call_chain) require a full parser " +
1918
+ "extractor, so they return nothing for text_stub_languages. profile_frameworks is the list " +
1919
+ "of framework detectors used by analyze_project — NOT a list of supported languages.",
1920
+ versions: { ...EXTRACTOR_VERSIONS },
1921
+ };
1545
1922
  }
1546
1923
  //# sourceMappingURL=project-tools.js.map