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.
- package/README.md +143 -20
- package/dist/cache/hono-cache.d.ts +50 -0
- package/dist/cache/hono-cache.d.ts.map +1 -0
- package/dist/cache/hono-cache.js +132 -0
- package/dist/cache/hono-cache.js.map +1 -0
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +17 -2
- package/dist/cli/setup.js.map +1 -1
- package/dist/formatters-shortening.d.ts +13 -0
- package/dist/formatters-shortening.d.ts.map +1 -1
- package/dist/formatters-shortening.js +131 -0
- package/dist/formatters-shortening.js.map +1 -1
- package/dist/formatters.d.ts +38 -0
- package/dist/formatters.d.ts.map +1 -1
- package/dist/formatters.js +498 -0
- package/dist/formatters.js.map +1 -1
- package/dist/instructions.d.ts +1 -1
- package/dist/instructions.d.ts.map +1 -1
- package/dist/instructions.js +27 -26
- package/dist/instructions.js.map +1 -1
- package/dist/lsp/lsp-servers.d.ts.map +1 -1
- package/dist/lsp/lsp-servers.js +5 -0
- package/dist/lsp/lsp-servers.js.map +1 -1
- package/dist/lsp/lsp-tools.d.ts.map +1 -1
- package/dist/lsp/lsp-tools.js +1 -0
- package/dist/lsp/lsp-tools.js.map +1 -1
- package/dist/parser/astro-template.d.ts +47 -0
- package/dist/parser/astro-template.d.ts.map +1 -0
- package/dist/parser/astro-template.js +171 -0
- package/dist/parser/astro-template.js.map +1 -0
- package/dist/parser/extractors/_shared.d.ts +4 -0
- package/dist/parser/extractors/_shared.d.ts.map +1 -1
- package/dist/parser/extractors/_shared.js +8 -0
- package/dist/parser/extractors/_shared.js.map +1 -1
- package/dist/parser/extractors/astro.d.ts +4 -5
- package/dist/parser/extractors/astro.d.ts.map +1 -1
- package/dist/parser/extractors/astro.js +102 -26
- package/dist/parser/extractors/astro.js.map +1 -1
- package/dist/parser/extractors/gradle-kts.d.ts +4 -0
- package/dist/parser/extractors/gradle-kts.d.ts.map +1 -0
- package/dist/parser/extractors/gradle-kts.js +246 -0
- package/dist/parser/extractors/gradle-kts.js.map +1 -0
- package/dist/parser/extractors/hono-inline-analyzer.d.ts +34 -0
- package/dist/parser/extractors/hono-inline-analyzer.d.ts.map +1 -0
- package/dist/parser/extractors/hono-inline-analyzer.js +465 -0
- package/dist/parser/extractors/hono-inline-analyzer.js.map +1 -0
- package/dist/parser/extractors/hono-model.d.ts +196 -0
- package/dist/parser/extractors/hono-model.d.ts.map +1 -0
- package/dist/parser/extractors/hono-model.js +10 -0
- package/dist/parser/extractors/hono-model.js.map +1 -0
- package/dist/parser/extractors/hono.d.ts +118 -0
- package/dist/parser/extractors/hono.d.ts.map +1 -0
- package/dist/parser/extractors/hono.js +1527 -0
- package/dist/parser/extractors/hono.js.map +1 -0
- package/dist/parser/extractors/kotlin.d.ts +4 -0
- package/dist/parser/extractors/kotlin.d.ts.map +1 -0
- package/dist/parser/extractors/kotlin.js +521 -0
- package/dist/parser/extractors/kotlin.js.map +1 -0
- package/dist/parser/extractors/php.d.ts +22 -0
- package/dist/parser/extractors/php.d.ts.map +1 -0
- package/dist/parser/extractors/php.js +326 -0
- package/dist/parser/extractors/php.js.map +1 -0
- package/dist/parser/extractors/python.d.ts.map +1 -1
- package/dist/parser/extractors/python.js +234 -11
- package/dist/parser/extractors/python.js.map +1 -1
- package/dist/parser/extractors/sql.d.ts +33 -0
- package/dist/parser/extractors/sql.d.ts.map +1 -0
- package/dist/parser/extractors/sql.js +506 -0
- package/dist/parser/extractors/sql.js.map +1 -0
- package/dist/parser/extractors/typescript.d.ts.map +1 -1
- package/dist/parser/extractors/typescript.js +166 -3
- package/dist/parser/extractors/typescript.js.map +1 -1
- package/dist/parser/languages/tree-sitter-javascript.wasm +0 -0
- package/dist/parser/languages/tree-sitter-kotlin.wasm +0 -0
- package/dist/parser/languages/tree-sitter-php.wasm +0 -0
- package/dist/parser/languages/tree-sitter-php_only.wasm +0 -0
- package/dist/parser/languages/tree-sitter-python.wasm +0 -0
- package/dist/parser/parser-manager.d.ts +32 -0
- package/dist/parser/parser-manager.d.ts.map +1 -1
- package/dist/parser/parser-manager.js +82 -3
- package/dist/parser/parser-manager.js.map +1 -1
- package/dist/parser/symbol-extractor.d.ts.map +1 -1
- package/dist/parser/symbol-extractor.js +16 -0
- package/dist/parser/symbol-extractor.js.map +1 -1
- package/dist/register-tools.d.ts +37 -1
- package/dist/register-tools.d.ts.map +1 -1
- package/dist/register-tools.js +2657 -191
- package/dist/register-tools.js.map +1 -1
- package/dist/search/reranker.js +1 -1
- package/dist/search/reranker.js.map +1 -1
- package/dist/server-helpers.d.ts.map +1 -1
- package/dist/server-helpers.js +11 -0
- package/dist/server-helpers.js.map +1 -1
- package/dist/server.js +28 -1
- package/dist/server.js.map +1 -1
- package/dist/storage/index-store.d.ts +15 -1
- package/dist/storage/index-store.d.ts.map +1 -1
- package/dist/storage/index-store.js +27 -1
- package/dist/storage/index-store.js.map +1 -1
- package/dist/storage/session-state.d.ts +1 -1
- package/dist/storage/session-state.d.ts.map +1 -1
- package/dist/storage/session-state.js +6 -4
- package/dist/storage/session-state.js.map +1 -1
- package/dist/tools/agent-config-tools.d.ts +24 -0
- package/dist/tools/agent-config-tools.d.ts.map +1 -0
- package/dist/tools/agent-config-tools.js +119 -0
- package/dist/tools/agent-config-tools.js.map +1 -0
- package/dist/tools/architecture-tools.d.ts +23 -0
- package/dist/tools/architecture-tools.d.ts.map +1 -0
- package/dist/tools/architecture-tools.js +140 -0
- package/dist/tools/architecture-tools.js.map +1 -0
- package/dist/tools/astro-config.d.ts +33 -0
- package/dist/tools/astro-config.d.ts.map +1 -0
- package/dist/tools/astro-config.js +260 -0
- package/dist/tools/astro-config.js.map +1 -0
- package/dist/tools/astro-islands.d.ts +61 -0
- package/dist/tools/astro-islands.d.ts.map +1 -0
- package/dist/tools/astro-islands.js +240 -0
- package/dist/tools/astro-islands.js.map +1 -0
- package/dist/tools/astro-routes.d.ts +49 -0
- package/dist/tools/astro-routes.d.ts.map +1 -0
- package/dist/tools/astro-routes.js +119 -0
- package/dist/tools/astro-routes.js.map +1 -0
- package/dist/tools/audit-tools.d.ts +38 -0
- package/dist/tools/audit-tools.d.ts.map +1 -0
- package/dist/tools/audit-tools.js +248 -0
- package/dist/tools/audit-tools.js.map +1 -0
- package/dist/tools/celery-tools.d.ts +38 -0
- package/dist/tools/celery-tools.d.ts.map +1 -0
- package/dist/tools/celery-tools.js +154 -0
- package/dist/tools/celery-tools.js.map +1 -0
- package/dist/tools/clone-tools.js +1 -1
- package/dist/tools/clone-tools.js.map +1 -1
- package/dist/tools/complexity-tools.d.ts +4 -0
- package/dist/tools/complexity-tools.d.ts.map +1 -1
- package/dist/tools/complexity-tools.js +78 -4
- package/dist/tools/complexity-tools.js.map +1 -1
- package/dist/tools/compose-tools.d.ts +60 -0
- package/dist/tools/compose-tools.d.ts.map +1 -0
- package/dist/tools/compose-tools.js +203 -0
- package/dist/tools/compose-tools.js.map +1 -0
- package/dist/tools/coupling-tools.d.ts +50 -0
- package/dist/tools/coupling-tools.d.ts.map +1 -0
- package/dist/tools/coupling-tools.js +262 -0
- package/dist/tools/coupling-tools.js.map +1 -0
- package/dist/tools/dependency-audit-tools.d.ts +65 -0
- package/dist/tools/dependency-audit-tools.d.ts.map +1 -0
- package/dist/tools/dependency-audit-tools.js +553 -0
- package/dist/tools/dependency-audit-tools.js.map +1 -0
- package/dist/tools/django-settings.d.ts +22 -0
- package/dist/tools/django-settings.d.ts.map +1 -0
- package/dist/tools/django-settings.js +301 -0
- package/dist/tools/django-settings.js.map +1 -0
- package/dist/tools/frequency-tools.js +1 -1
- package/dist/tools/frequency-tools.js.map +1 -1
- package/dist/tools/graph-tools.d.ts +8 -2
- package/dist/tools/graph-tools.d.ts.map +1 -1
- package/dist/tools/graph-tools.js +44 -3
- package/dist/tools/graph-tools.js.map +1 -1
- package/dist/tools/hilt-tools.d.ts +55 -0
- package/dist/tools/hilt-tools.d.ts.map +1 -0
- package/dist/tools/hilt-tools.js +258 -0
- package/dist/tools/hilt-tools.js.map +1 -0
- package/dist/tools/hono-analyze-app.d.ts +48 -0
- package/dist/tools/hono-analyze-app.d.ts.map +1 -0
- package/dist/tools/hono-analyze-app.js +102 -0
- package/dist/tools/hono-analyze-app.js.map +1 -0
- package/dist/tools/hono-api-contract.d.ts +22 -0
- package/dist/tools/hono-api-contract.d.ts.map +1 -0
- package/dist/tools/hono-api-contract.js +80 -0
- package/dist/tools/hono-api-contract.js.map +1 -0
- package/dist/tools/hono-conditional-middleware.d.ts +27 -0
- package/dist/tools/hono-conditional-middleware.d.ts.map +1 -0
- package/dist/tools/hono-conditional-middleware.js +62 -0
- package/dist/tools/hono-conditional-middleware.js.map +1 -0
- package/dist/tools/hono-context-flow.d.ts +24 -0
- package/dist/tools/hono-context-flow.d.ts.map +1 -0
- package/dist/tools/hono-context-flow.js +78 -0
- package/dist/tools/hono-context-flow.js.map +1 -0
- package/dist/tools/hono-dead-routes.d.ts +26 -0
- package/dist/tools/hono-dead-routes.d.ts.map +1 -0
- package/dist/tools/hono-dead-routes.js +109 -0
- package/dist/tools/hono-dead-routes.js.map +1 -0
- package/dist/tools/hono-env-regression.d.ts +29 -0
- package/dist/tools/hono-env-regression.d.ts.map +1 -0
- package/dist/tools/hono-env-regression.js +157 -0
- package/dist/tools/hono-env-regression.js.map +1 -0
- package/dist/tools/hono-inline-analyze.d.ts +31 -0
- package/dist/tools/hono-inline-analyze.d.ts.map +1 -0
- package/dist/tools/hono-inline-analyze.js +67 -0
- package/dist/tools/hono-inline-analyze.js.map +1 -0
- package/dist/tools/hono-middleware-chain.d.ts +22 -0
- package/dist/tools/hono-middleware-chain.d.ts.map +1 -0
- package/dist/tools/hono-middleware-chain.js +84 -0
- package/dist/tools/hono-middleware-chain.js.map +1 -0
- package/dist/tools/hono-modules.d.ts +22 -0
- package/dist/tools/hono-modules.d.ts.map +1 -0
- package/dist/tools/hono-modules.js +126 -0
- package/dist/tools/hono-modules.js.map +1 -0
- package/dist/tools/hono-response-types.d.ts +37 -0
- package/dist/tools/hono-response-types.d.ts.map +1 -0
- package/dist/tools/hono-response-types.js +84 -0
- package/dist/tools/hono-response-types.js.map +1 -0
- package/dist/tools/hono-rpc-types.d.ts +21 -0
- package/dist/tools/hono-rpc-types.d.ts.map +1 -0
- package/dist/tools/hono-rpc-types.js +57 -0
- package/dist/tools/hono-rpc-types.js.map +1 -0
- package/dist/tools/hono-security.d.ts +21 -0
- package/dist/tools/hono-security.d.ts.map +1 -0
- package/dist/tools/hono-security.js +98 -0
- package/dist/tools/hono-security.js.map +1 -0
- package/dist/tools/hono-visualize.d.ts +13 -0
- package/dist/tools/hono-visualize.d.ts.map +1 -0
- package/dist/tools/hono-visualize.js +72 -0
- package/dist/tools/hono-visualize.js.map +1 -0
- package/dist/tools/hotspot-tools.d.ts.map +1 -1
- package/dist/tools/hotspot-tools.js +9 -7
- package/dist/tools/hotspot-tools.js.map +1 -1
- package/dist/tools/index-tools.d.ts +17 -0
- package/dist/tools/index-tools.d.ts.map +1 -1
- package/dist/tools/index-tools.js +210 -10
- package/dist/tools/index-tools.js.map +1 -1
- package/dist/tools/kotlin-tools.d.ts +142 -0
- package/dist/tools/kotlin-tools.d.ts.map +1 -0
- package/dist/tools/kotlin-tools.js +572 -0
- package/dist/tools/kotlin-tools.js.map +1 -0
- package/dist/tools/legacy-hono-conventions.d.ts +14 -0
- package/dist/tools/legacy-hono-conventions.d.ts.map +1 -0
- package/dist/tools/legacy-hono-conventions.js +152 -0
- package/dist/tools/legacy-hono-conventions.js.map +1 -0
- package/dist/tools/migration-lint-tools.d.ts +26 -0
- package/dist/tools/migration-lint-tools.d.ts.map +1 -0
- package/dist/tools/migration-lint-tools.js +247 -0
- package/dist/tools/migration-lint-tools.js.map +1 -0
- package/dist/tools/model-tools.d.ts +30 -0
- package/dist/tools/model-tools.d.ts.map +1 -0
- package/dist/tools/model-tools.js +145 -0
- package/dist/tools/model-tools.js.map +1 -0
- package/dist/tools/nest-ext-tools.d.ts +92 -0
- package/dist/tools/nest-ext-tools.d.ts.map +1 -0
- package/dist/tools/nest-ext-tools.js +359 -0
- package/dist/tools/nest-ext-tools.js.map +1 -0
- package/dist/tools/nest-tools.d.ts +171 -0
- package/dist/tools/nest-tools.d.ts.map +1 -0
- package/dist/tools/nest-tools.js +1042 -0
- package/dist/tools/nest-tools.js.map +1 -0
- package/dist/tools/nextjs-api-contract-readers.d.ts +14 -0
- package/dist/tools/nextjs-api-contract-readers.d.ts.map +1 -0
- package/dist/tools/nextjs-api-contract-readers.js +204 -0
- package/dist/tools/nextjs-api-contract-readers.js.map +1 -0
- package/dist/tools/nextjs-api-contract-tools.d.ts +57 -0
- package/dist/tools/nextjs-api-contract-tools.d.ts.map +1 -0
- package/dist/tools/nextjs-api-contract-tools.js +144 -0
- package/dist/tools/nextjs-api-contract-tools.js.map +1 -0
- package/dist/tools/nextjs-boundary-tools.d.ts +39 -0
- package/dist/tools/nextjs-boundary-tools.d.ts.map +1 -0
- package/dist/tools/nextjs-boundary-tools.js +152 -0
- package/dist/tools/nextjs-boundary-tools.js.map +1 -0
- package/dist/tools/nextjs-component-tools.d.ts +121 -0
- package/dist/tools/nextjs-component-tools.d.ts.map +1 -0
- package/dist/tools/nextjs-component-tools.js +460 -0
- package/dist/tools/nextjs-component-tools.js.map +1 -0
- package/dist/tools/nextjs-data-flow-tools.d.ts +42 -0
- package/dist/tools/nextjs-data-flow-tools.d.ts.map +1 -0
- package/dist/tools/nextjs-data-flow-tools.js +158 -0
- package/dist/tools/nextjs-data-flow-tools.js.map +1 -0
- package/dist/tools/nextjs-framework-audit-tools.d.ts +37 -0
- package/dist/tools/nextjs-framework-audit-tools.d.ts.map +1 -0
- package/dist/tools/nextjs-framework-audit-tools.js +211 -0
- package/dist/tools/nextjs-framework-audit-tools.js.map +1 -0
- package/dist/tools/nextjs-link-tools.d.ts +41 -0
- package/dist/tools/nextjs-link-tools.d.ts.map +1 -0
- package/dist/tools/nextjs-link-tools.js +157 -0
- package/dist/tools/nextjs-link-tools.js.map +1 -0
- package/dist/tools/nextjs-metadata-tools.d.ts +74 -0
- package/dist/tools/nextjs-metadata-tools.d.ts.map +1 -0
- package/dist/tools/nextjs-metadata-tools.js +252 -0
- package/dist/tools/nextjs-metadata-tools.js.map +1 -0
- package/dist/tools/nextjs-middleware-coverage-tools.d.ts +41 -0
- package/dist/tools/nextjs-middleware-coverage-tools.d.ts.map +1 -0
- package/dist/tools/nextjs-middleware-coverage-tools.js +88 -0
- package/dist/tools/nextjs-middleware-coverage-tools.js.map +1 -0
- package/dist/tools/nextjs-route-tools.d.ts +100 -0
- package/dist/tools/nextjs-route-tools.d.ts.map +1 -0
- package/dist/tools/nextjs-route-tools.js +493 -0
- package/dist/tools/nextjs-route-tools.js.map +1 -0
- package/dist/tools/nextjs-security-readers.d.ts +22 -0
- package/dist/tools/nextjs-security-readers.d.ts.map +1 -0
- package/dist/tools/nextjs-security-readers.js +318 -0
- package/dist/tools/nextjs-security-readers.js.map +1 -0
- package/dist/tools/nextjs-security-scoring.d.ts +15 -0
- package/dist/tools/nextjs-security-scoring.d.ts.map +1 -0
- package/dist/tools/nextjs-security-scoring.js +65 -0
- package/dist/tools/nextjs-security-scoring.js.map +1 -0
- package/dist/tools/nextjs-security-tools.d.ts +75 -0
- package/dist/tools/nextjs-security-tools.d.ts.map +1 -0
- package/dist/tools/nextjs-security-tools.js +153 -0
- package/dist/tools/nextjs-security-tools.js.map +1 -0
- package/dist/tools/nextjs-tools.d.ts +15 -0
- package/dist/tools/nextjs-tools.d.ts.map +1 -0
- package/dist/tools/nextjs-tools.js +15 -0
- package/dist/tools/nextjs-tools.js.map +1 -0
- package/dist/tools/outline-tools.d.ts.map +1 -1
- package/dist/tools/outline-tools.js +20 -0
- package/dist/tools/outline-tools.js.map +1 -1
- package/dist/tools/pattern-tools.d.ts +8 -0
- package/dist/tools/pattern-tools.d.ts.map +1 -1
- package/dist/tools/pattern-tools.js +561 -3
- package/dist/tools/pattern-tools.js.map +1 -1
- package/dist/tools/perf-tools.d.ts +32 -0
- package/dist/tools/perf-tools.d.ts.map +1 -0
- package/dist/tools/perf-tools.js +227 -0
- package/dist/tools/perf-tools.js.map +1 -0
- package/dist/tools/php-tools.d.ts +176 -0
- package/dist/tools/php-tools.d.ts.map +1 -0
- package/dist/tools/php-tools.js +543 -0
- package/dist/tools/php-tools.js.map +1 -0
- package/dist/tools/prisma-schema-tools.d.ts +44 -0
- package/dist/tools/prisma-schema-tools.d.ts.map +1 -0
- package/dist/tools/prisma-schema-tools.js +358 -0
- package/dist/tools/prisma-schema-tools.js.map +1 -0
- package/dist/tools/project-tools.d.ts +115 -6
- package/dist/tools/project-tools.d.ts.map +1 -1
- package/dist/tools/project-tools.js +594 -217
- package/dist/tools/project-tools.js.map +1 -1
- package/dist/tools/pyproject-tools.d.ts +23 -0
- package/dist/tools/pyproject-tools.d.ts.map +1 -0
- package/dist/tools/pyproject-tools.js +133 -0
- package/dist/tools/pyproject-tools.js.map +1 -0
- package/dist/tools/pytest-tools.d.ts +20 -0
- package/dist/tools/pytest-tools.d.ts.map +1 -0
- package/dist/tools/pytest-tools.js +106 -0
- package/dist/tools/pytest-tools.js.map +1 -0
- package/dist/tools/python-callers.d.ts +28 -0
- package/dist/tools/python-callers.d.ts.map +1 -0
- package/dist/tools/python-callers.js +110 -0
- package/dist/tools/python-callers.js.map +1 -0
- package/dist/tools/python-circular-imports.d.ts +19 -0
- package/dist/tools/python-circular-imports.d.ts.map +1 -0
- package/dist/tools/python-circular-imports.js +126 -0
- package/dist/tools/python-circular-imports.js.map +1 -0
- package/dist/tools/python-deps-analyzer.d.ts +46 -0
- package/dist/tools/python-deps-analyzer.d.ts.map +1 -0
- package/dist/tools/python-deps-analyzer.js +227 -0
- package/dist/tools/python-deps-analyzer.js.map +1 -0
- package/dist/tools/query-tools.d.ts +23 -0
- package/dist/tools/query-tools.d.ts.map +1 -0
- package/dist/tools/query-tools.js +256 -0
- package/dist/tools/query-tools.js.map +1 -0
- package/dist/tools/react-tools.d.ts +218 -0
- package/dist/tools/react-tools.d.ts.map +1 -0
- package/dist/tools/react-tools.js +714 -0
- package/dist/tools/react-tools.js.map +1 -0
- package/dist/tools/report-tools.js +47 -0
- package/dist/tools/report-tools.js.map +1 -1
- package/dist/tools/review-diff-tools.d.ts +2 -6
- package/dist/tools/review-diff-tools.d.ts.map +1 -1
- package/dist/tools/review-diff-tools.js +51 -66
- package/dist/tools/review-diff-tools.js.map +1 -1
- package/dist/tools/room-tools.d.ts +36 -0
- package/dist/tools/room-tools.d.ts.map +1 -0
- package/dist/tools/room-tools.js +147 -0
- package/dist/tools/room-tools.js.map +1 -0
- package/dist/tools/route-tools.d.ts +27 -1
- package/dist/tools/route-tools.d.ts.map +1 -1
- package/dist/tools/route-tools.js +744 -18
- package/dist/tools/route-tools.js.map +1 -1
- package/dist/tools/ruff-tools.d.ts +32 -0
- package/dist/tools/ruff-tools.d.ts.map +1 -0
- package/dist/tools/ruff-tools.js +114 -0
- package/dist/tools/ruff-tools.js.map +1 -0
- package/dist/tools/search-ranker.d.ts.map +1 -1
- package/dist/tools/search-ranker.js +7 -0
- package/dist/tools/search-ranker.js.map +1 -1
- package/dist/tools/serialization-tools.d.ts +24 -0
- package/dist/tools/serialization-tools.d.ts.map +1 -0
- package/dist/tools/serialization-tools.js +156 -0
- package/dist/tools/serialization-tools.js.map +1 -0
- package/dist/tools/sql-tools.d.ts +234 -0
- package/dist/tools/sql-tools.d.ts.map +1 -0
- package/dist/tools/sql-tools.js +1037 -0
- package/dist/tools/sql-tools.js.map +1 -0
- package/dist/tools/status-tools.d.ts +10 -0
- package/dist/tools/status-tools.d.ts.map +1 -0
- package/dist/tools/status-tools.js +32 -0
- package/dist/tools/status-tools.js.map +1 -0
- package/dist/tools/symbol-tools.d.ts +19 -0
- package/dist/tools/symbol-tools.d.ts.map +1 -1
- package/dist/tools/symbol-tools.js +78 -4
- package/dist/tools/symbol-tools.js.map +1 -1
- package/dist/tools/test-impact-tools.d.ts +29 -0
- package/dist/tools/test-impact-tools.d.ts.map +1 -0
- package/dist/tools/test-impact-tools.js +156 -0
- package/dist/tools/test-impact-tools.js.map +1 -0
- package/dist/tools/typecheck-tools.d.ts +39 -0
- package/dist/tools/typecheck-tools.d.ts.map +1 -0
- package/dist/tools/typecheck-tools.js +191 -0
- package/dist/tools/typecheck-tools.js.map +1 -0
- package/dist/tools/wiring-tools.d.ts +19 -0
- package/dist/tools/wiring-tools.d.ts.map +1 -0
- package/dist/tools/wiring-tools.js +147 -0
- package/dist/tools/wiring-tools.js.map +1 -0
- package/dist/types.d.ts +9 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/framework-detect.d.ts +18 -2
- package/dist/utils/framework-detect.d.ts.map +1 -1
- package/dist/utils/framework-detect.js +150 -3
- package/dist/utils/framework-detect.js.map +1 -1
- package/dist/utils/import-graph.d.ts +36 -0
- package/dist/utils/import-graph.d.ts.map +1 -1
- package/dist/utils/import-graph.js +212 -9
- package/dist/utils/import-graph.js.map +1 -1
- package/dist/utils/language-detect.d.ts +21 -0
- package/dist/utils/language-detect.d.ts.map +1 -0
- package/dist/utils/language-detect.js +183 -0
- package/dist/utils/language-detect.js.map +1 -0
- package/dist/utils/nextjs-ast-readers.d.ts +44 -0
- package/dist/utils/nextjs-ast-readers.d.ts.map +1 -0
- package/dist/utils/nextjs-ast-readers.js +341 -0
- package/dist/utils/nextjs-ast-readers.js.map +1 -0
- package/dist/utils/nextjs-audit-cache.d.ts +51 -0
- package/dist/utils/nextjs-audit-cache.d.ts.map +1 -0
- package/dist/utils/nextjs-audit-cache.js +116 -0
- package/dist/utils/nextjs-audit-cache.js.map +1 -0
- package/dist/utils/nextjs-metadata-readers.d.ts +65 -0
- package/dist/utils/nextjs-metadata-readers.d.ts.map +1 -0
- package/dist/utils/nextjs-metadata-readers.js +447 -0
- package/dist/utils/nextjs-metadata-readers.js.map +1 -0
- package/dist/utils/nextjs.d.ts +42 -0
- package/dist/utils/nextjs.d.ts.map +1 -0
- package/dist/utils/nextjs.js +284 -0
- package/dist/utils/nextjs.js.map +1 -0
- package/dist/utils/python-import-resolver.d.ts +42 -0
- package/dist/utils/python-import-resolver.d.ts.map +1 -0
- package/dist/utils/python-import-resolver.js +101 -0
- package/dist/utils/python-import-resolver.js.map +1 -0
- package/dist/utils/python-imports.d.ts +28 -0
- package/dist/utils/python-imports.d.ts.map +1 -0
- package/dist/utils/python-imports.js +117 -0
- package/dist/utils/python-imports.js.map +1 -0
- package/dist/utils/react-alias.d.ts +15 -0
- package/dist/utils/react-alias.d.ts.map +1 -0
- package/dist/utils/react-alias.js +31 -0
- package/dist/utils/react-alias.js.map +1 -0
- package/dist/utils/test-file.d.ts.map +1 -1
- package/dist/utils/test-file.js +7 -0
- package/dist/utils/test-file.js.map +1 -1
- package/dist/utils/walk.d.ts +22 -0
- package/dist/utils/walk.d.ts.map +1 -1
- package/dist/utils/walk.js +70 -2
- package/dist/utils/walk.js.map +1 -1
- package/package.json +3 -2
- package/rules/codesift.md +33 -5
- package/rules/codesift.mdc +33 -5
- package/rules/codex.md +33 -5
- package/rules/gemini.md +33 -5
- package/src/parser/languages/tree-sitter-javascript.wasm +0 -0
- package/src/parser/languages/tree-sitter-kotlin.wasm +0 -0
- package/src/parser/languages/tree-sitter-php.wasm +0 -0
- package/src/parser/languages/tree-sitter-php_only.wasm +0 -0
- 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: "
|
|
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
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
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
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
const
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
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
|
-
|
|
523
|
-
|
|
524
|
-
|
|
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
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
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
|
-
//
|
|
602
|
-
for (const
|
|
603
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
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
|
-
|
|
672
|
-
|
|
673
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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
|
-
//
|
|
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 (
|
|
1267
|
+
if (/(^|\/)[Mm]iddleware\//.test(f.path) && f.path.endsWith(".php")) {
|
|
978
1268
|
middleware.push({ name, path: f.path });
|
|
979
1269
|
}
|
|
980
|
-
if (
|
|
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 {
|
|
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
|