mdcontext 0.0.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.changeset/README.md +28 -0
- package/.changeset/config.json +11 -0
- package/.claude/settings.local.json +25 -0
- package/.github/workflows/ci.yml +83 -0
- package/.github/workflows/claude-code-review.yml +44 -0
- package/.github/workflows/claude.yml +85 -0
- package/.github/workflows/release.yml +113 -0
- package/.tldrignore +112 -0
- package/BACKLOG.md +338 -0
- package/CONTRIBUTING.md +186 -0
- package/NOTES/NOTES +44 -0
- package/README.md +434 -11
- package/biome.json +36 -0
- package/cspell.config.yaml +14 -0
- package/dist/chunk-23UPXDNL.js +3044 -0
- package/dist/chunk-2W7MO2DL.js +1366 -0
- package/dist/chunk-3NUAZGMA.js +1689 -0
- package/dist/chunk-7TOWB2XB.js +366 -0
- package/dist/chunk-7XOTOADQ.js +3065 -0
- package/dist/chunk-AH2PDM2K.js +3042 -0
- package/dist/chunk-BNXWSZ63.js +3742 -0
- package/dist/chunk-BTL5DJVU.js +3222 -0
- package/dist/chunk-HDHYG7E4.js +104 -0
- package/dist/chunk-HLR4KZBP.js +3234 -0
- package/dist/chunk-IP3FRFEB.js +1045 -0
- package/dist/chunk-KHU56VDO.js +3042 -0
- package/dist/chunk-KRYIFLQR.js +88 -0
- package/dist/chunk-LBSDNLEM.js +287 -0
- package/dist/chunk-MNTQ7HCP.js +2643 -0
- package/dist/chunk-MUJELQQ6.js +1387 -0
- package/dist/chunk-MXJGMSLV.js +2199 -0
- package/dist/chunk-N6QJGC3Z.js +2636 -0
- package/dist/chunk-OBELGBPM.js +1713 -0
- package/dist/chunk-OT7R5XTA.js +3192 -0
- package/dist/chunk-P7X4RA2T.js +106 -0
- package/dist/chunk-PIDUQNC2.js +3185 -0
- package/dist/chunk-POGCDIH4.js +3187 -0
- package/dist/chunk-PSIEOQGZ.js +3043 -0
- package/dist/chunk-PVRT3IHA.js +3238 -0
- package/dist/chunk-QNN4TT23.js +1430 -0
- package/dist/chunk-RE3R45RJ.js +3042 -0
- package/dist/chunk-S7E6TFX6.js +803 -0
- package/dist/chunk-SG6GLU4U.js +1378 -0
- package/dist/chunk-SJCDV2ST.js +274 -0
- package/dist/chunk-SYE5XLF3.js +104 -0
- package/dist/chunk-T5VLYBZD.js +103 -0
- package/dist/chunk-TOQB7VWU.js +3238 -0
- package/dist/chunk-VFNMZ4ZQ.js +3228 -0
- package/dist/chunk-VVTGZNBT.js +1629 -0
- package/dist/chunk-W7Q4RFEV.js +104 -0
- package/dist/chunk-XTYYVRLO.js +3190 -0
- package/dist/chunk-Y6MDYVJD.js +3063 -0
- package/dist/cli/main.d.ts +1 -0
- package/dist/cli/main.js +5458 -0
- package/dist/index.d.ts +653 -0
- package/dist/index.js +79 -0
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.js +472 -0
- package/dist/schema-BAWSG7KY.js +22 -0
- package/dist/schema-E3QUPL26.js +20 -0
- package/dist/schema-EHL7WUT6.js +20 -0
- package/docs/019-USAGE.md +625 -0
- package/docs/020-current-implementation.md +364 -0
- package/docs/021-DOGFOODING-FINDINGS.md +175 -0
- package/docs/BACKLOG.md +80 -0
- package/docs/CONFIG.md +1123 -0
- package/docs/DESIGN.md +439 -0
- package/docs/ERRORS.md +383 -0
- package/docs/PROJECT.md +88 -0
- package/docs/ROADMAP.md +407 -0
- package/docs/summarization.md +320 -0
- package/docs/test-links.md +9 -0
- package/justfile +40 -0
- package/package.json +74 -9
- package/pnpm-workspace.yaml +5 -0
- package/research/INDEX.md +315 -0
- package/research/code-review/README.md +90 -0
- package/research/code-review/cli-error-handling-review.md +979 -0
- package/research/code-review/code-review-validation-report.md +464 -0
- package/research/code-review/main-ts-review.md +1128 -0
- package/research/config-analysis/01-current-implementation.md +470 -0
- package/research/config-analysis/02-strategy-recommendation.md +428 -0
- package/research/config-analysis/03-task-candidates.md +715 -0
- package/research/config-analysis/033-research-configuration-management.md +828 -0
- package/research/config-analysis/034-research-effect-cli-config.md +1504 -0
- package/research/config-analysis/04-consolidated-task-candidates.md +277 -0
- package/research/config-docs/SUMMARY.md +357 -0
- package/research/config-docs/TEST-RESULTS.md +776 -0
- package/research/config-docs/TODO.md +542 -0
- package/research/config-docs/analysis.md +744 -0
- package/research/config-docs/fix-validation.md +502 -0
- package/research/config-docs/help-audit.md +264 -0
- package/research/config-docs/help-system-analysis.md +890 -0
- package/research/dogfood/consolidated-tool-evaluation.md +373 -0
- package/research/dogfood/strategy-a/a-synthesis.md +184 -0
- package/research/dogfood/strategy-a/a1-docs.md +226 -0
- package/research/dogfood/strategy-a/a2-amorphic.md +156 -0
- package/research/dogfood/strategy-a/a3-llm.md +164 -0
- package/research/dogfood/strategy-b/b-synthesis.md +228 -0
- package/research/dogfood/strategy-b/b1-architecture.md +207 -0
- package/research/dogfood/strategy-b/b2-gaps.md +258 -0
- package/research/dogfood/strategy-b/b3-workflows.md +250 -0
- package/research/dogfood/strategy-c/c-synthesis.md +451 -0
- package/research/dogfood/strategy-c/c1-explorer.md +192 -0
- package/research/dogfood/strategy-c/c2-diver-memory.md +145 -0
- package/research/dogfood/strategy-c/c3-diver-control.md +148 -0
- package/research/dogfood/strategy-c/c4-diver-failure.md +151 -0
- package/research/dogfood/strategy-c/c5-diver-execution.md +221 -0
- package/research/dogfood/strategy-c/c6-diver-org.md +221 -0
- package/research/effect-cli-error-handling.md +845 -0
- package/research/effect-errors-as-values.md +943 -0
- package/research/errors-task-analysis/00-consolidated-tasks.md +207 -0
- package/research/errors-task-analysis/cli-commands-analysis.md +909 -0
- package/research/errors-task-analysis/embeddings-analysis.md +709 -0
- package/research/errors-task-analysis/index-search-analysis.md +812 -0
- package/research/frontmatter/COMMENTS-ARE-SKIPPED.md +149 -0
- package/research/frontmatter/LLM-CODE-NAVIGATION.md +276 -0
- package/research/issue-review.md +603 -0
- package/research/llm-summarization/agent-cli-tools-2026.md +1082 -0
- package/research/llm-summarization/alternative-providers-2026.md +1428 -0
- package/research/llm-summarization/anthropic-2026.md +367 -0
- package/research/llm-summarization/claude-cli-integration.md +1706 -0
- package/research/llm-summarization/cli-integration-patterns.md +3155 -0
- package/research/llm-summarization/openai-2026.md +473 -0
- package/research/llm-summarization/openai-compatible-providers-2026.md +1022 -0
- package/research/llm-summarization/opencode-cli-integration.md +1552 -0
- package/research/llm-summarization/prompt-engineering-2026.md +1426 -0
- package/research/llm-summarization/prototype-results.md +56 -0
- package/research/llm-summarization/provider-switching-patterns-2026.md +2153 -0
- package/research/llm-summarization/typescript-llm-libraries-2026.md +2436 -0
- package/research/mdcontext-error-analysis.md +521 -0
- package/research/mdcontext-pudding/00-EXECUTIVE-SUMMARY.md +282 -0
- package/research/mdcontext-pudding/01-index-embed.md +956 -0
- package/research/mdcontext-pudding/02-search-COMMANDS.md +142 -0
- package/research/mdcontext-pudding/02-search-SUMMARY.md +146 -0
- package/research/mdcontext-pudding/02-search.md +970 -0
- package/research/mdcontext-pudding/03-context.md +779 -0
- package/research/mdcontext-pudding/04-navigation-and-analytics.md +803 -0
- package/research/mdcontext-pudding/04-tree.md +704 -0
- package/research/mdcontext-pudding/05-config.md +1038 -0
- package/research/mdcontext-pudding/06-links-summary.txt +87 -0
- package/research/mdcontext-pudding/06-links.md +679 -0
- package/research/mdcontext-pudding/07-stats.md +693 -0
- package/research/mdcontext-pudding/BUG-FIX-PLAN.md +388 -0
- package/research/mdcontext-pudding/P0-BUG-VALIDATION.md +167 -0
- package/research/mdcontext-pudding/README.md +168 -0
- package/research/mdcontext-pudding/TESTING-SUMMARY.md +128 -0
- package/research/npm_publish/011-npm-workflow-research-agent2.md +792 -0
- package/research/npm_publish/012-npm-workflow-research-agent1.md +530 -0
- package/research/npm_publish/013-npm-workflow-research-agent3.md +722 -0
- package/research/npm_publish/014-npm-workflow-synthesis.md +556 -0
- package/research/npm_publish/031-npm-workflow-task-analysis.md +134 -0
- package/research/research-quality-review.md +834 -0
- package/research/semantic-search/002-research-embedding-models.md +490 -0
- package/research/semantic-search/003-research-rag-alternatives.md +523 -0
- package/research/semantic-search/004-research-vector-search.md +841 -0
- package/research/semantic-search/032-research-semantic-search.md +427 -0
- package/research/semantic-search/embedding-text-analysis.md +156 -0
- package/research/semantic-search/multi-word-failure-reproduction.md +171 -0
- package/research/semantic-search/query-processing-analysis.md +207 -0
- package/research/semantic-search/root-cause-and-solution.md +114 -0
- package/research/semantic-search/threshold-validation-report.md +69 -0
- package/research/semantic-search/vector-search-analysis.md +63 -0
- package/research/task-management-2026/00-synthesis-recommendations.md +295 -0
- package/research/task-management-2026/01-ai-workflow-tools.md +416 -0
- package/research/task-management-2026/02-agent-framework-patterns.md +476 -0
- package/research/task-management-2026/03-lightweight-file-based.md +567 -0
- package/research/task-management-2026/04-established-tools-ai-features.md +541 -0
- package/research/task-management-2026/linear/01-core-features-workflow.md +771 -0
- package/research/task-management-2026/linear/02-api-integrations.md +930 -0
- package/research/task-management-2026/linear/03-ai-features.md +368 -0
- package/research/task-management-2026/linear/04-pricing-setup.md +205 -0
- package/research/task-management-2026/linear/05-usage-patterns-best-practices.md +605 -0
- package/research/test-path-issues.md +276 -0
- package/review/ALP-76/1-error-type-design.md +962 -0
- package/review/ALP-76/2-error-handling-patterns.md +906 -0
- package/review/ALP-76/3-error-presentation.md +624 -0
- package/review/ALP-76/4-test-coverage.md +625 -0
- package/review/ALP-76/5-migration-completeness.md +440 -0
- package/review/ALP-76/6-effect-best-practices.md +755 -0
- package/scripts/apply-branch-protection.sh +47 -0
- package/scripts/branch-protection-templates.json +79 -0
- package/scripts/prototype-summarization.ts +346 -0
- package/scripts/rebuild-hnswlib.js +58 -0
- package/scripts/setup-branch-protection.sh +64 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/active-provider.json +7 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/bm25.json +541 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/bm25.meta.json +5 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/config.json +8 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.bin +0 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.meta.bin +0 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/indexes/documents.json +60 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/indexes/links.json +13 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/indexes/sections.json +1197 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/configuration-management.md +99 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/distributed-systems.md +92 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/error-handling.md +78 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/failure-automation.md +55 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/job-context.md +69 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/process-orchestration.md +99 -0
- package/src/cli/argv-preprocessor.test.ts +210 -0
- package/src/cli/argv-preprocessor.ts +202 -0
- package/src/cli/cli.test.ts +627 -0
- package/src/cli/commands/backlinks.ts +54 -0
- package/src/cli/commands/config-cmd.ts +642 -0
- package/src/cli/commands/context.ts +285 -0
- package/src/cli/commands/duplicates.ts +122 -0
- package/src/cli/commands/embeddings.ts +529 -0
- package/src/cli/commands/index-cmd.ts +480 -0
- package/src/cli/commands/index.ts +16 -0
- package/src/cli/commands/links.ts +52 -0
- package/src/cli/commands/search.ts +1281 -0
- package/src/cli/commands/stats.ts +149 -0
- package/src/cli/commands/tree.ts +128 -0
- package/src/cli/config-layer.ts +176 -0
- package/src/cli/error-handler.test.ts +235 -0
- package/src/cli/error-handler.ts +655 -0
- package/src/cli/flag-schemas.ts +341 -0
- package/src/cli/help.ts +588 -0
- package/src/cli/index.ts +9 -0
- package/src/cli/main.ts +435 -0
- package/src/cli/options.ts +41 -0
- package/src/cli/shared-error-handling.ts +199 -0
- package/src/cli/typo-suggester.test.ts +105 -0
- package/src/cli/typo-suggester.ts +130 -0
- package/src/cli/utils.ts +259 -0
- package/src/config/file-provider.test.ts +320 -0
- package/src/config/file-provider.ts +273 -0
- package/src/config/index.ts +72 -0
- package/src/config/integration.test.ts +667 -0
- package/src/config/precedence.test.ts +277 -0
- package/src/config/precedence.ts +451 -0
- package/src/config/schema.test.ts +414 -0
- package/src/config/schema.ts +603 -0
- package/src/config/service.test.ts +320 -0
- package/src/config/service.ts +243 -0
- package/src/config/testing.test.ts +264 -0
- package/src/config/testing.ts +110 -0
- package/src/core/index.ts +1 -0
- package/src/core/types.ts +113 -0
- package/src/duplicates/detector.test.ts +183 -0
- package/src/duplicates/detector.ts +414 -0
- package/src/duplicates/index.ts +18 -0
- package/src/embeddings/embedding-namespace.test.ts +300 -0
- package/src/embeddings/embedding-namespace.ts +947 -0
- package/src/embeddings/heading-boost.test.ts +222 -0
- package/src/embeddings/hnsw-build-options.test.ts +198 -0
- package/src/embeddings/hyde.test.ts +272 -0
- package/src/embeddings/hyde.ts +264 -0
- package/src/embeddings/index.ts +10 -0
- package/src/embeddings/openai-provider.ts +414 -0
- package/src/embeddings/pricing.json +22 -0
- package/src/embeddings/provider-constants.ts +204 -0
- package/src/embeddings/provider-errors.test.ts +967 -0
- package/src/embeddings/provider-errors.ts +565 -0
- package/src/embeddings/provider-factory.test.ts +240 -0
- package/src/embeddings/provider-factory.ts +225 -0
- package/src/embeddings/provider-integration.test.ts +788 -0
- package/src/embeddings/query-preprocessing.test.ts +187 -0
- package/src/embeddings/semantic-search-threshold.test.ts +508 -0
- package/src/embeddings/semantic-search.ts +1270 -0
- package/src/embeddings/types.ts +359 -0
- package/src/embeddings/vector-store.ts +708 -0
- package/src/embeddings/voyage-provider.ts +313 -0
- package/src/errors/errors.test.ts +845 -0
- package/src/errors/index.ts +533 -0
- package/src/index/ignore-patterns.test.ts +354 -0
- package/src/index/ignore-patterns.ts +305 -0
- package/src/index/index.ts +4 -0
- package/src/index/indexer.ts +684 -0
- package/src/index/storage.ts +260 -0
- package/src/index/types.ts +147 -0
- package/src/index/watcher.ts +189 -0
- package/src/index.ts +30 -0
- package/src/integration/search-keyword.test.ts +678 -0
- package/src/mcp/server.ts +612 -0
- package/src/parser/index.ts +1 -0
- package/src/parser/parser.test.ts +291 -0
- package/src/parser/parser.ts +394 -0
- package/src/parser/section-filter.test.ts +277 -0
- package/src/parser/section-filter.ts +392 -0
- package/src/search/__tests__/hybrid-search.test.ts +650 -0
- package/src/search/bm25-store.ts +366 -0
- package/src/search/cross-encoder.test.ts +253 -0
- package/src/search/cross-encoder.ts +406 -0
- package/src/search/fuzzy-search.test.ts +419 -0
- package/src/search/fuzzy-search.ts +273 -0
- package/src/search/hybrid-search.ts +448 -0
- package/src/search/path-matcher.test.ts +276 -0
- package/src/search/path-matcher.ts +33 -0
- package/src/search/query-parser.test.ts +260 -0
- package/src/search/query-parser.ts +319 -0
- package/src/search/searcher.test.ts +280 -0
- package/src/search/searcher.ts +724 -0
- package/src/search/wink-bm25.d.ts +30 -0
- package/src/summarization/cli-providers/claude.ts +202 -0
- package/src/summarization/cli-providers/detection.test.ts +273 -0
- package/src/summarization/cli-providers/detection.ts +118 -0
- package/src/summarization/cli-providers/index.ts +8 -0
- package/src/summarization/cost.test.ts +139 -0
- package/src/summarization/cost.ts +102 -0
- package/src/summarization/error-handler.test.ts +127 -0
- package/src/summarization/error-handler.ts +111 -0
- package/src/summarization/index.ts +102 -0
- package/src/summarization/pipeline.test.ts +498 -0
- package/src/summarization/pipeline.ts +231 -0
- package/src/summarization/prompts.test.ts +269 -0
- package/src/summarization/prompts.ts +133 -0
- package/src/summarization/provider-factory.test.ts +396 -0
- package/src/summarization/provider-factory.ts +178 -0
- package/src/summarization/types.ts +184 -0
- package/src/summarize/budget-bugs.test.ts +620 -0
- package/src/summarize/formatters.ts +419 -0
- package/src/summarize/index.ts +20 -0
- package/src/summarize/summarizer.test.ts +275 -0
- package/src/summarize/summarizer.ts +597 -0
- package/src/summarize/verify-bugs.test.ts +238 -0
- package/src/types/huggingface-transformers.d.ts +66 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/tokens.test.ts +142 -0
- package/src/utils/tokens.ts +186 -0
- package/tests/fixtures/cli/.mdcontext/active-provider.json +7 -0
- package/tests/fixtures/cli/.mdcontext/config.json +8 -0
- package/tests/fixtures/cli/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.bin +0 -0
- package/tests/fixtures/cli/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.meta.bin +0 -0
- package/tests/fixtures/cli/.mdcontext/indexes/documents.json +33 -0
- package/tests/fixtures/cli/.mdcontext/indexes/links.json +12 -0
- package/tests/fixtures/cli/.mdcontext/indexes/sections.json +247 -0
- package/tests/fixtures/cli/README.md +9 -0
- package/tests/fixtures/cli/api-reference.md +11 -0
- package/tests/fixtures/cli/getting-started.md +11 -0
- package/tests/integration/embed-index.test.ts +712 -0
- package/tests/integration/search-context.test.ts +469 -0
- package/tests/integration/search-semantic.test.ts +522 -0
- package/tsconfig.json +26 -0
- package/vitest.config.ts +16 -0
- package/vitest.setup.ts +12 -0
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration tests for semantic search with CLI flags
|
|
3
|
+
*
|
|
4
|
+
* Tests semantic search functionality including:
|
|
5
|
+
* - Basic semantic search
|
|
6
|
+
* - Context flags (-C, -A, -B)
|
|
7
|
+
* - Threshold filtering (--threshold)
|
|
8
|
+
* - Result limits (--limit)
|
|
9
|
+
* - Edge cases (no results, below threshold)
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import * as fs from 'node:fs/promises'
|
|
13
|
+
import * as path from 'node:path'
|
|
14
|
+
import { Effect } from 'effect'
|
|
15
|
+
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
|
|
16
|
+
import {
|
|
17
|
+
buildEmbeddings,
|
|
18
|
+
semanticSearchWithStats,
|
|
19
|
+
} from '../../src/embeddings/semantic-search.js'
|
|
20
|
+
import { buildIndex } from '../../src/index/indexer.js'
|
|
21
|
+
|
|
22
|
+
const TEST_DIR = path.join(
|
|
23
|
+
process.cwd(),
|
|
24
|
+
'tests',
|
|
25
|
+
'fixtures',
|
|
26
|
+
'semantic-search',
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
const runEffect = <A, E>(effect: Effect.Effect<A, E>) =>
|
|
30
|
+
Effect.runPromise(effect)
|
|
31
|
+
|
|
32
|
+
const skipIfNoApiKey = () => {
|
|
33
|
+
if (!process.env.OPENAI_API_KEY && !process.env.INCLUDE_EMBED_TESTS) {
|
|
34
|
+
return true
|
|
35
|
+
}
|
|
36
|
+
return false
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
describe('semantic search integration', () => {
|
|
40
|
+
beforeAll(async () => {
|
|
41
|
+
if (skipIfNoApiKey()) {
|
|
42
|
+
console.log(
|
|
43
|
+
'Skipping semantic search tests (set OPENAI_API_KEY or INCLUDE_EMBED_TESTS=true)',
|
|
44
|
+
)
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
await fs.mkdir(TEST_DIR, { recursive: true })
|
|
49
|
+
|
|
50
|
+
await fs.writeFile(
|
|
51
|
+
path.join(TEST_DIR, 'authentication.md'),
|
|
52
|
+
`# Authentication Guide
|
|
53
|
+
|
|
54
|
+
## Overview
|
|
55
|
+
|
|
56
|
+
This document explains how authentication works in the system.
|
|
57
|
+
|
|
58
|
+
## User Login
|
|
59
|
+
|
|
60
|
+
Users can log in using email and password. The system validates credentials
|
|
61
|
+
against the database and returns a JWT token for subsequent requests.
|
|
62
|
+
|
|
63
|
+
### Password Requirements
|
|
64
|
+
|
|
65
|
+
Passwords must be at least 8 characters and contain uppercase, lowercase,
|
|
66
|
+
numbers, and special characters.
|
|
67
|
+
|
|
68
|
+
## OAuth Integration
|
|
69
|
+
|
|
70
|
+
The system supports OAuth 2.0 for third-party authentication providers
|
|
71
|
+
including Google, GitHub, and Microsoft.
|
|
72
|
+
|
|
73
|
+
### Configuration
|
|
74
|
+
|
|
75
|
+
Set the following environment variables:
|
|
76
|
+
- OAUTH_CLIENT_ID
|
|
77
|
+
- OAUTH_CLIENT_SECRET
|
|
78
|
+
- OAUTH_REDIRECT_URI
|
|
79
|
+
|
|
80
|
+
## Session Management
|
|
81
|
+
|
|
82
|
+
Sessions are stored in Redis with a 30-minute expiration time.
|
|
83
|
+
Sessions can be refreshed by calling the /refresh endpoint.
|
|
84
|
+
|
|
85
|
+
## Security Considerations
|
|
86
|
+
|
|
87
|
+
Always use HTTPS in production. Never store passwords in plain text.
|
|
88
|
+
Implement rate limiting on authentication endpoints to prevent brute force attacks.
|
|
89
|
+
`,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
await fs.writeFile(
|
|
93
|
+
path.join(TEST_DIR, 'database.md'),
|
|
94
|
+
`# Database Schema
|
|
95
|
+
|
|
96
|
+
## Tables
|
|
97
|
+
|
|
98
|
+
### users
|
|
99
|
+
|
|
100
|
+
The users table stores account information.
|
|
101
|
+
|
|
102
|
+
Columns:
|
|
103
|
+
- id (primary key)
|
|
104
|
+
- email (unique)
|
|
105
|
+
- password_hash
|
|
106
|
+
- created_at
|
|
107
|
+
- updated_at
|
|
108
|
+
|
|
109
|
+
### sessions
|
|
110
|
+
|
|
111
|
+
The sessions table tracks active user sessions.
|
|
112
|
+
|
|
113
|
+
Columns:
|
|
114
|
+
- id (primary key)
|
|
115
|
+
- user_id (foreign key)
|
|
116
|
+
- token
|
|
117
|
+
- expires_at
|
|
118
|
+
|
|
119
|
+
## Migrations
|
|
120
|
+
|
|
121
|
+
Database migrations are managed using a migration tool.
|
|
122
|
+
Run migrations before deploying new code.
|
|
123
|
+
|
|
124
|
+
## Backup Strategy
|
|
125
|
+
|
|
126
|
+
Daily backups are stored in S3 with 30-day retention.
|
|
127
|
+
Point-in-time recovery is available for the last 7 days.
|
|
128
|
+
`,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
await fs.writeFile(
|
|
132
|
+
path.join(TEST_DIR, 'api.md'),
|
|
133
|
+
`# API Documentation
|
|
134
|
+
|
|
135
|
+
## REST Endpoints
|
|
136
|
+
|
|
137
|
+
### Authentication Endpoints
|
|
138
|
+
|
|
139
|
+
POST /api/auth/login - Authenticate user and get token
|
|
140
|
+
POST /api/auth/logout - Invalidate session
|
|
141
|
+
POST /api/auth/refresh - Refresh authentication token
|
|
142
|
+
|
|
143
|
+
### User Endpoints
|
|
144
|
+
|
|
145
|
+
GET /api/users/:id - Get user profile
|
|
146
|
+
PUT /api/users/:id - Update user profile
|
|
147
|
+
DELETE /api/users/:id - Delete user account
|
|
148
|
+
|
|
149
|
+
## Rate Limiting
|
|
150
|
+
|
|
151
|
+
API endpoints are rate limited to 100 requests per minute per IP address.
|
|
152
|
+
Authenticated users get 1000 requests per minute.
|
|
153
|
+
|
|
154
|
+
## Error Handling
|
|
155
|
+
|
|
156
|
+
All errors return JSON with status code and message.
|
|
157
|
+
Use the error code to determine appropriate retry logic.
|
|
158
|
+
`,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
await fs.writeFile(
|
|
162
|
+
path.join(TEST_DIR, 'deployment.md'),
|
|
163
|
+
`# Deployment Guide
|
|
164
|
+
|
|
165
|
+
## Production Setup
|
|
166
|
+
|
|
167
|
+
Deploy to AWS using Docker containers.
|
|
168
|
+
Use environment-specific configuration files.
|
|
169
|
+
|
|
170
|
+
## Environment Variables
|
|
171
|
+
|
|
172
|
+
Required variables:
|
|
173
|
+
- DATABASE_URL
|
|
174
|
+
- REDIS_URL
|
|
175
|
+
- JWT_SECRET
|
|
176
|
+
- OAUTH_CLIENT_ID
|
|
177
|
+
|
|
178
|
+
## Monitoring
|
|
179
|
+
|
|
180
|
+
Set up CloudWatch alarms for:
|
|
181
|
+
- High error rates
|
|
182
|
+
- Slow response times
|
|
183
|
+
- Database connection issues
|
|
184
|
+
|
|
185
|
+
## Rollback Procedure
|
|
186
|
+
|
|
187
|
+
If deployment fails, rollback using the previous Docker image tag.
|
|
188
|
+
Database migrations cannot be automatically rolled back.
|
|
189
|
+
`,
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
const shouldRebuild = process.env.REBUILD_TEST_INDEX === 'true'
|
|
193
|
+
|
|
194
|
+
await runEffect(buildIndex(TEST_DIR, { force: shouldRebuild }))
|
|
195
|
+
|
|
196
|
+
await runEffect(
|
|
197
|
+
buildEmbeddings(TEST_DIR, {
|
|
198
|
+
force: shouldRebuild,
|
|
199
|
+
}),
|
|
200
|
+
)
|
|
201
|
+
}, 60000)
|
|
202
|
+
|
|
203
|
+
afterAll(async () => {
|
|
204
|
+
if (skipIfNoApiKey()) return
|
|
205
|
+
await fs.rm(TEST_DIR, { recursive: true, force: true })
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
describe('basic semantic search', () => {
|
|
209
|
+
it('should find results for natural language query', async () => {
|
|
210
|
+
if (skipIfNoApiKey()) return
|
|
211
|
+
|
|
212
|
+
const result = await runEffect(
|
|
213
|
+
semanticSearchWithStats(TEST_DIR, 'how does user login work', {
|
|
214
|
+
limit: 5,
|
|
215
|
+
threshold: 0.3,
|
|
216
|
+
}),
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
expect(result.results.length).toBeGreaterThan(0)
|
|
220
|
+
expect(result.results[0]?.similarity).toBeGreaterThanOrEqual(0.3)
|
|
221
|
+
|
|
222
|
+
const topResult = result.results[0]
|
|
223
|
+
expect(topResult?.documentPath).toContain('.md')
|
|
224
|
+
expect(topResult?.heading).toBeTruthy()
|
|
225
|
+
expect(topResult?.sectionId).toBeTruthy()
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
it('should return results sorted by similarity score', async () => {
|
|
229
|
+
if (skipIfNoApiKey()) return
|
|
230
|
+
|
|
231
|
+
const result = await runEffect(
|
|
232
|
+
semanticSearchWithStats(TEST_DIR, 'authentication', {
|
|
233
|
+
limit: 10,
|
|
234
|
+
threshold: 0.2,
|
|
235
|
+
}),
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
expect(result.results.length).toBeGreaterThan(1)
|
|
239
|
+
|
|
240
|
+
for (let i = 1; i < result.results.length; i++) {
|
|
241
|
+
const prev = result.results[i - 1]
|
|
242
|
+
const curr = result.results[i]
|
|
243
|
+
expect(prev?.similarity).toBeGreaterThanOrEqual(curr?.similarity ?? 0)
|
|
244
|
+
}
|
|
245
|
+
})
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
describe('--threshold flag', () => {
|
|
249
|
+
it('should filter results below threshold', async () => {
|
|
250
|
+
if (skipIfNoApiKey()) return
|
|
251
|
+
|
|
252
|
+
const lowThreshold = await runEffect(
|
|
253
|
+
semanticSearchWithStats(TEST_DIR, 'deployment', {
|
|
254
|
+
limit: 10,
|
|
255
|
+
threshold: 0.2,
|
|
256
|
+
}),
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
const highThreshold = await runEffect(
|
|
260
|
+
semanticSearchWithStats(TEST_DIR, 'deployment', {
|
|
261
|
+
limit: 10,
|
|
262
|
+
threshold: 0.5,
|
|
263
|
+
}),
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
expect(lowThreshold.results.length).toBeGreaterThanOrEqual(
|
|
267
|
+
highThreshold.results.length,
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
for (const result of highThreshold.results) {
|
|
271
|
+
expect(result.similarity).toBeGreaterThanOrEqual(0.5)
|
|
272
|
+
}
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
it('should provide stats when no results meet threshold', async () => {
|
|
276
|
+
if (skipIfNoApiKey()) return
|
|
277
|
+
|
|
278
|
+
const result = await runEffect(
|
|
279
|
+
semanticSearchWithStats(TEST_DIR, 'authentication', {
|
|
280
|
+
limit: 5,
|
|
281
|
+
threshold: 0.99,
|
|
282
|
+
}),
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
expect(result.results.length).toBe(0)
|
|
286
|
+
|
|
287
|
+
if (result.belowThresholdCount !== undefined) {
|
|
288
|
+
expect(result.belowThresholdCount).toBeGreaterThan(0)
|
|
289
|
+
expect(result.belowThresholdHighest).toBeDefined()
|
|
290
|
+
expect(result.belowThresholdHighest).toBeLessThan(0.99)
|
|
291
|
+
}
|
|
292
|
+
})
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
describe('--limit flag', () => {
|
|
296
|
+
it('should respect limit parameter', async () => {
|
|
297
|
+
if (skipIfNoApiKey()) return
|
|
298
|
+
|
|
299
|
+
const result = await runEffect(
|
|
300
|
+
semanticSearchWithStats(TEST_DIR, 'api endpoints', {
|
|
301
|
+
limit: 3,
|
|
302
|
+
threshold: 0.2,
|
|
303
|
+
}),
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
expect(result.results.length).toBeLessThanOrEqual(3)
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
it('should provide totalAvailable when results exceed limit', async () => {
|
|
310
|
+
if (skipIfNoApiKey()) return
|
|
311
|
+
|
|
312
|
+
const result = await runEffect(
|
|
313
|
+
semanticSearchWithStats(TEST_DIR, 'user', {
|
|
314
|
+
limit: 2,
|
|
315
|
+
threshold: 0.2,
|
|
316
|
+
}),
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
if (result.totalAvailable !== undefined) {
|
|
320
|
+
expect(result.totalAvailable).toBeGreaterThanOrEqual(
|
|
321
|
+
result.results.length,
|
|
322
|
+
)
|
|
323
|
+
}
|
|
324
|
+
})
|
|
325
|
+
|
|
326
|
+
it('should handle limit larger than available results', async () => {
|
|
327
|
+
if (skipIfNoApiKey()) return
|
|
328
|
+
|
|
329
|
+
const result = await runEffect(
|
|
330
|
+
semanticSearchWithStats(TEST_DIR, 'authentication', {
|
|
331
|
+
limit: 100,
|
|
332
|
+
threshold: 0.3,
|
|
333
|
+
}),
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
expect(result.results.length).toBeLessThan(100)
|
|
337
|
+
expect(result.results.length).toBeGreaterThan(0)
|
|
338
|
+
})
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
describe('context flags (-C, -A, -B)', () => {
|
|
342
|
+
it('should include context lines when context flags are provided', async () => {
|
|
343
|
+
if (skipIfNoApiKey()) return
|
|
344
|
+
|
|
345
|
+
const result = await runEffect(
|
|
346
|
+
semanticSearchWithStats(TEST_DIR, 'authentication', {
|
|
347
|
+
limit: 5,
|
|
348
|
+
threshold: 0.3,
|
|
349
|
+
contextBefore: 2,
|
|
350
|
+
contextAfter: 2,
|
|
351
|
+
}),
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
expect(result.results.length).toBeGreaterThan(0)
|
|
355
|
+
|
|
356
|
+
const firstResult = result.results[0]
|
|
357
|
+
expect(firstResult?.contextLines).toBeDefined()
|
|
358
|
+
expect(firstResult!.contextLines!.length).toBeGreaterThan(0)
|
|
359
|
+
|
|
360
|
+
const matchingLine = firstResult!.contextLines!.find((ctx) => ctx.isMatch)
|
|
361
|
+
expect(matchingLine).toBeDefined()
|
|
362
|
+
expect(matchingLine?.lineNumber).toBeGreaterThan(0)
|
|
363
|
+
})
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
describe('edge cases', () => {
|
|
367
|
+
it('should handle query with no relevant results', async () => {
|
|
368
|
+
if (skipIfNoApiKey()) return
|
|
369
|
+
|
|
370
|
+
const result = await runEffect(
|
|
371
|
+
semanticSearchWithStats(TEST_DIR, 'quantum physics blockchain AI', {
|
|
372
|
+
limit: 5,
|
|
373
|
+
threshold: 0.7,
|
|
374
|
+
}),
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
expect(result.results.length).toBe(0)
|
|
378
|
+
})
|
|
379
|
+
|
|
380
|
+
it('should handle very short query', async () => {
|
|
381
|
+
if (skipIfNoApiKey()) return
|
|
382
|
+
|
|
383
|
+
const result = await runEffect(
|
|
384
|
+
semanticSearchWithStats(TEST_DIR, 'api', {
|
|
385
|
+
limit: 5,
|
|
386
|
+
threshold: 0.3,
|
|
387
|
+
}),
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
expect(result.results.length).toBeGreaterThanOrEqual(0)
|
|
391
|
+
})
|
|
392
|
+
|
|
393
|
+
it('should handle very long query', async () => {
|
|
394
|
+
if (skipIfNoApiKey()) return
|
|
395
|
+
|
|
396
|
+
const longQuery =
|
|
397
|
+
'I need to understand how the authentication system works including user login with email and password, OAuth integration with third-party providers like Google and GitHub, session management with Redis, and security best practices for production deployment'
|
|
398
|
+
|
|
399
|
+
const result = await runEffect(
|
|
400
|
+
semanticSearchWithStats(TEST_DIR, longQuery, {
|
|
401
|
+
limit: 5,
|
|
402
|
+
threshold: 0.3,
|
|
403
|
+
}),
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
expect(result.results.length).toBeGreaterThan(0)
|
|
407
|
+
})
|
|
408
|
+
|
|
409
|
+
it('should handle query with special characters', async () => {
|
|
410
|
+
if (skipIfNoApiKey()) return
|
|
411
|
+
|
|
412
|
+
const result = await runEffect(
|
|
413
|
+
semanticSearchWithStats(TEST_DIR, 'OAuth 2.0 & JWT tokens', {
|
|
414
|
+
limit: 5,
|
|
415
|
+
threshold: 0.3,
|
|
416
|
+
}),
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
expect(result.results.length).toBeGreaterThanOrEqual(0)
|
|
420
|
+
})
|
|
421
|
+
})
|
|
422
|
+
|
|
423
|
+
describe('result format validation', () => {
|
|
424
|
+
it('should return correctly structured results', async () => {
|
|
425
|
+
if (skipIfNoApiKey()) return
|
|
426
|
+
|
|
427
|
+
const result = await runEffect(
|
|
428
|
+
semanticSearchWithStats(TEST_DIR, 'database schema', {
|
|
429
|
+
limit: 5,
|
|
430
|
+
threshold: 0.3,
|
|
431
|
+
}),
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
expect(result).toHaveProperty('results')
|
|
435
|
+
expect(Array.isArray(result.results)).toBe(true)
|
|
436
|
+
|
|
437
|
+
if (result.results.length > 0) {
|
|
438
|
+
const firstResult = result.results[0]
|
|
439
|
+
expect(firstResult).toHaveProperty('sectionId')
|
|
440
|
+
expect(firstResult).toHaveProperty('documentPath')
|
|
441
|
+
expect(firstResult).toHaveProperty('heading')
|
|
442
|
+
expect(firstResult).toHaveProperty('similarity')
|
|
443
|
+
|
|
444
|
+
expect(typeof firstResult?.sectionId).toBe('string')
|
|
445
|
+
expect(typeof firstResult?.documentPath).toBe('string')
|
|
446
|
+
expect(typeof firstResult?.heading).toBe('string')
|
|
447
|
+
expect(typeof firstResult?.similarity).toBe('number')
|
|
448
|
+
|
|
449
|
+
expect(firstResult?.similarity).toBeGreaterThan(0)
|
|
450
|
+
expect(firstResult?.similarity).toBeLessThanOrEqual(1)
|
|
451
|
+
}
|
|
452
|
+
})
|
|
453
|
+
})
|
|
454
|
+
|
|
455
|
+
describe('multi-document search', () => {
|
|
456
|
+
it('should search across multiple documents', async () => {
|
|
457
|
+
if (skipIfNoApiKey()) return
|
|
458
|
+
|
|
459
|
+
const result = await runEffect(
|
|
460
|
+
semanticSearchWithStats(
|
|
461
|
+
TEST_DIR,
|
|
462
|
+
'configuration environment variables',
|
|
463
|
+
{
|
|
464
|
+
limit: 10,
|
|
465
|
+
threshold: 0.3,
|
|
466
|
+
},
|
|
467
|
+
),
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
expect(result.results.length).toBeGreaterThan(0)
|
|
471
|
+
|
|
472
|
+
const uniqueDocs = new Set(result.results.map((r) => r.documentPath))
|
|
473
|
+
expect(uniqueDocs.size).toBeGreaterThan(1)
|
|
474
|
+
})
|
|
475
|
+
|
|
476
|
+
it('should find related content across different sections', async () => {
|
|
477
|
+
if (skipIfNoApiKey()) return
|
|
478
|
+
|
|
479
|
+
const result = await runEffect(
|
|
480
|
+
semanticSearchWithStats(TEST_DIR, 'security and passwords', {
|
|
481
|
+
limit: 10,
|
|
482
|
+
threshold: 0.3,
|
|
483
|
+
}),
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
expect(result.results.length).toBeGreaterThan(0)
|
|
487
|
+
})
|
|
488
|
+
})
|
|
489
|
+
|
|
490
|
+
describe('similarity score behavior', () => {
|
|
491
|
+
it('should give higher scores for exact topic matches', async () => {
|
|
492
|
+
if (skipIfNoApiKey()) return
|
|
493
|
+
|
|
494
|
+
const authResult = await runEffect(
|
|
495
|
+
semanticSearchWithStats(TEST_DIR, 'OAuth authentication', {
|
|
496
|
+
limit: 5,
|
|
497
|
+
threshold: 0.2,
|
|
498
|
+
}),
|
|
499
|
+
)
|
|
500
|
+
|
|
501
|
+
const unrelatedResult = await runEffect(
|
|
502
|
+
semanticSearchWithStats(TEST_DIR, 'database backup', {
|
|
503
|
+
limit: 5,
|
|
504
|
+
threshold: 0.2,
|
|
505
|
+
}),
|
|
506
|
+
)
|
|
507
|
+
|
|
508
|
+
if (authResult.results.length > 0 && unrelatedResult.results.length > 0) {
|
|
509
|
+
const authDoc = authResult.results.find((r) =>
|
|
510
|
+
r.documentPath.includes('authentication'),
|
|
511
|
+
)
|
|
512
|
+
const backupDoc = unrelatedResult.results.find((r) =>
|
|
513
|
+
r.documentPath.includes('database'),
|
|
514
|
+
)
|
|
515
|
+
|
|
516
|
+
if (authDoc && backupDoc) {
|
|
517
|
+
expect(authDoc.similarity).toBeGreaterThan(0.3)
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
})
|
|
521
|
+
})
|
|
522
|
+
})
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"rootDir": "./src",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true,
|
|
13
|
+
"declaration": true,
|
|
14
|
+
"declarationMap": true,
|
|
15
|
+
"sourceMap": true,
|
|
16
|
+
"resolveJsonModule": true,
|
|
17
|
+
"noUnusedLocals": true,
|
|
18
|
+
"noUnusedParameters": true,
|
|
19
|
+
"noImplicitReturns": true,
|
|
20
|
+
"noFallthroughCasesInSwitch": true,
|
|
21
|
+
"exactOptionalPropertyTypes": true,
|
|
22
|
+
"noUncheckedIndexedAccess": true
|
|
23
|
+
},
|
|
24
|
+
"include": ["src/**/*"],
|
|
25
|
+
"exclude": ["node_modules", "dist"]
|
|
26
|
+
}
|
package/vitest.config.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineConfig } from 'vitest/config'
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
test: {
|
|
5
|
+
globals: true,
|
|
6
|
+
environment: 'node',
|
|
7
|
+
include: ['src/**/*.test.ts', 'tests/**/*.test.ts'],
|
|
8
|
+
testTimeout: 30000,
|
|
9
|
+
setupFiles: ['./vitest.setup.ts'],
|
|
10
|
+
pool: 'forks',
|
|
11
|
+
coverage: {
|
|
12
|
+
provider: 'v8',
|
|
13
|
+
reporter: ['text', 'json', 'html'],
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
})
|
package/vitest.setup.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vitest global setup - handles cleanup of native resources
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { afterAll } from 'vitest'
|
|
6
|
+
|
|
7
|
+
afterAll(async () => {
|
|
8
|
+
// Free tiktoken encoder to release WebAssembly resources
|
|
9
|
+
// This prevents the test process from hanging after tests complete
|
|
10
|
+
const { freeEncoder } = await import('./src/utils/tokens.js')
|
|
11
|
+
freeEncoder()
|
|
12
|
+
})
|