codesift-mcp 0.9.5 → 0.9.10
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 +39 -23
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +88 -2
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/help.d.ts +1 -1
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +18 -0
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/hooks/input.d.ts +11 -0
- package/dist/cli/hooks/input.d.ts.map +1 -0
- package/dist/cli/hooks/input.js +111 -0
- package/dist/cli/hooks/input.js.map +1 -0
- package/dist/cli/hooks/post-tool-use.d.ts +2 -0
- package/dist/cli/hooks/post-tool-use.d.ts.map +1 -0
- package/dist/cli/hooks/post-tool-use.js +339 -0
- package/dist/cli/hooks/post-tool-use.js.map +1 -0
- package/dist/cli/hooks/pre-compact.d.ts +2 -0
- package/dist/cli/hooks/pre-compact.d.ts.map +1 -0
- package/dist/cli/hooks/pre-compact.js +41 -0
- package/dist/cli/hooks/pre-compact.js.map +1 -0
- package/dist/cli/hooks/pre-tool-use.d.ts +5 -0
- package/dist/cli/hooks/pre-tool-use.d.ts.map +1 -0
- package/dist/cli/hooks/pre-tool-use.js +215 -0
- package/dist/cli/hooks/pre-tool-use.js.map +1 -0
- package/dist/cli/hooks/session.d.ts +5 -0
- package/dist/cli/hooks/session.d.ts.map +1 -0
- package/dist/cli/hooks/session.js +188 -0
- package/dist/cli/hooks/session.js.map +1 -0
- package/dist/cli/hooks/shared.d.ts +6 -0
- package/dist/cli/hooks/shared.d.ts.map +1 -0
- package/dist/cli/hooks/shared.js +70 -0
- package/dist/cli/hooks/shared.js.map +1 -0
- package/dist/cli/hooks/wiki.d.ts +10 -0
- package/dist/cli/hooks/wiki.d.ts.map +1 -0
- package/dist/cli/hooks/wiki.js +202 -0
- package/dist/cli/hooks/wiki.js.map +1 -0
- package/dist/cli/hooks.d.ts +5 -16
- package/dist/cli/hooks.d.ts.map +1 -1
- package/dist/cli/hooks.js +5 -1132
- package/dist/cli/hooks.js.map +1 -1
- package/dist/cli/setup/claude.d.ts +4 -0
- package/dist/cli/setup/claude.d.ts.map +1 -0
- package/dist/cli/setup/claude.js +123 -0
- package/dist/cli/setup/claude.js.map +1 -0
- package/dist/cli/setup/codex.d.ts +12 -0
- package/dist/cli/setup/codex.d.ts.map +1 -0
- package/dist/cli/setup/codex.js +182 -0
- package/dist/cli/setup/codex.js.map +1 -0
- package/dist/cli/setup/cursor.d.ts +3 -0
- package/dist/cli/setup/cursor.d.ts.map +1 -0
- package/dist/cli/setup/cursor.js +6 -0
- package/dist/cli/setup/cursor.js.map +1 -0
- package/dist/cli/setup/fs.d.ts +6 -0
- package/dist/cli/setup/fs.d.ts.map +1 -0
- package/dist/cli/setup/fs.js +44 -0
- package/dist/cli/setup/fs.js.map +1 -0
- package/dist/cli/setup/gemini.d.ts +5 -0
- package/dist/cli/setup/gemini.d.ts.map +1 -0
- package/dist/cli/setup/gemini.js +46 -0
- package/dist/cli/setup/gemini.js.map +1 -0
- package/dist/cli/setup/hooks.d.ts +12 -0
- package/dist/cli/setup/hooks.d.ts.map +1 -0
- package/dist/cli/setup/hooks.js +29 -0
- package/dist/cli/setup/hooks.js.map +1 -0
- package/dist/cli/setup/mcp.d.ts +13 -0
- package/dist/cli/setup/mcp.d.ts.map +1 -0
- package/dist/cli/setup/mcp.js +104 -0
- package/dist/cli/setup/mcp.js.map +1 -0
- package/dist/cli/setup/rules.d.ts +4 -0
- package/dist/cli/setup/rules.d.ts.map +1 -0
- package/dist/cli/setup/rules.js +157 -0
- package/dist/cli/setup/rules.js.map +1 -0
- package/dist/cli/setup/types.d.ts +24 -0
- package/dist/cli/setup/types.d.ts.map +1 -0
- package/dist/cli/setup/types.js +2 -0
- package/dist/cli/setup/types.js.map +1 -0
- package/dist/cli/setup.d.ts +8 -48
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +49 -705
- package/dist/cli/setup.js.map +1 -1
- package/dist/config.d.ts +15 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +56 -2
- package/dist/config.js.map +1 -1
- package/dist/formatter-dispatch.d.ts +53 -0
- package/dist/formatter-dispatch.d.ts.map +1 -0
- package/dist/formatter-dispatch.js +54 -0
- package/dist/formatter-dispatch.js.map +1 -0
- package/dist/formatters-analysis.d.ts +9 -0
- package/dist/formatters-analysis.d.ts.map +1 -0
- package/dist/formatters-analysis.js +166 -0
- package/dist/formatters-analysis.js.map +1 -0
- package/dist/formatters-core.d.ts +200 -0
- package/dist/formatters-core.d.ts.map +1 -0
- package/dist/formatters-core.js +206 -0
- package/dist/formatters-core.js.map +1 -0
- package/dist/formatters-graph.d.ts +124 -0
- package/dist/formatters-graph.d.ts.map +1 -0
- package/dist/formatters-graph.js +175 -0
- package/dist/formatters-graph.js.map +1 -0
- package/dist/formatters-nextjs.d.ts +22 -0
- package/dist/formatters-nextjs.d.ts.map +1 -0
- package/dist/formatters-nextjs.js +394 -0
- package/dist/formatters-nextjs.js.map +1 -0
- package/dist/formatters.d.ts +8 -349
- package/dist/formatters.d.ts.map +1 -1
- package/dist/formatters.js +10 -929
- package/dist/formatters.js.map +1 -1
- package/dist/parser/astro-template/preprocess.d.ts +12 -0
- package/dist/parser/astro-template/preprocess.d.ts.map +1 -0
- package/dist/parser/astro-template/preprocess.js +34 -0
- package/dist/parser/astro-template/preprocess.js.map +1 -0
- package/dist/parser/astro-template/resolution.d.ts +16 -0
- package/dist/parser/astro-template/resolution.d.ts.map +1 -0
- package/dist/parser/astro-template/resolution.js +58 -0
- package/dist/parser/astro-template/resolution.js.map +1 -0
- package/dist/parser/astro-template/scanner.d.ts +4 -0
- package/dist/parser/astro-template/scanner.d.ts.map +1 -0
- package/dist/parser/astro-template/scanner.js +71 -0
- package/dist/parser/astro-template/scanner.js.map +1 -0
- package/dist/parser/astro-template/state.d.ts +22 -0
- package/dist/parser/astro-template/state.d.ts.map +1 -0
- package/dist/parser/astro-template/state.js +48 -0
- package/dist/parser/astro-template/state.js.map +1 -0
- package/dist/parser/astro-template/tag-processor.d.ts +3 -0
- package/dist/parser/astro-template/tag-processor.d.ts.map +1 -0
- package/dist/parser/astro-template/tag-processor.js +63 -0
- package/dist/parser/astro-template/tag-processor.js.map +1 -0
- package/dist/parser/astro-template/types.d.ts +42 -0
- package/dist/parser/astro-template/types.d.ts.map +1 -0
- package/dist/parser/astro-template/types.js +2 -0
- package/dist/parser/astro-template/types.js.map +1 -0
- package/dist/parser/astro-template.d.ts +2 -45
- package/dist/parser/astro-template.d.ts.map +1 -1
- package/dist/parser/astro-template.js +7 -168
- package/dist/parser/astro-template.js.map +1 -1
- package/dist/parser/extractors/_shared.js +1 -1
- package/dist/parser/extractors/_shared.js.map +1 -1
- package/dist/parser/extractors/astro.js +1 -1
- package/dist/parser/extractors/astro.js.map +1 -1
- package/dist/parser/extractors/conversation.js +1 -1
- package/dist/parser/extractors/conversation.js.map +1 -1
- package/dist/parser/extractors/hono-app-discovery.d.ts +4 -0
- package/dist/parser/extractors/hono-app-discovery.d.ts.map +1 -0
- package/dist/parser/extractors/hono-app-discovery.js +169 -0
- package/dist/parser/extractors/hono-app-discovery.js.map +1 -0
- package/dist/parser/extractors/hono-ast-utils.d.ts +6 -0
- package/dist/parser/extractors/hono-ast-utils.d.ts.map +1 -0
- package/dist/parser/extractors/hono-ast-utils.js +77 -0
- package/dist/parser/extractors/hono-ast-utils.js.map +1 -0
- package/dist/parser/extractors/hono-context-extractor.d.ts +15 -0
- package/dist/parser/extractors/hono-context-extractor.d.ts.map +1 -0
- package/dist/parser/extractors/hono-context-extractor.js +191 -0
- package/dist/parser/extractors/hono-context-extractor.js.map +1 -0
- package/dist/parser/extractors/hono-middleware-extractor.d.ts +38 -0
- package/dist/parser/extractors/hono-middleware-extractor.d.ts.map +1 -0
- package/dist/parser/extractors/hono-middleware-extractor.js +480 -0
- package/dist/parser/extractors/hono-middleware-extractor.js.map +1 -0
- package/dist/parser/extractors/hono-openapi-extractor.d.ts +11 -0
- package/dist/parser/extractors/hono-openapi-extractor.d.ts.map +1 -0
- package/dist/parser/extractors/hono-openapi-extractor.js +119 -0
- package/dist/parser/extractors/hono-openapi-extractor.js.map +1 -0
- package/dist/parser/extractors/hono-route-extractor.d.ts +11 -0
- package/dist/parser/extractors/hono-route-extractor.d.ts.map +1 -0
- package/dist/parser/extractors/hono-route-extractor.js +128 -0
- package/dist/parser/extractors/hono-route-extractor.js.map +1 -0
- package/dist/parser/extractors/hono-route-utils.d.ts +11 -0
- package/dist/parser/extractors/hono-route-utils.d.ts.map +1 -0
- package/dist/parser/extractors/hono-route-utils.js +40 -0
- package/dist/parser/extractors/hono-route-utils.js.map +1 -0
- package/dist/parser/extractors/hono-runtime-extractor.d.ts +17 -0
- package/dist/parser/extractors/hono-runtime-extractor.d.ts.map +1 -0
- package/dist/parser/extractors/hono-runtime-extractor.js +141 -0
- package/dist/parser/extractors/hono-runtime-extractor.js.map +1 -0
- package/dist/parser/extractors/hono.d.ts +5 -79
- package/dist/parser/extractors/hono.d.ts.map +1 -1
- package/dist/parser/extractors/hono.js +32 -1159
- package/dist/parser/extractors/hono.js.map +1 -1
- package/dist/parser/extractors/kotlin-ast-helpers.d.ts +43 -0
- package/dist/parser/extractors/kotlin-ast-helpers.d.ts.map +1 -0
- package/dist/parser/extractors/kotlin-ast-helpers.js +187 -0
- package/dist/parser/extractors/kotlin-ast-helpers.js.map +1 -0
- package/dist/parser/extractors/kotlin-test-symbols.d.ts +21 -0
- package/dist/parser/extractors/kotlin-test-symbols.d.ts.map +1 -0
- package/dist/parser/extractors/kotlin-test-symbols.js +162 -0
- package/dist/parser/extractors/kotlin-test-symbols.js.map +1 -0
- package/dist/parser/extractors/kotlin.d.ts.map +1 -1
- package/dist/parser/extractors/kotlin.js +3 -334
- package/dist/parser/extractors/kotlin.js.map +1 -1
- package/dist/parser/extractors/markdown.js +1 -1
- package/dist/parser/extractors/markdown.js.map +1 -1
- package/dist/parser/extractors/prisma.js +1 -1
- package/dist/parser/extractors/prisma.js.map +1 -1
- package/dist/parser/extractors/sql-boundaries.d.ts +3 -0
- package/dist/parser/extractors/sql-boundaries.d.ts.map +1 -0
- package/dist/parser/extractors/sql-boundaries.js +19 -0
- package/dist/parser/extractors/sql-boundaries.js.map +1 -0
- package/dist/parser/extractors/sql-columns.d.ts +14 -0
- package/dist/parser/extractors/sql-columns.d.ts.map +1 -0
- package/dist/parser/extractors/sql-columns.js +59 -0
- package/dist/parser/extractors/sql-columns.js.map +1 -0
- package/dist/parser/extractors/sql-docstrings.d.ts +2 -0
- package/dist/parser/extractors/sql-docstrings.d.ts.map +1 -0
- package/dist/parser/extractors/sql-docstrings.js +45 -0
- package/dist/parser/extractors/sql-docstrings.js.map +1 -0
- package/dist/parser/extractors/sql-end-scanner.d.ts +5 -0
- package/dist/parser/extractors/sql-end-scanner.d.ts.map +1 -0
- package/dist/parser/extractors/sql-end-scanner.js +84 -0
- package/dist/parser/extractors/sql-end-scanner.js.map +1 -0
- package/dist/parser/extractors/sql-jinja.d.ts +2 -0
- package/dist/parser/extractors/sql-jinja.d.ts.map +1 -0
- package/dist/parser/extractors/sql-jinja.js +8 -0
- package/dist/parser/extractors/sql-jinja.js.map +1 -0
- package/dist/parser/extractors/sql-matcher-catalog.d.ts +9 -0
- package/dist/parser/extractors/sql-matcher-catalog.d.ts.map +1 -0
- package/dist/parser/extractors/sql-matcher-catalog.js +15 -0
- package/dist/parser/extractors/sql-matcher-catalog.js.map +1 -0
- package/dist/parser/extractors/sql-matchers.d.ts +9 -0
- package/dist/parser/extractors/sql-matchers.d.ts.map +1 -0
- package/dist/parser/extractors/sql-matchers.js +20 -0
- package/dist/parser/extractors/sql-matchers.js.map +1 -0
- package/dist/parser/extractors/sql-offsets.d.ts +2 -0
- package/dist/parser/extractors/sql-offsets.d.ts.map +1 -0
- package/dist/parser/extractors/sql-offsets.js +25 -0
- package/dist/parser/extractors/sql-offsets.js.map +1 -0
- package/dist/parser/extractors/sql-parens.d.ts +4 -0
- package/dist/parser/extractors/sql-parens.d.ts.map +1 -0
- package/dist/parser/extractors/sql-parens.js +70 -0
- package/dist/parser/extractors/sql-parens.js.map +1 -0
- package/dist/parser/extractors/sql-symbols.d.ts +11 -0
- package/dist/parser/extractors/sql-symbols.d.ts.map +1 -0
- package/dist/parser/extractors/sql-symbols.js +23 -0
- package/dist/parser/extractors/sql-symbols.js.map +1 -0
- package/dist/parser/extractors/sql.d.ts +2 -30
- package/dist/parser/extractors/sql.d.ts.map +1 -1
- package/dist/parser/extractors/sql.js +23 -496
- package/dist/parser/extractors/sql.js.map +1 -1
- package/dist/parser/extractors/typescript-cjs-nodes.d.ts +5 -0
- package/dist/parser/extractors/typescript-cjs-nodes.d.ts.map +1 -0
- package/dist/parser/extractors/typescript-cjs-nodes.js +148 -0
- package/dist/parser/extractors/typescript-cjs-nodes.js.map +1 -0
- package/dist/parser/extractors/typescript-class-nodes.d.ts +8 -0
- package/dist/parser/extractors/typescript-class-nodes.d.ts.map +1 -0
- package/dist/parser/extractors/typescript-class-nodes.js +183 -0
- package/dist/parser/extractors/typescript-class-nodes.js.map +1 -0
- package/dist/parser/extractors/typescript-declaration-nodes.d.ts +5 -0
- package/dist/parser/extractors/typescript-declaration-nodes.d.ts.map +1 -0
- package/dist/parser/extractors/typescript-declaration-nodes.js +105 -0
- package/dist/parser/extractors/typescript-declaration-nodes.js.map +1 -0
- package/dist/parser/extractors/typescript-module-nodes.d.ts +6 -0
- package/dist/parser/extractors/typescript-module-nodes.d.ts.map +1 -0
- package/dist/parser/extractors/typescript-module-nodes.js +151 -0
- package/dist/parser/extractors/typescript-module-nodes.js.map +1 -0
- package/dist/parser/extractors/typescript-react.d.ts +11 -0
- package/dist/parser/extractors/typescript-react.d.ts.map +1 -0
- package/dist/parser/extractors/typescript-react.js +101 -0
- package/dist/parser/extractors/typescript-react.js.map +1 -0
- package/dist/parser/extractors/typescript-shared.d.ts +26 -0
- package/dist/parser/extractors/typescript-shared.d.ts.map +1 -0
- package/dist/parser/extractors/typescript-shared.js +101 -0
- package/dist/parser/extractors/typescript-shared.js.map +1 -0
- package/dist/parser/extractors/typescript-test-nodes.d.ts +4 -0
- package/dist/parser/extractors/typescript-test-nodes.d.ts.map +1 -0
- package/dist/parser/extractors/typescript-test-nodes.js +112 -0
- package/dist/parser/extractors/typescript-test-nodes.js.map +1 -0
- package/dist/parser/extractors/typescript-type-nodes.d.ts +6 -0
- package/dist/parser/extractors/typescript-type-nodes.d.ts.map +1 -0
- package/dist/parser/extractors/typescript-type-nodes.js +48 -0
- package/dist/parser/extractors/typescript-type-nodes.js.map +1 -0
- package/dist/parser/extractors/typescript.d.ts.map +1 -1
- package/dist/parser/extractors/typescript.js +68 -1127
- package/dist/parser/extractors/typescript.js.map +1 -1
- package/dist/parser/parser-manager.d.ts.map +1 -1
- package/dist/parser/parser-manager.js +5 -0
- package/dist/parser/parser-manager.js.map +1 -1
- package/dist/parser/symbol-extractor.d.ts +1 -12
- package/dist/parser/symbol-extractor.d.ts.map +1 -1
- package/dist/parser/symbol-extractor.js +2 -24
- package/dist/parser/symbol-extractor.js.map +1 -1
- package/dist/parser/symbol-utils.d.ts +4 -0
- package/dist/parser/symbol-utils.d.ts.map +1 -0
- package/dist/parser/symbol-utils.js +20 -0
- package/dist/parser/symbol-utils.js.map +1 -0
- package/dist/register-tool-groups/analysis/cross-repo.d.ts +4 -0
- package/dist/register-tool-groups/analysis/cross-repo.d.ts.map +1 -0
- package/dist/register-tool-groups/analysis/cross-repo.js +108 -0
- package/dist/register-tool-groups/analysis/cross-repo.js.map +1 -0
- package/dist/register-tool-groups/analysis/review.d.ts +3 -0
- package/dist/register-tool-groups/analysis/review.d.ts.map +1 -0
- package/dist/register-tool-groups/analysis/review.js +96 -0
- package/dist/register-tool-groups/analysis/review.js.map +1 -0
- package/dist/register-tool-groups/analysis/workspace.d.ts +3 -0
- package/dist/register-tool-groups/analysis/workspace.d.ts.map +1 -0
- package/dist/register-tool-groups/analysis/workspace.js +82 -0
- package/dist/register-tool-groups/analysis/workspace.js.map +1 -0
- package/dist/register-tool-groups/analysis.d.ts +3 -0
- package/dist/register-tool-groups/analysis.d.ts.map +1 -0
- package/dist/register-tool-groups/analysis.js +541 -0
- package/dist/register-tool-groups/analysis.js.map +1 -0
- package/dist/register-tool-groups/astro.d.ts +3 -0
- package/dist/register-tool-groups/astro.d.ts.map +1 -0
- package/dist/register-tool-groups/astro.js +302 -0
- package/dist/register-tool-groups/astro.js.map +1 -0
- package/dist/register-tool-groups/core/index.d.ts +3 -0
- package/dist/register-tool-groups/core/index.d.ts.map +1 -0
- package/dist/register-tool-groups/core/index.js +96 -0
- package/dist/register-tool-groups/core/index.js.map +1 -0
- package/dist/register-tool-groups/core/meta.d.ts +3 -0
- package/dist/register-tool-groups/core/meta.d.ts.map +1 -0
- package/dist/register-tool-groups/core/meta.js +180 -0
- package/dist/register-tool-groups/core/meta.js.map +1 -0
- package/dist/register-tool-groups/core/schema.d.ts +3 -0
- package/dist/register-tool-groups/core/schema.d.ts.map +1 -0
- package/dist/register-tool-groups/core/schema.js +16 -0
- package/dist/register-tool-groups/core/schema.js.map +1 -0
- package/dist/register-tool-groups/core/search.d.ts +4 -0
- package/dist/register-tool-groups/core/search.d.ts.map +1 -0
- package/dist/register-tool-groups/core/search.js +262 -0
- package/dist/register-tool-groups/core/search.js.map +1 -0
- package/dist/register-tool-groups/core/symbols.d.ts +3 -0
- package/dist/register-tool-groups/core/symbols.d.ts.map +1 -0
- package/dist/register-tool-groups/core/symbols.js +338 -0
- package/dist/register-tool-groups/core/symbols.js.map +1 -0
- package/dist/register-tool-groups/core.d.ts +3 -0
- package/dist/register-tool-groups/core.d.ts.map +1 -0
- package/dist/register-tool-groups/core.js +12 -0
- package/dist/register-tool-groups/core.js.map +1 -0
- package/dist/register-tool-groups/deps.d.ts +10 -0
- package/dist/register-tool-groups/deps.d.ts.map +1 -0
- package/dist/register-tool-groups/deps.js +6 -0
- package/dist/register-tool-groups/deps.js.map +1 -0
- package/dist/register-tool-groups/handler-wrappers.d.ts +63 -0
- package/dist/register-tool-groups/handler-wrappers.d.ts.map +1 -0
- package/dist/register-tool-groups/handler-wrappers.js +106 -0
- package/dist/register-tool-groups/handler-wrappers.js.map +1 -0
- package/dist/register-tool-groups/hono.d.ts +3 -0
- package/dist/register-tool-groups/hono.d.ts.map +1 -0
- package/dist/register-tool-groups/hono.js +179 -0
- package/dist/register-tool-groups/hono.js.map +1 -0
- package/dist/register-tool-groups/index.d.ts +3 -0
- package/dist/register-tool-groups/index.d.ts.map +1 -0
- package/dist/register-tool-groups/index.js +28 -0
- package/dist/register-tool-groups/index.js.map +1 -0
- package/dist/register-tool-groups/kotlin.d.ts +3 -0
- package/dist/register-tool-groups/kotlin.d.ts.map +1 -0
- package/dist/register-tool-groups/kotlin.js +162 -0
- package/dist/register-tool-groups/kotlin.js.map +1 -0
- package/dist/register-tool-groups/meta.d.ts +3 -0
- package/dist/register-tool-groups/meta.d.ts.map +1 -0
- package/dist/register-tool-groups/meta.js +563 -0
- package/dist/register-tool-groups/meta.js.map +1 -0
- package/dist/register-tool-groups/nextjs.d.ts +3 -0
- package/dist/register-tool-groups/nextjs.d.ts.map +1 -0
- package/dist/register-tool-groups/nextjs.js +78 -0
- package/dist/register-tool-groups/nextjs.js.map +1 -0
- package/dist/register-tool-groups/php.d.ts +3 -0
- package/dist/register-tool-groups/php.d.ts.map +1 -0
- package/dist/register-tool-groups/php.js +312 -0
- package/dist/register-tool-groups/php.js.map +1 -0
- package/dist/register-tool-groups/python.d.ts +3 -0
- package/dist/register-tool-groups/python.d.ts.map +1 -0
- package/dist/register-tool-groups/python.js +366 -0
- package/dist/register-tool-groups/python.js.map +1 -0
- package/dist/register-tool-groups/react.d.ts +3 -0
- package/dist/register-tool-groups/react.d.ts.map +1 -0
- package/dist/register-tool-groups/react.js +119 -0
- package/dist/register-tool-groups/react.js.map +1 -0
- package/dist/register-tool-groups/shared.d.ts +156 -0
- package/dist/register-tool-groups/shared.d.ts.map +1 -0
- package/dist/register-tool-groups/shared.js +182 -0
- package/dist/register-tool-groups/shared.js.map +1 -0
- package/dist/register-tool-groups/sql.d.ts +3 -0
- package/dist/register-tool-groups/sql.d.ts.map +1 -0
- package/dist/register-tool-groups/sql.js +260 -0
- package/dist/register-tool-groups/sql.js.map +1 -0
- package/dist/register-tool-loaders.d.ts +22 -22
- package/dist/register-tool-loaders.d.ts.map +1 -1
- package/dist/register-tools/autoload.d.ts +8 -0
- package/dist/register-tools/autoload.d.ts.map +1 -0
- package/dist/register-tools/autoload.js +517 -0
- package/dist/register-tools/autoload.js.map +1 -0
- package/dist/register-tools/discovery.d.ts +55 -0
- package/dist/register-tools/discovery.d.ts.map +1 -0
- package/dist/register-tools/discovery.js +206 -0
- package/dist/register-tools/discovery.js.map +1 -0
- package/dist/register-tools/runtime.d.ts +89 -0
- package/dist/register-tools/runtime.d.ts.map +1 -0
- package/dist/register-tools/runtime.js +491 -0
- package/dist/register-tools/runtime.js.map +1 -0
- package/dist/register-tools.d.ts +5 -172
- package/dist/register-tools.d.ts.map +1 -1
- package/dist/register-tools.js +27 -4878
- package/dist/register-tools.js.map +1 -1
- package/dist/search/bm25.js +1 -1
- package/dist/search/bm25.js.map +1 -1
- package/dist/search/tool-embedding-cache.d.ts +4 -0
- package/dist/search/tool-embedding-cache.d.ts.map +1 -0
- package/dist/search/tool-embedding-cache.js +44 -0
- package/dist/search/tool-embedding-cache.js.map +1 -0
- package/dist/search/tool-embedding-storage.d.ts +8 -0
- package/dist/search/tool-embedding-storage.d.ts.map +1 -0
- package/dist/search/tool-embedding-storage.js +56 -0
- package/dist/search/tool-embedding-storage.js.map +1 -0
- package/dist/search/tool-ranker-bm25.d.ts +6 -0
- package/dist/search/tool-ranker-bm25.d.ts.map +1 -0
- package/dist/search/tool-ranker-bm25.js +40 -0
- package/dist/search/tool-ranker-bm25.js.map +1 -0
- package/dist/search/tool-ranker-orchestrator.d.ts +3 -0
- package/dist/search/tool-ranker-orchestrator.d.ts.map +1 -0
- package/dist/search/tool-ranker-orchestrator.js +49 -0
- package/dist/search/tool-ranker-orchestrator.js.map +1 -0
- package/dist/search/tool-ranker-reasoning.d.ts +4 -0
- package/dist/search/tool-ranker-reasoning.d.ts.map +1 -0
- package/dist/search/tool-ranker-reasoning.js +35 -0
- package/dist/search/tool-ranker-reasoning.js.map +1 -0
- package/dist/search/tool-ranker-signal-math.d.ts +6 -0
- package/dist/search/tool-ranker-signal-math.d.ts.map +1 -0
- package/dist/search/tool-ranker-signal-math.js +51 -0
- package/dist/search/tool-ranker-signal-math.js.map +1 -0
- package/dist/search/tool-ranker-signals.d.ts +20 -0
- package/dist/search/tool-ranker-signals.d.ts.map +1 -0
- package/dist/search/tool-ranker-signals.js +50 -0
- package/dist/search/tool-ranker-signals.js.map +1 -0
- package/dist/search/tool-ranker-types.d.ts +26 -0
- package/dist/search/tool-ranker-types.d.ts.map +1 -0
- package/dist/search/tool-ranker-types.js +2 -0
- package/dist/search/tool-ranker-types.js.map +1 -0
- package/dist/search/tool-ranker.d.ts +6 -89
- package/dist/search/tool-ranker.d.ts.map +1 -1
- package/dist/search/tool-ranker.js +5 -419
- package/dist/search/tool-ranker.js.map +1 -1
- package/dist/server-helpers/repo-resolution.d.ts +45 -0
- package/dist/server-helpers/repo-resolution.d.ts.map +1 -0
- package/dist/server-helpers/repo-resolution.js +113 -0
- package/dist/server-helpers/repo-resolution.js.map +1 -0
- package/dist/server-helpers/response-hints.d.ts +9 -0
- package/dist/server-helpers/response-hints.d.ts.map +1 -0
- package/dist/server-helpers/response-hints.js +225 -0
- package/dist/server-helpers/response-hints.js.map +1 -0
- package/dist/server-helpers.d.ts +14 -35
- package/dist/server-helpers.d.ts.map +1 -1
- package/dist/server-helpers.js +53 -320
- package/dist/server-helpers.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +39 -0
- package/dist/server.js.map +1 -1
- package/dist/storage/usage-tracker.d.ts.map +1 -1
- package/dist/storage/usage-tracker.js +12 -0
- package/dist/storage/usage-tracker.js.map +1 -1
- package/dist/tools/astro-actions/ast.d.ts +14 -0
- package/dist/tools/astro-actions/ast.d.ts.map +1 -0
- package/dist/tools/astro-actions/ast.js +75 -0
- package/dist/tools/astro-actions/ast.js.map +1 -0
- package/dist/tools/astro-actions/audit.d.ts +5 -0
- package/dist/tools/astro-actions/audit.d.ts.map +1 -0
- package/dist/tools/astro-actions/audit.js +102 -0
- package/dist/tools/astro-actions/audit.js.map +1 -0
- package/dist/tools/astro-actions/callers.d.ts +9 -0
- package/dist/tools/astro-actions/callers.d.ts.map +1 -0
- package/dist/tools/astro-actions/callers.js +84 -0
- package/dist/tools/astro-actions/callers.js.map +1 -0
- package/dist/tools/astro-actions/parser.d.ts +3 -0
- package/dist/tools/astro-actions/parser.d.ts.map +1 -0
- package/dist/tools/astro-actions/parser.js +156 -0
- package/dist/tools/astro-actions/parser.js.map +1 -0
- package/dist/tools/astro-actions/types.d.ts +58 -0
- package/dist/tools/astro-actions/types.d.ts.map +1 -0
- package/dist/tools/astro-actions/types.js +2 -0
- package/dist/tools/astro-actions/types.js.map +1 -0
- package/dist/tools/astro-actions/zod-ast.d.ts +7 -0
- package/dist/tools/astro-actions/zod-ast.d.ts.map +1 -0
- package/dist/tools/astro-actions/zod-ast.js +51 -0
- package/dist/tools/astro-actions/zod-ast.js.map +1 -0
- package/dist/tools/astro-actions.d.ts +6 -46
- package/dist/tools/astro-actions.d.ts.map +1 -1
- package/dist/tools/astro-actions.js +6 -545
- package/dist/tools/astro-actions.js.map +1 -1
- package/dist/tools/astro-content-collections/diagnostics.d.ts +3 -0
- package/dist/tools/astro-content-collections/diagnostics.d.ts.map +1 -0
- package/dist/tools/astro-content-collections/diagnostics.js +277 -0
- package/dist/tools/astro-content-collections/diagnostics.js.map +1 -0
- package/dist/tools/astro-content-collections/discovery.d.ts +6 -0
- package/dist/tools/astro-content-collections/discovery.d.ts.map +1 -0
- package/dist/tools/astro-content-collections/discovery.js +152 -0
- package/dist/tools/astro-content-collections/discovery.js.map +1 -0
- package/dist/tools/astro-content-collections/schema.d.ts +15 -0
- package/dist/tools/astro-content-collections/schema.d.ts.map +1 -0
- package/dist/tools/astro-content-collections/schema.js +119 -0
- package/dist/tools/astro-content-collections/schema.js.map +1 -0
- package/dist/tools/astro-content-collections/types.d.ts +61 -0
- package/dist/tools/astro-content-collections/types.d.ts.map +1 -0
- package/dist/tools/astro-content-collections/types.js +2 -0
- package/dist/tools/astro-content-collections/types.js.map +1 -0
- package/dist/tools/astro-content-collections.d.ts +5 -40
- package/dist/tools/astro-content-collections.d.ts.map +1 -1
- package/dist/tools/astro-content-collections.js +42 -449
- package/dist/tools/astro-content-collections.js.map +1 -1
- package/dist/tools/astro-routes.js +1 -1
- package/dist/tools/astro-routes.js.map +1 -1
- package/dist/tools/complexity-tools.d.ts +6 -0
- package/dist/tools/complexity-tools.d.ts.map +1 -1
- package/dist/tools/complexity-tools.js +26 -0
- package/dist/tools/complexity-tools.js.map +1 -1
- package/dist/tools/context-levels/l0.d.ts +4 -0
- package/dist/tools/context-levels/l0.d.ts.map +1 -0
- package/dist/tools/context-levels/l0.js +17 -0
- package/dist/tools/context-levels/l0.js.map +1 -0
- package/dist/tools/context-levels/l1.d.ts +4 -0
- package/dist/tools/context-levels/l1.d.ts.map +1 -0
- package/dist/tools/context-levels/l1.js +26 -0
- package/dist/tools/context-levels/l1.js.map +1 -0
- package/dist/tools/context-levels/l2.d.ts +4 -0
- package/dist/tools/context-levels/l2.d.ts.map +1 -0
- package/dist/tools/context-levels/l2.js +36 -0
- package/dist/tools/context-levels/l2.js.map +1 -0
- package/dist/tools/context-levels/l3.d.ts +4 -0
- package/dist/tools/context-levels/l3.d.ts.map +1 -0
- package/dist/tools/context-levels/l3.js +30 -0
- package/dist/tools/context-levels/l3.js.map +1 -0
- package/dist/tools/context-levels/shared.d.ts +2 -0
- package/dist/tools/context-levels/shared.d.ts.map +1 -0
- package/dist/tools/context-levels/shared.js +4 -0
- package/dist/tools/context-levels/shared.js.map +1 -0
- package/dist/tools/context-levels/types.d.ts +34 -0
- package/dist/tools/context-levels/types.d.ts.map +1 -0
- package/dist/tools/context-levels/types.js +2 -0
- package/dist/tools/context-levels/types.js.map +1 -0
- package/dist/tools/context-tools.d.ts +2 -34
- package/dist/tools/context-tools.d.ts.map +1 -1
- package/dist/tools/context-tools.js +11 -127
- package/dist/tools/context-tools.js.map +1 -1
- package/dist/tools/conversation-cache.d.ts +6 -0
- package/dist/tools/conversation-cache.d.ts.map +1 -0
- package/dist/tools/conversation-cache.js +29 -0
- package/dist/tools/conversation-cache.js.map +1 -0
- package/dist/tools/conversation-index-tools.d.ts +39 -0
- package/dist/tools/conversation-index-tools.d.ts.map +1 -0
- package/dist/tools/conversation-index-tools.js +136 -0
- package/dist/tools/conversation-index-tools.js.map +1 -0
- package/dist/tools/conversation-paths.d.ts +4 -0
- package/dist/tools/conversation-paths.d.ts.map +1 -0
- package/dist/tools/conversation-paths.js +15 -0
- package/dist/tools/conversation-paths.js.map +1 -0
- package/dist/tools/conversation-search-tools.d.ts +52 -0
- package/dist/tools/conversation-search-tools.d.ts.map +1 -0
- package/dist/tools/conversation-search-tools.js +229 -0
- package/dist/tools/conversation-search-tools.js.map +1 -0
- package/dist/tools/conversation-tools.d.ts +6 -112
- package/dist/tools/conversation-tools.d.ts.map +1 -1
- package/dist/tools/conversation-tools.js +4 -425
- package/dist/tools/conversation-tools.js.map +1 -1
- package/dist/tools/cross-repo-outbound-lexer/call-detectors.d.ts +5 -0
- package/dist/tools/cross-repo-outbound-lexer/call-detectors.d.ts.map +1 -0
- package/dist/tools/cross-repo-outbound-lexer/call-detectors.js +76 -0
- package/dist/tools/cross-repo-outbound-lexer/call-detectors.js.map +1 -0
- package/dist/tools/cross-repo-outbound-lexer/context.d.ts +18 -0
- package/dist/tools/cross-repo-outbound-lexer/context.d.ts.map +1 -0
- package/dist/tools/cross-repo-outbound-lexer/context.js +33 -0
- package/dist/tools/cross-repo-outbound-lexer/context.js.map +1 -0
- package/dist/tools/cross-repo-outbound-lexer/literal-readers.d.ts +6 -0
- package/dist/tools/cross-repo-outbound-lexer/literal-readers.d.ts.map +1 -0
- package/dist/tools/cross-repo-outbound-lexer/literal-readers.js +65 -0
- package/dist/tools/cross-repo-outbound-lexer/literal-readers.js.map +1 -0
- package/dist/tools/cross-repo-outbound-lexer/non-code-states.d.ts +3 -0
- package/dist/tools/cross-repo-outbound-lexer/non-code-states.d.ts.map +1 -0
- package/dist/tools/cross-repo-outbound-lexer/non-code-states.js +103 -0
- package/dist/tools/cross-repo-outbound-lexer/non-code-states.js.map +1 -0
- package/dist/tools/cross-repo-outbound-lexer/template-reader.d.ts +3 -0
- package/dist/tools/cross-repo-outbound-lexer/template-reader.d.ts.map +1 -0
- package/dist/tools/cross-repo-outbound-lexer/template-reader.js +82 -0
- package/dist/tools/cross-repo-outbound-lexer/template-reader.js.map +1 -0
- package/dist/tools/cross-repo-outbound-lexer/types.d.ts +18 -0
- package/dist/tools/cross-repo-outbound-lexer/types.d.ts.map +1 -0
- package/dist/tools/cross-repo-outbound-lexer/types.js +2 -0
- package/dist/tools/cross-repo-outbound-lexer/types.js.map +1 -0
- package/dist/tools/cross-repo-outbound-lexer.d.ts +5 -31
- package/dist/tools/cross-repo-outbound-lexer.d.ts.map +1 -1
- package/dist/tools/cross-repo-outbound-lexer.js +75 -575
- package/dist/tools/cross-repo-outbound-lexer.js.map +1 -1
- package/dist/tools/dependency-audit-freshness-tools.d.ts +3 -0
- package/dist/tools/dependency-audit-freshness-tools.d.ts.map +1 -0
- package/dist/tools/dependency-audit-freshness-tools.js +62 -0
- package/dist/tools/dependency-audit-freshness-tools.js.map +1 -0
- package/dist/tools/dependency-audit-license-tools.d.ts +5 -0
- package/dist/tools/dependency-audit-license-tools.d.ts.map +1 -0
- package/dist/tools/dependency-audit-license-tools.js +75 -0
- package/dist/tools/dependency-audit-license-tools.js.map +1 -0
- package/dist/tools/dependency-audit-lockfile-tools.d.ts +3 -0
- package/dist/tools/dependency-audit-lockfile-tools.d.ts.map +1 -0
- package/dist/tools/dependency-audit-lockfile-tools.js +147 -0
- package/dist/tools/dependency-audit-lockfile-tools.js.map +1 -0
- package/dist/tools/dependency-audit-tools.d.ts +2 -54
- package/dist/tools/dependency-audit-tools.d.ts.map +1 -1
- package/dist/tools/dependency-audit-tools.js +69 -484
- package/dist/tools/dependency-audit-tools.js.map +1 -1
- package/dist/tools/dependency-audit-types.d.ts +65 -0
- package/dist/tools/dependency-audit-types.d.ts.map +1 -0
- package/dist/tools/dependency-audit-types.js +19 -0
- package/dist/tools/dependency-audit-types.js.map +1 -0
- package/dist/tools/dependency-audit-vulnerability-tools.d.ts +4 -0
- package/dist/tools/dependency-audit-vulnerability-tools.d.ts.map +1 -0
- package/dist/tools/dependency-audit-vulnerability-tools.js +103 -0
- package/dist/tools/dependency-audit-vulnerability-tools.js.map +1 -0
- package/dist/tools/diff-tools.d.ts.map +1 -1
- package/dist/tools/diff-tools.js +5 -7
- package/dist/tools/diff-tools.js.map +1 -1
- package/dist/tools/impact-tools.d.ts.map +1 -1
- package/dist/tools/impact-tools.js +5 -4
- package/dist/tools/impact-tools.js.map +1 -1
- package/dist/tools/index-shared.d.ts +17 -0
- package/dist/tools/index-shared.d.ts.map +1 -0
- package/dist/tools/index-shared.js +18 -0
- package/dist/tools/index-shared.js.map +1 -0
- package/dist/tools/index-tools/file-indexer.d.ts +27 -0
- package/dist/tools/index-tools/file-indexer.d.ts.map +1 -0
- package/dist/tools/index-tools/file-indexer.js +223 -0
- package/dist/tools/index-tools/file-indexer.js.map +1 -0
- package/dist/tools/index-tools/folder-indexer.d.ts +21 -0
- package/dist/tools/index-tools/folder-indexer.d.ts.map +1 -0
- package/dist/tools/index-tools/folder-indexer.js +399 -0
- package/dist/tools/index-tools/folder-indexer.js.map +1 -0
- package/dist/tools/index-tools/folder-merge.d.ts +23 -0
- package/dist/tools/index-tools/folder-merge.d.ts.map +1 -0
- package/dist/tools/index-tools/folder-merge.js +205 -0
- package/dist/tools/index-tools/folder-merge.js.map +1 -0
- package/dist/tools/index-tools/parse.d.ts +35 -0
- package/dist/tools/index-tools/parse.d.ts.map +1 -0
- package/dist/tools/index-tools/parse.js +262 -0
- package/dist/tools/index-tools/parse.js.map +1 -0
- package/dist/tools/index-tools/registry.d.ts +40 -0
- package/dist/tools/index-tools/registry.d.ts.map +1 -0
- package/dist/tools/index-tools/registry.js +258 -0
- package/dist/tools/index-tools/registry.js.map +1 -0
- package/dist/tools/index-tools/snapshots.d.ts +51 -0
- package/dist/tools/index-tools/snapshots.d.ts.map +1 -0
- package/dist/tools/index-tools/snapshots.js +223 -0
- package/dist/tools/index-tools/snapshots.js.map +1 -0
- package/dist/tools/index-tools/state.d.ts +8 -0
- package/dist/tools/index-tools/state.d.ts.map +1 -0
- package/dist/tools/index-tools/state.js +6 -0
- package/dist/tools/index-tools/state.js.map +1 -0
- package/dist/tools/index-tools/types.d.ts +20 -0
- package/dist/tools/index-tools/types.d.ts.map +1 -0
- package/dist/tools/index-tools/types.js +2 -0
- package/dist/tools/index-tools/types.js.map +1 -0
- package/dist/tools/index-tools/watcher.d.ts +10 -0
- package/dist/tools/index-tools/watcher.d.ts.map +1 -0
- package/dist/tools/index-tools/watcher.js +130 -0
- package/dist/tools/index-tools/watcher.js.map +1 -0
- package/dist/tools/index-tools.d.ts +16 -152
- package/dist/tools/index-tools.d.ts.map +1 -1
- package/dist/tools/index-tools.js +12 -1674
- package/dist/tools/index-tools.js.map +1 -1
- package/dist/tools/kotlin-extension-tools.d.ts +16 -0
- package/dist/tools/kotlin-extension-tools.d.ts.map +1 -0
- package/dist/tools/kotlin-extension-tools.js +31 -0
- package/dist/tools/kotlin-extension-tools.js.map +1 -0
- package/dist/tools/kotlin-flow-tools.d.ts +12 -0
- package/dist/tools/kotlin-flow-tools.d.ts.map +1 -0
- package/dist/tools/kotlin-flow-tools.js +72 -0
- package/dist/tools/kotlin-flow-tools.js.map +1 -0
- package/dist/tools/kotlin-kmp-tools.d.ts +29 -0
- package/dist/tools/kotlin-kmp-tools.d.ts.map +1 -0
- package/dist/tools/kotlin-kmp-tools.js +87 -0
- package/dist/tools/kotlin-kmp-tools.js.map +1 -0
- package/dist/tools/kotlin-sealed-tools.d.ts +27 -0
- package/dist/tools/kotlin-sealed-tools.d.ts.map +1 -0
- package/dist/tools/kotlin-sealed-tools.js +130 -0
- package/dist/tools/kotlin-sealed-tools.js.map +1 -0
- package/dist/tools/kotlin-suspend-tools.d.ts +24 -0
- package/dist/tools/kotlin-suspend-tools.d.ts.map +1 -0
- package/dist/tools/kotlin-suspend-tools.js +161 -0
- package/dist/tools/kotlin-suspend-tools.js.map +1 -0
- package/dist/tools/kotlin-tools.d.ts +6 -141
- package/dist/tools/kotlin-tools.d.ts.map +1 -1
- package/dist/tools/kotlin-tools.js +6 -571
- package/dist/tools/kotlin-tools.js.map +1 -1
- package/dist/tools/nest-audit-tools.d.ts +43 -0
- package/dist/tools/nest-audit-tools.d.ts.map +1 -0
- package/dist/tools/nest-audit-tools.js +232 -0
- package/dist/tools/nest-audit-tools.js.map +1 -0
- package/dist/tools/nest-di-tools.d.ts +32 -0
- package/dist/tools/nest-di-tools.d.ts.map +1 -0
- package/dist/tools/nest-di-tools.js +161 -0
- package/dist/tools/nest-di-tools.js.map +1 -0
- package/dist/tools/nest-guard-tools.d.ts +34 -0
- package/dist/tools/nest-guard-tools.d.ts.map +1 -0
- package/dist/tools/nest-guard-tools.js +240 -0
- package/dist/tools/nest-guard-tools.js.map +1 -0
- package/dist/tools/nest-lifecycle-tools.d.ts +16 -0
- package/dist/tools/nest-lifecycle-tools.d.ts.map +1 -0
- package/dist/tools/nest-lifecycle-tools.js +54 -0
- package/dist/tools/nest-lifecycle-tools.js.map +1 -0
- package/dist/tools/nest-module-tools.d.ts +28 -0
- package/dist/tools/nest-module-tools.d.ts.map +1 -0
- package/dist/tools/nest-module-tools.js +86 -0
- package/dist/tools/nest-module-tools.js.map +1 -0
- package/dist/tools/nest-pipeline-tools.d.ts +32 -0
- package/dist/tools/nest-pipeline-tools.d.ts.map +1 -0
- package/dist/tools/nest-pipeline-tools.js +122 -0
- package/dist/tools/nest-pipeline-tools.js.map +1 -0
- package/dist/tools/nest-route-tools.d.ts +43 -0
- package/dist/tools/nest-route-tools.d.ts.map +1 -0
- package/dist/tools/nest-route-tools.js +261 -0
- package/dist/tools/nest-route-tools.js.map +1 -0
- package/dist/tools/nest-shared-tools.d.ts +13 -0
- package/dist/tools/nest-shared-tools.d.ts.map +1 -0
- package/dist/tools/nest-shared-tools.js +36 -0
- package/dist/tools/nest-shared-tools.js.map +1 -0
- package/dist/tools/nest-tools.d.ts +18 -195
- package/dist/tools/nest-tools.d.ts.map +1 -1
- package/dist/tools/nest-tools.js +11 -1140
- package/dist/tools/nest-tools.js.map +1 -1
- package/dist/tools/pattern-registry.d.ts +4 -0
- package/dist/tools/pattern-registry.d.ts.map +1 -0
- package/dist/tools/pattern-registry.js +3 -0
- package/dist/tools/pattern-registry.js.map +1 -0
- package/dist/tools/pattern-tools.d.ts +5 -46
- package/dist/tools/pattern-tools.d.ts.map +1 -1
- package/dist/tools/pattern-tools.js +4 -1102
- package/dist/tools/pattern-tools.js.map +1 -1
- package/dist/tools/patterns/adapters/astro.d.ts +3 -0
- package/dist/tools/patterns/adapters/astro.d.ts.map +1 -0
- package/dist/tools/patterns/adapters/astro.js +56 -0
- package/dist/tools/patterns/adapters/astro.js.map +1 -0
- package/dist/tools/patterns/adapters/common.d.ts +3 -0
- package/dist/tools/patterns/adapters/common.d.ts.map +1 -0
- package/dist/tools/patterns/adapters/common.js +38 -0
- package/dist/tools/patterns/adapters/common.js.map +1 -0
- package/dist/tools/patterns/adapters/database.d.ts +3 -0
- package/dist/tools/patterns/adapters/database.d.ts.map +1 -0
- package/dist/tools/patterns/adapters/database.js +32 -0
- package/dist/tools/patterns/adapters/database.js.map +1 -0
- package/dist/tools/patterns/adapters/hono.d.ts +3 -0
- package/dist/tools/patterns/adapters/hono.d.ts.map +1 -0
- package/dist/tools/patterns/adapters/hono.js +32 -0
- package/dist/tools/patterns/adapters/hono.js.map +1 -0
- package/dist/tools/patterns/adapters/kotlin.d.ts +3 -0
- package/dist/tools/patterns/adapters/kotlin.d.ts.map +1 -0
- package/dist/tools/patterns/adapters/kotlin.js +50 -0
- package/dist/tools/patterns/adapters/kotlin.js.map +1 -0
- package/dist/tools/patterns/adapters/nest.d.ts +3 -0
- package/dist/tools/patterns/adapters/nest.d.ts.map +1 -0
- package/dist/tools/patterns/adapters/nest.js +122 -0
- package/dist/tools/patterns/adapters/nest.js.map +1 -0
- package/dist/tools/patterns/adapters/nextjs.d.ts +4 -0
- package/dist/tools/patterns/adapters/nextjs.d.ts.map +1 -0
- package/dist/tools/patterns/adapters/nextjs.js +59 -0
- package/dist/tools/patterns/adapters/nextjs.js.map +1 -0
- package/dist/tools/patterns/adapters/php.d.ts +3 -0
- package/dist/tools/patterns/adapters/php.d.ts.map +1 -0
- package/dist/tools/patterns/adapters/php.js +157 -0
- package/dist/tools/patterns/adapters/php.js.map +1 -0
- package/dist/tools/patterns/adapters/python.d.ts +3 -0
- package/dist/tools/patterns/adapters/python.d.ts.map +1 -0
- package/dist/tools/patterns/adapters/python.js +72 -0
- package/dist/tools/patterns/adapters/python.js.map +1 -0
- package/dist/tools/patterns/adapters/react.d.ts +4 -0
- package/dist/tools/patterns/adapters/react.d.ts.map +1 -0
- package/dist/tools/patterns/adapters/react.js +345 -0
- package/dist/tools/patterns/adapters/react.js.map +1 -0
- package/dist/tools/patterns/catalog.d.ts +8 -0
- package/dist/tools/patterns/catalog.d.ts.map +1 -0
- package/dist/tools/patterns/catalog.js +30 -0
- package/dist/tools/patterns/catalog.js.map +1 -0
- package/dist/tools/patterns/execution.d.ts +38 -0
- package/dist/tools/patterns/execution.d.ts.map +1 -0
- package/dist/tools/patterns/execution.js +298 -0
- package/dist/tools/patterns/execution.js.map +1 -0
- package/dist/tools/patterns/types.d.ts +13 -0
- package/dist/tools/patterns/types.d.ts.map +1 -0
- package/dist/tools/patterns/types.js +2 -0
- package/dist/tools/patterns/types.js.map +1 -0
- package/dist/tools/pg-drift-compare.d.ts +3 -0
- package/dist/tools/pg-drift-compare.d.ts.map +1 -0
- package/dist/tools/pg-drift-compare.js +30 -0
- package/dist/tools/pg-drift-compare.js.map +1 -0
- package/dist/tools/pg-drift-schema.d.ts +5 -0
- package/dist/tools/pg-drift-schema.d.ts.map +1 -0
- package/dist/tools/pg-drift-schema.js +28 -0
- package/dist/tools/pg-drift-schema.js.map +1 -0
- package/dist/tools/pg-drift-types.d.ts +25 -0
- package/dist/tools/pg-drift-types.d.ts.map +1 -0
- package/dist/tools/pg-drift-types.js +2 -0
- package/dist/tools/pg-drift-types.js.map +1 -0
- package/dist/tools/pg-drift.d.ts +5 -0
- package/dist/tools/pg-drift.d.ts.map +1 -0
- package/dist/tools/pg-drift.js +30 -0
- package/dist/tools/pg-drift.js.map +1 -0
- package/dist/tools/pg-introspect-formatters.d.ts +7 -0
- package/dist/tools/pg-introspect-formatters.d.ts.map +1 -0
- package/dist/tools/pg-introspect-formatters.js +66 -0
- package/dist/tools/pg-introspect-formatters.js.map +1 -0
- package/dist/tools/pg-introspect-tools.d.ts +5 -146
- package/dist/tools/pg-introspect-tools.d.ts.map +1 -1
- package/dist/tools/pg-introspect-tools.js +3 -395
- package/dist/tools/pg-introspect-tools.js.map +1 -1
- package/dist/tools/pg-introspection.d.ts +90 -0
- package/dist/tools/pg-introspection.d.ts.map +1 -0
- package/dist/tools/pg-introspection.js +288 -0
- package/dist/tools/pg-introspection.js.map +1 -0
- package/dist/tools/php-active-record-tools.d.ts +22 -0
- package/dist/tools/php-active-record-tools.d.ts.map +1 -0
- package/dist/tools/php-active-record-tools.js +165 -0
- package/dist/tools/php-active-record-tools.js.map +1 -0
- package/dist/tools/php-event-tools.d.ts +21 -0
- package/dist/tools/php-event-tools.d.ts.map +1 -0
- package/dist/tools/php-event-tools.js +205 -0
- package/dist/tools/php-event-tools.js.map +1 -0
- package/dist/tools/php-god-model-tools.d.ts +34 -0
- package/dist/tools/php-god-model-tools.d.ts.map +1 -0
- package/dist/tools/php-god-model-tools.js +88 -0
- package/dist/tools/php-god-model-tools.js.map +1 -0
- package/dist/tools/php-import-utils.d.ts +10 -0
- package/dist/tools/php-import-utils.d.ts.map +1 -0
- package/dist/tools/php-import-utils.js +71 -0
- package/dist/tools/php-import-utils.js.map +1 -0
- package/dist/tools/php-namespace-tools.d.ts +10 -0
- package/dist/tools/php-namespace-tools.d.ts.map +1 -0
- package/dist/tools/php-namespace-tools.js +98 -0
- package/dist/tools/php-namespace-tools.js.map +1 -0
- package/dist/tools/php-nplus1-tools.d.ts +27 -0
- package/dist/tools/php-nplus1-tools.d.ts.map +1 -0
- package/dist/tools/php-nplus1-tools.js +191 -0
- package/dist/tools/php-nplus1-tools.js.map +1 -0
- package/dist/tools/php-project-audit-tools.d.ts +31 -0
- package/dist/tools/php-project-audit-tools.d.ts.map +1 -0
- package/dist/tools/php-project-audit-tools.js +173 -0
- package/dist/tools/php-project-audit-tools.js.map +1 -0
- package/dist/tools/php-security-tools.d.ts +24 -0
- package/dist/tools/php-security-tools.d.ts.map +1 -0
- package/dist/tools/php-security-tools.js +172 -0
- package/dist/tools/php-security-tools.js.map +1 -0
- package/dist/tools/php-service-tools.d.ts +25 -0
- package/dist/tools/php-service-tools.d.ts.map +1 -0
- package/dist/tools/php-service-tools.js +326 -0
- package/dist/tools/php-service-tools.js.map +1 -0
- package/dist/tools/php-tools.d.ts +21 -255
- package/dist/tools/php-tools.d.ts.map +1 -1
- package/dist/tools/php-tools.js +12 -1423
- package/dist/tools/php-tools.js.map +1 -1
- package/dist/tools/php-view-tools.d.ts +76 -0
- package/dist/tools/php-view-tools.d.ts.map +1 -0
- package/dist/tools/php-view-tools.js +413 -0
- package/dist/tools/php-view-tools.js.map +1 -0
- package/dist/tools/plan-turn/formatter.d.ts +3 -0
- package/dist/tools/plan-turn/formatter.d.ts.map +1 -0
- package/dist/tools/plan-turn/formatter.js +88 -0
- package/dist/tools/plan-turn/formatter.js.map +1 -0
- package/dist/tools/plan-turn/framework-context.d.ts +5 -0
- package/dist/tools/plan-turn/framework-context.d.ts.map +1 -0
- package/dist/tools/plan-turn/framework-context.js +68 -0
- package/dist/tools/plan-turn/framework-context.js.map +1 -0
- package/dist/tools/plan-turn/framework-data.d.ts +5 -0
- package/dist/tools/plan-turn/framework-data.d.ts.map +1 -0
- package/dist/tools/plan-turn/framework-data.js +48 -0
- package/dist/tools/plan-turn/framework-data.js.map +1 -0
- package/dist/tools/plan-turn/orchestrator.d.ts +4 -0
- package/dist/tools/plan-turn/orchestrator.d.ts.map +1 -0
- package/dist/tools/plan-turn/orchestrator.js +262 -0
- package/dist/tools/plan-turn/orchestrator.js.map +1 -0
- package/dist/tools/plan-turn/query-parser.d.ts +6 -0
- package/dist/tools/plan-turn/query-parser.d.ts.map +1 -0
- package/dist/tools/plan-turn/query-parser.js +60 -0
- package/dist/tools/plan-turn/query-parser.js.map +1 -0
- package/dist/tools/plan-turn/recommendations.d.ts +11 -0
- package/dist/tools/plan-turn/recommendations.d.ts.map +1 -0
- package/dist/tools/plan-turn/recommendations.js +93 -0
- package/dist/tools/plan-turn/recommendations.js.map +1 -0
- package/dist/tools/plan-turn/stale-index.d.ts +4 -0
- package/dist/tools/plan-turn/stale-index.d.ts.map +1 -0
- package/dist/tools/plan-turn/stale-index.js +17 -0
- package/dist/tools/plan-turn/stale-index.js.map +1 -0
- package/dist/tools/plan-turn/types.d.ts +61 -0
- package/dist/tools/plan-turn/types.d.ts.map +1 -0
- package/dist/tools/plan-turn/types.js +2 -0
- package/dist/tools/plan-turn/types.js.map +1 -0
- package/dist/tools/plan-turn-tools.d.ts +8 -111
- package/dist/tools/plan-turn-tools.d.ts.map +1 -1
- package/dist/tools/plan-turn-tools.js +6 -696
- package/dist/tools/plan-turn-tools.js.map +1 -1
- package/dist/tools/project-profile-express.d.ts +3 -0
- package/dist/tools/project-profile-express.d.ts.map +1 -0
- package/dist/tools/project-profile-express.js +46 -0
- package/dist/tools/project-profile-express.js.map +1 -0
- package/dist/tools/project-profile-extractors.d.ts +9 -0
- package/dist/tools/project-profile-extractors.d.ts.map +1 -0
- package/dist/tools/project-profile-extractors.js +312 -0
- package/dist/tools/project-profile-extractors.js.map +1 -0
- package/dist/tools/project-profile-fs.d.ts +3 -0
- package/dist/tools/project-profile-fs.d.ts.map +1 -0
- package/dist/tools/project-profile-fs.js +19 -0
- package/dist/tools/project-profile-fs.js.map +1 -0
- package/dist/tools/project-profile-hono.d.ts +13 -0
- package/dist/tools/project-profile-hono.d.ts.map +1 -0
- package/dist/tools/project-profile-hono.js +110 -0
- package/dist/tools/project-profile-hono.js.map +1 -0
- package/dist/tools/project-profile-imports.d.ts +6 -0
- package/dist/tools/project-profile-imports.d.ts.map +1 -0
- package/dist/tools/project-profile-imports.js +95 -0
- package/dist/tools/project-profile-imports.js.map +1 -0
- package/dist/tools/project-profile-nest.d.ts +8 -0
- package/dist/tools/project-profile-nest.d.ts.map +1 -0
- package/dist/tools/project-profile-nest.js +352 -0
- package/dist/tools/project-profile-nest.js.map +1 -0
- package/dist/tools/project-profile-next.d.ts +5 -0
- package/dist/tools/project-profile-next.d.ts.map +1 -0
- package/dist/tools/project-profile-next.js +92 -0
- package/dist/tools/project-profile-next.js.map +1 -0
- package/dist/tools/project-profile-persistence.d.ts +3 -0
- package/dist/tools/project-profile-persistence.d.ts.map +1 -0
- package/dist/tools/project-profile-persistence.js +19 -0
- package/dist/tools/project-profile-persistence.js.map +1 -0
- package/dist/tools/project-profile-php.d.ts +8 -0
- package/dist/tools/project-profile-php.d.ts.map +1 -0
- package/dist/tools/project-profile-php.js +76 -0
- package/dist/tools/project-profile-php.js.map +1 -0
- package/dist/tools/project-profile-python.d.ts +5 -0
- package/dist/tools/project-profile-python.d.ts.map +1 -0
- package/dist/tools/project-profile-python.js +51 -0
- package/dist/tools/project-profile-python.js.map +1 -0
- package/dist/tools/project-profile-react.d.ts +6 -0
- package/dist/tools/project-profile-react.d.ts.map +1 -0
- package/dist/tools/project-profile-react.js +106 -0
- package/dist/tools/project-profile-react.js.map +1 -0
- package/dist/tools/project-profile-stack.d.ts +3 -0
- package/dist/tools/project-profile-stack.d.ts.map +1 -0
- package/dist/tools/project-profile-stack.js +354 -0
- package/dist/tools/project-profile-stack.js.map +1 -0
- package/dist/tools/project-profile-summary.d.ts +30 -0
- package/dist/tools/project-profile-summary.d.ts.map +1 -0
- package/dist/tools/project-profile-summary.js +112 -0
- package/dist/tools/project-profile-summary.js.map +1 -0
- package/dist/tools/project-profile-types.d.ts +315 -0
- package/dist/tools/project-profile-types.d.ts.map +1 -0
- package/dist/tools/project-profile-types.js +2 -0
- package/dist/tools/project-profile-types.js.map +1 -0
- package/dist/tools/project-tools.d.ts +17 -383
- package/dist/tools/project-tools.d.ts.map +1 -1
- package/dist/tools/project-tools.js +35 -1621
- package/dist/tools/project-tools.js.map +1 -1
- package/dist/tools/python-audit/aggregate.d.ts +3 -0
- package/dist/tools/python-audit/aggregate.d.ts.map +1 -0
- package/dist/tools/python-audit/aggregate.js +103 -0
- package/dist/tools/python-audit/aggregate.js.map +1 -0
- package/dist/tools/python-audit/checks/anti-patterns.d.ts +6 -0
- package/dist/tools/python-audit/checks/anti-patterns.d.ts.map +1 -0
- package/dist/tools/python-audit/checks/anti-patterns.js +14 -0
- package/dist/tools/python-audit/checks/anti-patterns.js.map +1 -0
- package/dist/tools/python-audit/checks/celery.d.ts +2 -0
- package/dist/tools/python-audit/checks/celery.d.ts.map +1 -0
- package/dist/tools/python-audit/checks/celery.js +5 -0
- package/dist/tools/python-audit/checks/celery.js.map +1 -0
- package/dist/tools/python-audit/checks/circular-imports.d.ts +2 -0
- package/dist/tools/python-audit/checks/circular-imports.d.ts.map +1 -0
- package/dist/tools/python-audit/checks/circular-imports.js +5 -0
- package/dist/tools/python-audit/checks/circular-imports.js.map +1 -0
- package/dist/tools/python-audit/checks/dead-code.d.ts +2 -0
- package/dist/tools/python-audit/checks/dead-code.d.ts.map +1 -0
- package/dist/tools/python-audit/checks/dead-code.js +5 -0
- package/dist/tools/python-audit/checks/dead-code.js.map +1 -0
- package/dist/tools/python-audit/checks/dependencies.d.ts +2 -0
- package/dist/tools/python-audit/checks/dependencies.d.ts.map +1 -0
- package/dist/tools/python-audit/checks/dependencies.js +5 -0
- package/dist/tools/python-audit/checks/dependencies.js.map +1 -0
- package/dist/tools/python-audit/checks/django-settings.d.ts +2 -0
- package/dist/tools/python-audit/checks/django-settings.d.ts.map +1 -0
- package/dist/tools/python-audit/checks/django-settings.js +5 -0
- package/dist/tools/python-audit/checks/django-settings.js.map +1 -0
- package/dist/tools/python-audit/checks/framework-wiring.d.ts +2 -0
- package/dist/tools/python-audit/checks/framework-wiring.d.ts.map +1 -0
- package/dist/tools/python-audit/checks/framework-wiring.js +5 -0
- package/dist/tools/python-audit/checks/framework-wiring.js.map +1 -0
- package/dist/tools/python-audit/checks/pytest-fixtures.d.ts +2 -0
- package/dist/tools/python-audit/checks/pytest-fixtures.d.ts.map +1 -0
- package/dist/tools/python-audit/checks/pytest-fixtures.js +5 -0
- package/dist/tools/python-audit/checks/pytest-fixtures.js.map +1 -0
- package/dist/tools/python-audit/runner.d.ts +5 -0
- package/dist/tools/python-audit/runner.d.ts.map +1 -0
- package/dist/tools/python-audit/runner.js +18 -0
- package/dist/tools/python-audit/runner.js.map +1 -0
- package/dist/tools/python-audit/types.d.ts +43 -0
- package/dist/tools/python-audit/types.d.ts.map +1 -0
- package/dist/tools/python-audit/types.js +2 -0
- package/dist/tools/python-audit/types.js.map +1 -0
- package/dist/tools/python-audit.d.ts +3 -39
- package/dist/tools/python-audit.d.ts.map +1 -1
- package/dist/tools/python-audit.js +34 -233
- package/dist/tools/python-audit.js.map +1 -1
- package/dist/tools/react-compiler-tools.d.ts +37 -0
- package/dist/tools/react-compiler-tools.d.ts.map +1 -0
- package/dist/tools/react-compiler-tools.js +104 -0
- package/dist/tools/react-compiler-tools.js.map +1 -0
- package/dist/tools/react-component-tree-tools.d.ts +58 -0
- package/dist/tools/react-component-tree-tools.d.ts.map +1 -0
- package/dist/tools/react-component-tree-tools.js +292 -0
- package/dist/tools/react-component-tree-tools.js.map +1 -0
- package/dist/tools/react-context-tools.d.ts +41 -0
- package/dist/tools/react-context-tools.d.ts.map +1 -0
- package/dist/tools/react-context-tools.js +76 -0
- package/dist/tools/react-context-tools.js.map +1 -0
- package/dist/tools/react-hooks-tools.d.ts +58 -0
- package/dist/tools/react-hooks-tools.d.ts.map +1 -0
- package/dist/tools/react-hooks-tools.js +144 -0
- package/dist/tools/react-hooks-tools.js.map +1 -0
- package/dist/tools/react-quickstart-tools.d.ts +59 -0
- package/dist/tools/react-quickstart-tools.d.ts.map +1 -0
- package/dist/tools/react-quickstart-tools.js +164 -0
- package/dist/tools/react-quickstart-tools.js.map +1 -0
- package/dist/tools/react-render-tools.d.ts +75 -0
- package/dist/tools/react-render-tools.d.ts.map +1 -0
- package/dist/tools/react-render-tools.js +234 -0
- package/dist/tools/react-render-tools.js.map +1 -0
- package/dist/tools/react-shared-tools.d.ts +5 -0
- package/dist/tools/react-shared-tools.d.ts.map +1 -0
- package/dist/tools/react-shared-tools.js +12 -0
- package/dist/tools/react-shared-tools.js.map +1 -0
- package/dist/tools/react-suspense-tools.d.ts +29 -0
- package/dist/tools/react-suspense-tools.d.ts.map +1 -0
- package/dist/tools/react-suspense-tools.js +269 -0
- package/dist/tools/react-suspense-tools.js.map +1 -0
- package/dist/tools/react-tools.d.ts +19 -340
- package/dist/tools/react-tools.d.ts.map +1 -1
- package/dist/tools/react-tools.js +10 -1263
- package/dist/tools/react-tools.js.map +1 -1
- package/dist/tools/review-diff/check-runner.d.ts +5 -0
- package/dist/tools/review-diff/check-runner.d.ts.map +1 -0
- package/dist/tools/review-diff/check-runner.js +26 -0
- package/dist/tools/review-diff/check-runner.js.map +1 -0
- package/dist/tools/review-diff/checks/astro-hydration.d.ts +8 -0
- package/dist/tools/review-diff/checks/astro-hydration.d.ts.map +1 -0
- package/dist/tools/review-diff/checks/astro-hydration.js +54 -0
- package/dist/tools/review-diff/checks/astro-hydration.js.map +1 -0
- package/dist/tools/review-diff/checks/blast-radius.d.ts +7 -0
- package/dist/tools/review-diff/checks/blast-radius.d.ts.map +1 -0
- package/dist/tools/review-diff/checks/blast-radius.js +39 -0
- package/dist/tools/review-diff/checks/blast-radius.js.map +1 -0
- package/dist/tools/review-diff/checks/breaking.d.ts +13 -0
- package/dist/tools/review-diff/checks/breaking.d.ts.map +1 -0
- package/dist/tools/review-diff/checks/breaking.js +115 -0
- package/dist/tools/review-diff/checks/breaking.js.map +1 -0
- package/dist/tools/review-diff/checks/bug-patterns.d.ts +8 -0
- package/dist/tools/review-diff/checks/bug-patterns.d.ts.map +1 -0
- package/dist/tools/review-diff/checks/bug-patterns.js +130 -0
- package/dist/tools/review-diff/checks/bug-patterns.js.map +1 -0
- package/dist/tools/review-diff/checks/complexity.d.ts +8 -0
- package/dist/tools/review-diff/checks/complexity.d.ts.map +1 -0
- package/dist/tools/review-diff/checks/complexity.js +41 -0
- package/dist/tools/review-diff/checks/complexity.js.map +1 -0
- package/dist/tools/review-diff/checks/coupling.d.ts +7 -0
- package/dist/tools/review-diff/checks/coupling.d.ts.map +1 -0
- package/dist/tools/review-diff/checks/coupling.js +52 -0
- package/dist/tools/review-diff/checks/coupling.js.map +1 -0
- package/dist/tools/review-diff/checks/dead-code.d.ts +7 -0
- package/dist/tools/review-diff/checks/dead-code.d.ts.map +1 -0
- package/dist/tools/review-diff/checks/dead-code.js +39 -0
- package/dist/tools/review-diff/checks/dead-code.js.map +1 -0
- package/dist/tools/review-diff/checks/hotspots.d.ts +8 -0
- package/dist/tools/review-diff/checks/hotspots.d.ts.map +1 -0
- package/dist/tools/review-diff/checks/hotspots.js +39 -0
- package/dist/tools/review-diff/checks/hotspots.js.map +1 -0
- package/dist/tools/review-diff/checks/secrets.d.ts +7 -0
- package/dist/tools/review-diff/checks/secrets.d.ts.map +1 -0
- package/dist/tools/review-diff/checks/secrets.js +38 -0
- package/dist/tools/review-diff/checks/secrets.js.map +1 -0
- package/dist/tools/review-diff/checks/test-gaps.d.ts +16 -0
- package/dist/tools/review-diff/checks/test-gaps.d.ts.map +1 -0
- package/dist/tools/review-diff/checks/test-gaps.js +77 -0
- package/dist/tools/review-diff/checks/test-gaps.js.map +1 -0
- package/dist/tools/review-diff/constants.d.ts +6 -0
- package/dist/tools/review-diff/constants.d.ts.map +1 -0
- package/dist/tools/review-diff/constants.js +16 -0
- package/dist/tools/review-diff/constants.js.map +1 -0
- package/dist/tools/review-diff/file-pattern.d.ts +2 -0
- package/dist/tools/review-diff/file-pattern.d.ts.map +1 -0
- package/dist/tools/review-diff/file-pattern.js +9 -0
- package/dist/tools/review-diff/file-pattern.js.map +1 -0
- package/dist/tools/review-diff/orchestrator.d.ts +3 -0
- package/dist/tools/review-diff/orchestrator.d.ts.map +1 -0
- package/dist/tools/review-diff/orchestrator.js +173 -0
- package/dist/tools/review-diff/orchestrator.js.map +1 -0
- package/dist/tools/review-diff/scoring.d.ts +31 -0
- package/dist/tools/review-diff/scoring.d.ts.map +1 -0
- package/dist/tools/review-diff/scoring.js +82 -0
- package/dist/tools/review-diff/scoring.js.map +1 -0
- package/dist/tools/review-diff/timeout.d.ts +5 -0
- package/dist/tools/review-diff/timeout.d.ts.map +1 -0
- package/dist/tools/review-diff/timeout.js +14 -0
- package/dist/tools/review-diff/timeout.js.map +1 -0
- package/dist/tools/review-diff/types.d.ts +56 -0
- package/dist/tools/review-diff/types.d.ts.map +1 -0
- package/dist/tools/review-diff/types.js +2 -0
- package/dist/tools/review-diff/types.js.map +1 -0
- package/dist/tools/review-diff-tools.d.ts +19 -147
- package/dist/tools/review-diff-tools.d.ts.map +1 -1
- package/dist/tools/review-diff-tools.js +18 -941
- package/dist/tools/review-diff-tools.js.map +1 -1
- package/dist/tools/route-shared.d.ts +6 -0
- package/dist/tools/route-shared.d.ts.map +1 -0
- package/dist/tools/route-shared.js +37 -0
- package/dist/tools/route-shared.js.map +1 -0
- package/dist/tools/route-tools.d.ts +1 -7
- package/dist/tools/route-tools.d.ts.map +1 -1
- package/dist/tools/route-tools.js +2 -22
- package/dist/tools/route-tools.js.map +1 -1
- package/dist/tools/search-tools/constants.d.ts +33 -0
- package/dist/tools/search-tools/constants.d.ts.map +1 -0
- package/dist/tools/search-tools/constants.js +54 -0
- package/dist/tools/search-tools/constants.js.map +1 -0
- package/dist/tools/search-tools/ripgrep.d.ts +13 -0
- package/dist/tools/search-tools/ripgrep.d.ts.map +1 -0
- package/dist/tools/search-tools/ripgrep.js +138 -0
- package/dist/tools/search-tools/ripgrep.js.map +1 -0
- package/dist/tools/search-tools/semantic-search.d.ts +9 -0
- package/dist/tools/search-tools/semantic-search.d.ts.map +1 -0
- package/dist/tools/search-tools/semantic-search.js +19 -0
- package/dist/tools/search-tools/semantic-search.js.map +1 -0
- package/dist/tools/search-tools/symbol-search.d.ts +4 -0
- package/dist/tools/search-tools/symbol-search.d.ts.map +1 -0
- package/dist/tools/search-tools/symbol-search.js +146 -0
- package/dist/tools/search-tools/symbol-search.js.map +1 -0
- package/dist/tools/search-tools/text-search.d.ts +10 -0
- package/dist/tools/search-tools/text-search.d.ts.map +1 -0
- package/dist/tools/search-tools/text-search.js +260 -0
- package/dist/tools/search-tools/text-search.js.map +1 -0
- package/dist/tools/search-tools/types.d.ts +28 -0
- package/dist/tools/search-tools/types.d.ts.map +1 -0
- package/dist/tools/search-tools/types.js +2 -0
- package/dist/tools/search-tools/types.js.map +1 -0
- package/dist/tools/search-tools/zero-hit.d.ts +3 -0
- package/dist/tools/search-tools/zero-hit.d.ts.map +1 -0
- package/dist/tools/search-tools/zero-hit.js +92 -0
- package/dist/tools/search-tools/zero-hit.js.map +1 -0
- package/dist/tools/search-tools.d.ts +5 -64
- package/dist/tools/search-tools.d.ts.map +1 -1
- package/dist/tools/search-tools.js +4 -770
- package/dist/tools/search-tools.js.map +1 -1
- package/dist/tools/secret-detectors.d.ts +12 -0
- package/dist/tools/secret-detectors.d.ts.map +1 -0
- package/dist/tools/secret-detectors.js +86 -0
- package/dist/tools/secret-detectors.js.map +1 -0
- package/dist/tools/secret-file-scanner.d.ts +7 -0
- package/dist/tools/secret-file-scanner.d.ts.map +1 -0
- package/dist/tools/secret-file-scanner.js +52 -0
- package/dist/tools/secret-file-scanner.js.map +1 -0
- package/dist/tools/secret-scan-cache.d.ts +6 -0
- package/dist/tools/secret-scan-cache.d.ts.map +1 -0
- package/dist/tools/secret-scan-cache.js +14 -0
- package/dist/tools/secret-scan-cache.js.map +1 -0
- package/dist/tools/secret-scan-shared.d.ts +5 -0
- package/dist/tools/secret-scan-shared.d.ts.map +1 -0
- package/dist/tools/secret-scan-shared.js +4 -0
- package/dist/tools/secret-scan-shared.js.map +1 -0
- package/dist/tools/secret-scan-types.d.ts +21 -0
- package/dist/tools/secret-scan-types.d.ts.map +1 -0
- package/dist/tools/secret-scan-types.js +2 -0
- package/dist/tools/secret-scan-types.js.map +1 -0
- package/dist/tools/secret-tools.d.ts +3 -75
- package/dist/tools/secret-tools.d.ts.map +1 -1
- package/dist/tools/secret-tools.js +4 -316
- package/dist/tools/secret-tools.js.map +1 -1
- package/dist/tools/sql-audit-tools.d.ts +42 -0
- package/dist/tools/sql-audit-tools.d.ts.map +1 -0
- package/dist/tools/sql-audit-tools.js +130 -0
- package/dist/tools/sql-audit-tools.js.map +1 -0
- package/dist/tools/sql-column-tools.d.ts +28 -0
- package/dist/tools/sql-column-tools.d.ts.map +1 -0
- package/dist/tools/sql-column-tools.js +58 -0
- package/dist/tools/sql-column-tools.js.map +1 -0
- package/dist/tools/sql-complexity-tools.d.ts +22 -0
- package/dist/tools/sql-complexity-tools.d.ts.map +1 -0
- package/dist/tools/sql-complexity-tools.js +60 -0
- package/dist/tools/sql-complexity-tools.js.map +1 -0
- package/dist/tools/sql-dml-safety-tools.d.ts +26 -0
- package/dist/tools/sql-dml-safety-tools.d.ts.map +1 -0
- package/dist/tools/sql-dml-safety-tools.js +104 -0
- package/dist/tools/sql-dml-safety-tools.js.map +1 -0
- package/dist/tools/sql-drift-tools.d.ts +39 -0
- package/dist/tools/sql-drift-tools.d.ts.map +1 -0
- package/dist/tools/sql-drift-tools.js +238 -0
- package/dist/tools/sql-drift-tools.js.map +1 -0
- package/dist/tools/sql-lint-schema-tools.d.ts +28 -0
- package/dist/tools/sql-lint-schema-tools.d.ts.map +1 -0
- package/dist/tools/sql-lint-schema-tools.js +96 -0
- package/dist/tools/sql-lint-schema-tools.js.map +1 -0
- package/dist/tools/sql-migration-diff-tools.d.ts +24 -0
- package/dist/tools/sql-migration-diff-tools.d.ts.map +1 -0
- package/dist/tools/sql-migration-diff-tools.js +120 -0
- package/dist/tools/sql-migration-diff-tools.js.map +1 -0
- package/dist/tools/sql-orphan-table-tools.d.ts +20 -0
- package/dist/tools/sql-orphan-table-tools.d.ts.map +1 -0
- package/dist/tools/sql-orphan-table-tools.js +70 -0
- package/dist/tools/sql-orphan-table-tools.js.map +1 -0
- package/dist/tools/sql-query-tools.d.ts +32 -0
- package/dist/tools/sql-query-tools.d.ts.map +1 -0
- package/dist/tools/sql-query-tools.js +130 -0
- package/dist/tools/sql-query-tools.js.map +1 -0
- package/dist/tools/sql-schema-tools.d.ts +47 -0
- package/dist/tools/sql-schema-tools.d.ts.map +1 -0
- package/dist/tools/sql-schema-tools.js +202 -0
- package/dist/tools/sql-schema-tools.js.map +1 -0
- package/dist/tools/sql-shared-tools.d.ts +6 -0
- package/dist/tools/sql-shared-tools.d.ts.map +1 -0
- package/dist/tools/sql-shared-tools.js +59 -0
- package/dist/tools/sql-shared-tools.js.map +1 -0
- package/dist/tools/sql-tools.d.ts +25 -296
- package/dist/tools/sql-tools.d.ts.map +1 -1
- package/dist/tools/sql-tools.js +15 -1232
- package/dist/tools/sql-tools.js.map +1 -1
- package/dist/tools/status-tools.js +1 -1
- package/dist/tools/status-tools.js.map +1 -1
- package/dist/tools/workspace-tools.d.ts +1 -1
- package/dist/tools/yii3-migration-categories-application.d.ts +4 -0
- package/dist/tools/yii3-migration-categories-application.d.ts.map +1 -0
- package/dist/tools/yii3-migration-categories-application.js +56 -0
- package/dist/tools/yii3-migration-categories-application.js.map +1 -0
- package/dist/tools/yii3-migration-categories-infrastructure.d.ts +4 -0
- package/dist/tools/yii3-migration-categories-infrastructure.d.ts.map +1 -0
- package/dist/tools/yii3-migration-categories-infrastructure.js +67 -0
- package/dist/tools/yii3-migration-categories-infrastructure.js.map +1 -0
- package/dist/tools/yii3-migration-categories-model-view.d.ts +4 -0
- package/dist/tools/yii3-migration-categories-model-view.d.ts.map +1 -0
- package/dist/tools/yii3-migration-categories-model-view.js +59 -0
- package/dist/tools/yii3-migration-categories-model-view.js.map +1 -0
- package/dist/tools/yii3-migration-categories-runtime.d.ts +4 -0
- package/dist/tools/yii3-migration-categories-runtime.d.ts.map +1 -0
- package/dist/tools/yii3-migration-categories-runtime.js +49 -0
- package/dist/tools/yii3-migration-categories-runtime.js.map +1 -0
- package/dist/tools/yii3-migration-categories.d.ts +5 -0
- package/dist/tools/yii3-migration-categories.d.ts.map +1 -0
- package/dist/tools/yii3-migration-categories.js +18 -0
- package/dist/tools/yii3-migration-categories.js.map +1 -0
- package/dist/tools/yii3-migration-report.d.ts +4 -0
- package/dist/tools/yii3-migration-report.d.ts.map +1 -0
- package/dist/tools/yii3-migration-report.js +88 -0
- package/dist/tools/yii3-migration-report.js.map +1 -0
- package/dist/tools/yii3-migration-scanner.d.ts +28 -0
- package/dist/tools/yii3-migration-scanner.d.ts.map +1 -0
- package/dist/tools/yii3-migration-scanner.js +87 -0
- package/dist/tools/yii3-migration-scanner.js.map +1 -0
- package/dist/tools/yii3-migration-tools.d.ts +4 -73
- package/dist/tools/yii3-migration-tools.d.ts.map +1 -1
- package/dist/tools/yii3-migration-tools.js +5 -433
- package/dist/tools/yii3-migration-tools.js.map +1 -1
- package/dist/tools/yii3-migration-types.d.ts +50 -0
- package/dist/tools/yii3-migration-types.d.ts.map +1 -0
- package/dist/tools/yii3-migration-types.js +3 -0
- package/dist/tools/yii3-migration-types.js.map +1 -0
- package/dist/utils/git-validation.d.ts +24 -0
- package/dist/utils/git-validation.d.ts.map +1 -1
- package/dist/utils/git-validation.js +30 -0
- package/dist/utils/git-validation.js.map +1 -1
- package/dist/utils/source-stripper/code-handler.d.ts +3 -0
- package/dist/utils/source-stripper/code-handler.d.ts.map +1 -0
- package/dist/utils/source-stripper/code-handler.js +79 -0
- package/dist/utils/source-stripper/code-handler.js.map +1 -0
- package/dist/utils/source-stripper/comment-handler.d.ts +4 -0
- package/dist/utils/source-stripper/comment-handler.d.ts.map +1 -0
- package/dist/utils/source-stripper/comment-handler.js +24 -0
- package/dist/utils/source-stripper/comment-handler.js.map +1 -0
- package/dist/utils/source-stripper/machine.d.ts +2 -0
- package/dist/utils/source-stripper/machine.d.ts.map +1 -0
- package/dist/utils/source-stripper/machine.js +34 -0
- package/dist/utils/source-stripper/machine.js.map +1 -0
- package/dist/utils/source-stripper/regex-handler.d.ts +3 -0
- package/dist/utils/source-stripper/regex-handler.d.ts.map +1 -0
- package/dist/utils/source-stripper/regex-handler.js +44 -0
- package/dist/utils/source-stripper/regex-handler.js.map +1 -0
- package/dist/utils/source-stripper/state.d.ts +4 -0
- package/dist/utils/source-stripper/state.d.ts.map +1 -0
- package/dist/utils/source-stripper/state.js +20 -0
- package/dist/utils/source-stripper/state.js.map +1 -0
- package/dist/utils/source-stripper/string-handler.d.ts +3 -0
- package/dist/utils/source-stripper/string-handler.d.ts.map +1 -0
- package/dist/utils/source-stripper/string-handler.js +19 -0
- package/dist/utils/source-stripper/string-handler.js.map +1 -0
- package/dist/utils/source-stripper/template-handler.d.ts +4 -0
- package/dist/utils/source-stripper/template-handler.d.ts.map +1 -0
- package/dist/utils/source-stripper/template-handler.js +40 -0
- package/dist/utils/source-stripper/template-handler.js.map +1 -0
- package/dist/utils/source-stripper/types.d.ts +17 -0
- package/dist/utils/source-stripper/types.d.ts.map +1 -0
- package/dist/utils/source-stripper/types.js +4 -0
- package/dist/utils/source-stripper/types.js.map +1 -0
- package/dist/utils/source-stripper.d.ts +0 -21
- package/dist/utils/source-stripper.d.ts.map +1 -1
- package/dist/utils/source-stripper.js +6 -234
- package/dist/utils/source-stripper.js.map +1 -1
- package/dist/utils/walk/errors.d.ts +4 -0
- package/dist/utils/walk/errors.d.ts.map +1 -0
- package/dist/utils/walk/errors.js +18 -0
- package/dist/utils/walk/errors.js.map +1 -0
- package/dist/utils/walk/filters.d.ts +11 -0
- package/dist/utils/walk/filters.d.ts.map +1 -0
- package/dist/utils/walk/filters.js +53 -0
- package/dist/utils/walk/filters.js.map +1 -0
- package/dist/utils/walk/limits.d.ts +10 -0
- package/dist/utils/walk/limits.d.ts.map +1 -0
- package/dist/utils/walk/limits.js +22 -0
- package/dist/utils/walk/limits.js.map +1 -0
- package/dist/utils/walk/traversal.d.ts +9 -0
- package/dist/utils/walk/traversal.d.ts.map +1 -0
- package/dist/utils/walk/traversal.js +38 -0
- package/dist/utils/walk/traversal.js.map +1 -0
- package/dist/utils/walk/types.d.ts +18 -0
- package/dist/utils/walk/types.d.ts.map +1 -0
- package/dist/utils/walk/types.js +2 -0
- package/dist/utils/walk/types.js.map +1 -0
- package/dist/utils/walk.d.ts +4 -65
- package/dist/utils/walk.d.ts.map +1 -1
- package/dist/utils/walk.js +23 -151
- package/dist/utils/walk.js.map +1 -1
- package/package.json +6 -5
|
@@ -1,981 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { join,
|
|
3
|
-
import { openSync, closeSync, statSync, unlinkSync, writeFileSync, renameSync } from "node:fs";
|
|
1
|
+
import { stat, rm, mkdir as mkdirAsync } from "node:fs/promises";
|
|
2
|
+
import { join, basename } from "node:path";
|
|
4
3
|
import { execFileSync } from "node:child_process";
|
|
5
|
-
import { createHash } from "node:crypto";
|
|
6
|
-
import { EXTRACTOR_VERSIONS } from "./project-tools.js";
|
|
7
|
-
import { extractMarkdownSymbols, extractPrismaSymbols, extractAstroSymbols, extractConversationSymbols } from "../parser/symbol-extractor.js";
|
|
8
|
-
import { runTreeSitterParse } from "../parser/parser-pool.js";
|
|
9
|
-
import { extractSqlSymbols, stripJinjaTokens } from "../parser/extractors/sql.js";
|
|
10
|
-
import { getLanguageForExtension, getLanguageForPath } from "../parser/parser-manager.js";
|
|
11
|
-
import { saveIndex, loadIndex, loadIndexOrStale, getIndexPath, saveIncremental, removeFileFromIndex } from "../storage/index-store.js";
|
|
12
|
-
import { clearTsconfigCache } from "../utils/tsconfig-paths.js";
|
|
13
|
-
import { registerRepo, listRepos as listRegistryRepos, getRepo, removeRepo, getRepoName, updateRepoMeta, resolveRegisteredRepoMeta } from "../storage/registry.js";
|
|
14
|
-
import { startWatcher, stopWatcher } from "../storage/watcher.js";
|
|
15
|
-
import { buildBM25Index } from "../search/bm25.js";
|
|
16
|
-
import { buildSymbolText, createEmbeddingProvider } from "../search/semantic.js";
|
|
17
|
-
import { loadEmbeddings, saveEmbeddings, saveEmbeddingMeta, getEmbeddingPath, getEmbeddingMetaPath, batchEmbed } from "../storage/embedding-store.js";
|
|
18
|
-
import { saveChunks, saveChunkEmbeddings, loadChunkEmbeddings, getChunkPath, getChunkEmbeddingPath } from "../storage/chunk-store.js";
|
|
19
|
-
import { chunkFile, chunkBySymbols } from "../search/chunker.js";
|
|
20
|
-
import { loadConfig } from "../config.js";
|
|
21
4
|
import { validateGitUrl, validateGitRef } from "../utils/git-validation.js";
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
|
|
27
|
-
|
|
5
|
+
import { loadConfig } from "../config.js";
|
|
6
|
+
import { stopWatcher } from "../storage/watcher.js";
|
|
7
|
+
import { indexFolder, resetIndexFolderRedundancyForTesting } from "./index-tools/folder-indexer.js";
|
|
8
|
+
import { indexFile, clearLastIndexedStateForTesting, ensureIndexFresh, resetFreshnessCache } from "./index-tools/file-indexer.js";
|
|
9
|
+
import { listAllRepos, invalidateCache, getBM25Index, getCodeIndex, getEmbeddingCache, autoIndexCurrentRepo, _cachedEmbeddingReposForTesting, _embeddingLoadCountForTesting, _resetEmbeddingLoadCountForTesting } from "./index-tools/registry.js";
|
|
10
|
+
import { embedSymbols } from "./index-tools/parse.js";
|
|
11
|
+
import { drainLegacyHashQueue, ASTRO_LOCK_FILENAME, EXTRACTOR_VERSIONS_FILENAME, checkAstroExtractorVersion } from "./index-tools/snapshots.js";
|
|
12
|
+
import { activeWatchers, bm25Indexes, codeIndexes, embeddingCaches } from "./index-tools/state.js";
|
|
28
13
|
const GIT_CLONE_TIMEOUT_MS = 120_000;
|
|
29
14
|
const GIT_CHECKOUT_TIMEOUT_MS = 30_000;
|
|
30
15
|
const GIT_PULL_TIMEOUT_MS = 60_000;
|
|
31
|
-
|
|
32
|
-
* Default cap on files returned by walkDirectory during indexFolder. Without
|
|
33
|
-
* this, repos with hundreds of thousands of code files (large CMS, monorepos,
|
|
34
|
-
* vendored data sets) accumulate symbols in memory and OOM the Node process,
|
|
35
|
-
* which manifests to MCP clients as a "Connection closed" error mid-index.
|
|
36
|
-
*
|
|
37
|
-
* 50_000 covers every project we've seen in practice; the largest indexed
|
|
38
|
-
* monorepo (tgm-survey-platform) is around 5_000 files. Override via the
|
|
39
|
-
* MCP `max_files` argument or CODESIFT_MAX_FILES env var when a legitimately
|
|
40
|
-
* larger repo needs full coverage.
|
|
41
|
-
*/
|
|
42
|
-
const DEFAULT_MAX_FILES = 50_000;
|
|
43
|
-
function getDefaultMaxFiles() {
|
|
44
|
-
const envVal = process.env.CODESIFT_MAX_FILES;
|
|
45
|
-
if (envVal) {
|
|
46
|
-
const parsed = Number.parseInt(envVal, 10);
|
|
47
|
-
if (Number.isFinite(parsed) && parsed > 0)
|
|
48
|
-
return parsed;
|
|
49
|
-
}
|
|
50
|
-
return DEFAULT_MAX_FILES;
|
|
51
|
-
}
|
|
52
|
-
// Active watchers and in-memory indexes keyed by repo name
|
|
53
|
-
const activeWatchers = new Map();
|
|
54
|
-
const bm25Indexes = new Map();
|
|
55
|
-
const codeIndexes = new Map();
|
|
56
|
-
const embeddingCaches = new Map();
|
|
57
|
-
// Tracks last successful full indexFolder run keyed by absolute rootPath, used
|
|
58
|
-
// to short-circuit redundant scans while a watcher is keeping the index live.
|
|
59
|
-
// Populated at the end of every indexFolder call that completes normally.
|
|
60
|
-
const lastFullIndexAt = new Map();
|
|
61
|
-
/**
|
|
62
|
-
* Window during which a re-run of indexFolder against the same root is treated
|
|
63
|
-
* as redundant when a watcher is active. Telemetry showed 35 calls > 30s and a
|
|
64
|
-
* 786s outlier — these are agents defensively re-indexing repos the watcher is
|
|
65
|
-
* already maintaining. 60s matches the "fresh enough" threshold used elsewhere.
|
|
66
|
-
*/
|
|
67
|
-
const INDEX_FOLDER_REDUNDANT_WINDOW_MS = 60_000;
|
|
68
|
-
/** Test-only — clear short-circuit state between cases. */
|
|
69
|
-
export function resetIndexFolderRedundancyForTesting() {
|
|
70
|
-
lastFullIndexAt.clear();
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Parse a single file and extract its symbols + metadata.
|
|
74
|
-
* Returns null if the file cannot be parsed.
|
|
75
|
-
*/
|
|
76
|
-
async function parseOneFile(filePath, repoRoot, repoName) {
|
|
77
|
-
try {
|
|
78
|
-
const stat = await import("node:fs/promises").then((fs) => fs.stat(filePath));
|
|
79
|
-
const source = await readFile(filePath, "utf-8");
|
|
80
|
-
// CRITICAL-1 (TOCTOU parse↔hash): hash the EXACT source string we parse,
|
|
81
|
-
// here — never via a post-parse re-read. A re-read can observe a different
|
|
82
|
-
// on-disk version if the file is modified between parse and hash, pairing
|
|
83
|
-
// OLD symbols with a NEW sha so future runs permanently reuse mismatched
|
|
84
|
-
// symbols. The sha is NOT persisted inside FileEntry; callers thread it
|
|
85
|
-
// into the hash snapshot (and it saves one extra full read per parsed file).
|
|
86
|
-
const fileSha1 = createHash("sha1").update(source).digest("hex");
|
|
87
|
-
const relPath = relative(repoRoot, filePath);
|
|
88
|
-
const baseName = filePath.split("/").pop() ?? "";
|
|
89
|
-
// Use full-path resolver so multi-dot suffixes like `.gradle.kts` beat
|
|
90
|
-
// single-extension lookups (which would otherwise map to plain Kotlin).
|
|
91
|
-
const language = getLanguageForPath(filePath)
|
|
92
|
-
?? (baseName.startsWith(".env") ? "config" : "unknown");
|
|
93
|
-
let symbols;
|
|
94
|
-
let effectiveLanguage = language;
|
|
95
|
-
if (language === "markdown") {
|
|
96
|
-
symbols = extractMarkdownSymbols(source, relPath, repoName);
|
|
97
|
-
}
|
|
98
|
-
else if (language === "prisma") {
|
|
99
|
-
symbols = extractPrismaSymbols(source, relPath, repoName);
|
|
100
|
-
}
|
|
101
|
-
else if (language === "astro") {
|
|
102
|
-
symbols = extractAstroSymbols(source, relPath, repoName);
|
|
103
|
-
}
|
|
104
|
-
else if (language === "conversation") {
|
|
105
|
-
symbols = extractConversationSymbols(source, relPath, repoName);
|
|
106
|
-
}
|
|
107
|
-
else if (language === "sql") {
|
|
108
|
-
// SQL: regex extractor, no tree-sitter. Detect Jinja/dbt templates.
|
|
109
|
-
const hasJinja = /\{\{|\{%|\{#/.test(source);
|
|
110
|
-
if (hasJinja) {
|
|
111
|
-
const stripped = stripJinjaTokens(source);
|
|
112
|
-
symbols = extractSqlSymbols(stripped, relPath, repoName, source);
|
|
113
|
-
effectiveLanguage = "sql-jinja";
|
|
114
|
-
}
|
|
115
|
-
else {
|
|
116
|
-
symbols = extractSqlSymbols(source, relPath, repoName);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
else if (language === "config" || language === "text_stub") {
|
|
120
|
-
// text_stub: Swift/Dart/Scala/etc. — indexed as FileEntry but no symbol
|
|
121
|
-
// extraction until a tree-sitter grammar + extractor is added.
|
|
122
|
-
// search_text (ripgrep path) and scan_secrets still work on these files.
|
|
123
|
-
symbols = [];
|
|
124
|
-
}
|
|
125
|
-
else {
|
|
126
|
-
// Tree-sitter languages (TS/JS/Python/Go/Rust/Java/Ruby/PHP/CSS/Kotlin):
|
|
127
|
-
// dispatch to the worker pool. Synchronously-hung WASM parses kill the
|
|
128
|
-
// worker (terminated on timeout) instead of the MCP server itself.
|
|
129
|
-
// See src/parser/parser-pool.ts for details.
|
|
130
|
-
try {
|
|
131
|
-
symbols = await runTreeSitterParse({ filePath, source, language, relPath, repoName });
|
|
132
|
-
}
|
|
133
|
-
catch (err) {
|
|
134
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
135
|
-
console.warn(`[codesift] worker parse failed for ${relPath}: ${message}`);
|
|
136
|
-
return null;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
const entry = {
|
|
140
|
-
path: relPath,
|
|
141
|
-
language: effectiveLanguage,
|
|
142
|
-
symbol_count: symbols.length,
|
|
143
|
-
last_modified: Date.now(),
|
|
144
|
-
mtime_ms: Math.round(stat.mtimeMs),
|
|
145
|
-
};
|
|
146
|
-
return { symbols, entry, sha1: fileSha1 };
|
|
147
|
-
}
|
|
148
|
-
catch (err) {
|
|
149
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
150
|
-
console.warn(`[codesift] Failed to parse ${relative(repoRoot, filePath)}: ${message}`);
|
|
151
|
-
return null;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
/**
|
|
155
|
-
* Parse files in parallel batches.
|
|
156
|
-
*/
|
|
157
|
-
async function parseFiles(files, repoRoot, repoName) {
|
|
158
|
-
const allSymbols = [];
|
|
159
|
-
const fileEntries = [];
|
|
160
|
-
// CRITICAL-1: sha1 of the exact parsed source, keyed by relPath. Carried out
|
|
161
|
-
// of parseOneFile so the snapshot never re-reads (and never races) the file.
|
|
162
|
-
const shas = {};
|
|
163
|
-
for (let i = 0; i < files.length; i += PARSE_CONCURRENCY) {
|
|
164
|
-
const batch = files.slice(i, i + PARSE_CONCURRENCY);
|
|
165
|
-
const results = await Promise.all(batch.map((filePath) => parseOneFile(filePath, repoRoot, repoName)));
|
|
166
|
-
for (const result of results) {
|
|
167
|
-
if (result) {
|
|
168
|
-
allSymbols.push(...result.symbols);
|
|
169
|
-
fileEntries.push(result.entry);
|
|
170
|
-
shas[result.entry.path] = result.sha1;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
return { symbols: allSymbols, fileEntries, shas };
|
|
175
|
-
}
|
|
176
|
-
// ---------------------------------------------------------------------------
|
|
177
|
-
// Dirty propagation — mark caller files stale when a callee signature changes
|
|
178
|
-
// ---------------------------------------------------------------------------
|
|
179
|
-
/**
|
|
180
|
-
* Compute a hash of a symbol's public interface (name + kind + signature).
|
|
181
|
-
* Body changes don't trigger propagation — only signature changes.
|
|
182
|
-
*/
|
|
183
|
-
function computeSignatureHash(sym) {
|
|
184
|
-
const key = `${sym.name}|${sym.kind}|${sym.signature ?? ""}`;
|
|
185
|
-
return createHash("sha256").update(key).digest("hex").slice(0, 16);
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* Detect signature changes and mark caller files as stale.
|
|
189
|
-
* Returns the set of files marked stale.
|
|
190
|
-
*/
|
|
191
|
-
function propagateDirtySignatures(oldSymbols, newSymbols, fileEntries) {
|
|
192
|
-
// Build old signature hashes
|
|
193
|
-
const oldHashes = new Map();
|
|
194
|
-
for (const sym of oldSymbols) {
|
|
195
|
-
oldHashes.set(sym.id, computeSignatureHash(sym));
|
|
196
|
-
}
|
|
197
|
-
// Find symbols with changed signatures
|
|
198
|
-
const changedSymbolFiles = new Set();
|
|
199
|
-
for (const sym of newSymbols) {
|
|
200
|
-
const oldHash = oldHashes.get(sym.id);
|
|
201
|
-
if (oldHash && oldHash !== computeSignatureHash(sym)) {
|
|
202
|
-
changedSymbolFiles.add(sym.file);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
if (changedSymbolFiles.size === 0)
|
|
206
|
-
return new Set();
|
|
207
|
-
// Find files that import from changed files (1 level of callers)
|
|
208
|
-
// Use a simple heuristic: check if any symbol source mentions a changed file's name
|
|
209
|
-
const changedBasenames = new Set();
|
|
210
|
-
for (const f of changedSymbolFiles) {
|
|
211
|
-
const base = f.split("/").pop()?.replace(/\.\w+$/, "");
|
|
212
|
-
if (base)
|
|
213
|
-
changedBasenames.add(base);
|
|
214
|
-
}
|
|
215
|
-
const staleFiles = new Set();
|
|
216
|
-
for (const sym of newSymbols) {
|
|
217
|
-
if (changedSymbolFiles.has(sym.file))
|
|
218
|
-
continue; // Don't mark the changed file itself
|
|
219
|
-
if (!sym.source)
|
|
220
|
-
continue;
|
|
221
|
-
for (const base of changedBasenames) {
|
|
222
|
-
if (sym.source.includes(base)) {
|
|
223
|
-
staleFiles.add(sym.file);
|
|
224
|
-
break;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
// Mark stale in file entries (clear mtime so next index re-parses them)
|
|
229
|
-
for (const entry of fileEntries) {
|
|
230
|
-
if (staleFiles.has(entry.path)) {
|
|
231
|
-
entry.stale = true;
|
|
232
|
-
delete entry.mtime_ms; // Force re-parse on next indexFolder
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
return staleFiles;
|
|
236
|
-
}
|
|
237
|
-
/**
|
|
238
|
-
* Embed symbols using the configured embedding provider.
|
|
239
|
-
* Non-fatal — BM25 search still works if embedding fails.
|
|
240
|
-
*/
|
|
241
|
-
export async function embedSymbols(symbols, indexPath, repoName, config) {
|
|
242
|
-
if (!config.embeddingProvider)
|
|
243
|
-
return;
|
|
244
|
-
const embeddingPath = getEmbeddingPath(indexPath);
|
|
245
|
-
const metaPath = getEmbeddingMetaPath(indexPath);
|
|
246
|
-
try {
|
|
247
|
-
const provider = createEmbeddingProvider(config.embeddingProvider, config);
|
|
248
|
-
const symbolTexts = new Map(symbols.map((s) => [s.id, buildSymbolText(s)]));
|
|
249
|
-
const existing = await loadEmbeddings(embeddingPath);
|
|
250
|
-
const embeddings = await batchEmbed(symbolTexts, existing, (texts) => provider.embed(texts, "document"), config.embeddingBatchSize, repoName);
|
|
251
|
-
await saveEmbeddings(embeddingPath, embeddings);
|
|
252
|
-
await saveEmbeddingMeta(metaPath, {
|
|
253
|
-
model: provider.model,
|
|
254
|
-
provider: config.embeddingProvider,
|
|
255
|
-
dimensions: provider.dimensions,
|
|
256
|
-
symbol_count: embeddings.size,
|
|
257
|
-
updated_at: Date.now(),
|
|
258
|
-
});
|
|
259
|
-
embeddingCaches.set(repoName, embeddings);
|
|
260
|
-
}
|
|
261
|
-
catch (err) {
|
|
262
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
263
|
-
console.error(`[codesift] Embedding failed for ${repoName}: ${message}`);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
/**
|
|
267
|
-
* Read files in parallel batches and split each into chunks.
|
|
268
|
-
*/
|
|
269
|
-
async function readAndChunkFiles(fileEntries, rootPath, repoName, symbols) {
|
|
270
|
-
const allChunks = [];
|
|
271
|
-
for (let i = 0; i < fileEntries.length; i += PARSE_CONCURRENCY) {
|
|
272
|
-
const batch = fileEntries.slice(i, i + PARSE_CONCURRENCY);
|
|
273
|
-
const batchResults = await Promise.all(batch.map(async (entry) => {
|
|
274
|
-
const fullPath = join(rootPath, entry.path);
|
|
275
|
-
try {
|
|
276
|
-
const content = await readFile(fullPath, "utf-8");
|
|
277
|
-
if (symbols) {
|
|
278
|
-
const fileSymbols = symbols
|
|
279
|
-
.filter((s) => s.file === entry.path)
|
|
280
|
-
.map((s) => ({ name: s.name, start_line: s.start_line, end_line: s.end_line }));
|
|
281
|
-
return chunkBySymbols(entry.path, content, repoName, fileSymbols);
|
|
282
|
-
}
|
|
283
|
-
return chunkFile(entry.path, content, repoName);
|
|
284
|
-
}
|
|
285
|
-
catch (err) {
|
|
286
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
287
|
-
console.warn(`[codesift] Failed to read ${entry.path} for chunking: ${message}`);
|
|
288
|
-
return [];
|
|
289
|
-
}
|
|
290
|
-
}));
|
|
291
|
-
for (const chunks of batchResults) {
|
|
292
|
-
allChunks.push(...chunks);
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
return allChunks;
|
|
296
|
-
}
|
|
297
|
-
/**
|
|
298
|
-
* Embed file chunks using the configured embedding provider.
|
|
299
|
-
* Non-fatal — symbol-level and BM25 search still work if this fails.
|
|
300
|
-
*/
|
|
301
|
-
async function embedChunks(fileEntries, rootPath, repoName, indexPath, config, symbols) {
|
|
302
|
-
if (!config.embeddingProvider)
|
|
303
|
-
return;
|
|
304
|
-
const chunkPath = getChunkPath(indexPath);
|
|
305
|
-
const chunkEmbeddingPath = getChunkEmbeddingPath(indexPath);
|
|
306
|
-
try {
|
|
307
|
-
const provider = createEmbeddingProvider(config.embeddingProvider, config);
|
|
308
|
-
const existingChunkEmbeddings = await loadChunkEmbeddings(chunkEmbeddingPath) ?? new Map();
|
|
309
|
-
const allChunks = await readAndChunkFiles(fileEntries, rootPath, repoName, symbols);
|
|
310
|
-
if (allChunks.length > 0) {
|
|
311
|
-
const chunkTexts = new Map(allChunks.map((c) => [c.id, c.text]));
|
|
312
|
-
const chunkEmbeddings = await batchEmbed(chunkTexts, existingChunkEmbeddings, (texts) => provider.embed(texts, "document"), CHUNK_EMBEDDING_BATCH_SIZE, `${repoName}:chunks`);
|
|
313
|
-
await saveChunks(chunkPath, allChunks);
|
|
314
|
-
await saveChunkEmbeddings(chunkEmbeddingPath, chunkEmbeddings);
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
catch (err) {
|
|
318
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
319
|
-
console.error(`[codesift] Chunk embedding failed for ${repoName}: ${message}`);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
/**
|
|
323
|
-
* Decide whether a previously stored index no longer reflects the working
|
|
324
|
-
* tree. Samples up to 256 of its file paths (even stride) and stats them;
|
|
325
|
-
* when at least half are gone the old index is treated as stale. Used by the
|
|
326
|
-
* indexFolder sanity check to break the poisoned-baseline deadlock: an old
|
|
327
|
-
* index bloated with since-deleted trees (.worktrees/, vendored dirs) would
|
|
328
|
-
* otherwise reject every honest reindex as "truncated" forever.
|
|
329
|
-
*/
|
|
330
|
-
const STALE_SAMPLE_LIMIT = 256;
|
|
331
|
-
const STALE_MISSING_FRACTION = 0.5;
|
|
332
|
-
async function isExistingIndexStale(existing, rootPath) {
|
|
333
|
-
const paths = existing.files.map((f) => f.path);
|
|
334
|
-
if (paths.length === 0)
|
|
335
|
-
return true;
|
|
336
|
-
const stride = Math.max(1, Math.floor(paths.length / STALE_SAMPLE_LIMIT));
|
|
337
|
-
const sampled = [];
|
|
338
|
-
for (let i = 0; i < paths.length && sampled.length < STALE_SAMPLE_LIMIT; i += stride) {
|
|
339
|
-
const p = paths[i];
|
|
340
|
-
if (p)
|
|
341
|
-
sampled.push(p);
|
|
342
|
-
}
|
|
343
|
-
let missing = 0;
|
|
344
|
-
await Promise.all(sampled.map(async (relPath) => {
|
|
345
|
-
try {
|
|
346
|
-
await stat(join(rootPath, relPath));
|
|
347
|
-
}
|
|
348
|
-
catch {
|
|
349
|
-
missing++;
|
|
350
|
-
}
|
|
351
|
-
}));
|
|
352
|
-
return missing >= sampled.length * STALE_MISSING_FRACTION;
|
|
353
|
-
}
|
|
354
|
-
/**
|
|
355
|
-
* Read a file and return the sha1 hex of its UTF-8 content, or null on read
|
|
356
|
-
* failure (deleted mid-walk, permission error). Code-sized files only — same
|
|
357
|
-
* assumption parseOneFile already makes. Non-throwing: callers treat null as
|
|
358
|
-
* "could not hash → fall through to re-parse".
|
|
359
|
-
*/
|
|
360
|
-
async function sha1OfFile(absPath) {
|
|
361
|
-
try {
|
|
362
|
-
const content = await readFile(absPath, "utf-8");
|
|
363
|
-
return createHash("sha1").update(content).digest("hex");
|
|
364
|
-
}
|
|
365
|
-
catch {
|
|
366
|
-
return null;
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
/**
|
|
370
|
-
* Exported for unit testing only — not part of the public API.
|
|
371
|
-
*
|
|
372
|
-
* Drains a legacy-hash queue: hashes each file, then re-stats to confirm the
|
|
373
|
-
* mtime has not drifted since the decision-time stat. Entries whose mtime
|
|
374
|
-
* drifted (or whose stat fails) are omitted from the returned map so the next
|
|
375
|
-
* run re-parses them rather than reusing symbols against a mismatched sha.
|
|
376
|
-
*
|
|
377
|
-
* @param queue Items from the legacyHashQueue (relPath + filePath + decision-time mtimeMs).
|
|
378
|
-
* @param hashFn Injectable hash function (default: sha1OfFile). Tests inject a
|
|
379
|
-
* function that also modifies the file so they can trigger the
|
|
380
|
-
* TOCTOU drift-detection path without real concurrency.
|
|
381
|
-
* @param statFn Injectable stat function (default: fs.stat). Tests can stub this
|
|
382
|
-
* to return a post-modification mtime.
|
|
383
|
-
*/
|
|
384
|
-
export async function drainLegacyHashQueue(queue, hashFn = sha1OfFile, statFn = (p) => import("node:fs/promises").then((m) => m.stat(p))) {
|
|
385
|
-
const result = {};
|
|
386
|
-
for (let i = 0; i < queue.length; i += PARSE_CONCURRENCY) {
|
|
387
|
-
const batch = queue.slice(i, i + PARSE_CONCURRENCY);
|
|
388
|
-
const shas = await Promise.all(batch.map((q) => hashFn(q.filePath)));
|
|
389
|
-
const stats = await Promise.all(batch.map((q) => statFn(q.filePath).then((st) => Math.round(st.mtimeMs), () => null)));
|
|
390
|
-
batch.forEach((q, j) => {
|
|
391
|
-
const currentMtime = stats[j];
|
|
392
|
-
if (currentMtime === null || currentMtime !== q.mtimeMs) {
|
|
393
|
-
// Mtime drifted or file gone — omit so next run re-parses.
|
|
394
|
-
return;
|
|
395
|
-
}
|
|
396
|
-
// Omit on null hash — never persist an empty-string sentinel that a
|
|
397
|
-
// snapshot reader could mistake for a valid sha1.
|
|
398
|
-
if (shas[j])
|
|
399
|
-
result[q.relPath] = shas[j];
|
|
400
|
-
});
|
|
401
|
-
}
|
|
402
|
-
return result;
|
|
403
|
-
}
|
|
404
|
-
export async function indexFolder(folderPath, options) {
|
|
405
|
-
if (!folderPath || typeof folderPath !== "string") {
|
|
406
|
-
throw new Error("folderPath is required and must be a non-empty string");
|
|
407
|
-
}
|
|
408
|
-
const rootPath = resolve(folderPath);
|
|
409
|
-
const repoName = getRepoName(rootPath);
|
|
410
|
-
// Short-circuit: if a watcher is already keeping the index for this root
|
|
411
|
-
// live and we re-indexed recently, return a skipped status instead of
|
|
412
|
-
// walking the filesystem again.
|
|
413
|
-
if (!options?.force) {
|
|
414
|
-
const lastTs = lastFullIndexAt.get(rootPath);
|
|
415
|
-
const watcher = activeWatchers.get(repoName);
|
|
416
|
-
if (watcher && lastTs && Date.now() - lastTs < INDEX_FOLDER_REDUNDANT_WINDOW_MS) {
|
|
417
|
-
return {
|
|
418
|
-
repo: repoName,
|
|
419
|
-
root: rootPath,
|
|
420
|
-
file_count: 0,
|
|
421
|
-
symbol_count: 0,
|
|
422
|
-
duration_ms: 0,
|
|
423
|
-
status: "skipped",
|
|
424
|
-
reason: "watcher active, recent index",
|
|
425
|
-
last_indexed: new Date(lastTs).toISOString(),
|
|
426
|
-
hint: "pass force=true to override",
|
|
427
|
-
};
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
// Clear tsconfig path resolver cache so config edits between runs take effect.
|
|
431
|
-
// The two-level cache (configCache + dirToConfigCache) is module-level and
|
|
432
|
-
// would otherwise serve stale alias mappings if a user edited tsconfig.json
|
|
433
|
-
// between successive index_folder calls within the same MCP server process.
|
|
434
|
-
clearTsconfigCache();
|
|
435
|
-
const config = loadConfig();
|
|
436
|
-
const startTime = Date.now();
|
|
437
|
-
const indexPath = getIndexPath(config.dataDir, rootPath);
|
|
438
|
-
// Read .codesiftignore for user-defined exclude patterns
|
|
439
|
-
let excludePatterns;
|
|
440
|
-
try {
|
|
441
|
-
const ignoreContent = await readFile(join(rootPath, ".codesiftignore"), "utf-8");
|
|
442
|
-
excludePatterns = ignoreContent
|
|
443
|
-
.split("\n")
|
|
444
|
-
.map((line) => line.replace(/#.*$/, "").trim())
|
|
445
|
-
.filter((line) => line.length > 0);
|
|
446
|
-
if (excludePatterns.length === 0)
|
|
447
|
-
excludePatterns = undefined;
|
|
448
|
-
}
|
|
449
|
-
catch {
|
|
450
|
-
// .codesiftignore not found — proceed without patterns
|
|
451
|
-
}
|
|
452
|
-
// Walk directory and collect parseable files. maxFiles caps unbounded walks
|
|
453
|
-
// (huge monorepos, vendored data sets) before they OOM the process — see
|
|
454
|
-
// DEFAULT_MAX_FILES rationale above.
|
|
455
|
-
const maxFiles = options?.max_files ?? getDefaultMaxFiles();
|
|
456
|
-
const files = await walkDirectory(rootPath, {
|
|
457
|
-
includePaths: options?.include_paths,
|
|
458
|
-
excludePatterns,
|
|
459
|
-
maxFiles,
|
|
460
|
-
fileFilter: (ext, name) => !!getLanguageForExtension(ext) || (name?.startsWith(".env") ?? false),
|
|
461
|
-
});
|
|
462
|
-
const hitFileLimit = files.length >= maxFiles;
|
|
463
|
-
if (hitFileLimit) {
|
|
464
|
-
console.error(`[codesift] index_folder: ${rootPath} hit max_files=${maxFiles}; ` +
|
|
465
|
-
`partial index. Pass include_paths to scope the walk or raise max_files.`);
|
|
466
|
-
}
|
|
467
|
-
// mtime-based incremental: skip files unchanged since last index
|
|
468
|
-
const existing = await loadIndex(indexPath);
|
|
469
|
-
const mtimeMap = new Map();
|
|
470
|
-
if (existing) {
|
|
471
|
-
for (const f of existing.files) {
|
|
472
|
-
if (f.mtime_ms)
|
|
473
|
-
mtimeMap.set(f.path, f.mtime_ms);
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
// Persistent hash snapshot (Task 6): relPath → sha1 from the previous index.
|
|
477
|
-
// mtime stays the cheap pre-filter (unchanged mtime → reuse without hashing,
|
|
478
|
-
// the fastest path). When mtime *changed*, the snapshot sha1 lets us still
|
|
479
|
-
// reuse symbols for touch/checkout no-op rewrites that bumped mtime without
|
|
480
|
-
// changing content — something mtime-only logic could never catch.
|
|
481
|
-
// null when absent/corrupt/version-or-repo-mismatch → degrade to full parse.
|
|
482
|
-
const snapshotPath = getSnapshotPath(indexPath);
|
|
483
|
-
let oldSnapshot = existing
|
|
484
|
-
? await loadHashSnapshot(snapshotPath, repoName)
|
|
485
|
-
: null;
|
|
486
|
-
// Staleness guard (Task 6, CRITICAL-2): an incremental saveIncremental /
|
|
487
|
-
// removeFileFromIndex advances index.updated_at WITHOUT touching the
|
|
488
|
-
// snapshot. If saveIndex landed but the subsequent snapshot save failed (or
|
|
489
|
-
// an incremental edit ran after the last full index), the on-disk snapshot
|
|
490
|
-
// is OLDER than the index and its SHAs may no longer match the indexed
|
|
491
|
-
// symbols — carrying them forward (fast path) or sha-matching against them
|
|
492
|
-
// (changed path) would produce wrong reuse on revert+touch sequences. When
|
|
493
|
-
// the snapshot predates the index, discard it: the legacy hash-now
|
|
494
|
-
// convergence path below repopulates a fresh, correct snapshot this run.
|
|
495
|
-
// Guard uses strict inequality (!==), not <. The fresh-write contract is
|
|
496
|
-
// snapshot.created_at === index.updated_at exactly (created_at is anchored to
|
|
497
|
-
// codeIndex.updated_at, not a fresh Date.now()). So ANY mismatch — older OR
|
|
498
|
-
// newer — means the snapshot is not the one paired with this index and must
|
|
499
|
-
// be discarded. A FUTURE created_at (e.g. a snapshot written against a later,
|
|
500
|
-
// since-rolled-back index, or clock skew) is just as untrustworthy as a stale
|
|
501
|
-
// one: its SHAs may not match the indexed symbols.
|
|
502
|
-
if (oldSnapshot && existing && oldSnapshot.created_at !== existing.updated_at) {
|
|
503
|
-
console.warn(`[codesift] hash-snapshot older than index — rebuilding (${repoName})`);
|
|
504
|
-
oldSnapshot = null;
|
|
505
|
-
}
|
|
506
|
-
const filesToParse = [];
|
|
507
|
-
const keptSymbols = [];
|
|
508
|
-
const keptEntries = [];
|
|
509
|
-
// sha1 of every file in the NEW index, by relPath. Populated for reused files
|
|
510
|
-
// here (from the old snapshot when present, else hashed-now for convergence)
|
|
511
|
-
// and for parsed files after parseFiles resolves.
|
|
512
|
-
const newSnapshotFiles = {};
|
|
513
|
-
// CRITICAL-1: reused files whose sha1 must be (re)computed because the old
|
|
514
|
-
// snapshot lacks it (legacy snapshot-less index, or stale snapshot discarded
|
|
515
|
-
// above). Collected here and hashed AFTER the loop in PARSE_CONCURRENCY
|
|
516
|
-
// batches instead of one serial await per file inside the loop — on a first
|
|
517
|
-
// run after upgrade against a many-thousand-file repo the serial version cost
|
|
518
|
-
// thousands of sequential awaits. Behavior is identical, wall-clock is
|
|
519
|
-
// parallelized.
|
|
520
|
-
//
|
|
521
|
-
// mtimeMs: the mtime observed at decision time (the moment we confirmed
|
|
522
|
-
// mtime === prevMtime and placed the file in the queue). We re-stat after
|
|
523
|
-
// hashing to detect any concurrent modification that landed between the two
|
|
524
|
-
// operations. If the mtime drifted, we omit the file from newSnapshotFiles
|
|
525
|
-
// entirely — the missing sha causes the next cold run to re-parse, avoiding
|
|
526
|
-
// a snapshot that pairs new-content sha against old (reused) symbols.
|
|
527
|
-
const legacyHashQueue = [];
|
|
528
|
-
// PERF: pre-build per-file lookups ONCE before the reuse loop. Both reuse
|
|
529
|
-
// branches need (a) the existing index's symbols for a given relPath and (b)
|
|
530
|
-
// its FileEntry. Doing `existing.symbols.filter(s => s.file === relPath)` /
|
|
531
|
-
// `existing.files.find(f => f.path === relPath)` per file is O(files ×
|
|
532
|
-
// symbols) and O(files²) respectively — quadratic, and on a many-thousand
|
|
533
|
-
// file/symbol repo that dominated the reuse-heavy fast path. A single pass
|
|
534
|
-
// builds Map lookups each branch hits in O(1). Built only when there's an
|
|
535
|
-
// existing index to reuse from.
|
|
536
|
-
const symbolsByFile = new Map();
|
|
537
|
-
const fileEntryByPath = new Map();
|
|
538
|
-
if (existing) {
|
|
539
|
-
for (const sym of existing.symbols) {
|
|
540
|
-
const list = symbolsByFile.get(sym.file);
|
|
541
|
-
if (list)
|
|
542
|
-
list.push(sym);
|
|
543
|
-
else
|
|
544
|
-
symbolsByFile.set(sym.file, [sym]);
|
|
545
|
-
}
|
|
546
|
-
for (const fe of existing.files) {
|
|
547
|
-
fileEntryByPath.set(fe.path, fe);
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
if (mtimeMap.size > 0) {
|
|
551
|
-
const { stat } = await import("node:fs/promises");
|
|
552
|
-
for (const filePath of files) {
|
|
553
|
-
const relPath = relative(rootPath, filePath);
|
|
554
|
-
const prevMtime = mtimeMap.get(relPath);
|
|
555
|
-
if (prevMtime !== undefined) {
|
|
556
|
-
const fileEntry = fileEntryByPath.get(relPath);
|
|
557
|
-
// Force re-parse if file is marked stale (callee signature changed)
|
|
558
|
-
if (fileEntry?.stale) {
|
|
559
|
-
filesToParse.push(filePath);
|
|
560
|
-
continue;
|
|
561
|
-
}
|
|
562
|
-
try {
|
|
563
|
-
const st = await stat(filePath);
|
|
564
|
-
if (Math.round(st.mtimeMs) === prevMtime) {
|
|
565
|
-
// Fast path: mtime unchanged → reuse symbols without hashing.
|
|
566
|
-
const fileSymbols = symbolsByFile.get(relPath) ?? [];
|
|
567
|
-
if (fileEntry) {
|
|
568
|
-
keptSymbols.push(...fileSymbols);
|
|
569
|
-
keptEntries.push(fileEntry);
|
|
570
|
-
// Carry the sha1 forward: reuse from old snapshot if present,
|
|
571
|
-
// else DEFER hashing so legacy (snapshot-less) indexes converge
|
|
572
|
-
// to a complete snapshot after one run — without paying a serial
|
|
573
|
-
// hash per file inside this loop.
|
|
574
|
-
const carried = oldSnapshot?.files[relPath];
|
|
575
|
-
if (carried !== undefined) {
|
|
576
|
-
newSnapshotFiles[relPath] = carried;
|
|
577
|
-
}
|
|
578
|
-
else {
|
|
579
|
-
legacyHashQueue.push({ relPath, filePath, mtimeMs: Math.round(st.mtimeMs) });
|
|
580
|
-
}
|
|
581
|
-
continue;
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
else {
|
|
585
|
-
// mtime changed — hash decides reuse vs re-parse. This catches
|
|
586
|
-
// touch/checkout that bumped mtime without changing content.
|
|
587
|
-
const snapSha = oldSnapshot?.files[relPath];
|
|
588
|
-
if (snapSha !== undefined && fileEntry && !fileEntry.stale) {
|
|
589
|
-
const currentSha = await sha1OfFile(filePath);
|
|
590
|
-
if (currentSha !== null && currentSha === snapSha) {
|
|
591
|
-
const fileSymbols = symbolsByFile.get(relPath) ?? [];
|
|
592
|
-
keptSymbols.push(...fileSymbols);
|
|
593
|
-
// FIX: the file's mtime changed but content is identical (touch /
|
|
594
|
-
// checkout no-op rewrite). Reuse the symbols, but DON'T carry the
|
|
595
|
-
// stale FileEntry verbatim — its mtime_ms still holds the OLD
|
|
596
|
-
// mtime, so every future run would see mtime !== prevMtime and
|
|
597
|
-
// re-hash this file forever, permanently degrading it off the
|
|
598
|
-
// mtime fast path. Clone the entry with mtime_ms bumped to the
|
|
599
|
-
// CURRENT stat's mtime so the next run takes the cheap fast path.
|
|
600
|
-
keptEntries.push({ ...fileEntry, mtime_ms: Math.round(st.mtimeMs) });
|
|
601
|
-
newSnapshotFiles[relPath] = currentSha;
|
|
602
|
-
continue;
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
catch { /* file may have been deleted — reparse */ }
|
|
608
|
-
}
|
|
609
|
-
filesToParse.push(filePath);
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
else {
|
|
613
|
-
filesToParse.push(...files);
|
|
614
|
-
}
|
|
615
|
-
// Drain the deferred legacy-hash queue (CRITICAL-1): files reused via the
|
|
616
|
-
// mtime fast path that had no carried sha1 (legacy snapshot-less index, or a
|
|
617
|
-
// stale snapshot discarded by the guard above). See drainLegacyHashQueue for
|
|
618
|
-
// the TOCTOU guard details — entries whose mtime drifted between decision
|
|
619
|
-
// time and hash time are omitted so the next run re-parses rather than
|
|
620
|
-
// reusing symbols against a mismatched sha.
|
|
621
|
-
if (legacyHashQueue.length > 0) {
|
|
622
|
-
const drained = await drainLegacyHashQueue(legacyHashQueue);
|
|
623
|
-
Object.assign(newSnapshotFiles, drained);
|
|
624
|
-
}
|
|
625
|
-
// Parse only changed/new files
|
|
626
|
-
const { symbols: parsedSymbols, fileEntries: parsedEntries, shas: parsedShas } = await parseFiles(filesToParse, rootPath, repoName);
|
|
627
|
-
const symbols = [...keptSymbols, ...parsedSymbols];
|
|
628
|
-
const fileEntries = [...keptEntries, ...parsedEntries];
|
|
629
|
-
// Record sha1s for the files that were actually parsed (changed/new).
|
|
630
|
-
// CRITICAL-1 (TOCTOU): these hashes come straight from parseOneFile — they
|
|
631
|
-
// are the sha1 of the EXACT source string that produced the symbols, so the
|
|
632
|
-
// snapshot can never pair old symbols with a newer file's sha. Only entries
|
|
633
|
-
// that survived parseFiles (parseOneFile returned non-null) have a sha here,
|
|
634
|
-
// keeping the snapshot in lockstep with fileEntries. The previous post-parse
|
|
635
|
-
// double-read loop is gone — one fewer full read per parsed file.
|
|
636
|
-
for (const entry of parsedEntries) {
|
|
637
|
-
const sha = parsedShas[entry.path];
|
|
638
|
-
if (sha !== undefined)
|
|
639
|
-
newSnapshotFiles[entry.path] = sha;
|
|
640
|
-
}
|
|
641
|
-
// Dirty propagation: detect signature changes and mark caller files stale
|
|
642
|
-
if (existing && filesToParse.length > 0 && filesToParse.length < files.length) {
|
|
643
|
-
const staleFiles = propagateDirtySignatures(existing.symbols, symbols, fileEntries);
|
|
644
|
-
if (staleFiles.size > 0) {
|
|
645
|
-
console.error(`[codesift] Dirty propagation: ${staleFiles.size} caller files marked stale`);
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
// Invalidate code index cache (BM25 is rebuilt below from the FINAL symbol
|
|
649
|
-
// set — possibly merged with out-of-scope existing symbols, see merge block).
|
|
650
|
-
codeIndexes.delete(repoName);
|
|
651
|
-
// Sanity check: don't overwrite a complete index with a partial one
|
|
652
|
-
// (WASM crash or walk failure can produce truncated results).
|
|
653
|
-
//
|
|
654
|
-
// IMPORTANT: skip the guard when the walk was explicitly narrowed — either
|
|
655
|
-
// max_files was hit (truncated at cap) or include_paths scoped the walk to a
|
|
656
|
-
// subdirectory. In both cases the small result count is EXPECTED and rejecting
|
|
657
|
-
// it would be a false positive (the "1139 vs 9512" bug class). For unrestricted
|
|
658
|
-
// walks the guard stays as-is, protecting against genuine silent truncations.
|
|
659
|
-
//
|
|
660
|
-
// CRITICAL (T7 correctness fix): skipping the guard is necessary but NOT
|
|
661
|
-
// sufficient. A scoped/capped walk only SEES a narrow slice of the repo; if we
|
|
662
|
-
// persisted that slice as the WHOLE index we would wipe every out-of-scope
|
|
663
|
-
// file's symbols from index+snapshot (worse than the guard's old reject,
|
|
664
|
-
// which at least preserved the prior index). So for scoped/capped walks with
|
|
665
|
-
// an existing index we MERGE: keep out-of-scope existing entries verbatim and
|
|
666
|
-
// overlay the walk's results. See the merge block below.
|
|
667
|
-
//
|
|
668
|
-
// "max_files hit" detection: files.length === effective maxFiles. This is the
|
|
669
|
-
// only signal walkDirectory exposes (it sets limitReached internally but does
|
|
670
|
-
// not surface it on the return value). A 1-in-a-million exact-count false
|
|
671
|
-
// positive (repo has exactly maxFiles parseable files) is accepted — the
|
|
672
|
-
// guard skip is conservative (allows write), not destructive.
|
|
673
|
-
const DROP_THRESHOLD = 0.5; // Reject if new index has <50% of old file count
|
|
674
|
-
const walkExplicitlyCapped = hitFileLimit;
|
|
675
|
-
const walkExplicitlyScoped = options?.include_paths !== undefined && options.include_paths.length > 0;
|
|
676
|
-
// MIN_GUARD_FILES: the unrestricted guard only arms above this existing
|
|
677
|
-
// file_count (`existing.file_count > 50` below). The scoped-granularity guard
|
|
678
|
-
// mirrors that shape against the in-scope subset so a tiny scope can't be
|
|
679
|
-
// rejected on noise. Single source of truth so both guards stay in lockstep.
|
|
680
|
-
const MIN_GUARD_FILES = 50;
|
|
681
|
-
if (walkExplicitlyCapped || walkExplicitlyScoped) {
|
|
682
|
-
// ROUND-2 FIX (scoped-granularity guard): the unrestricted guard is skipped
|
|
683
|
-
// for scoped/capped walks because a small *overall* result is expected. But
|
|
684
|
-
// that skip was total — a scoped walk that aborts mid-enumeration (WASM
|
|
685
|
-
// crash, transient FS error, an over-broad exclude) silently truncates the
|
|
686
|
-
// IN-SCOPE slice, and the merge below treats every unwalked in-scope file as
|
|
687
|
-
// a deletion → wipes it from index+snapshot. So for a purely SCOPED (uncapped)
|
|
688
|
-
// walk we re-arm a guard against the IN-SCOPE subset: if the walk enumerated
|
|
689
|
-
// far fewer in-scope files than the existing index held in that same scope,
|
|
690
|
-
// AND those files are still on disk, the enumeration was truncated → reject
|
|
691
|
-
// before any merge/save, leaving the old index+snapshot intact.
|
|
692
|
-
//
|
|
693
|
-
// Capped walks are intentionally EXEMPT: a cap means unseen ≠ deleted (the
|
|
694
|
-
// merge preserves all unwalked files), so there is no truncation to detect —
|
|
695
|
-
// nothing in-scope is dropped. A walk that is BOTH scoped and capped also
|
|
696
|
-
// takes capped semantics (preserve everything unwalked), so the same
|
|
697
|
-
// exemption applies — no in-scope file can be lost.
|
|
698
|
-
if (walkExplicitlyScoped && !walkExplicitlyCapped && existing) {
|
|
699
|
-
const includePaths = options.include_paths;
|
|
700
|
-
const inScopeRel = (relPath) => includePaths.some((p) => relPath.startsWith(p)); // mirror walkDirectory
|
|
701
|
-
const existingInScope = existing.files.filter((fe) => inScopeRel(fe.path));
|
|
702
|
-
// All walked files are in scope by construction (walkDirectory honored
|
|
703
|
-
// includePaths), so walkedInScope is simply the walk's file count.
|
|
704
|
-
const walkedInScope = fileEntries.length;
|
|
705
|
-
if (existingInScope.length > MIN_GUARD_FILES &&
|
|
706
|
-
walkedInScope < existingInScope.length * DROP_THRESHOLD) {
|
|
707
|
-
// Auto-heal analog (in-scope): the shrink may be a genuine mass deletion
|
|
708
|
-
// within the scope, not a truncated walk. Sample the existing in-scope
|
|
709
|
-
// paths on disk (mirrors isExistingIndexStale, but restricted to the
|
|
710
|
-
// scope) — if most are gone, accept the merge.
|
|
711
|
-
const inScopePaths = existingInScope.map((fe) => fe.path);
|
|
712
|
-
const stride = Math.max(1, Math.floor(inScopePaths.length / STALE_SAMPLE_LIMIT));
|
|
713
|
-
const sampled = [];
|
|
714
|
-
for (let i = 0; i < inScopePaths.length && sampled.length < STALE_SAMPLE_LIMIT; i += stride) {
|
|
715
|
-
const p = inScopePaths[i];
|
|
716
|
-
if (p)
|
|
717
|
-
sampled.push(p);
|
|
718
|
-
}
|
|
719
|
-
let missing = 0;
|
|
720
|
-
await Promise.all(sampled.map(async (relPath) => {
|
|
721
|
-
try {
|
|
722
|
-
await stat(join(rootPath, relPath));
|
|
723
|
-
}
|
|
724
|
-
catch {
|
|
725
|
-
missing++;
|
|
726
|
-
}
|
|
727
|
-
}));
|
|
728
|
-
const mostGone = missing >= sampled.length * STALE_MISSING_FRACTION;
|
|
729
|
-
if (mostGone) {
|
|
730
|
-
console.error(`[codesift] Scoped sanity auto-heal for ${repoName}: walked ` +
|
|
731
|
-
`${walkedInScope} of ${existingInScope.length} in-scope files but ` +
|
|
732
|
-
`most sampled in-scope paths no longer exist on disk. Accepting ` +
|
|
733
|
-
`scoped merge (legit in-scope mass deletion).`);
|
|
734
|
-
}
|
|
735
|
-
else {
|
|
736
|
-
console.error(`[codesift] SCOPED SANITY CHECK FAILED for ${repoName}: scoped walk ` +
|
|
737
|
-
`under-enumerated — walked ${walkedInScope} of ${existingInScope.length} ` +
|
|
738
|
-
`in-scope files, which still exist on disk. Keeping old index.`);
|
|
739
|
-
return {
|
|
740
|
-
repo: repoName,
|
|
741
|
-
root: rootPath,
|
|
742
|
-
file_count: existing.file_count,
|
|
743
|
-
symbol_count: existing.symbol_count,
|
|
744
|
-
duration_ms: Date.now() - startTime,
|
|
745
|
-
status: "rejected_partial",
|
|
746
|
-
reason: `scoped walk under-enumerated: walked ${walkedInScope} of ${existingInScope.length} in-scope files (still on disk) — kept old index, nothing was re-registered`,
|
|
747
|
-
hint: "If the in-scope shrink is expected (deleted files, new excludes), run invalidate_cache then index_folder to rebuild from scratch.",
|
|
748
|
-
};
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
const detail = walkExplicitlyCapped
|
|
753
|
-
? `max_files=${maxFiles} hit (${files.length} files returned)`
|
|
754
|
-
: `include_paths=[${options.include_paths.join(", ")}]`;
|
|
755
|
-
console.error(`[codesift] sanity guard skipped: walk explicitly capped/scoped (${detail})`);
|
|
756
|
-
}
|
|
757
|
-
else if (existing && fileEntries.length < existing.file_count * DROP_THRESHOLD && existing.file_count > MIN_GUARD_FILES) {
|
|
758
|
-
// The shrink can also mean the OLD index is the bogus one: an earlier
|
|
759
|
-
// walker may have swept since-deleted trees (.worktrees/, vendored dirs),
|
|
760
|
-
// permanently inflating the baseline so every honest reindex looks
|
|
761
|
-
// truncated and gets rejected forever. Disambiguate by sampling the old
|
|
762
|
-
// index's paths: if most of them no longer exist on disk, the old index
|
|
763
|
-
// is stale dead weight — accept the new result instead of keeping it.
|
|
764
|
-
if (await isExistingIndexStale(existing, rootPath)) {
|
|
765
|
-
console.error(`[codesift] Sanity check auto-heal for ${repoName}: old index has ` +
|
|
766
|
-
`${existing.file_count} files but most sampled paths no longer exist ` +
|
|
767
|
-
`on disk. Accepting new index (${fileEntries.length} files).`);
|
|
768
|
-
}
|
|
769
|
-
else {
|
|
770
|
-
console.error(`[codesift] SANITY CHECK FAILED for ${repoName}: ` +
|
|
771
|
-
`new index has ${fileEntries.length} files vs ${existing.file_count} previously. ` +
|
|
772
|
-
`Keeping old index. Use invalidate_cache + index_folder to force reindex.`);
|
|
773
|
-
return {
|
|
774
|
-
repo: repoName,
|
|
775
|
-
root: rootPath,
|
|
776
|
-
file_count: existing.file_count,
|
|
777
|
-
symbol_count: existing.symbol_count,
|
|
778
|
-
duration_ms: Date.now() - startTime,
|
|
779
|
-
status: "rejected_partial",
|
|
780
|
-
reason: `new walk found ${fileEntries.length} files, <50% of the ${existing.file_count} previously indexed — kept old index, nothing was re-registered`,
|
|
781
|
-
hint: "If the shrink is expected (deleted trees, new excludes), run invalidate_cache then index_folder to rebuild from scratch.",
|
|
782
|
-
};
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
// ── MERGE-persist for scoped/capped walks (T7 correctness fix) ────────────
|
|
786
|
-
// A scoped (include_paths) or capped (max_files-hit) walk only enumerated a
|
|
787
|
-
// slice of the repo. Persisting that slice verbatim would delete every
|
|
788
|
-
// out-of-scope file's symbols from index+snapshot. When an existing index is
|
|
789
|
-
// present we instead MERGE: preserve out-of-scope existing entries/symbols/
|
|
790
|
-
// shas and overlay the walk's results.
|
|
791
|
-
//
|
|
792
|
-
// - include_paths scoped (and NOT capped): "scope" = files whose relPath is
|
|
793
|
-
// under any include root (mirror walkDirectory's relPath.startsWith(p)
|
|
794
|
-
// test EXACTLY). Out-of-scope existing files are preserved verbatim;
|
|
795
|
-
// in-scope existing files NOT in the walk set W are dropped (genuine
|
|
796
|
-
// in-scope deletions — the walk fully enumerated the scope).
|
|
797
|
-
// - capped (max_files hit): scope is UNDEFINED — the cap means an unseen
|
|
798
|
-
// file is not necessarily deleted. Preserve ALL existing entries not in W,
|
|
799
|
-
// overlay W. (If a capped walk also passed include_paths, the cap makes the
|
|
800
|
-
// in-scope enumeration incomplete too, so we still only trust W and
|
|
801
|
-
// preserve everything else — capped semantics win.)
|
|
802
|
-
//
|
|
803
|
-
// First run (no existing index) with a scoped/capped walk → save what we have
|
|
804
|
-
// (current behavior, documented): there is nothing to preserve.
|
|
805
|
-
let mergedSymbols = symbols;
|
|
806
|
-
let mergedEntries = fileEntries;
|
|
807
|
-
let mergedSnapshotFiles = newSnapshotFiles;
|
|
808
|
-
if ((walkExplicitlyCapped || walkExplicitlyScoped) && existing) {
|
|
809
|
-
const walkedPaths = new Set(fileEntries.map((fe) => fe.path));
|
|
810
|
-
// A capped walk has undefined scope (unseen ≠ deleted), so it preserves
|
|
811
|
-
// everything not walked. A purely scoped (uncapped) walk additionally drops
|
|
812
|
-
// in-scope-but-unwalked files, since the walk fully enumerated the scope.
|
|
813
|
-
const includePaths = options?.include_paths;
|
|
814
|
-
const inScope = (relPath) => {
|
|
815
|
-
if (walkExplicitlyCapped)
|
|
816
|
-
return false; // cap → never treat as deletable
|
|
817
|
-
if (!includePaths || includePaths.length === 0)
|
|
818
|
-
return false;
|
|
819
|
-
// Mirror walkDirectory's include-path filter exactly.
|
|
820
|
-
return includePaths.some((p) => relPath.startsWith(p));
|
|
821
|
-
};
|
|
822
|
-
const preservedEntries = [];
|
|
823
|
-
const preservedFilePaths = new Set();
|
|
824
|
-
for (const fe of existing.files) {
|
|
825
|
-
if (walkedPaths.has(fe.path))
|
|
826
|
-
continue; // walk result wins for these
|
|
827
|
-
if (inScope(fe.path))
|
|
828
|
-
continue; // in-scope + not walked = deleted-in-scope
|
|
829
|
-
preservedEntries.push(fe);
|
|
830
|
-
preservedFilePaths.add(fe.path);
|
|
831
|
-
}
|
|
832
|
-
const preservedSymbols = existing.symbols.filter((s) => preservedFilePaths.has(s.file));
|
|
833
|
-
mergedEntries = [...preservedEntries, ...fileEntries];
|
|
834
|
-
mergedSymbols = [...preservedSymbols, ...symbols];
|
|
835
|
-
// Snapshot: preserve out-of-scope shas, overlay walked ones.
|
|
836
|
-
mergedSnapshotFiles = {};
|
|
837
|
-
if (oldSnapshot) {
|
|
838
|
-
for (const relPath of preservedFilePaths) {
|
|
839
|
-
const sha = oldSnapshot.files[relPath];
|
|
840
|
-
if (sha !== undefined)
|
|
841
|
-
mergedSnapshotFiles[relPath] = sha;
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
Object.assign(mergedSnapshotFiles, newSnapshotFiles);
|
|
845
|
-
}
|
|
846
|
-
// Build and cache BM25 index from the FINAL (possibly merged) symbol set.
|
|
847
|
-
// Built here (not before the guard) so a rejected_partial early-return leaves
|
|
848
|
-
// the previous in-memory BM25 index intact rather than swapping in a partial.
|
|
849
|
-
const bm25 = buildBM25Index(mergedSymbols);
|
|
850
|
-
bm25Indexes.set(repoName, bm25);
|
|
851
|
-
// Resolve workspaces (Task 7) — runs before persistence so collectImportEdges
|
|
852
|
-
// and other downstream consumers see the populated `workspaces` field.
|
|
853
|
-
// Gated behind CODESIFT_DISABLE_MONOREPO=1 kill switch (spec D-FB).
|
|
854
|
-
let workspaces;
|
|
855
|
-
if (process.env.CODESIFT_DISABLE_MONOREPO !== "1") {
|
|
856
|
-
try {
|
|
857
|
-
const { resolveWorkspaces } = await import("../storage/workspace-resolver.js");
|
|
858
|
-
const resolved = await resolveWorkspaces(rootPath);
|
|
859
|
-
if (resolved)
|
|
860
|
-
workspaces = resolved.workspaces;
|
|
861
|
-
}
|
|
862
|
-
catch {
|
|
863
|
-
// Resolver should never throw, but guard belt-and-braces — flat-repo
|
|
864
|
-
// mode is the safe fallback.
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
// Build and save code index from the FINAL (possibly merged) sets.
|
|
868
|
-
const codeIndex = {
|
|
869
|
-
repo: repoName,
|
|
870
|
-
root: rootPath,
|
|
871
|
-
symbols: mergedSymbols,
|
|
872
|
-
files: mergedEntries,
|
|
873
|
-
created_at: Date.now(),
|
|
874
|
-
updated_at: Date.now(),
|
|
875
|
-
symbol_count: mergedSymbols.length,
|
|
876
|
-
file_count: mergedEntries.length,
|
|
877
|
-
extractor_version: { ...EXTRACTOR_VERSIONS },
|
|
878
|
-
...(workspaces ? { workspaces } : {}),
|
|
879
|
-
};
|
|
880
|
-
await saveIndex(indexPath, codeIndex);
|
|
881
|
-
// Persist the hash snapshot AFTER the index lands (mirrors registerRepo
|
|
882
|
-
// ordering) and only on the success path — the rejected_partial branch
|
|
883
|
-
// returned earlier, leaving the previous snapshot intact. Non-fatal: the
|
|
884
|
-
// snapshot is a reuse-optimization cache; a write failure just costs a full
|
|
885
|
-
// re-parse next run, so we warn and continue.
|
|
886
|
-
try {
|
|
887
|
-
const newSnapshot = {
|
|
888
|
-
version: HASH_SNAPSHOT_VERSION,
|
|
889
|
-
repo: repoName,
|
|
890
|
-
// CRITICAL-2 (created_at race): use the EXACT timestamp serialized into
|
|
891
|
-
// the index, not a fresh Date.now(). A watcher's saveIncremental that
|
|
892
|
-
// lands between saveIndex and this write would otherwise leave the
|
|
893
|
-
// snapshot OLDER than created_at, blinding the staleness guard above. By
|
|
894
|
-
// anchoring to codeIndex.updated_at, snapshot.created_at === the index's
|
|
895
|
-
// updated_at on a fresh write, so any later incremental strictly advances
|
|
896
|
-
// index.updated_at past it and the guard fires correctly.
|
|
897
|
-
created_at: codeIndex.updated_at,
|
|
898
|
-
files: mergedSnapshotFiles,
|
|
899
|
-
};
|
|
900
|
-
await saveHashSnapshot(snapshotPath, newSnapshot);
|
|
901
|
-
}
|
|
902
|
-
catch (err) {
|
|
903
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
904
|
-
console.warn(`[codesift] hash-snapshot save failed for ${repoName} (non-fatal): ${msg}`);
|
|
905
|
-
}
|
|
906
|
-
// Embed symbols and chunks in background (non-fatal, don't block MCP response)
|
|
907
|
-
// Large repos (71K symbols) can take minutes — fire-and-forget to prevent timeout
|
|
908
|
-
embedSymbols(mergedSymbols, indexPath, repoName, config)
|
|
909
|
-
.then(() => embedChunks(mergedEntries, rootPath, repoName, indexPath, config, mergedSymbols))
|
|
910
|
-
.catch((err) => {
|
|
911
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
912
|
-
console.error(`[codesift] Background embedding failed for ${repoName}: ${msg}`);
|
|
913
|
-
});
|
|
914
|
-
// Register in the global registry. If a stale entry exists with the same
|
|
915
|
-
// root but a different name (e.g. `local/workspace` from before the git
|
|
916
|
-
// origin auto-detect landed), drop it so `list_repos` doesn't show ghosts.
|
|
917
|
-
const existingRepos = await listRegistryRepos(config.registryPath);
|
|
918
|
-
for (const stale of existingRepos) {
|
|
919
|
-
if (stale.root === rootPath && stale.name !== repoName) {
|
|
920
|
-
await removeRepo(config.registryPath, stale.name);
|
|
921
|
-
console.error(`[codesift] Migrated registry: ${stale.name} -> ${repoName} (same root)`);
|
|
922
|
-
}
|
|
923
|
-
}
|
|
924
|
-
const meta = {
|
|
925
|
-
name: repoName,
|
|
926
|
-
root: rootPath,
|
|
927
|
-
index_path: indexPath,
|
|
928
|
-
symbol_count: mergedSymbols.length,
|
|
929
|
-
file_count: mergedEntries.length,
|
|
930
|
-
updated_at: Date.now(),
|
|
931
|
-
};
|
|
932
|
-
await registerRepo(config.registryPath, meta);
|
|
933
|
-
// Capture git HEAD for auto-refresh tracking
|
|
934
|
-
try {
|
|
935
|
-
const head = execFileSync("git", ["rev-parse", "HEAD"], {
|
|
936
|
-
cwd: rootPath, encoding: "utf-8", timeout: 5000,
|
|
937
|
-
}).trim();
|
|
938
|
-
await updateRepoMeta(config.registryPath, repoName, { last_git_commit: head });
|
|
939
|
-
}
|
|
940
|
-
catch {
|
|
941
|
-
// Not a git repo — skip
|
|
942
|
-
}
|
|
943
|
-
// Start file watcher for incremental updates (unless disabled)
|
|
944
|
-
if (options?.watch !== false) {
|
|
945
|
-
await setupWatcher(rootPath, repoName, indexPath);
|
|
946
|
-
}
|
|
947
|
-
// Auto-enable framework-specific tool bundles (NestJS, etc.)
|
|
948
|
-
// Lazy import to avoid circular dep: index-tools → register-tools → tool handlers → index-tools
|
|
949
|
-
try {
|
|
950
|
-
const { detectFrameworks } = await import("../utils/framework-detect.js");
|
|
951
|
-
const { enableFrameworkToolBundle } = await import("../register-tools.js");
|
|
952
|
-
const tempIndex = { root: rootPath, files: mergedEntries, symbols: mergedSymbols };
|
|
953
|
-
const frameworks = detectFrameworks(tempIndex);
|
|
954
|
-
for (const fw of frameworks) {
|
|
955
|
-
const enabled = enableFrameworkToolBundle(fw);
|
|
956
|
-
if (enabled.length > 0) {
|
|
957
|
-
console.error(`[codesift] auto-enabled ${enabled.length} ${fw} tools for ${repoName}: ${enabled.join(", ")}`);
|
|
958
|
-
}
|
|
959
|
-
}
|
|
960
|
-
}
|
|
961
|
-
catch {
|
|
962
|
-
// Non-fatal — framework auto-enable is a convenience feature
|
|
963
|
-
}
|
|
964
|
-
// Record completion timestamp so subsequent re-runs can short-circuit when
|
|
965
|
-
// the watcher is keeping the index fresh.
|
|
966
|
-
lastFullIndexAt.set(rootPath, Date.now());
|
|
967
|
-
return {
|
|
968
|
-
repo: repoName,
|
|
969
|
-
root: rootPath,
|
|
970
|
-
file_count: mergedEntries.length,
|
|
971
|
-
symbol_count: mergedSymbols.length,
|
|
972
|
-
duration_ms: Date.now() - startTime,
|
|
973
|
-
};
|
|
974
|
-
}
|
|
975
|
-
/**
|
|
976
|
-
* Clone a remote git repository and index it.
|
|
977
|
-
* Clones into ~/.codesift/repos/{name}. If already cloned, pulls latest.
|
|
978
|
-
*/
|
|
16
|
+
export { indexFolder, resetIndexFolderRedundancyForTesting, indexFile, clearLastIndexedStateForTesting, ensureIndexFresh, resetFreshnessCache, listAllRepos, invalidateCache, getBM25Index, getCodeIndex, getEmbeddingCache, autoIndexCurrentRepo, _cachedEmbeddingReposForTesting, _embeddingLoadCountForTesting, _resetEmbeddingLoadCountForTesting, embedSymbols, drainLegacyHashQueue, ASTRO_LOCK_FILENAME, EXTRACTOR_VERSIONS_FILENAME, checkAstroExtractorVersion, };
|
|
979
17
|
export async function indexRepo(url, options) {
|
|
980
18
|
validateGitUrl(url);
|
|
981
19
|
if (options?.branch) {
|
|
@@ -1051,383 +89,6 @@ export async function indexRepo(url, options) {
|
|
|
1051
89
|
* Override via CODESIFT_MAX_WATCHERS env var. Set to 0 to disable watchers
|
|
1052
90
|
* entirely (suitable for CI / batch index runs).
|
|
1053
91
|
*/
|
|
1054
|
-
const DEFAULT_MAX_WATCHERS = 8;
|
|
1055
|
-
function getMaxWatchers() {
|
|
1056
|
-
const env = process.env.CODESIFT_MAX_WATCHERS;
|
|
1057
|
-
if (env !== undefined) {
|
|
1058
|
-
const n = Number.parseInt(env, 10);
|
|
1059
|
-
if (Number.isFinite(n) && n >= 0)
|
|
1060
|
-
return n;
|
|
1061
|
-
}
|
|
1062
|
-
return DEFAULT_MAX_WATCHERS;
|
|
1063
|
-
}
|
|
1064
|
-
// Insertion-ordered Map gives us free LRU semantics: re-inserting a key
|
|
1065
|
-
// moves it to the end, so the first key is the least-recently set up.
|
|
1066
|
-
const watcherInsertionOrder = new Set();
|
|
1067
|
-
/**
|
|
1068
|
-
* Replace or create a file watcher for incremental index updates.
|
|
1069
|
-
*
|
|
1070
|
-
* Enforces a max-active-watchers cap with LRU eviction so that bulk indexing
|
|
1071
|
-
* of many repos in one session cannot exhaust the system file table. When the
|
|
1072
|
-
* cap is exceeded the oldest watcher is closed; that repo's index becomes
|
|
1073
|
-
* stale-on-disk until the next explicit `index_folder` or `index_file`.
|
|
1074
|
-
*/
|
|
1075
|
-
async function setupWatcher(rootPath, repoName, indexPath) {
|
|
1076
|
-
const existingWatcher = activeWatchers.get(repoName);
|
|
1077
|
-
if (existingWatcher) {
|
|
1078
|
-
await stopWatcher(existingWatcher);
|
|
1079
|
-
watcherInsertionOrder.delete(repoName);
|
|
1080
|
-
}
|
|
1081
|
-
// Evict oldest watchers until we fit under the cap (with room for the new one)
|
|
1082
|
-
const maxWatchers = getMaxWatchers();
|
|
1083
|
-
if (maxWatchers === 0) {
|
|
1084
|
-
// Caller of indexFolder can also opt out via watch:false; this env is the
|
|
1085
|
-
// global kill-switch (e.g. for CI bulk-index jobs).
|
|
1086
|
-
return;
|
|
1087
|
-
}
|
|
1088
|
-
while (watcherInsertionOrder.size >= maxWatchers) {
|
|
1089
|
-
const oldest = watcherInsertionOrder.values().next().value;
|
|
1090
|
-
if (oldest === undefined)
|
|
1091
|
-
break;
|
|
1092
|
-
const oldWatcher = activeWatchers.get(oldest);
|
|
1093
|
-
if (oldWatcher) {
|
|
1094
|
-
await stopWatcher(oldWatcher).catch(() => { });
|
|
1095
|
-
activeWatchers.delete(oldest);
|
|
1096
|
-
}
|
|
1097
|
-
watcherInsertionOrder.delete(oldest);
|
|
1098
|
-
console.error(`[codesift] watcher cap reached (${maxWatchers}); evicted oldest watcher: ${oldest}`);
|
|
1099
|
-
}
|
|
1100
|
-
const watcher = await startWatcher(rootPath, (changedFile) => {
|
|
1101
|
-
handleFileChange(rootPath, repoName, indexPath, changedFile).catch((err) => {
|
|
1102
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1103
|
-
console.error(`[codesift] Watcher error for ${changedFile}: ${message}`);
|
|
1104
|
-
});
|
|
1105
|
-
}, (deletedFile) => {
|
|
1106
|
-
handleFileDelete(rootPath, repoName, indexPath, deletedFile).catch((err) => {
|
|
1107
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1108
|
-
console.error(`[codesift] Watcher delete error for ${deletedFile}: ${message}`);
|
|
1109
|
-
});
|
|
1110
|
-
});
|
|
1111
|
-
activeWatchers.set(repoName, watcher);
|
|
1112
|
-
watcherInsertionOrder.add(repoName);
|
|
1113
|
-
}
|
|
1114
|
-
/**
|
|
1115
|
-
* Handle a file change event from the watcher.
|
|
1116
|
-
* Re-parses the changed file and updates the index incrementally.
|
|
1117
|
-
*/
|
|
1118
|
-
async function handleFileChange(repoRoot, repoName, indexPath, relativeFile) {
|
|
1119
|
-
const fullPath = join(repoRoot, relativeFile);
|
|
1120
|
-
// Invalidate cached findings so the next scan sees the updated file contents.
|
|
1121
|
-
scanOnChanged(repoName, relativeFile);
|
|
1122
|
-
// Invalidate negative evidence for this file's subtree
|
|
1123
|
-
try {
|
|
1124
|
-
const { invalidateNegativeEvidence } = await import("../storage/session-state.js");
|
|
1125
|
-
invalidateNegativeEvidence(repoName, relativeFile);
|
|
1126
|
-
}
|
|
1127
|
-
catch {
|
|
1128
|
-
// Best-effort — session-state may not be loaded
|
|
1129
|
-
}
|
|
1130
|
-
// Invalidate Hono model cache for this file (canonicalized absolute path)
|
|
1131
|
-
try {
|
|
1132
|
-
const { honoCache } = await import("../cache/hono-cache.js");
|
|
1133
|
-
honoCache.invalidate(fullPath);
|
|
1134
|
-
}
|
|
1135
|
-
catch {
|
|
1136
|
-
// Best-effort — hono-cache may not be loaded
|
|
1137
|
-
}
|
|
1138
|
-
const result = await parseOneFile(fullPath, repoRoot, repoName);
|
|
1139
|
-
if (!result)
|
|
1140
|
-
return;
|
|
1141
|
-
await saveIncremental(indexPath, relativeFile, result.symbols, result.entry);
|
|
1142
|
-
if (loadConfig().secretScanEnabled) {
|
|
1143
|
-
try {
|
|
1144
|
-
await scanFileForSecrets(fullPath, relativeFile, repoName, result.symbols);
|
|
1145
|
-
}
|
|
1146
|
-
catch (err) {
|
|
1147
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1148
|
-
console.warn(`[codesift] Secret scan failed for ${relativeFile}: ${message}`);
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
// Invalidate caches — lazy rebuild on next query via getBM25Index()
|
|
1152
|
-
bm25Indexes.delete(repoName);
|
|
1153
|
-
codeIndexes.delete(repoName);
|
|
1154
|
-
embeddingCaches.delete(repoName);
|
|
1155
|
-
}
|
|
1156
|
-
/**
|
|
1157
|
-
* Handle a file deletion event from the watcher.
|
|
1158
|
-
* Removes all symbols for the deleted file from the index.
|
|
1159
|
-
*/
|
|
1160
|
-
async function handleFileDelete(repoRoot, repoName, indexPath, relativeFile) {
|
|
1161
|
-
await removeFileFromIndex(indexPath, relativeFile);
|
|
1162
|
-
// Invalidate caches — lazy rebuild on next query via getBM25Index()
|
|
1163
|
-
bm25Indexes.delete(repoName);
|
|
1164
|
-
codeIndexes.delete(repoName);
|
|
1165
|
-
embeddingCaches.delete(repoName);
|
|
1166
|
-
scanOnDeleted(repoName, relativeFile);
|
|
1167
|
-
// Invalidate Hono model cache
|
|
1168
|
-
try {
|
|
1169
|
-
const { honoCache } = await import("../cache/hono-cache.js");
|
|
1170
|
-
honoCache.invalidate(join(repoRoot, relativeFile));
|
|
1171
|
-
}
|
|
1172
|
-
catch {
|
|
1173
|
-
// Best-effort
|
|
1174
|
-
}
|
|
1175
|
-
}
|
|
1176
|
-
export async function listAllRepos(options) {
|
|
1177
|
-
const config = loadConfig();
|
|
1178
|
-
let repos = await listRegistryRepos(config.registryPath);
|
|
1179
|
-
// Filter by name substring (case-insensitive)
|
|
1180
|
-
if (options?.name_contains) {
|
|
1181
|
-
const filter = options.name_contains.toLowerCase();
|
|
1182
|
-
repos = repos.filter((r) => r.name.toLowerCase().includes(filter));
|
|
1183
|
-
}
|
|
1184
|
-
if (options?.compact === false)
|
|
1185
|
-
return repos;
|
|
1186
|
-
// Default: ultra-compact — just repo names (agents only need the identifier)
|
|
1187
|
-
return repos.map((r) => r.name);
|
|
1188
|
-
}
|
|
1189
|
-
export async function invalidateCache(repoName) {
|
|
1190
|
-
const config = loadConfig();
|
|
1191
|
-
const meta = await getRepo(config.registryPath, repoName);
|
|
1192
|
-
if (!meta)
|
|
1193
|
-
return false;
|
|
1194
|
-
// Stop watcher
|
|
1195
|
-
const watcher = activeWatchers.get(repoName);
|
|
1196
|
-
if (watcher) {
|
|
1197
|
-
await stopWatcher(watcher);
|
|
1198
|
-
activeWatchers.delete(repoName);
|
|
1199
|
-
}
|
|
1200
|
-
// Remove in-memory caches
|
|
1201
|
-
bm25Indexes.delete(repoName);
|
|
1202
|
-
codeIndexes.delete(repoName);
|
|
1203
|
-
embeddingCaches.delete(repoName);
|
|
1204
|
-
// Delete index file + embedding files + chunk files
|
|
1205
|
-
const embeddingPath = getEmbeddingPath(meta.index_path);
|
|
1206
|
-
const embeddingMetaPath = getEmbeddingMetaPath(meta.index_path);
|
|
1207
|
-
const chunkPath = getChunkPath(meta.index_path);
|
|
1208
|
-
const chunkEmbeddingPath = getChunkEmbeddingPath(meta.index_path);
|
|
1209
|
-
const graphStorePath = getGraphPath(meta.index_path);
|
|
1210
|
-
const snapshotPath = getSnapshotPath(meta.index_path);
|
|
1211
|
-
for (const fp of [meta.index_path, embeddingPath, embeddingMetaPath, chunkPath, chunkEmbeddingPath, graphStorePath, snapshotPath]) {
|
|
1212
|
-
try {
|
|
1213
|
-
await unlink(fp);
|
|
1214
|
-
}
|
|
1215
|
-
catch { /* File may not exist */ }
|
|
1216
|
-
}
|
|
1217
|
-
// Remove from registry
|
|
1218
|
-
await removeRepo(config.registryPath, repoName);
|
|
1219
|
-
return true;
|
|
1220
|
-
}
|
|
1221
|
-
/**
|
|
1222
|
-
* In-process record of the last indexed state per absolute file path.
|
|
1223
|
-
*
|
|
1224
|
-
* Telemetry (30d, 2026-06): 750 consecutive duplicate index_file calls at
|
|
1225
|
-
* avg 3.7s each (~47 min of agent wall-clock). Two causes: (1) duplicate
|
|
1226
|
-
* hook registrations firing index_file twice per edit, and (2) a race where
|
|
1227
|
-
* call N+1's on-disk mtime pre-check read the index before call N's
|
|
1228
|
-
* serialized saveIncremental landed, forcing a full re-parse + full-index
|
|
1229
|
-
* save. This map short-circuits both in-process in ~1ms (mtime first, then
|
|
1230
|
-
* content hash for touch/no-op rewrites) without loading the on-disk index.
|
|
1231
|
-
*/
|
|
1232
|
-
const lastIndexedState = new Map();
|
|
1233
|
-
/** Test hook — clear the in-process last-indexed state. */
|
|
1234
|
-
export function clearLastIndexedStateForTesting() {
|
|
1235
|
-
lastIndexedState.clear();
|
|
1236
|
-
}
|
|
1237
|
-
/**
|
|
1238
|
-
* Re-index a single file instantly. Finds the repo by matching the file
|
|
1239
|
-
* path against indexed repo roots. Updates symbols, BM25 index, and
|
|
1240
|
-
* invalidates embedding cache — no full repo walk needed.
|
|
1241
|
-
*/
|
|
1242
|
-
export async function indexFile(filePath) {
|
|
1243
|
-
const absPath = resolve(filePath);
|
|
1244
|
-
const config = loadConfig();
|
|
1245
|
-
const repos = await listRegistryRepos(config.registryPath);
|
|
1246
|
-
// Find the most specific repo root that contains this file
|
|
1247
|
-
const matchingRepo = repos
|
|
1248
|
-
.filter((r) => absPath.startsWith(r.root + "/") || absPath === r.root)
|
|
1249
|
-
.sort((a, b) => b.root.length - a.root.length)[0];
|
|
1250
|
-
if (!matchingRepo) {
|
|
1251
|
-
throw new Error(`No indexed repo contains "${absPath}". Run index_folder first.`);
|
|
1252
|
-
}
|
|
1253
|
-
const startTime = Date.now();
|
|
1254
|
-
const relPath = relative(matchingRepo.root, absPath);
|
|
1255
|
-
// If the changed file is a TS/JS config that drives path resolution, drop
|
|
1256
|
-
// caches so incremental indexing picks up new `paths` / `extends`.
|
|
1257
|
-
{
|
|
1258
|
-
const cfg = basename(absPath).toLowerCase();
|
|
1259
|
-
if ((cfg.startsWith("tsconfig") || cfg.startsWith("jsconfig")) &&
|
|
1260
|
-
cfg.endsWith(".json")) {
|
|
1261
|
-
clearTsconfigCache();
|
|
1262
|
-
}
|
|
1263
|
-
}
|
|
1264
|
-
// In-process short-circuit: mtime, then content hash. Both avoid loading
|
|
1265
|
-
// the on-disk index entirely (the expensive part on large repos).
|
|
1266
|
-
const st = await stat(absPath);
|
|
1267
|
-
const mem = lastIndexedState.get(absPath);
|
|
1268
|
-
if (mem && Math.round(st.mtimeMs) === mem.mtimeMs) {
|
|
1269
|
-
return {
|
|
1270
|
-
repo: matchingRepo.name,
|
|
1271
|
-
file: relPath,
|
|
1272
|
-
symbol_count: mem.symbolCount,
|
|
1273
|
-
duration_ms: Date.now() - startTime,
|
|
1274
|
-
skipped: true,
|
|
1275
|
-
};
|
|
1276
|
-
}
|
|
1277
|
-
const content = await readFile(absPath, "utf-8").catch(() => null);
|
|
1278
|
-
const contentHash = content !== null ? createHash("sha1").update(content).digest("hex") : null;
|
|
1279
|
-
if (mem && contentHash !== null && contentHash === mem.contentHash) {
|
|
1280
|
-
// Touched / rewritten with identical content — refresh mtime, skip work.
|
|
1281
|
-
mem.mtimeMs = Math.round(st.mtimeMs);
|
|
1282
|
-
return {
|
|
1283
|
-
repo: matchingRepo.name,
|
|
1284
|
-
file: relPath,
|
|
1285
|
-
symbol_count: mem.symbolCount,
|
|
1286
|
-
duration_ms: Date.now() - startTime,
|
|
1287
|
-
skipped: true,
|
|
1288
|
-
};
|
|
1289
|
-
}
|
|
1290
|
-
// On-disk mtime check — first touch of this file in this process (CLI
|
|
1291
|
-
// hook invocations, fresh server). Skips files unchanged since the last
|
|
1292
|
-
// full index, and seeds the in-process state for subsequent calls.
|
|
1293
|
-
if (!mem) {
|
|
1294
|
-
const existing = await loadIndex(matchingRepo.index_path);
|
|
1295
|
-
if (existing) {
|
|
1296
|
-
const prevEntry = existing.files.find((f) => f.path === relPath);
|
|
1297
|
-
if (prevEntry?.mtime_ms && Math.round(st.mtimeMs) === prevEntry.mtime_ms) {
|
|
1298
|
-
if (contentHash !== null) {
|
|
1299
|
-
lastIndexedState.set(absPath, {
|
|
1300
|
-
mtimeMs: Math.round(st.mtimeMs),
|
|
1301
|
-
contentHash,
|
|
1302
|
-
symbolCount: prevEntry.symbol_count,
|
|
1303
|
-
});
|
|
1304
|
-
}
|
|
1305
|
-
return {
|
|
1306
|
-
repo: matchingRepo.name,
|
|
1307
|
-
file: relPath,
|
|
1308
|
-
symbol_count: prevEntry.symbol_count,
|
|
1309
|
-
duration_ms: Date.now() - startTime,
|
|
1310
|
-
skipped: true,
|
|
1311
|
-
};
|
|
1312
|
-
}
|
|
1313
|
-
}
|
|
1314
|
-
}
|
|
1315
|
-
const result = await parseOneFile(absPath, matchingRepo.root, matchingRepo.name);
|
|
1316
|
-
if (!result) {
|
|
1317
|
-
throw new Error(`Failed to parse "${relPath}"`);
|
|
1318
|
-
}
|
|
1319
|
-
await saveIncremental(matchingRepo.index_path, relPath, result.symbols, result.entry);
|
|
1320
|
-
if (contentHash !== null) {
|
|
1321
|
-
lastIndexedState.set(absPath, {
|
|
1322
|
-
mtimeMs: Math.round(st.mtimeMs),
|
|
1323
|
-
contentHash,
|
|
1324
|
-
symbolCount: result.symbols.length,
|
|
1325
|
-
});
|
|
1326
|
-
}
|
|
1327
|
-
let secretFindingsCount = 0;
|
|
1328
|
-
if (config.secretScanEnabled) {
|
|
1329
|
-
try {
|
|
1330
|
-
secretFindingsCount = (await scanFileForSecrets(absPath, relPath, matchingRepo.name, result.symbols)).length;
|
|
1331
|
-
}
|
|
1332
|
-
catch (err) {
|
|
1333
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1334
|
-
console.warn(`[codesift] Secret scan failed for ${relPath}: ${message}`);
|
|
1335
|
-
}
|
|
1336
|
-
}
|
|
1337
|
-
// Invalidate caches — lazy rebuild on next query via getBM25Index()
|
|
1338
|
-
bm25Indexes.delete(matchingRepo.name);
|
|
1339
|
-
codeIndexes.delete(matchingRepo.name);
|
|
1340
|
-
embeddingCaches.delete(matchingRepo.name);
|
|
1341
|
-
let secretsWarning;
|
|
1342
|
-
if (secretFindingsCount > 0) {
|
|
1343
|
-
secretsWarning = `\u26A0 ${secretFindingsCount} potential secret(s) detected`;
|
|
1344
|
-
}
|
|
1345
|
-
return {
|
|
1346
|
-
repo: matchingRepo.name,
|
|
1347
|
-
file: relPath,
|
|
1348
|
-
symbol_count: result.symbols.length,
|
|
1349
|
-
duration_ms: Date.now() - startTime,
|
|
1350
|
-
...(secretsWarning ? { secrets_warning: secretsWarning } : {}),
|
|
1351
|
-
};
|
|
1352
|
-
}
|
|
1353
|
-
// ---------------------------------------------------------------------------
|
|
1354
|
-
// Git-based auto-refresh — transparent freshness check before index access
|
|
1355
|
-
// ---------------------------------------------------------------------------
|
|
1356
|
-
const freshnessChecked = new Map();
|
|
1357
|
-
const FRESHNESS_INTERVAL_MS = 60_000;
|
|
1358
|
-
const MAX_DIFF_FILES = 50;
|
|
1359
|
-
/**
|
|
1360
|
-
* Ensure the index for a repo is fresh relative to git HEAD.
|
|
1361
|
-
* Throttled to once per minute per repo. Reindexes changed files if HEAD moved.
|
|
1362
|
-
* No-op for non-git repos.
|
|
1363
|
-
*/
|
|
1364
|
-
export async function ensureIndexFresh(repoName) {
|
|
1365
|
-
const lastCheck = freshnessChecked.get(repoName);
|
|
1366
|
-
if (lastCheck && Date.now() - lastCheck < FRESHNESS_INTERVAL_MS) {
|
|
1367
|
-
return { status: "fresh" };
|
|
1368
|
-
}
|
|
1369
|
-
const config = loadConfig();
|
|
1370
|
-
const meta = await getRepo(config.registryPath, repoName);
|
|
1371
|
-
if (!meta)
|
|
1372
|
-
return { status: "skipped" };
|
|
1373
|
-
let currentCommit;
|
|
1374
|
-
try {
|
|
1375
|
-
currentCommit = execFileSync("git", ["rev-parse", "HEAD"], {
|
|
1376
|
-
cwd: meta.root, encoding: "utf-8", timeout: 5000,
|
|
1377
|
-
}).trim();
|
|
1378
|
-
}
|
|
1379
|
-
catch {
|
|
1380
|
-
freshnessChecked.set(repoName, Date.now());
|
|
1381
|
-
return { status: "skipped" };
|
|
1382
|
-
}
|
|
1383
|
-
if (meta.last_git_commit === currentCommit) {
|
|
1384
|
-
freshnessChecked.set(repoName, Date.now());
|
|
1385
|
-
return { status: "fresh" };
|
|
1386
|
-
}
|
|
1387
|
-
// HEAD moved — find changed files
|
|
1388
|
-
let changedFiles = [];
|
|
1389
|
-
if (meta.last_git_commit) {
|
|
1390
|
-
try {
|
|
1391
|
-
const diff = execFileSync("git", [
|
|
1392
|
-
"diff", "--name-only", "--diff-filter=ACMR",
|
|
1393
|
-
`${meta.last_git_commit}..${currentCommit}`,
|
|
1394
|
-
], {
|
|
1395
|
-
cwd: meta.root, encoding: "utf-8", timeout: 10_000,
|
|
1396
|
-
});
|
|
1397
|
-
changedFiles = diff.trim().split("\n").filter(Boolean);
|
|
1398
|
-
}
|
|
1399
|
-
catch {
|
|
1400
|
-
// Stored commit gone (rebase/squash) — will do full incremental
|
|
1401
|
-
changedFiles = [];
|
|
1402
|
-
}
|
|
1403
|
-
}
|
|
1404
|
-
if (changedFiles.length > 0 && changedFiles.length <= MAX_DIFF_FILES) {
|
|
1405
|
-
for (const file of changedFiles) {
|
|
1406
|
-
try {
|
|
1407
|
-
await indexFile(join(meta.root, file));
|
|
1408
|
-
}
|
|
1409
|
-
catch {
|
|
1410
|
-
// File deleted or unparseable — skip
|
|
1411
|
-
}
|
|
1412
|
-
}
|
|
1413
|
-
}
|
|
1414
|
-
else if (changedFiles.length > MAX_DIFF_FILES || !meta.last_git_commit) {
|
|
1415
|
-
await indexFolder(meta.root, { incremental: true, watch: false });
|
|
1416
|
-
}
|
|
1417
|
-
await updateRepoMeta(config.registryPath, repoName, {
|
|
1418
|
-
last_git_commit: currentCommit,
|
|
1419
|
-
updated_at: Date.now(),
|
|
1420
|
-
});
|
|
1421
|
-
bm25Indexes.delete(repoName);
|
|
1422
|
-
codeIndexes.delete(repoName);
|
|
1423
|
-
embeddingCaches.delete(repoName);
|
|
1424
|
-
freshnessChecked.set(repoName, Date.now());
|
|
1425
|
-
return { status: "refreshed", files_updated: changedFiles.length };
|
|
1426
|
-
}
|
|
1427
|
-
/** Reset freshness throttle cache. Exported for testing. */
|
|
1428
|
-
export function resetFreshnessCache() {
|
|
1429
|
-
freshnessChecked.clear();
|
|
1430
|
-
}
|
|
1431
92
|
/** Stop every active watcher and clear the in-memory index caches. Exported
|
|
1432
93
|
* for testing — afterAll teardown of integration tests that index temp dirs
|
|
1433
94
|
* must call this before `rm -rf`-ing the temp dir, otherwise the chokidar
|
|
@@ -1440,329 +101,6 @@ export async function stopAllWatchersForTesting() {
|
|
|
1440
101
|
bm25Indexes.clear();
|
|
1441
102
|
codeIndexes.clear();
|
|
1442
103
|
embeddingCaches.clear();
|
|
1443
|
-
|
|
1444
|
-
}
|
|
1445
|
-
// ---------------------------------------------------------------------------
|
|
1446
|
-
// Index access — with auto-refresh
|
|
1447
|
-
// ---------------------------------------------------------------------------
|
|
1448
|
-
/**
|
|
1449
|
-
* Get the in-memory BM25 index for a repo.
|
|
1450
|
-
* Loads from disk if not cached. Auto-refreshes if git HEAD moved.
|
|
1451
|
-
*/
|
|
1452
|
-
export async function getBM25Index(repoName) {
|
|
1453
|
-
await ensureIndexFresh(repoName);
|
|
1454
|
-
const cached = bm25Indexes.get(repoName);
|
|
1455
|
-
if (cached)
|
|
1456
|
-
return cached;
|
|
1457
|
-
const config = loadConfig();
|
|
1458
|
-
const meta = await getRepo(config.registryPath, repoName);
|
|
1459
|
-
if (!meta)
|
|
1460
|
-
return null;
|
|
1461
|
-
const index = await loadIndex(meta.index_path);
|
|
1462
|
-
if (!index)
|
|
1463
|
-
return null;
|
|
1464
|
-
const bm25 = buildBM25Index(index.symbols);
|
|
1465
|
-
bm25Indexes.set(repoName, bm25);
|
|
1466
|
-
return bm25;
|
|
1467
|
-
}
|
|
1468
|
-
/**
|
|
1469
|
-
* Get the code index for a repo from disk.
|
|
1470
|
-
* Starts watcher if not running (lazy start after server restart).
|
|
1471
|
-
*/
|
|
1472
|
-
/**
|
|
1473
|
-
* Get the code index for a repo from disk. Auto-refreshes if git HEAD moved.
|
|
1474
|
-
*/
|
|
1475
|
-
export async function getCodeIndex(repoName, options) {
|
|
1476
|
-
const config = loadConfig();
|
|
1477
|
-
const resolved = await resolveRegisteredRepoMeta(config.registryPath, repoName);
|
|
1478
|
-
if (!resolved)
|
|
1479
|
-
return null;
|
|
1480
|
-
const { resolvedName, meta } = resolved;
|
|
1481
|
-
if (!options?.skipFreshness) {
|
|
1482
|
-
await ensureIndexFresh(resolvedName);
|
|
1483
|
-
}
|
|
1484
|
-
const cached = codeIndexes.get(resolvedName);
|
|
1485
|
-
if (cached)
|
|
1486
|
-
return cached;
|
|
1487
|
-
const result = await loadIndexOrStale(meta.index_path, { ...EXTRACTOR_VERSIONS });
|
|
1488
|
-
if (!result)
|
|
1489
|
-
return null;
|
|
1490
|
-
if (result.status === "stale") {
|
|
1491
|
-
const extra = result.mismatch_detail ? ` — ${result.mismatch_detail}` : "";
|
|
1492
|
-
console.warn(`[codesift] stale index for ${resolvedName}: extractor_version_mismatch ` +
|
|
1493
|
-
`(${result.language} expected ${result.expected_version}, got ${result.actual_version})${extra}. ` +
|
|
1494
|
-
`Run index_folder to refresh.`);
|
|
1495
|
-
return null;
|
|
1496
|
-
}
|
|
1497
|
-
codeIndexes.set(resolvedName, result.index);
|
|
1498
|
-
return result.index;
|
|
1499
|
-
}
|
|
1500
|
-
/**
|
|
1501
|
-
* Walk up from dir until a .git directory is found. Returns the git root or null.
|
|
1502
|
-
*/
|
|
1503
|
-
async function findGitRoot(dir) {
|
|
1504
|
-
let current = resolve(dir);
|
|
1505
|
-
while (true) {
|
|
1506
|
-
try {
|
|
1507
|
-
await stat(join(current, ".git"));
|
|
1508
|
-
return current;
|
|
1509
|
-
}
|
|
1510
|
-
catch {
|
|
1511
|
-
const parent = dirname(current);
|
|
1512
|
-
if (parent === current)
|
|
1513
|
-
return null;
|
|
1514
|
-
current = parent;
|
|
1515
|
-
}
|
|
1516
|
-
}
|
|
1517
|
-
}
|
|
1518
|
-
/**
|
|
1519
|
-
* Called at server startup. If the CWD is inside a git repo that isn't indexed yet,
|
|
1520
|
-
* index it automatically in the background so tools work without manual setup.
|
|
1521
|
-
*/
|
|
1522
|
-
export async function autoIndexCurrentRepo(cwd) {
|
|
1523
|
-
const gitRoot = await findGitRoot(cwd);
|
|
1524
|
-
if (!gitRoot)
|
|
1525
|
-
return;
|
|
1526
|
-
const repoName = getRepoName(gitRoot);
|
|
1527
|
-
const config = loadConfig();
|
|
1528
|
-
const existing = await getRepo(config.registryPath, repoName);
|
|
1529
|
-
if (existing)
|
|
1530
|
-
return;
|
|
1531
|
-
console.error(`[codesift] Auto-indexing ${repoName} (first use)...`);
|
|
1532
|
-
await indexFolder(gitRoot);
|
|
1533
|
-
console.error(`[codesift] Auto-index complete: ${repoName}`);
|
|
1534
|
-
}
|
|
1535
|
-
/** True when embeddings are disabled entirely (low-RAM / lite mode). */
|
|
1536
|
-
function embeddingsDisabled() {
|
|
1537
|
-
const v = process.env["CODESIFT_DISABLE_LOCAL_EMBEDDINGS"];
|
|
1538
|
-
return v === "1" || v === "true";
|
|
1539
|
-
}
|
|
1540
|
-
/** Resident-embedding RAM budget in MB (default 1024). 0/invalid → default. */
|
|
1541
|
-
function embeddingMemBudgetBytes() {
|
|
1542
|
-
const raw = process.env["CODESIFT_MAX_EMBEDDING_MEM_MB"];
|
|
1543
|
-
const n = raw ? parseInt(raw, 10) : NaN;
|
|
1544
|
-
return (Number.isNaN(n) || n <= 0 ? 1024 : n) * 1024 * 1024;
|
|
1545
|
-
}
|
|
1546
|
-
function embeddingMapBytes(m) {
|
|
1547
|
-
let b = 0;
|
|
1548
|
-
for (const v of m.values())
|
|
1549
|
-
b += v.byteLength;
|
|
1550
|
-
return b;
|
|
1551
|
-
}
|
|
1552
|
-
/**
|
|
1553
|
-
* Evict least-recently-used repo embeddings while total resident bytes exceed
|
|
1554
|
-
* the budget. `embeddingCaches` insertion order is the LRU order (getter
|
|
1555
|
-
* re-inserts on hit). `pinned` (the repo being served right now) is never
|
|
1556
|
-
* evicted mid-query. Bytes are summed from the live maps (source of truth) so
|
|
1557
|
-
* the many `.delete` call sites can't drift an accounting counter.
|
|
1558
|
-
*/
|
|
1559
|
-
function evictEmbeddingCachesOverBudget(pinned) {
|
|
1560
|
-
const budget = embeddingMemBudgetBytes();
|
|
1561
|
-
const sizes = new Map();
|
|
1562
|
-
let total = 0;
|
|
1563
|
-
for (const [k, m] of embeddingCaches) {
|
|
1564
|
-
const b = embeddingMapBytes(m);
|
|
1565
|
-
sizes.set(k, b);
|
|
1566
|
-
total += b;
|
|
1567
|
-
}
|
|
1568
|
-
if (total <= budget)
|
|
1569
|
-
return;
|
|
1570
|
-
for (const k of [...embeddingCaches.keys()]) {
|
|
1571
|
-
if (total <= budget)
|
|
1572
|
-
break;
|
|
1573
|
-
if (k === pinned)
|
|
1574
|
-
continue;
|
|
1575
|
-
embeddingCaches.delete(k);
|
|
1576
|
-
total -= sizes.get(k) ?? 0;
|
|
1577
|
-
}
|
|
1578
|
-
}
|
|
1579
|
-
/** Test-only: repo names currently resident in the embedding cache (LRU order, oldest first). */
|
|
1580
|
-
export function _cachedEmbeddingReposForTesting() {
|
|
1581
|
-
return [...embeddingCaches.keys()];
|
|
1582
|
-
}
|
|
1583
|
-
/**
|
|
1584
|
-
* In-flight embedding loads, keyed by repo. Two MCP sessions (e.g. two editor
|
|
1585
|
-
* windows on one `codesift serve` daemon) that first-access the same repo
|
|
1586
|
-
* concurrently must trigger ONE disk load, not one per session — otherwise a
|
|
1587
|
-
* GB-scale load runs N times in parallel. Concurrent callers await the same
|
|
1588
|
-
* promise; the entry clears once the load settles.
|
|
1589
|
-
*/
|
|
1590
|
-
const embeddingLoadsInFlight = new Map();
|
|
1591
|
-
/** Count of actual disk loads (test-only) — proves load-once across sessions. */
|
|
1592
|
-
let embeddingLoadCount = 0;
|
|
1593
|
-
export function _embeddingLoadCountForTesting() {
|
|
1594
|
-
return embeddingLoadCount;
|
|
1595
|
-
}
|
|
1596
|
-
export function _resetEmbeddingLoadCountForTesting() {
|
|
1597
|
-
embeddingLoadCount = 0;
|
|
1598
|
-
}
|
|
1599
|
-
/**
|
|
1600
|
-
* Get the in-memory embedding cache for a repo.
|
|
1601
|
-
* Loads from disk if not cached. Returns null if no embeddings file exists,
|
|
1602
|
-
* or if embeddings are disabled (lite mode). Bounds resident RAM via LRU, and
|
|
1603
|
-
* dedupes concurrent first-access so the load runs exactly once per repo.
|
|
1604
|
-
*/
|
|
1605
|
-
export async function getEmbeddingCache(repoName) {
|
|
1606
|
-
// Lite mode: never hold embeddings in RAM (semantic falls back to BM25).
|
|
1607
|
-
if (embeddingsDisabled())
|
|
1608
|
-
return null;
|
|
1609
|
-
const cached = embeddingCaches.get(repoName);
|
|
1610
|
-
if (cached) {
|
|
1611
|
-
// LRU touch: move to most-recently-used end.
|
|
1612
|
-
embeddingCaches.delete(repoName);
|
|
1613
|
-
embeddingCaches.set(repoName, cached);
|
|
1614
|
-
return cached;
|
|
1615
|
-
}
|
|
1616
|
-
// Coalesce concurrent first-access onto one load.
|
|
1617
|
-
const inFlight = embeddingLoadsInFlight.get(repoName);
|
|
1618
|
-
if (inFlight)
|
|
1619
|
-
return inFlight;
|
|
1620
|
-
const loadPromise = (async () => {
|
|
1621
|
-
const config = loadConfig();
|
|
1622
|
-
const meta = await getRepo(config.registryPath, repoName);
|
|
1623
|
-
if (!meta)
|
|
1624
|
-
return null;
|
|
1625
|
-
const embeddingPath = getEmbeddingPath(meta.index_path);
|
|
1626
|
-
embeddingLoadCount++;
|
|
1627
|
-
const embeddings = await loadEmbeddings(embeddingPath);
|
|
1628
|
-
if (embeddings.size === 0)
|
|
1629
|
-
return null;
|
|
1630
|
-
// Pin-on-access: this repo is the one being served, so eviction never drops
|
|
1631
|
-
// it out from under the load/query that just requested it.
|
|
1632
|
-
embeddingCaches.set(repoName, embeddings);
|
|
1633
|
-
evictEmbeddingCachesOverBudget(repoName);
|
|
1634
|
-
return embeddings;
|
|
1635
|
-
})();
|
|
1636
|
-
embeddingLoadsInFlight.set(repoName, loadPromise);
|
|
1637
|
-
try {
|
|
1638
|
-
return await loadPromise;
|
|
1639
|
-
}
|
|
1640
|
-
finally {
|
|
1641
|
-
embeddingLoadsInFlight.delete(repoName);
|
|
1642
|
-
}
|
|
1643
|
-
}
|
|
1644
|
-
// ---------------------------------------------------------------------------
|
|
1645
|
-
// Astro extractor version check + lockfile re-index
|
|
1646
|
-
// ---------------------------------------------------------------------------
|
|
1647
|
-
export const ASTRO_LOCK_FILENAME = "astro-reindex.lock";
|
|
1648
|
-
export const EXTRACTOR_VERSIONS_FILENAME = "extractor-versions.json";
|
|
1649
|
-
const LOCK_STALE_MS = 60_000; // 60 seconds
|
|
1650
|
-
/**
|
|
1651
|
-
* Check if the stored astro extractor version matches the current one.
|
|
1652
|
-
* If not, re-extract all .astro files with lockfile protection.
|
|
1653
|
-
*
|
|
1654
|
-
* @param dataDir The data directory (e.g., ~/.codesift or a test tmpdir)
|
|
1655
|
-
* @param repoRoot The repo root path (for locating .astro files)
|
|
1656
|
-
* @param astroFiles Relative paths to .astro files in the repo
|
|
1657
|
-
*/
|
|
1658
|
-
export async function checkAstroExtractorVersion(dataDir, repoRoot, astroFiles) {
|
|
1659
|
-
// Read stored version snapshot
|
|
1660
|
-
const versionsPath = join(dataDir, EXTRACTOR_VERSIONS_FILENAME);
|
|
1661
|
-
let storedVersions = {};
|
|
1662
|
-
try {
|
|
1663
|
-
const raw = await readFile(versionsPath, "utf-8");
|
|
1664
|
-
storedVersions = JSON.parse(raw);
|
|
1665
|
-
}
|
|
1666
|
-
catch {
|
|
1667
|
-
// File doesn't exist or is invalid — treat as version mismatch
|
|
1668
|
-
}
|
|
1669
|
-
// Check if astro version matches
|
|
1670
|
-
if (storedVersions.astro === EXTRACTOR_VERSIONS.astro) {
|
|
1671
|
-
return { reindexed: false, reason: "astro extractor up to date" };
|
|
1672
|
-
}
|
|
1673
|
-
if (astroFiles.length === 0) {
|
|
1674
|
-
// No astro files to re-extract — just update the version snapshot
|
|
1675
|
-
await writeVersionSnapshot(dataDir, storedVersions);
|
|
1676
|
-
return { reindexed: false, reason: "no astro files to re-extract" };
|
|
1677
|
-
}
|
|
1678
|
-
// Acquire lockfile
|
|
1679
|
-
const lockPath = join(dataDir, ASTRO_LOCK_FILENAME);
|
|
1680
|
-
if (!acquireLock(lockPath)) {
|
|
1681
|
-
return { reindexed: false, reason: "astro re-index in progress (locked by another process)" };
|
|
1682
|
-
}
|
|
1683
|
-
try {
|
|
1684
|
-
// Re-extract all .astro files
|
|
1685
|
-
let count = 0;
|
|
1686
|
-
for (const relPath of astroFiles) {
|
|
1687
|
-
const absPath = join(repoRoot, relPath);
|
|
1688
|
-
try {
|
|
1689
|
-
const result = await parseOneFile(absPath, repoRoot, "");
|
|
1690
|
-
if (result) {
|
|
1691
|
-
count++;
|
|
1692
|
-
if (count % 100 === 0) {
|
|
1693
|
-
console.error(`[codesift] Astro re-index progress: ${count}/${astroFiles.length} files`);
|
|
1694
|
-
}
|
|
1695
|
-
}
|
|
1696
|
-
}
|
|
1697
|
-
catch {
|
|
1698
|
-
// File may have been deleted or is unparseable
|
|
1699
|
-
}
|
|
1700
|
-
}
|
|
1701
|
-
// Write version snapshot atomically
|
|
1702
|
-
await writeVersionSnapshot(dataDir, storedVersions);
|
|
1703
|
-
return {
|
|
1704
|
-
reindexed: true,
|
|
1705
|
-
files_reindexed: count,
|
|
1706
|
-
reason: `re-extracted ${count} astro files (version ${storedVersions.astro ?? "none"} → ${EXTRACTOR_VERSIONS.astro})`,
|
|
1707
|
-
};
|
|
1708
|
-
}
|
|
1709
|
-
finally {
|
|
1710
|
-
// Release lockfile
|
|
1711
|
-
try {
|
|
1712
|
-
unlinkSync(lockPath);
|
|
1713
|
-
}
|
|
1714
|
-
catch {
|
|
1715
|
-
// Lock may have been cleaned up already
|
|
1716
|
-
}
|
|
1717
|
-
}
|
|
1718
|
-
}
|
|
1719
|
-
/**
|
|
1720
|
-
* Acquire an exclusive lockfile. Returns true if lock was acquired.
|
|
1721
|
-
* If lockfile exists but is stale (mtime > 60s), deletes it and retries once.
|
|
1722
|
-
*/
|
|
1723
|
-
function acquireLock(lockPath) {
|
|
1724
|
-
try {
|
|
1725
|
-
const fd = openSync(lockPath, "wx");
|
|
1726
|
-
closeSync(fd);
|
|
1727
|
-
return true;
|
|
1728
|
-
}
|
|
1729
|
-
catch (err) {
|
|
1730
|
-
if (err.code !== "EEXIST") {
|
|
1731
|
-
return false;
|
|
1732
|
-
}
|
|
1733
|
-
// Lock exists — check if stale
|
|
1734
|
-
try {
|
|
1735
|
-
const lockStat = statSync(lockPath);
|
|
1736
|
-
const age = Date.now() - lockStat.mtimeMs;
|
|
1737
|
-
if (age > LOCK_STALE_MS) {
|
|
1738
|
-
// Stale lock — delete and retry once
|
|
1739
|
-
unlinkSync(lockPath);
|
|
1740
|
-
try {
|
|
1741
|
-
const fd = openSync(lockPath, "wx");
|
|
1742
|
-
closeSync(fd);
|
|
1743
|
-
return true;
|
|
1744
|
-
}
|
|
1745
|
-
catch {
|
|
1746
|
-
return false;
|
|
1747
|
-
}
|
|
1748
|
-
}
|
|
1749
|
-
}
|
|
1750
|
-
catch {
|
|
1751
|
-
// stat failed — lock was just removed by another process
|
|
1752
|
-
return false;
|
|
1753
|
-
}
|
|
1754
|
-
return false;
|
|
1755
|
-
}
|
|
1756
|
-
}
|
|
1757
|
-
/**
|
|
1758
|
-
* Atomically write the extractor versions snapshot.
|
|
1759
|
-
* Uses write-to-tmp + rename for crash safety.
|
|
1760
|
-
*/
|
|
1761
|
-
async function writeVersionSnapshot(dataDir, existingVersions) {
|
|
1762
|
-
const versionsPath = join(dataDir, EXTRACTOR_VERSIONS_FILENAME);
|
|
1763
|
-
const tmpPath = `${versionsPath}.tmp.${Date.now()}`;
|
|
1764
|
-
const snapshot = { ...existingVersions, astro: EXTRACTOR_VERSIONS.astro };
|
|
1765
|
-
writeFileSync(tmpPath, JSON.stringify(snapshot), "utf-8");
|
|
1766
|
-
renameSync(tmpPath, versionsPath);
|
|
104
|
+
resetFreshnessCache();
|
|
1767
105
|
}
|
|
1768
106
|
//# sourceMappingURL=index-tools.js.map
|