distill-mcp 0.6.0-beta
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/bin/cli.js +133 -0
- package/dist/analytics/session-tracker.d.ts +74 -0
- package/dist/analytics/session-tracker.d.ts.map +1 -0
- package/dist/analytics/session-tracker.js +123 -0
- package/dist/ast/benchmark.test.d.ts +7 -0
- package/dist/ast/benchmark.test.d.ts.map +1 -0
- package/dist/ast/benchmark.test.js +175 -0
- package/dist/ast/go/index.d.ts +9 -0
- package/dist/ast/go/index.d.ts.map +1 -0
- package/dist/ast/go/index.js +8 -0
- package/dist/ast/go/parser.d.ts +31 -0
- package/dist/ast/go/parser.d.ts.map +1 -0
- package/dist/ast/go/parser.js +428 -0
- package/dist/ast/go/parser.test.d.ts +5 -0
- package/dist/ast/go/parser.test.d.ts.map +1 -0
- package/dist/ast/go/parser.test.js +241 -0
- package/dist/ast/go/queries.d.ts +51 -0
- package/dist/ast/go/queries.d.ts.map +1 -0
- package/dist/ast/go/queries.js +114 -0
- package/dist/ast/go/utils.d.ts +66 -0
- package/dist/ast/go/utils.d.ts.map +1 -0
- package/dist/ast/go/utils.js +140 -0
- package/dist/ast/index.d.ts +39 -0
- package/dist/ast/index.d.ts.map +1 -0
- package/dist/ast/index.js +245 -0
- package/dist/ast/php/index.d.ts +9 -0
- package/dist/ast/php/index.d.ts.map +1 -0
- package/dist/ast/php/index.js +8 -0
- package/dist/ast/php/parser.d.ts +31 -0
- package/dist/ast/php/parser.d.ts.map +1 -0
- package/dist/ast/php/parser.js +388 -0
- package/dist/ast/php/parser.test.d.ts +5 -0
- package/dist/ast/php/parser.test.d.ts.map +1 -0
- package/dist/ast/php/parser.test.js +328 -0
- package/dist/ast/php/queries.d.ts +61 -0
- package/dist/ast/php/queries.d.ts.map +1 -0
- package/dist/ast/php/queries.js +117 -0
- package/dist/ast/php/utils.d.ts +83 -0
- package/dist/ast/php/utils.d.ts.map +1 -0
- package/dist/ast/php/utils.js +246 -0
- package/dist/ast/python/index.d.ts +9 -0
- package/dist/ast/python/index.d.ts.map +1 -0
- package/dist/ast/python/index.js +8 -0
- package/dist/ast/python/parser.d.ts +32 -0
- package/dist/ast/python/parser.d.ts.map +1 -0
- package/dist/ast/python/parser.js +422 -0
- package/dist/ast/python/parser.test.d.ts +5 -0
- package/dist/ast/python/parser.test.d.ts.map +1 -0
- package/dist/ast/python/parser.test.js +186 -0
- package/dist/ast/python/queries.d.ts +73 -0
- package/dist/ast/python/queries.d.ts.map +1 -0
- package/dist/ast/python/queries.js +137 -0
- package/dist/ast/python/utils.d.ts +63 -0
- package/dist/ast/python/utils.d.ts.map +1 -0
- package/dist/ast/python/utils.js +159 -0
- package/dist/ast/quick-scan.d.ts +40 -0
- package/dist/ast/quick-scan.d.ts.map +1 -0
- package/dist/ast/quick-scan.js +287 -0
- package/dist/ast/rust/index.d.ts +9 -0
- package/dist/ast/rust/index.d.ts.map +1 -0
- package/dist/ast/rust/index.js +8 -0
- package/dist/ast/rust/parser.d.ts +31 -0
- package/dist/ast/rust/parser.d.ts.map +1 -0
- package/dist/ast/rust/parser.js +416 -0
- package/dist/ast/rust/parser.test.d.ts +5 -0
- package/dist/ast/rust/parser.test.d.ts.map +1 -0
- package/dist/ast/rust/parser.test.js +329 -0
- package/dist/ast/rust/queries.d.ts +66 -0
- package/dist/ast/rust/queries.d.ts.map +1 -0
- package/dist/ast/rust/queries.js +132 -0
- package/dist/ast/rust/utils.d.ts +91 -0
- package/dist/ast/rust/utils.d.ts.map +1 -0
- package/dist/ast/rust/utils.js +254 -0
- package/dist/ast/swift/index.d.ts +10 -0
- package/dist/ast/swift/index.d.ts.map +1 -0
- package/dist/ast/swift/index.js +8 -0
- package/dist/ast/swift/parser.d.ts +31 -0
- package/dist/ast/swift/parser.d.ts.map +1 -0
- package/dist/ast/swift/parser.js +554 -0
- package/dist/ast/swift/parser.test.d.ts +5 -0
- package/dist/ast/swift/parser.test.d.ts.map +1 -0
- package/dist/ast/swift/parser.test.js +398 -0
- package/dist/ast/swift/queries.d.ts +71 -0
- package/dist/ast/swift/queries.d.ts.map +1 -0
- package/dist/ast/swift/queries.js +137 -0
- package/dist/ast/swift/utils.d.ts +94 -0
- package/dist/ast/swift/utils.d.ts.map +1 -0
- package/dist/ast/swift/utils.js +411 -0
- package/dist/ast/types.d.ts +96 -0
- package/dist/ast/types.d.ts.map +1 -0
- package/dist/ast/types.js +21 -0
- package/dist/ast/typescript.d.ts +24 -0
- package/dist/ast/typescript.d.ts.map +1 -0
- package/dist/ast/typescript.js +357 -0
- package/dist/cache/file-hash.d.ts +33 -0
- package/dist/cache/file-hash.d.ts.map +1 -0
- package/dist/cache/file-hash.js +59 -0
- package/dist/cache/index.d.ts +9 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +8 -0
- package/dist/cache/smart-cache.d.ts +68 -0
- package/dist/cache/smart-cache.d.ts.map +1 -0
- package/dist/cache/smart-cache.js +266 -0
- package/dist/cache/types.d.ts +102 -0
- package/dist/cache/types.d.ts.map +1 -0
- package/dist/cache/types.js +6 -0
- package/dist/cli/analyze.d.ts +43 -0
- package/dist/cli/analyze.d.ts.map +1 -0
- package/dist/cli/analyze.js +250 -0
- package/dist/cli/doctor.d.ts +2 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/doctor.js +127 -0
- package/dist/cli/hooks.d.ts +14 -0
- package/dist/cli/hooks.d.ts.map +1 -0
- package/dist/cli/hooks.js +229 -0
- package/dist/cli/index.d.ts +5 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +4 -0
- package/dist/cli/setup.d.ts +10 -0
- package/dist/cli/setup.d.ts.map +1 -0
- package/dist/cli/setup.js +117 -0
- package/dist/cli/utils.d.ts +30 -0
- package/dist/cli/utils.d.ts.map +1 -0
- package/dist/cli/utils.js +116 -0
- package/dist/compressors/config.d.ts +9 -0
- package/dist/compressors/config.d.ts.map +1 -0
- package/dist/compressors/config.js +183 -0
- package/dist/compressors/conversation.d.ts +109 -0
- package/dist/compressors/conversation.d.ts.map +1 -0
- package/dist/compressors/conversation.js +404 -0
- package/dist/compressors/diff.d.ts +35 -0
- package/dist/compressors/diff.d.ts.map +1 -0
- package/dist/compressors/diff.js +389 -0
- package/dist/compressors/generic.d.ts +9 -0
- package/dist/compressors/generic.d.ts.map +1 -0
- package/dist/compressors/generic.js +188 -0
- package/dist/compressors/index.d.ts +31 -0
- package/dist/compressors/index.d.ts.map +1 -0
- package/dist/compressors/index.js +82 -0
- package/dist/compressors/logs.d.ts +9 -0
- package/dist/compressors/logs.d.ts.map +1 -0
- package/dist/compressors/logs.js +245 -0
- package/dist/compressors/multifile.d.ts +106 -0
- package/dist/compressors/multifile.d.ts.map +1 -0
- package/dist/compressors/multifile.js +498 -0
- package/dist/compressors/semantic.d.ts +33 -0
- package/dist/compressors/semantic.d.ts.map +1 -0
- package/dist/compressors/semantic.js +233 -0
- package/dist/compressors/stacktrace.d.ts +9 -0
- package/dist/compressors/stacktrace.d.ts.map +1 -0
- package/dist/compressors/stacktrace.js +259 -0
- package/dist/compressors/types.d.ts +146 -0
- package/dist/compressors/types.d.ts.map +1 -0
- package/dist/compressors/types.js +6 -0
- package/dist/config/output-config.d.ts +56 -0
- package/dist/config/output-config.d.ts.map +1 -0
- package/dist/config/output-config.js +78 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/middleware/chain.d.ts +49 -0
- package/dist/middleware/chain.d.ts.map +1 -0
- package/dist/middleware/chain.js +126 -0
- package/dist/middleware/index.d.ts +4 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +3 -0
- package/dist/middleware/logging.d.ts +8 -0
- package/dist/middleware/logging.d.ts.map +1 -0
- package/dist/middleware/logging.js +71 -0
- package/dist/middleware/types.d.ts +58 -0
- package/dist/middleware/types.d.ts.map +1 -0
- package/dist/middleware/types.js +7 -0
- package/dist/parsers/eslint.d.ts +8 -0
- package/dist/parsers/eslint.d.ts.map +1 -0
- package/dist/parsers/eslint.js +132 -0
- package/dist/parsers/generic.d.ts +8 -0
- package/dist/parsers/generic.d.ts.map +1 -0
- package/dist/parsers/generic.js +234 -0
- package/dist/parsers/index.d.ts +34 -0
- package/dist/parsers/index.d.ts.map +1 -0
- package/dist/parsers/index.js +216 -0
- package/dist/parsers/types.d.ts +84 -0
- package/dist/parsers/types.d.ts.map +1 -0
- package/dist/parsers/types.js +6 -0
- package/dist/parsers/typescript.d.ts +8 -0
- package/dist/parsers/typescript.d.ts.map +1 -0
- package/dist/parsers/typescript.js +107 -0
- package/dist/pipelines/definitions.d.ts +50 -0
- package/dist/pipelines/definitions.d.ts.map +1 -0
- package/dist/pipelines/definitions.js +206 -0
- package/dist/sandbox/executor.d.ts +12 -0
- package/dist/sandbox/executor.d.ts.map +1 -0
- package/dist/sandbox/executor.js +191 -0
- package/dist/sandbox/index.d.ts +11 -0
- package/dist/sandbox/index.d.ts.map +1 -0
- package/dist/sandbox/index.js +9 -0
- package/dist/sandbox/sandbox.test.d.ts +7 -0
- package/dist/sandbox/sandbox.test.d.ts.map +1 -0
- package/dist/sandbox/sandbox.test.js +202 -0
- package/dist/sandbox/sdk/analyze.d.ts +36 -0
- package/dist/sandbox/sdk/analyze.d.ts.map +1 -0
- package/dist/sandbox/sdk/analyze.js +413 -0
- package/dist/sandbox/sdk/analyze.test.d.ts +7 -0
- package/dist/sandbox/sdk/analyze.test.d.ts.map +1 -0
- package/dist/sandbox/sdk/analyze.test.js +191 -0
- package/dist/sandbox/sdk/code.d.ts +20 -0
- package/dist/sandbox/sdk/code.d.ts.map +1 -0
- package/dist/sandbox/sdk/code.js +104 -0
- package/dist/sandbox/sdk/compress.d.ts +23 -0
- package/dist/sandbox/sdk/compress.d.ts.map +1 -0
- package/dist/sandbox/sdk/compress.js +107 -0
- package/dist/sandbox/sdk/conversation.d.ts +148 -0
- package/dist/sandbox/sdk/conversation.d.ts.map +1 -0
- package/dist/sandbox/sdk/conversation.js +177 -0
- package/dist/sandbox/sdk/files.d.ts +29 -0
- package/dist/sandbox/sdk/files.d.ts.map +1 -0
- package/dist/sandbox/sdk/files.js +41 -0
- package/dist/sandbox/sdk/git.d.ts +37 -0
- package/dist/sandbox/sdk/git.d.ts.map +1 -0
- package/dist/sandbox/sdk/git.js +313 -0
- package/dist/sandbox/sdk/git.test.d.ts +8 -0
- package/dist/sandbox/sdk/git.test.d.ts.map +1 -0
- package/dist/sandbox/sdk/git.test.js +160 -0
- package/dist/sandbox/sdk/index.d.ts +16 -0
- package/dist/sandbox/sdk/index.d.ts.map +1 -0
- package/dist/sandbox/sdk/index.js +15 -0
- package/dist/sandbox/sdk/multifile.d.ts +63 -0
- package/dist/sandbox/sdk/multifile.d.ts.map +1 -0
- package/dist/sandbox/sdk/multifile.js +130 -0
- package/dist/sandbox/sdk/pipeline.d.ts +16 -0
- package/dist/sandbox/sdk/pipeline.d.ts.map +1 -0
- package/dist/sandbox/sdk/pipeline.js +454 -0
- package/dist/sandbox/sdk/pipeline.test.d.ts +7 -0
- package/dist/sandbox/sdk/pipeline.test.d.ts.map +1 -0
- package/dist/sandbox/sdk/pipeline.test.js +197 -0
- package/dist/sandbox/sdk/search.d.ts +36 -0
- package/dist/sandbox/sdk/search.d.ts.map +1 -0
- package/dist/sandbox/sdk/search.js +338 -0
- package/dist/sandbox/sdk/search.test.d.ts +7 -0
- package/dist/sandbox/sdk/search.test.d.ts.map +1 -0
- package/dist/sandbox/sdk/search.test.js +183 -0
- package/dist/sandbox/sdk/utils.d.ts +18 -0
- package/dist/sandbox/sdk/utils.d.ts.map +1 -0
- package/dist/sandbox/sdk/utils.js +24 -0
- package/dist/sandbox/security/code-analyzer.d.ts +15 -0
- package/dist/sandbox/security/code-analyzer.d.ts.map +1 -0
- package/dist/sandbox/security/code-analyzer.js +87 -0
- package/dist/sandbox/security/index.d.ts +6 -0
- package/dist/sandbox/security/index.d.ts.map +1 -0
- package/dist/sandbox/security/index.js +5 -0
- package/dist/sandbox/security/path-validator.d.ts +23 -0
- package/dist/sandbox/security/path-validator.d.ts.map +1 -0
- package/dist/sandbox/security/path-validator.js +113 -0
- package/dist/sandbox/types.d.ts +577 -0
- package/dist/sandbox/types.d.ts.map +1 -0
- package/dist/sandbox/types.js +14 -0
- package/dist/server.d.ts +36 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +133 -0
- package/dist/summarizers/build-logs.d.ts +11 -0
- package/dist/summarizers/build-logs.d.ts.map +1 -0
- package/dist/summarizers/build-logs.js +234 -0
- package/dist/summarizers/generic.d.ts +11 -0
- package/dist/summarizers/generic.d.ts.map +1 -0
- package/dist/summarizers/generic.js +93 -0
- package/dist/summarizers/index.d.ts +20 -0
- package/dist/summarizers/index.d.ts.map +1 -0
- package/dist/summarizers/index.js +43 -0
- package/dist/summarizers/server-logs.d.ts +11 -0
- package/dist/summarizers/server-logs.d.ts.map +1 -0
- package/dist/summarizers/server-logs.js +215 -0
- package/dist/summarizers/test-logs.d.ts +11 -0
- package/dist/summarizers/test-logs.d.ts.map +1 -0
- package/dist/summarizers/test-logs.js +258 -0
- package/dist/summarizers/types.d.ts +146 -0
- package/dist/summarizers/types.d.ts.map +1 -0
- package/dist/summarizers/types.js +21 -0
- package/dist/tools/analyze-build-output.d.ts +30 -0
- package/dist/tools/analyze-build-output.d.ts.map +1 -0
- package/dist/tools/analyze-build-output.js +45 -0
- package/dist/tools/analyze-context.d.ts +23 -0
- package/dist/tools/analyze-context.d.ts.map +1 -0
- package/dist/tools/analyze-context.js +78 -0
- package/dist/tools/auto-optimize.d.ts +9 -0
- package/dist/tools/auto-optimize.d.ts.map +1 -0
- package/dist/tools/auto-optimize.js +191 -0
- package/dist/tools/code-execute.d.ts +9 -0
- package/dist/tools/code-execute.d.ts.map +1 -0
- package/dist/tools/code-execute.js +84 -0
- package/dist/tools/code-skeleton.d.ts +33 -0
- package/dist/tools/code-skeleton.d.ts.map +1 -0
- package/dist/tools/code-skeleton.js +206 -0
- package/dist/tools/compress-context.d.ts +33 -0
- package/dist/tools/compress-context.d.ts.map +1 -0
- package/dist/tools/compress-context.js +64 -0
- package/dist/tools/context-budget.d.ts +43 -0
- package/dist/tools/context-budget.d.ts.map +1 -0
- package/dist/tools/context-budget.js +260 -0
- package/dist/tools/context-budget.test.d.ts +5 -0
- package/dist/tools/context-budget.test.d.ts.map +1 -0
- package/dist/tools/context-budget.test.js +219 -0
- package/dist/tools/conversation-compress.d.ts +46 -0
- package/dist/tools/conversation-compress.d.ts.map +1 -0
- package/dist/tools/conversation-compress.js +78 -0
- package/dist/tools/conversation-memory.d.ts +75 -0
- package/dist/tools/conversation-memory.d.ts.map +1 -0
- package/dist/tools/conversation-memory.js +289 -0
- package/dist/tools/deduplicate-errors.d.ts +30 -0
- package/dist/tools/deduplicate-errors.d.ts.map +1 -0
- package/dist/tools/deduplicate-errors.js +72 -0
- package/dist/tools/detect-retry-loop.d.ts +40 -0
- package/dist/tools/detect-retry-loop.d.ts.map +1 -0
- package/dist/tools/detect-retry-loop.js +212 -0
- package/dist/tools/diff-compress.d.ts +40 -0
- package/dist/tools/diff-compress.d.ts.map +1 -0
- package/dist/tools/diff-compress.js +94 -0
- package/dist/tools/discover-tools.d.ts +11 -0
- package/dist/tools/discover-tools.d.ts.map +1 -0
- package/dist/tools/discover-tools.js +163 -0
- package/dist/tools/dynamic-loader.d.ts +131 -0
- package/dist/tools/dynamic-loader.d.ts.map +1 -0
- package/dist/tools/dynamic-loader.js +378 -0
- package/dist/tools/dynamic-loader.test.d.ts +10 -0
- package/dist/tools/dynamic-loader.test.d.ts.map +1 -0
- package/dist/tools/dynamic-loader.test.js +164 -0
- package/dist/tools/lazy-mcp.d.ts +31 -0
- package/dist/tools/lazy-mcp.d.ts.map +1 -0
- package/dist/tools/lazy-mcp.js +151 -0
- package/dist/tools/lazy-mcp.test.d.ts +10 -0
- package/dist/tools/lazy-mcp.test.d.ts.map +1 -0
- package/dist/tools/lazy-mcp.test.js +172 -0
- package/dist/tools/multifile-compress.d.ts +36 -0
- package/dist/tools/multifile-compress.d.ts.map +1 -0
- package/dist/tools/multifile-compress.js +223 -0
- package/dist/tools/optimization-tips.d.ts +18 -0
- package/dist/tools/optimization-tips.d.ts.map +1 -0
- package/dist/tools/optimization-tips.js +133 -0
- package/dist/tools/registry.d.ts +70 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +169 -0
- package/dist/tools/semantic-compress.d.ts +39 -0
- package/dist/tools/semantic-compress.d.ts.map +1 -0
- package/dist/tools/semantic-compress.js +113 -0
- package/dist/tools/semantic-compress.test.d.ts +5 -0
- package/dist/tools/semantic-compress.test.d.ts.map +1 -0
- package/dist/tools/semantic-compress.test.js +182 -0
- package/dist/tools/session-stats.d.ts +34 -0
- package/dist/tools/session-stats.d.ts.map +1 -0
- package/dist/tools/session-stats.js +194 -0
- package/dist/tools/set-output-config.d.ts +38 -0
- package/dist/tools/set-output-config.d.ts.map +1 -0
- package/dist/tools/set-output-config.js +122 -0
- package/dist/tools/smart-cache-tool.d.ts +38 -0
- package/dist/tools/smart-cache-tool.d.ts.map +1 -0
- package/dist/tools/smart-cache-tool.js +224 -0
- package/dist/tools/smart-file-read.d.ts +52 -0
- package/dist/tools/smart-file-read.d.ts.map +1 -0
- package/dist/tools/smart-file-read.js +481 -0
- package/dist/tools/smart-pipeline.d.ts +40 -0
- package/dist/tools/smart-pipeline.d.ts.map +1 -0
- package/dist/tools/smart-pipeline.js +295 -0
- package/dist/tools/summarize-logs.d.ts +36 -0
- package/dist/tools/summarize-logs.d.ts.map +1 -0
- package/dist/tools/summarize-logs.js +184 -0
- package/dist/tools/token-budget.test.d.ts +11 -0
- package/dist/tools/token-budget.test.d.ts.map +1 -0
- package/dist/tools/token-budget.test.js +275 -0
- package/dist/utils/bm25.d.ts +86 -0
- package/dist/utils/bm25.d.ts.map +1 -0
- package/dist/utils/bm25.js +153 -0
- package/dist/utils/bm25.test.d.ts +5 -0
- package/dist/utils/bm25.test.d.ts.map +1 -0
- package/dist/utils/bm25.test.js +156 -0
- package/dist/utils/command-normalizer.d.ts +39 -0
- package/dist/utils/command-normalizer.d.ts.map +1 -0
- package/dist/utils/command-normalizer.js +90 -0
- package/dist/utils/content-detector.d.ts +27 -0
- package/dist/utils/content-detector.d.ts.map +1 -0
- package/dist/utils/content-detector.js +127 -0
- package/dist/utils/embeddings.d.ts +54 -0
- package/dist/utils/embeddings.d.ts.map +1 -0
- package/dist/utils/embeddings.js +97 -0
- package/dist/utils/embeddings.test.d.ts +8 -0
- package/dist/utils/embeddings.test.d.ts.map +1 -0
- package/dist/utils/embeddings.test.js +96 -0
- package/dist/utils/error-normalizer.d.ts +39 -0
- package/dist/utils/error-normalizer.d.ts.map +1 -0
- package/dist/utils/error-normalizer.js +233 -0
- package/dist/utils/hybrid-search.d.ts +79 -0
- package/dist/utils/hybrid-search.d.ts.map +1 -0
- package/dist/utils/hybrid-search.js +146 -0
- package/dist/utils/hybrid-search.test.d.ts +5 -0
- package/dist/utils/hybrid-search.test.d.ts.map +1 -0
- package/dist/utils/hybrid-search.test.js +172 -0
- package/dist/utils/index.d.ts +13 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +12 -0
- package/dist/utils/language-detector.d.ts +27 -0
- package/dist/utils/language-detector.d.ts.map +1 -0
- package/dist/utils/language-detector.js +94 -0
- package/dist/utils/log-parser.d.ts +46 -0
- package/dist/utils/log-parser.d.ts.map +1 -0
- package/dist/utils/log-parser.js +287 -0
- package/dist/utils/output-estimator.d.ts +54 -0
- package/dist/utils/output-estimator.d.ts.map +1 -0
- package/dist/utils/output-estimator.js +119 -0
- package/dist/utils/output-estimator.test.d.ts +5 -0
- package/dist/utils/output-estimator.test.d.ts.map +1 -0
- package/dist/utils/output-estimator.test.js +115 -0
- package/dist/utils/output-similarity.d.ts +48 -0
- package/dist/utils/output-similarity.d.ts.map +1 -0
- package/dist/utils/output-similarity.js +140 -0
- package/dist/utils/project-detector.d.ts +16 -0
- package/dist/utils/project-detector.d.ts.map +1 -0
- package/dist/utils/project-detector.js +119 -0
- package/dist/utils/segment-scorer.d.ts +99 -0
- package/dist/utils/segment-scorer.d.ts.map +1 -0
- package/dist/utils/segment-scorer.js +148 -0
- package/dist/utils/signature-grouper.d.ts +58 -0
- package/dist/utils/signature-grouper.d.ts.map +1 -0
- package/dist/utils/signature-grouper.js +185 -0
- package/dist/utils/tfidf.d.ts +45 -0
- package/dist/utils/tfidf.d.ts.map +1 -0
- package/dist/utils/tfidf.js +204 -0
- package/dist/utils/tfidf.test.d.ts +5 -0
- package/dist/utils/tfidf.test.d.ts.map +1 -0
- package/dist/utils/tfidf.test.js +115 -0
- package/dist/utils/token-counter.d.ts +35 -0
- package/dist/utils/token-counter.d.ts.map +1 -0
- package/dist/utils/token-counter.js +83 -0
- package/dist/utils/toon-serializer.d.ts +120 -0
- package/dist/utils/toon-serializer.d.ts.map +1 -0
- package/dist/utils/toon-serializer.js +472 -0
- package/dist/utils/toon-serializer.test.d.ts +7 -0
- package/dist/utils/toon-serializer.test.d.ts.map +1 -0
- package/dist/utils/toon-serializer.test.js +290 -0
- package/package.json +63 -0
- package/scripts/install.ps1 +133 -0
- package/scripts/install.sh +183 -0
- package/scripts/pre-commit-hook.sh +86 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Normalizer
|
|
3
|
+
*
|
|
4
|
+
* Normalizes error lines to create consistent signatures for deduplication.
|
|
5
|
+
* Removes variable parts (file paths, line numbers, values) while preserving
|
|
6
|
+
* the error pattern.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Normalize an error line by replacing variable parts with placeholders.
|
|
10
|
+
* This creates a consistent signature for grouping similar errors.
|
|
11
|
+
*/
|
|
12
|
+
export function normalizeErrorLine(line) {
|
|
13
|
+
return (line
|
|
14
|
+
// Remove file paths (Unix and Windows)
|
|
15
|
+
.replace(/[A-Za-z]:\\[\w\-\\\.]+\.(ts|tsx|js|jsx|py|go|rs|java|c|cpp|h|hpp)/gi, "<FILE>")
|
|
16
|
+
.replace(/\/[\w\-\.\/]+\.(ts|tsx|js|jsx|py|go|rs|java|c|cpp|h|hpp)/g, "<FILE>")
|
|
17
|
+
// Remove relative paths
|
|
18
|
+
.replace(/\.\.?\/[\w\-\.\/]+\.(ts|tsx|js|jsx|py|go|rs|java|c|cpp|h|hpp)/g, "<FILE>")
|
|
19
|
+
// Remove line:column patterns
|
|
20
|
+
.replace(/:\d+:\d+/g, ":<LINE>")
|
|
21
|
+
.replace(/\(\d+,\s*\d+\)/g, "(<LINE>)")
|
|
22
|
+
.replace(/\[\d+,\s*\d+\]/g, "[<LINE>]")
|
|
23
|
+
.replace(/line\s+\d+/gi, "line <LINE>")
|
|
24
|
+
.replace(/col(?:umn)?\s+\d+/gi, "col <LINE>")
|
|
25
|
+
// Remove quoted values (but preserve the quotes for structure)
|
|
26
|
+
.replace(/'[^']*'/g, "'<VALUE>'")
|
|
27
|
+
.replace(/"[^"]*"/g, '"<VALUE>"')
|
|
28
|
+
.replace(/`[^`]*`/g, "`<VALUE>`")
|
|
29
|
+
// Remove timestamps
|
|
30
|
+
.replace(/\d{4}-\d{2}-\d{2}[\sT]\d{2}:\d{2}:\d{2}(\.\d+)?([+-]\d{2}:?\d{2}|Z)?/g, "<TIMESTAMP>")
|
|
31
|
+
.replace(/\d{2}\/\d{2}\/\d{4}\s+\d{2}:\d{2}:\d{2}/g, "<TIMESTAMP>")
|
|
32
|
+
// Remove numeric IDs and hashes
|
|
33
|
+
.replace(/\b[0-9a-f]{32,}\b/gi, "<HASH>")
|
|
34
|
+
.replace(/\b\d{5,}\b/g, "<ID>")
|
|
35
|
+
// Normalize whitespace
|
|
36
|
+
.replace(/\s+/g, " ")
|
|
37
|
+
.trim());
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Common error patterns for different tools
|
|
41
|
+
*/
|
|
42
|
+
const ERROR_PATTERNS = [
|
|
43
|
+
// TypeScript: src/file.ts(12,5): error TS2304: Cannot find name 'foo'.
|
|
44
|
+
{
|
|
45
|
+
name: "typescript",
|
|
46
|
+
regex: /^(.+?)\((\d+),(\d+)\):\s*(error|warning)\s+(TS\d+):\s*(.+)$/,
|
|
47
|
+
extract: (match) => ({
|
|
48
|
+
file: match[1] ?? "",
|
|
49
|
+
line: parseInt(match[2] ?? "0", 10),
|
|
50
|
+
column: parseInt(match[3] ?? "0", 10),
|
|
51
|
+
code: match[5] ?? "",
|
|
52
|
+
message: match[6] ?? "",
|
|
53
|
+
raw: match[0] ?? "",
|
|
54
|
+
}),
|
|
55
|
+
},
|
|
56
|
+
// ESLint: src/file.ts:12:5 - error rule-name: Message
|
|
57
|
+
{
|
|
58
|
+
name: "eslint",
|
|
59
|
+
regex: /^(.+?):(\d+):(\d+)\s*-?\s*(error|warning|info)\s+([a-z\-@\/]+):\s*(.+)$/i,
|
|
60
|
+
extract: (match) => ({
|
|
61
|
+
file: match[1] ?? "",
|
|
62
|
+
line: parseInt(match[2] ?? "0", 10),
|
|
63
|
+
column: parseInt(match[3] ?? "0", 10),
|
|
64
|
+
code: match[5] ?? "",
|
|
65
|
+
message: match[6] ?? "",
|
|
66
|
+
raw: match[0] ?? "",
|
|
67
|
+
}),
|
|
68
|
+
},
|
|
69
|
+
// GCC/Clang: file.c:12:5: error: message
|
|
70
|
+
{
|
|
71
|
+
name: "gcc",
|
|
72
|
+
regex: /^(.+?):(\d+):(\d+):\s*(error|warning|note):\s*(.+)$/,
|
|
73
|
+
extract: (match) => ({
|
|
74
|
+
file: match[1] ?? "",
|
|
75
|
+
line: parseInt(match[2] ?? "0", 10),
|
|
76
|
+
column: parseInt(match[3] ?? "0", 10),
|
|
77
|
+
code: match[4] ?? "",
|
|
78
|
+
message: match[5] ?? "",
|
|
79
|
+
raw: match[0] ?? "",
|
|
80
|
+
}),
|
|
81
|
+
},
|
|
82
|
+
// Python: File "file.py", line 12, in function
|
|
83
|
+
{
|
|
84
|
+
name: "python",
|
|
85
|
+
regex: /^File "(.+?)", line (\d+)(?:, in .+)?$/,
|
|
86
|
+
extract: (match) => ({
|
|
87
|
+
file: match[1] ?? "",
|
|
88
|
+
line: parseInt(match[2] ?? "0", 10),
|
|
89
|
+
message: match[0] ?? "",
|
|
90
|
+
raw: match[0] ?? "",
|
|
91
|
+
}),
|
|
92
|
+
},
|
|
93
|
+
// Python error: ErrorType: message
|
|
94
|
+
{
|
|
95
|
+
name: "python-error",
|
|
96
|
+
regex: /^([A-Z][a-zA-Z]+Error):\s*(.+)$/,
|
|
97
|
+
extract: (match) => ({
|
|
98
|
+
code: match[1] ?? "",
|
|
99
|
+
message: match[2] ?? "",
|
|
100
|
+
raw: match[0] ?? "",
|
|
101
|
+
}),
|
|
102
|
+
},
|
|
103
|
+
// Go: file.go:12:5: message
|
|
104
|
+
{
|
|
105
|
+
name: "go",
|
|
106
|
+
regex: /^(.+?\.go):(\d+):(\d+):\s*(.+)$/,
|
|
107
|
+
extract: (match) => ({
|
|
108
|
+
file: match[1] ?? "",
|
|
109
|
+
line: parseInt(match[2] ?? "0", 10),
|
|
110
|
+
column: parseInt(match[3] ?? "0", 10),
|
|
111
|
+
message: match[4] ?? "",
|
|
112
|
+
raw: match[0] ?? "",
|
|
113
|
+
}),
|
|
114
|
+
},
|
|
115
|
+
// Rust: error[E0425]: cannot find value `x` in this scope
|
|
116
|
+
{
|
|
117
|
+
name: "rust",
|
|
118
|
+
regex: /^(error|warning)\[(E\d+)\]:\s*(.+)$/,
|
|
119
|
+
extract: (match) => ({
|
|
120
|
+
code: match[2] ?? "",
|
|
121
|
+
message: match[3] ?? "",
|
|
122
|
+
raw: match[0] ?? "",
|
|
123
|
+
}),
|
|
124
|
+
},
|
|
125
|
+
// Rust location: --> file.rs:12:5
|
|
126
|
+
{
|
|
127
|
+
name: "rust-location",
|
|
128
|
+
regex: /^\s*-->\s*(.+?):(\d+):(\d+)$/,
|
|
129
|
+
extract: (match) => ({
|
|
130
|
+
file: match[1] ?? "",
|
|
131
|
+
line: parseInt(match[2] ?? "0", 10),
|
|
132
|
+
column: parseInt(match[3] ?? "0", 10),
|
|
133
|
+
message: match[0] ?? "",
|
|
134
|
+
raw: match[0] ?? "",
|
|
135
|
+
}),
|
|
136
|
+
},
|
|
137
|
+
// Generic: [ERROR] message or ERROR: message
|
|
138
|
+
{
|
|
139
|
+
name: "generic-bracket",
|
|
140
|
+
regex: /^\[(ERROR|WARN(?:ING)?|INFO)\]\s*(.+)$/i,
|
|
141
|
+
extract: (match) => ({
|
|
142
|
+
code: (match[1] ?? "").toUpperCase(),
|
|
143
|
+
message: match[2] ?? "",
|
|
144
|
+
raw: match[0] ?? "",
|
|
145
|
+
}),
|
|
146
|
+
},
|
|
147
|
+
// Generic colon format
|
|
148
|
+
{
|
|
149
|
+
name: "generic-colon",
|
|
150
|
+
regex: /^(ERROR|WARN(?:ING)?|FATAL):\s*(.+)$/i,
|
|
151
|
+
extract: (match) => ({
|
|
152
|
+
code: (match[1] ?? "").toUpperCase(),
|
|
153
|
+
message: match[2] ?? "",
|
|
154
|
+
raw: match[0] ?? "",
|
|
155
|
+
}),
|
|
156
|
+
},
|
|
157
|
+
];
|
|
158
|
+
/**
|
|
159
|
+
* Extract structured parts from an error line.
|
|
160
|
+
* Returns null if the line doesn't match any known error pattern.
|
|
161
|
+
*/
|
|
162
|
+
export function extractErrorParts(line) {
|
|
163
|
+
const trimmed = line.trim();
|
|
164
|
+
if (!trimmed)
|
|
165
|
+
return null;
|
|
166
|
+
for (const pattern of ERROR_PATTERNS) {
|
|
167
|
+
const match = trimmed.match(pattern.regex);
|
|
168
|
+
if (match) {
|
|
169
|
+
return pattern.extract(match);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// If no pattern matches but line looks like an error
|
|
173
|
+
if (isLikelyError(trimmed)) {
|
|
174
|
+
return {
|
|
175
|
+
message: trimmed,
|
|
176
|
+
raw: trimmed,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Check if a line is likely an error message
|
|
183
|
+
*/
|
|
184
|
+
export function isLikelyError(line) {
|
|
185
|
+
const errorIndicators = [
|
|
186
|
+
/\berror\b/i,
|
|
187
|
+
/\bfailed\b/i,
|
|
188
|
+
/\bfailure\b/i,
|
|
189
|
+
/\bexception\b/i,
|
|
190
|
+
/\bcannot\b/i,
|
|
191
|
+
/\bunable\b/i,
|
|
192
|
+
/\binvalid\b/i,
|
|
193
|
+
/\bunexpected\b/i,
|
|
194
|
+
/\bmissing\b/i,
|
|
195
|
+
/\bundefined\b/i,
|
|
196
|
+
/\bnot found\b/i,
|
|
197
|
+
/\bdoes not exist\b/i,
|
|
198
|
+
/\bnot defined\b/i,
|
|
199
|
+
/\btype mismatch\b/i,
|
|
200
|
+
/\bsyntax error\b/i,
|
|
201
|
+
/^\s*\^+\s*$/, // Error pointer line (^^^)
|
|
202
|
+
];
|
|
203
|
+
return errorIndicators.some((pattern) => pattern.test(line));
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Create a signature from error parts for grouping.
|
|
207
|
+
* The signature represents the "type" of error, ignoring location.
|
|
208
|
+
*/
|
|
209
|
+
export function createSignature(parts) {
|
|
210
|
+
const components = [];
|
|
211
|
+
if (parts.code) {
|
|
212
|
+
components.push(parts.code);
|
|
213
|
+
}
|
|
214
|
+
// Normalize the message
|
|
215
|
+
const normalizedMessage = normalizeErrorLine(parts.message);
|
|
216
|
+
components.push(normalizedMessage);
|
|
217
|
+
return components.join(": ");
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Extract file location from error parts
|
|
221
|
+
*/
|
|
222
|
+
export function formatLocation(parts) {
|
|
223
|
+
if (!parts.file)
|
|
224
|
+
return "";
|
|
225
|
+
let location = parts.file;
|
|
226
|
+
if (parts.line !== undefined) {
|
|
227
|
+
location += `:${parts.line}`;
|
|
228
|
+
if (parts.column !== undefined) {
|
|
229
|
+
location += `:${parts.column}`;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return location;
|
|
233
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hybrid Search combining BM25 (lexical) and Semantic Similarity
|
|
3
|
+
*
|
|
4
|
+
* Uses a weighted combination of:
|
|
5
|
+
* - BM25 for exact/partial keyword matches (40%)
|
|
6
|
+
* - Cosine similarity on embeddings for semantic matches (60%)
|
|
7
|
+
*
|
|
8
|
+
* This allows finding tools even when the query uses different
|
|
9
|
+
* words than the tool description (e.g., "shrink output" → compress).
|
|
10
|
+
*/
|
|
11
|
+
import { type BM25Result } from "./bm25.js";
|
|
12
|
+
/**
|
|
13
|
+
* Configuration options for hybrid search
|
|
14
|
+
*/
|
|
15
|
+
export interface HybridSearchOptions {
|
|
16
|
+
/** Weight for BM25 lexical matching (default: 0.4) */
|
|
17
|
+
bm25Weight?: number;
|
|
18
|
+
/** Weight for semantic similarity (default: 0.6) */
|
|
19
|
+
semanticWeight?: number;
|
|
20
|
+
/** Minimum semantic similarity for semantic-only matches (default: 0.5) */
|
|
21
|
+
semanticThreshold?: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Result from hybrid search with both scores
|
|
25
|
+
*/
|
|
26
|
+
export interface HybridSearchResult<T> {
|
|
27
|
+
/** The matched item */
|
|
28
|
+
item: T;
|
|
29
|
+
/** Combined score (weighted BM25 + semantic) */
|
|
30
|
+
score: number;
|
|
31
|
+
/** Raw BM25 score */
|
|
32
|
+
bm25Score: number;
|
|
33
|
+
/** Semantic similarity score (0-1) */
|
|
34
|
+
semanticScore: number;
|
|
35
|
+
/** Terms that matched in BM25 */
|
|
36
|
+
matchedTerms: string[];
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Hybrid search index interface
|
|
40
|
+
*/
|
|
41
|
+
export interface HybridSearchIndex<T> {
|
|
42
|
+
/** Search using hybrid BM25 + semantic */
|
|
43
|
+
search: (query: string) => Promise<HybridSearchResult<T>[]>;
|
|
44
|
+
/** Search using BM25 only (synchronous, always available) */
|
|
45
|
+
searchBM25Only: (query: string) => BM25Result<T>[];
|
|
46
|
+
/** Precompute embeddings for all items (call during idle time) */
|
|
47
|
+
precomputeEmbeddings: () => Promise<void>;
|
|
48
|
+
/** Check if semantic search is ready */
|
|
49
|
+
isSemanticReady: () => boolean;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Create a hybrid search index combining BM25 and semantic similarity
|
|
53
|
+
*
|
|
54
|
+
* @param items - Array of items to index
|
|
55
|
+
* @param getSearchableText - Function to extract text from each item
|
|
56
|
+
* @param options - Search configuration options
|
|
57
|
+
* @returns Hybrid search index
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* const tools = [
|
|
62
|
+
* { name: "compress", description: "Compress and reduce output" },
|
|
63
|
+
* { name: "analyze", description: "Analyze build errors" }
|
|
64
|
+
* ];
|
|
65
|
+
*
|
|
66
|
+
* const index = createHybridSearchIndex(
|
|
67
|
+
* tools,
|
|
68
|
+
* (tool) => `${tool.name} ${tool.description}`
|
|
69
|
+
* );
|
|
70
|
+
*
|
|
71
|
+
* // BM25 match
|
|
72
|
+
* await index.search("compress"); // finds "compress" tool
|
|
73
|
+
*
|
|
74
|
+
* // Semantic match (no keyword overlap)
|
|
75
|
+
* await index.search("shrink output"); // also finds "compress" tool!
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export declare function createHybridSearchIndex<T>(items: T[], getSearchableText: (item: T) => string, options?: HybridSearchOptions): HybridSearchIndex<T>;
|
|
79
|
+
//# sourceMappingURL=hybrid-search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hybrid-search.d.ts","sourceRoot":"","sources":["../../src/utils/hybrid-search.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AAO7D;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,sDAAsD;IACtD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oDAAoD;IACpD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,2EAA2E;IAC3E,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB,CAAC,CAAC;IACnC,uBAAuB;IACvB,IAAI,EAAE,CAAC,CAAC;IACR,gDAAgD;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,qBAAqB;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,sCAAsC;IACtC,aAAa,EAAE,MAAM,CAAC;IACtB,iCAAiC;IACjC,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAClC,0CAA0C;IAC1C,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC5D,6DAA6D;IAC7D,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;IACnD,kEAAkE;IAClE,oBAAoB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,wCAAwC;IACxC,eAAe,EAAE,MAAM,OAAO,CAAC;CAChC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,EACvC,KAAK,EAAE,CAAC,EAAE,EACV,iBAAiB,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,EACtC,OAAO,CAAC,EAAE,mBAAmB,GAC5B,iBAAiB,CAAC,CAAC,CAAC,CA4HtB"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hybrid Search combining BM25 (lexical) and Semantic Similarity
|
|
3
|
+
*
|
|
4
|
+
* Uses a weighted combination of:
|
|
5
|
+
* - BM25 for exact/partial keyword matches (40%)
|
|
6
|
+
* - Cosine similarity on embeddings for semantic matches (60%)
|
|
7
|
+
*
|
|
8
|
+
* This allows finding tools even when the query uses different
|
|
9
|
+
* words than the tool description (e.g., "shrink output" → compress).
|
|
10
|
+
*/
|
|
11
|
+
import { createBM25Index } from "./bm25.js";
|
|
12
|
+
import { computeEmbedding, computeEmbeddings, cosineSimilarity, } from "./embeddings.js";
|
|
13
|
+
/**
|
|
14
|
+
* Create a hybrid search index combining BM25 and semantic similarity
|
|
15
|
+
*
|
|
16
|
+
* @param items - Array of items to index
|
|
17
|
+
* @param getSearchableText - Function to extract text from each item
|
|
18
|
+
* @param options - Search configuration options
|
|
19
|
+
* @returns Hybrid search index
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* const tools = [
|
|
24
|
+
* { name: "compress", description: "Compress and reduce output" },
|
|
25
|
+
* { name: "analyze", description: "Analyze build errors" }
|
|
26
|
+
* ];
|
|
27
|
+
*
|
|
28
|
+
* const index = createHybridSearchIndex(
|
|
29
|
+
* tools,
|
|
30
|
+
* (tool) => `${tool.name} ${tool.description}`
|
|
31
|
+
* );
|
|
32
|
+
*
|
|
33
|
+
* // BM25 match
|
|
34
|
+
* await index.search("compress"); // finds "compress" tool
|
|
35
|
+
*
|
|
36
|
+
* // Semantic match (no keyword overlap)
|
|
37
|
+
* await index.search("shrink output"); // also finds "compress" tool!
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export function createHybridSearchIndex(items, getSearchableText, options) {
|
|
41
|
+
const bm25Weight = options?.bm25Weight ?? 0.4;
|
|
42
|
+
const semanticWeight = options?.semanticWeight ?? 0.6;
|
|
43
|
+
const semanticThreshold = options?.semanticThreshold ?? 0.5;
|
|
44
|
+
// BM25 index (synchronous, always available)
|
|
45
|
+
const bm25Index = createBM25Index(items, getSearchableText);
|
|
46
|
+
// Item embeddings (lazy computed)
|
|
47
|
+
let itemEmbeddings = null;
|
|
48
|
+
let embeddingsPromise = null;
|
|
49
|
+
/**
|
|
50
|
+
* Ensure embeddings are computed
|
|
51
|
+
*/
|
|
52
|
+
async function ensureEmbeddings() {
|
|
53
|
+
if (itemEmbeddings)
|
|
54
|
+
return itemEmbeddings;
|
|
55
|
+
if (!embeddingsPromise) {
|
|
56
|
+
embeddingsPromise = (async () => {
|
|
57
|
+
const texts = items.map(getSearchableText);
|
|
58
|
+
itemEmbeddings = await computeEmbeddings(texts);
|
|
59
|
+
})();
|
|
60
|
+
}
|
|
61
|
+
await embeddingsPromise;
|
|
62
|
+
return itemEmbeddings;
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
async search(query) {
|
|
66
|
+
// Get BM25 results first (always available)
|
|
67
|
+
const bm25Results = bm25Index.search(query);
|
|
68
|
+
// Try to compute semantic scores
|
|
69
|
+
let queryEmbedding = null;
|
|
70
|
+
let embeddings = null;
|
|
71
|
+
try {
|
|
72
|
+
embeddings = await ensureEmbeddings();
|
|
73
|
+
queryEmbedding = await computeEmbedding(query);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// Semantic search failed, fall back to BM25 only
|
|
77
|
+
return bm25Results.map((r) => ({
|
|
78
|
+
item: r.item,
|
|
79
|
+
score: r.score,
|
|
80
|
+
bm25Score: r.score,
|
|
81
|
+
semanticScore: 0,
|
|
82
|
+
matchedTerms: r.matchedTerms,
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
85
|
+
// Build item index map for O(1) lookup
|
|
86
|
+
const itemIndexMap = new Map();
|
|
87
|
+
items.forEach((item, idx) => itemIndexMap.set(item, idx));
|
|
88
|
+
// Normalize BM25 scores to 0-1 range
|
|
89
|
+
const maxBM25 = Math.max(...bm25Results.map((r) => r.score), 0.001);
|
|
90
|
+
// Combine BM25 results with semantic scores
|
|
91
|
+
const results = [];
|
|
92
|
+
const processedItems = new Set();
|
|
93
|
+
for (const bm25Result of bm25Results) {
|
|
94
|
+
const idx = itemIndexMap.get(bm25Result.item);
|
|
95
|
+
if (idx === undefined || !embeddings || !queryEmbedding)
|
|
96
|
+
continue;
|
|
97
|
+
const itemEmbedding = embeddings[idx];
|
|
98
|
+
if (!itemEmbedding)
|
|
99
|
+
continue;
|
|
100
|
+
const semanticScore = cosineSimilarity(queryEmbedding, itemEmbedding);
|
|
101
|
+
const normalizedBM25 = bm25Result.score / maxBM25;
|
|
102
|
+
results.push({
|
|
103
|
+
item: bm25Result.item,
|
|
104
|
+
score: normalizedBM25 * bm25Weight + semanticScore * semanticWeight,
|
|
105
|
+
bm25Score: bm25Result.score,
|
|
106
|
+
semanticScore,
|
|
107
|
+
matchedTerms: bm25Result.matchedTerms,
|
|
108
|
+
});
|
|
109
|
+
processedItems.add(bm25Result.item);
|
|
110
|
+
}
|
|
111
|
+
// Add items that only match semantically (no BM25 match)
|
|
112
|
+
if (embeddings && queryEmbedding) {
|
|
113
|
+
for (let i = 0; i < items.length; i++) {
|
|
114
|
+
const item = items[i];
|
|
115
|
+
if (!item || processedItems.has(item))
|
|
116
|
+
continue;
|
|
117
|
+
const itemEmbedding = embeddings[i];
|
|
118
|
+
if (!itemEmbedding)
|
|
119
|
+
continue;
|
|
120
|
+
const similarity = cosineSimilarity(queryEmbedding, itemEmbedding);
|
|
121
|
+
// Only include if above semantic threshold
|
|
122
|
+
if (similarity >= semanticThreshold) {
|
|
123
|
+
results.push({
|
|
124
|
+
item,
|
|
125
|
+
score: similarity * semanticWeight,
|
|
126
|
+
bm25Score: 0,
|
|
127
|
+
semanticScore: similarity,
|
|
128
|
+
matchedTerms: [],
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// Sort by combined score (highest first)
|
|
134
|
+
return results.sort((a, b) => b.score - a.score);
|
|
135
|
+
},
|
|
136
|
+
searchBM25Only(query) {
|
|
137
|
+
return bm25Index.search(query);
|
|
138
|
+
},
|
|
139
|
+
async precomputeEmbeddings() {
|
|
140
|
+
await ensureEmbeddings();
|
|
141
|
+
},
|
|
142
|
+
isSemanticReady() {
|
|
143
|
+
return itemEmbeddings !== null;
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hybrid-search.test.d.ts","sourceRoot":"","sources":["../../src/utils/hybrid-search.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hybrid Search Tests
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
5
|
+
import { createHybridSearchIndex } from "./hybrid-search.js";
|
|
6
|
+
// Mock the embeddings module for fast tests
|
|
7
|
+
vi.mock("./embeddings.js", () => {
|
|
8
|
+
// Simple mock embeddings based on word overlap
|
|
9
|
+
const mockEmbedding = (text) => {
|
|
10
|
+
const words = text.toLowerCase().split(/\s+/);
|
|
11
|
+
const dim = 384;
|
|
12
|
+
const embedding = new Array(dim).fill(0);
|
|
13
|
+
// Create deterministic embedding based on words
|
|
14
|
+
for (const word of words) {
|
|
15
|
+
for (let i = 0; i < word.length; i++) {
|
|
16
|
+
const idx = (word.charCodeAt(i) * (i + 1)) % dim;
|
|
17
|
+
embedding[idx] += 0.1;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
// Normalize
|
|
21
|
+
const magnitude = Math.sqrt(embedding.reduce((sum, x) => sum + x * x, 0));
|
|
22
|
+
if (magnitude > 0) {
|
|
23
|
+
for (let i = 0; i < dim; i++) {
|
|
24
|
+
embedding[i] /= magnitude;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return embedding;
|
|
28
|
+
};
|
|
29
|
+
return {
|
|
30
|
+
computeEmbedding: vi.fn(async (text) => mockEmbedding(text)),
|
|
31
|
+
computeEmbeddings: vi.fn(async (texts) => texts.map((t) => mockEmbedding(t))),
|
|
32
|
+
cosineSimilarity: vi.fn((a, b) => {
|
|
33
|
+
if (a.length !== b.length)
|
|
34
|
+
return 0;
|
|
35
|
+
let dot = 0;
|
|
36
|
+
for (let i = 0; i < a.length; i++) {
|
|
37
|
+
dot += (a[i] ?? 0) * (b[i] ?? 0);
|
|
38
|
+
}
|
|
39
|
+
return dot;
|
|
40
|
+
}),
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
describe("Hybrid Search", () => {
|
|
44
|
+
const testItems = [
|
|
45
|
+
{ name: "compress", description: "Compress and reduce output size" },
|
|
46
|
+
{ name: "analyze", description: "Analyze build errors and warnings" },
|
|
47
|
+
{ name: "summarize", description: "Summarize log files" },
|
|
48
|
+
{ name: "optimize", description: "Optimize token usage" },
|
|
49
|
+
];
|
|
50
|
+
const getSearchableText = (item) => `${item.name} ${item.description}`;
|
|
51
|
+
beforeEach(() => {
|
|
52
|
+
vi.clearAllMocks();
|
|
53
|
+
});
|
|
54
|
+
describe("createHybridSearchIndex", () => {
|
|
55
|
+
it("should create an index with all methods", () => {
|
|
56
|
+
const index = createHybridSearchIndex(testItems, getSearchableText);
|
|
57
|
+
expect(index.search).toBeDefined();
|
|
58
|
+
expect(index.searchBM25Only).toBeDefined();
|
|
59
|
+
expect(index.precomputeEmbeddings).toBeDefined();
|
|
60
|
+
expect(index.isSemanticReady).toBeDefined();
|
|
61
|
+
});
|
|
62
|
+
it("should start with semantic not ready", () => {
|
|
63
|
+
const index = createHybridSearchIndex(testItems, getSearchableText);
|
|
64
|
+
expect(index.isSemanticReady()).toBe(false);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
describe("searchBM25Only", () => {
|
|
68
|
+
it("should return BM25 results synchronously", () => {
|
|
69
|
+
const index = createHybridSearchIndex(testItems, getSearchableText);
|
|
70
|
+
const results = index.searchBM25Only("compress");
|
|
71
|
+
expect(results.length).toBeGreaterThan(0);
|
|
72
|
+
expect(results[0].item.name).toBe("compress");
|
|
73
|
+
});
|
|
74
|
+
it("should return empty array for no matches", () => {
|
|
75
|
+
const index = createHybridSearchIndex(testItems, getSearchableText);
|
|
76
|
+
const results = index.searchBM25Only("nonexistent");
|
|
77
|
+
expect(results).toHaveLength(0);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
describe("search (hybrid)", () => {
|
|
81
|
+
it("should return results with both BM25 and semantic scores", async () => {
|
|
82
|
+
const index = createHybridSearchIndex(testItems, getSearchableText);
|
|
83
|
+
const results = await index.search("compress output");
|
|
84
|
+
expect(results.length).toBeGreaterThan(0);
|
|
85
|
+
expect(results[0]).toHaveProperty("bm25Score");
|
|
86
|
+
expect(results[0]).toHaveProperty("semanticScore");
|
|
87
|
+
expect(results[0]).toHaveProperty("score");
|
|
88
|
+
expect(results[0]).toHaveProperty("matchedTerms");
|
|
89
|
+
});
|
|
90
|
+
it("should find exact keyword matches", async () => {
|
|
91
|
+
const index = createHybridSearchIndex(testItems, getSearchableText);
|
|
92
|
+
const results = await index.search("compress");
|
|
93
|
+
expect(results.length).toBeGreaterThan(0);
|
|
94
|
+
expect(results[0].item.name).toBe("compress");
|
|
95
|
+
expect(results[0].bm25Score).toBeGreaterThan(0);
|
|
96
|
+
});
|
|
97
|
+
it("should sort results by combined score", async () => {
|
|
98
|
+
const index = createHybridSearchIndex(testItems, getSearchableText);
|
|
99
|
+
const results = await index.search("compress reduce");
|
|
100
|
+
for (let i = 1; i < results.length; i++) {
|
|
101
|
+
expect(results[i - 1].score).toBeGreaterThanOrEqual(results[i].score);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
it("should handle empty query", async () => {
|
|
105
|
+
const index = createHybridSearchIndex(testItems, getSearchableText);
|
|
106
|
+
const results = await index.search("");
|
|
107
|
+
// Empty query might return all items with semantic similarity
|
|
108
|
+
// or no results depending on implementation
|
|
109
|
+
expect(Array.isArray(results)).toBe(true);
|
|
110
|
+
});
|
|
111
|
+
it("should use custom weights", async () => {
|
|
112
|
+
const index = createHybridSearchIndex(testItems, getSearchableText, {
|
|
113
|
+
bm25Weight: 0.8,
|
|
114
|
+
semanticWeight: 0.2,
|
|
115
|
+
});
|
|
116
|
+
const results = await index.search("compress");
|
|
117
|
+
expect(results.length).toBeGreaterThan(0);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
describe("precomputeEmbeddings", () => {
|
|
121
|
+
it("should mark semantic as ready after precomputing", async () => {
|
|
122
|
+
const index = createHybridSearchIndex(testItems, getSearchableText);
|
|
123
|
+
expect(index.isSemanticReady()).toBe(false);
|
|
124
|
+
await index.precomputeEmbeddings();
|
|
125
|
+
expect(index.isSemanticReady()).toBe(true);
|
|
126
|
+
});
|
|
127
|
+
it("should only compute embeddings once", async () => {
|
|
128
|
+
const { computeEmbeddings } = await import("./embeddings.js");
|
|
129
|
+
const index = createHybridSearchIndex(testItems, getSearchableText);
|
|
130
|
+
await index.precomputeEmbeddings();
|
|
131
|
+
await index.precomputeEmbeddings();
|
|
132
|
+
expect(computeEmbeddings).toHaveBeenCalledTimes(1);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
describe("edge cases", () => {
|
|
136
|
+
it("should handle empty items array", async () => {
|
|
137
|
+
const index = createHybridSearchIndex([], getSearchableText);
|
|
138
|
+
const bm25Results = index.searchBM25Only("test");
|
|
139
|
+
expect(bm25Results).toHaveLength(0);
|
|
140
|
+
const hybridResults = await index.search("test");
|
|
141
|
+
expect(hybridResults).toHaveLength(0);
|
|
142
|
+
});
|
|
143
|
+
it("should handle single item", async () => {
|
|
144
|
+
const singleItem = [{ name: "test", description: "Test item" }];
|
|
145
|
+
const index = createHybridSearchIndex(singleItem, getSearchableText);
|
|
146
|
+
const results = await index.search("test");
|
|
147
|
+
expect(results.length).toBeLessThanOrEqual(1);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
describe("score composition", () => {
|
|
151
|
+
it("should combine BM25 and semantic scores", async () => {
|
|
152
|
+
const index = createHybridSearchIndex(testItems, getSearchableText, {
|
|
153
|
+
bm25Weight: 0.4,
|
|
154
|
+
semanticWeight: 0.6,
|
|
155
|
+
});
|
|
156
|
+
const results = await index.search("compress");
|
|
157
|
+
const firstResult = results[0];
|
|
158
|
+
if (firstResult && firstResult.bm25Score > 0) {
|
|
159
|
+
// If there's a BM25 match, score should be combination
|
|
160
|
+
expect(firstResult.score).toBeGreaterThan(0);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
it("should include matched terms from BM25", async () => {
|
|
164
|
+
const index = createHybridSearchIndex(testItems, getSearchableText);
|
|
165
|
+
const results = await index.search("compress output");
|
|
166
|
+
const compressResult = results.find((r) => r.item.name === "compress");
|
|
167
|
+
if (compressResult) {
|
|
168
|
+
expect(compressResult.matchedTerms.length).toBeGreaterThan(0);
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export * from "./project-detector.js";
|
|
2
|
+
export * from "./command-normalizer.js";
|
|
3
|
+
export * from "./output-similarity.js";
|
|
4
|
+
export * from "./content-detector.js";
|
|
5
|
+
export * from "./language-detector.js";
|
|
6
|
+
export * from "./error-normalizer.js";
|
|
7
|
+
export * from "./signature-grouper.js";
|
|
8
|
+
export * from "./log-parser.js";
|
|
9
|
+
export * from "./token-counter.js";
|
|
10
|
+
export * from "./bm25.js";
|
|
11
|
+
export * from "./embeddings.js";
|
|
12
|
+
export * from "./hybrid-search.js";
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from "./project-detector.js";
|
|
2
|
+
export * from "./command-normalizer.js";
|
|
3
|
+
export * from "./output-similarity.js";
|
|
4
|
+
export * from "./content-detector.js";
|
|
5
|
+
export * from "./language-detector.js";
|
|
6
|
+
export * from "./error-normalizer.js";
|
|
7
|
+
export * from "./signature-grouper.js";
|
|
8
|
+
export * from "./log-parser.js";
|
|
9
|
+
export * from "./token-counter.js";
|
|
10
|
+
export * from "./bm25.js";
|
|
11
|
+
export * from "./embeddings.js";
|
|
12
|
+
export * from "./hybrid-search.js";
|