mdcontext 0.1.0 → 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.
Files changed (251) hide show
  1. package/.changeset/config.json +9 -9
  2. package/.claude/settings.local.json +25 -0
  3. package/.github/workflows/claude-code-review.yml +44 -0
  4. package/.github/workflows/claude.yml +85 -0
  5. package/CONTRIBUTING.md +186 -0
  6. package/NOTES/NOTES +44 -0
  7. package/README.md +206 -3
  8. package/biome.json +1 -1
  9. package/dist/chunk-23UPXDNL.js +3044 -0
  10. package/dist/chunk-2W7MO2DL.js +1366 -0
  11. package/dist/chunk-3NUAZGMA.js +1689 -0
  12. package/dist/chunk-7TOWB2XB.js +366 -0
  13. package/dist/chunk-7XOTOADQ.js +3065 -0
  14. package/dist/chunk-AH2PDM2K.js +3042 -0
  15. package/dist/chunk-BNXWSZ63.js +3742 -0
  16. package/dist/chunk-BTL5DJVU.js +3222 -0
  17. package/dist/chunk-HDHYG7E4.js +104 -0
  18. package/dist/chunk-HLR4KZBP.js +3234 -0
  19. package/dist/chunk-IP3FRFEB.js +1045 -0
  20. package/dist/chunk-KHU56VDO.js +3042 -0
  21. package/dist/chunk-KRYIFLQR.js +85 -89
  22. package/dist/chunk-LBSDNLEM.js +287 -0
  23. package/dist/chunk-MNTQ7HCP.js +2643 -0
  24. package/dist/chunk-MUJELQQ6.js +1387 -0
  25. package/dist/chunk-MXJGMSLV.js +2199 -0
  26. package/dist/chunk-N6QJGC3Z.js +2636 -0
  27. package/dist/chunk-OBELGBPM.js +1713 -0
  28. package/dist/chunk-OT7R5XTA.js +3192 -0
  29. package/dist/chunk-P7X4RA2T.js +106 -0
  30. package/dist/chunk-PIDUQNC2.js +3185 -0
  31. package/dist/chunk-POGCDIH4.js +3187 -0
  32. package/dist/chunk-PSIEOQGZ.js +3043 -0
  33. package/dist/chunk-PVRT3IHA.js +3238 -0
  34. package/dist/chunk-QNN4TT23.js +1430 -0
  35. package/dist/chunk-RE3R45RJ.js +3042 -0
  36. package/dist/chunk-S7E6TFX6.js +718 -657
  37. package/dist/chunk-SG6GLU4U.js +1378 -0
  38. package/dist/chunk-SJCDV2ST.js +274 -0
  39. package/dist/chunk-SYE5XLF3.js +104 -0
  40. package/dist/chunk-T5VLYBZD.js +103 -0
  41. package/dist/chunk-TOQB7VWU.js +3238 -0
  42. package/dist/chunk-VFNMZ4ZQ.js +3228 -0
  43. package/dist/chunk-VVTGZNBT.js +1533 -1423
  44. package/dist/chunk-W7Q4RFEV.js +104 -0
  45. package/dist/chunk-XTYYVRLO.js +3190 -0
  46. package/dist/chunk-Y6MDYVJD.js +3063 -0
  47. package/dist/cli/main.js +4072 -629
  48. package/dist/index.d.ts +420 -33
  49. package/dist/index.js +8 -15
  50. package/dist/mcp/server.js +103 -7
  51. package/dist/schema-BAWSG7KY.js +22 -0
  52. package/dist/schema-E3QUPL26.js +20 -0
  53. package/dist/schema-EHL7WUT6.js +20 -0
  54. package/docs/019-USAGE.md +44 -5
  55. package/docs/020-current-implementation.md +8 -8
  56. package/docs/021-DOGFOODING-FINDINGS.md +1 -1
  57. package/docs/CONFIG.md +1123 -0
  58. package/docs/ERRORS.md +383 -0
  59. package/docs/summarization.md +320 -0
  60. package/justfile +40 -0
  61. package/package.json +39 -33
  62. package/research/INDEX.md +315 -0
  63. package/research/code-review/README.md +90 -0
  64. package/research/code-review/cli-error-handling-review.md +979 -0
  65. package/research/code-review/code-review-validation-report.md +464 -0
  66. package/research/code-review/main-ts-review.md +1128 -0
  67. package/research/config-docs/SUMMARY.md +357 -0
  68. package/research/config-docs/TEST-RESULTS.md +776 -0
  69. package/research/config-docs/TODO.md +542 -0
  70. package/research/config-docs/analysis.md +744 -0
  71. package/research/config-docs/fix-validation.md +502 -0
  72. package/research/config-docs/help-audit.md +264 -0
  73. package/research/config-docs/help-system-analysis.md +890 -0
  74. package/research/frontmatter/COMMENTS-ARE-SKIPPED.md +149 -0
  75. package/research/frontmatter/LLM-CODE-NAVIGATION.md +276 -0
  76. package/research/issue-review.md +603 -0
  77. package/research/llm-summarization/agent-cli-tools-2026.md +1082 -0
  78. package/research/llm-summarization/alternative-providers-2026.md +1428 -0
  79. package/research/llm-summarization/anthropic-2026.md +367 -0
  80. package/research/llm-summarization/claude-cli-integration.md +1706 -0
  81. package/research/llm-summarization/cli-integration-patterns.md +3155 -0
  82. package/research/llm-summarization/openai-2026.md +473 -0
  83. package/research/llm-summarization/openai-compatible-providers-2026.md +1022 -0
  84. package/research/llm-summarization/opencode-cli-integration.md +1552 -0
  85. package/research/llm-summarization/prompt-engineering-2026.md +1426 -0
  86. package/research/llm-summarization/prototype-results.md +56 -0
  87. package/research/llm-summarization/provider-switching-patterns-2026.md +2153 -0
  88. package/research/llm-summarization/typescript-llm-libraries-2026.md +2436 -0
  89. package/research/mdcontext-pudding/00-EXECUTIVE-SUMMARY.md +282 -0
  90. package/research/mdcontext-pudding/01-index-embed.md +956 -0
  91. package/research/mdcontext-pudding/02-search-COMMANDS.md +142 -0
  92. package/research/mdcontext-pudding/02-search-SUMMARY.md +146 -0
  93. package/research/mdcontext-pudding/02-search.md +970 -0
  94. package/research/mdcontext-pudding/03-context.md +779 -0
  95. package/research/mdcontext-pudding/04-navigation-and-analytics.md +803 -0
  96. package/research/mdcontext-pudding/04-tree.md +704 -0
  97. package/research/mdcontext-pudding/05-config.md +1038 -0
  98. package/research/mdcontext-pudding/06-links-summary.txt +87 -0
  99. package/research/mdcontext-pudding/06-links.md +679 -0
  100. package/research/mdcontext-pudding/07-stats.md +693 -0
  101. package/research/mdcontext-pudding/BUG-FIX-PLAN.md +388 -0
  102. package/research/mdcontext-pudding/P0-BUG-VALIDATION.md +167 -0
  103. package/research/mdcontext-pudding/README.md +168 -0
  104. package/research/mdcontext-pudding/TESTING-SUMMARY.md +128 -0
  105. package/research/research-quality-review.md +834 -0
  106. package/research/semantic-search/embedding-text-analysis.md +156 -0
  107. package/research/semantic-search/multi-word-failure-reproduction.md +171 -0
  108. package/research/semantic-search/query-processing-analysis.md +207 -0
  109. package/research/semantic-search/root-cause-and-solution.md +114 -0
  110. package/research/semantic-search/threshold-validation-report.md +69 -0
  111. package/research/semantic-search/vector-search-analysis.md +63 -0
  112. package/research/test-path-issues.md +276 -0
  113. package/review/ALP-76/1-error-type-design.md +962 -0
  114. package/review/ALP-76/2-error-handling-patterns.md +906 -0
  115. package/review/ALP-76/3-error-presentation.md +624 -0
  116. package/review/ALP-76/4-test-coverage.md +625 -0
  117. package/review/ALP-76/5-migration-completeness.md +440 -0
  118. package/review/ALP-76/6-effect-best-practices.md +755 -0
  119. package/scripts/apply-branch-protection.sh +47 -0
  120. package/scripts/branch-protection-templates.json +79 -0
  121. package/scripts/prototype-summarization.ts +346 -0
  122. package/scripts/rebuild-hnswlib.js +32 -37
  123. package/scripts/setup-branch-protection.sh +64 -0
  124. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/active-provider.json +7 -0
  125. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/bm25.json +541 -0
  126. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/bm25.meta.json +5 -0
  127. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/config.json +8 -0
  128. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.bin +0 -0
  129. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.meta.bin +0 -0
  130. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/indexes/documents.json +60 -0
  131. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/indexes/links.json +13 -0
  132. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/indexes/sections.json +1197 -0
  133. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/configuration-management.md +99 -0
  134. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/distributed-systems.md +92 -0
  135. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/error-handling.md +78 -0
  136. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/failure-automation.md +55 -0
  137. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/job-context.md +69 -0
  138. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/process-orchestration.md +99 -0
  139. package/src/cli/argv-preprocessor.test.ts +2 -2
  140. package/src/cli/cli.test.ts +230 -33
  141. package/src/cli/commands/config-cmd.ts +642 -0
  142. package/src/cli/commands/context.ts +97 -9
  143. package/src/cli/commands/duplicates.ts +122 -0
  144. package/src/cli/commands/embeddings.ts +529 -0
  145. package/src/cli/commands/index-cmd.ts +210 -30
  146. package/src/cli/commands/index.ts +3 -0
  147. package/src/cli/commands/search.ts +894 -64
  148. package/src/cli/commands/stats.ts +3 -0
  149. package/src/cli/commands/tree.ts +26 -5
  150. package/src/cli/config-layer.ts +176 -0
  151. package/src/cli/error-handler.test.ts +235 -0
  152. package/src/cli/error-handler.ts +655 -0
  153. package/src/cli/flag-schemas.ts +66 -0
  154. package/src/cli/help.ts +209 -7
  155. package/src/cli/main.ts +348 -58
  156. package/src/cli/options.ts +10 -0
  157. package/src/cli/shared-error-handling.ts +199 -0
  158. package/src/cli/utils.ts +150 -17
  159. package/src/config/file-provider.test.ts +320 -0
  160. package/src/config/file-provider.ts +273 -0
  161. package/src/config/index.ts +72 -0
  162. package/src/config/integration.test.ts +667 -0
  163. package/src/config/precedence.test.ts +277 -0
  164. package/src/config/precedence.ts +451 -0
  165. package/src/config/schema.test.ts +414 -0
  166. package/src/config/schema.ts +603 -0
  167. package/src/config/service.test.ts +320 -0
  168. package/src/config/service.ts +243 -0
  169. package/src/config/testing.test.ts +264 -0
  170. package/src/config/testing.ts +110 -0
  171. package/src/core/types.ts +6 -33
  172. package/src/duplicates/detector.test.ts +183 -0
  173. package/src/duplicates/detector.ts +414 -0
  174. package/src/duplicates/index.ts +18 -0
  175. package/src/embeddings/embedding-namespace.test.ts +300 -0
  176. package/src/embeddings/embedding-namespace.ts +947 -0
  177. package/src/embeddings/heading-boost.test.ts +222 -0
  178. package/src/embeddings/hnsw-build-options.test.ts +198 -0
  179. package/src/embeddings/hyde.test.ts +272 -0
  180. package/src/embeddings/hyde.ts +264 -0
  181. package/src/embeddings/index.ts +2 -0
  182. package/src/embeddings/openai-provider.ts +332 -83
  183. package/src/embeddings/pricing.json +22 -0
  184. package/src/embeddings/provider-constants.ts +204 -0
  185. package/src/embeddings/provider-errors.test.ts +967 -0
  186. package/src/embeddings/provider-errors.ts +565 -0
  187. package/src/embeddings/provider-factory.test.ts +240 -0
  188. package/src/embeddings/provider-factory.ts +225 -0
  189. package/src/embeddings/provider-integration.test.ts +788 -0
  190. package/src/embeddings/query-preprocessing.test.ts +187 -0
  191. package/src/embeddings/semantic-search-threshold.test.ts +508 -0
  192. package/src/embeddings/semantic-search.ts +780 -93
  193. package/src/embeddings/types.ts +293 -16
  194. package/src/embeddings/vector-store.ts +486 -77
  195. package/src/embeddings/voyage-provider.ts +313 -0
  196. package/src/errors/errors.test.ts +845 -0
  197. package/src/errors/index.ts +533 -0
  198. package/src/index/ignore-patterns.test.ts +354 -0
  199. package/src/index/ignore-patterns.ts +305 -0
  200. package/src/index/indexer.ts +286 -48
  201. package/src/index/storage.ts +94 -30
  202. package/src/index/types.ts +40 -2
  203. package/src/index/watcher.ts +67 -9
  204. package/src/index.ts +22 -0
  205. package/src/integration/search-keyword.test.ts +678 -0
  206. package/src/mcp/server.ts +135 -6
  207. package/src/parser/parser.ts +18 -19
  208. package/src/parser/section-filter.test.ts +277 -0
  209. package/src/parser/section-filter.ts +125 -3
  210. package/src/search/__tests__/hybrid-search.test.ts +650 -0
  211. package/src/search/bm25-store.ts +366 -0
  212. package/src/search/cross-encoder.test.ts +253 -0
  213. package/src/search/cross-encoder.ts +406 -0
  214. package/src/search/fuzzy-search.test.ts +419 -0
  215. package/src/search/fuzzy-search.ts +273 -0
  216. package/src/search/hybrid-search.ts +448 -0
  217. package/src/search/path-matcher.test.ts +276 -0
  218. package/src/search/path-matcher.ts +33 -0
  219. package/src/search/searcher.test.ts +99 -1
  220. package/src/search/searcher.ts +189 -67
  221. package/src/search/wink-bm25.d.ts +30 -0
  222. package/src/summarization/cli-providers/claude.ts +202 -0
  223. package/src/summarization/cli-providers/detection.test.ts +273 -0
  224. package/src/summarization/cli-providers/detection.ts +118 -0
  225. package/src/summarization/cli-providers/index.ts +8 -0
  226. package/src/summarization/cost.test.ts +139 -0
  227. package/src/summarization/cost.ts +102 -0
  228. package/src/summarization/error-handler.test.ts +127 -0
  229. package/src/summarization/error-handler.ts +111 -0
  230. package/src/summarization/index.ts +102 -0
  231. package/src/summarization/pipeline.test.ts +498 -0
  232. package/src/summarization/pipeline.ts +231 -0
  233. package/src/summarization/prompts.test.ts +269 -0
  234. package/src/summarization/prompts.ts +133 -0
  235. package/src/summarization/provider-factory.test.ts +396 -0
  236. package/src/summarization/provider-factory.ts +178 -0
  237. package/src/summarization/types.ts +184 -0
  238. package/src/summarize/summarizer.ts +104 -35
  239. package/src/types/huggingface-transformers.d.ts +66 -0
  240. package/tests/fixtures/cli/.mdcontext/active-provider.json +7 -0
  241. package/tests/fixtures/cli/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.bin +0 -0
  242. package/tests/fixtures/cli/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.meta.bin +0 -0
  243. package/tests/fixtures/cli/.mdcontext/indexes/documents.json +4 -4
  244. package/tests/fixtures/cli/.mdcontext/indexes/sections.json +14 -0
  245. package/tests/integration/embed-index.test.ts +712 -0
  246. package/tests/integration/search-context.test.ts +469 -0
  247. package/tests/integration/search-semantic.test.ts +522 -0
  248. package/vitest.config.ts +1 -6
  249. package/AGENTS.md +0 -46
  250. package/tests/fixtures/cli/.mdcontext/vectors.bin +0 -0
  251. package/tests/fixtures/cli/.mdcontext/vectors.meta.json +0 -1264
@@ -0,0 +1,277 @@
1
+ /**
2
+ * Config Precedence Chain Unit Tests
3
+ *
4
+ * Tests for the config provider precedence chain.
5
+ */
6
+
7
+ import * as fs from 'node:fs'
8
+ import * as os from 'node:os'
9
+ import * as path from 'node:path'
10
+ import { ConfigProvider, Effect } from 'effect'
11
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest'
12
+ import {
13
+ createCliConfigProvider,
14
+ createConfigProviderSync,
15
+ createEnvConfigProvider,
16
+ createTestConfigProvider,
17
+ flattenConfig,
18
+ readEnvConfig,
19
+ } from './precedence.js'
20
+ import { MdContextConfig } from './schema.js'
21
+
22
+ describe('Config Precedence Chain', () => {
23
+ let tempDir: string
24
+
25
+ beforeEach(() => {
26
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mdcontext-precedence-'))
27
+ // Clear any MDCONTEXT env vars
28
+ for (const key of Object.keys(process.env)) {
29
+ if (key.startsWith('MDCONTEXT_') || key.startsWith('CUSTOM_')) {
30
+ delete process.env[key]
31
+ }
32
+ }
33
+ })
34
+
35
+ afterEach(() => {
36
+ fs.rmSync(tempDir, { recursive: true, force: true })
37
+ for (const key of Object.keys(process.env)) {
38
+ if (key.startsWith('MDCONTEXT_') || key.startsWith('CUSTOM_')) {
39
+ delete process.env[key]
40
+ }
41
+ }
42
+ })
43
+
44
+ describe('ConfigProvider.fromMap basics', () => {
45
+ it('should read values from a simple map', async () => {
46
+ const provider = ConfigProvider.fromMap(
47
+ new Map([['index.maxDepth', '25']]),
48
+ { pathDelim: '.' },
49
+ )
50
+
51
+ const program = Effect.gen(function* () {
52
+ return yield* MdContextConfig
53
+ })
54
+
55
+ const result = await Effect.runPromise(
56
+ Effect.withConfigProvider(program, provider),
57
+ )
58
+
59
+ expect(result.index.maxDepth).toBe(25)
60
+ })
61
+ })
62
+
63
+ describe('createConfigProviderSync', () => {
64
+ it('should apply CLI overrides with highest priority', async () => {
65
+ const provider = createConfigProviderSync({
66
+ cliOverrides: { index: { maxDepth: 5 } },
67
+ fileConfig: { index: { maxDepth: 15 } },
68
+ skipEnv: true,
69
+ })
70
+
71
+ const program = Effect.gen(function* () {
72
+ return yield* MdContextConfig
73
+ })
74
+
75
+ const result = await Effect.runPromise(
76
+ Effect.withConfigProvider(program, provider),
77
+ )
78
+
79
+ expect(result.index.maxDepth).toBe(5) // CLI wins
80
+ })
81
+
82
+ it('should fall back to file config when CLI not specified', async () => {
83
+ const provider = createConfigProviderSync({
84
+ cliOverrides: { output: { verbose: true } },
85
+ fileConfig: { index: { maxDepth: 15 } },
86
+ skipEnv: true,
87
+ })
88
+
89
+ const program = Effect.gen(function* () {
90
+ return yield* MdContextConfig
91
+ })
92
+
93
+ const result = await Effect.runPromise(
94
+ Effect.withConfigProvider(program, provider),
95
+ )
96
+
97
+ expect(result.index.maxDepth).toBe(15) // File value used
98
+ expect(result.output.verbose).toBe(true) // CLI value used
99
+ })
100
+ })
101
+
102
+ describe('flattenConfig', () => {
103
+ it('should flatten simple nested objects', () => {
104
+ const result = flattenConfig({ index: { maxDepth: 5 } })
105
+ expect(result.get('index.maxDepth')).toBe('5')
106
+ })
107
+
108
+ it('should flatten deeply nested objects', () => {
109
+ const result = flattenConfig({
110
+ index: { maxDepth: 5, followSymlinks: true },
111
+ search: { defaultLimit: 100 },
112
+ })
113
+ expect(result.get('index.maxDepth')).toBe('5')
114
+ expect(result.get('index.followSymlinks')).toBe('true')
115
+ expect(result.get('search.defaultLimit')).toBe('100')
116
+ })
117
+
118
+ it('should handle arrays as comma-separated values', () => {
119
+ const result = flattenConfig({
120
+ index: { excludePatterns: ['node_modules', 'dist', '.git'] },
121
+ })
122
+ expect(result.get('index.excludePatterns')).toBe('node_modules,dist,.git')
123
+ })
124
+
125
+ it('should handle null and undefined values', () => {
126
+ const result = flattenConfig({
127
+ index: { maxDepth: null as unknown as number },
128
+ search: { defaultLimit: undefined as unknown as number },
129
+ })
130
+ expect(result.has('index.maxDepth')).toBe(false)
131
+ expect(result.has('search.defaultLimit')).toBe(false)
132
+ })
133
+
134
+ it('should handle string values', () => {
135
+ const result = flattenConfig({
136
+ embeddings: { model: 'text-embedding-3-small' },
137
+ })
138
+ expect(result.get('embeddings.model')).toBe('text-embedding-3-small')
139
+ })
140
+ })
141
+
142
+ describe('readEnvConfig', () => {
143
+ it('should read MDCONTEXT_ prefixed env vars', () => {
144
+ process.env.MDCONTEXT_INDEX_MAXDEPTH = '25'
145
+ const result = readEnvConfig()
146
+ expect(result.get('index.maxDepth')).toBe('25')
147
+ })
148
+
149
+ it('should map env vars to correct config keys', () => {
150
+ process.env.MDCONTEXT_SEARCH_DEFAULTLIMIT = '50'
151
+ process.env.MDCONTEXT_OUTPUT_VERBOSE = 'true'
152
+ const result = readEnvConfig()
153
+ expect(result.get('search.defaultLimit')).toBe('50')
154
+ expect(result.get('output.verbose')).toBe('true')
155
+ })
156
+
157
+ it('should support custom prefix', () => {
158
+ process.env.CUSTOM_INDEX_MAXDEPTH = '30'
159
+ const result = readEnvConfig('CUSTOM')
160
+ expect(result.get('index.maxDepth')).toBe('30')
161
+ })
162
+
163
+ it('should ignore unknown env vars', () => {
164
+ process.env.MDCONTEXT_UNKNOWN_SETTING = 'value'
165
+ const result = readEnvConfig()
166
+ expect(result.has('unknown.setting')).toBe(false)
167
+ })
168
+ })
169
+
170
+ describe('createEnvConfigProvider', () => {
171
+ it('should create provider from env vars', async () => {
172
+ process.env.MDCONTEXT_INDEX_MAXDEPTH = '42'
173
+ const provider = createEnvConfigProvider()
174
+
175
+ const program = Effect.gen(function* () {
176
+ return yield* MdContextConfig
177
+ })
178
+
179
+ const result = await Effect.runPromise(
180
+ Effect.withConfigProvider(program, provider),
181
+ )
182
+
183
+ expect(result.index.maxDepth).toBe(42)
184
+ })
185
+ })
186
+
187
+ describe('createCliConfigProvider', () => {
188
+ it('should create provider from CLI overrides', async () => {
189
+ const provider = createCliConfigProvider({
190
+ index: { maxDepth: 7 },
191
+ output: { verbose: true },
192
+ })
193
+
194
+ const program = Effect.gen(function* () {
195
+ return yield* MdContextConfig
196
+ })
197
+
198
+ const result = await Effect.runPromise(
199
+ Effect.withConfigProvider(program, provider),
200
+ )
201
+
202
+ expect(result.index.maxDepth).toBe(7)
203
+ expect(result.output.verbose).toBe(true)
204
+ })
205
+ })
206
+
207
+ describe('CONFIG_SCHEMA_KEYS mapping completeness', () => {
208
+ it('should have all schema keys mapped correctly to defaultConfig', async () => {
209
+ const { CONFIG_SCHEMA_KEYS } = await import('./precedence.js')
210
+ const { defaultConfig } = await import('./schema.js')
211
+
212
+ // Verify CONFIG_SCHEMA_KEYS matches defaultConfig structure
213
+ for (const [section, keys] of Object.entries(CONFIG_SCHEMA_KEYS)) {
214
+ const sectionConfig =
215
+ defaultConfig[section as keyof typeof defaultConfig]
216
+ expect(sectionConfig).toBeDefined()
217
+ for (const key of keys as readonly string[]) {
218
+ expect(sectionConfig).toHaveProperty(key)
219
+ }
220
+ }
221
+ })
222
+
223
+ it('should generate correct env key format', () => {
224
+ process.env.MDCONTEXT_INDEX_MAXDEPTH = '42'
225
+ const result = readEnvConfig()
226
+ expect(result.get('index.maxDepth')).toBe('42')
227
+ })
228
+ })
229
+
230
+ describe('createTestConfigProvider', () => {
231
+ it('should create provider with CLI overrides', async () => {
232
+ const provider = createTestConfigProvider({ index: { maxDepth: 5 } })
233
+
234
+ const program = Effect.gen(function* () {
235
+ return yield* MdContextConfig
236
+ })
237
+
238
+ const result = await Effect.runPromise(
239
+ Effect.withConfigProvider(program, provider),
240
+ )
241
+
242
+ expect(result.index.maxDepth).toBe(5)
243
+ })
244
+
245
+ it('should work with only file config', async () => {
246
+ const provider = createTestConfigProvider(undefined, {
247
+ search: { defaultLimit: 100 },
248
+ })
249
+
250
+ const program = Effect.gen(function* () {
251
+ return yield* MdContextConfig
252
+ })
253
+
254
+ const result = await Effect.runPromise(
255
+ Effect.withConfigProvider(program, provider),
256
+ )
257
+
258
+ expect(result.search.defaultLimit).toBe(100)
259
+ })
260
+
261
+ it('should work with empty options', async () => {
262
+ const provider = createTestConfigProvider()
263
+
264
+ const program = Effect.gen(function* () {
265
+ return yield* MdContextConfig
266
+ })
267
+
268
+ const result = await Effect.runPromise(
269
+ Effect.withConfigProvider(program, provider),
270
+ )
271
+
272
+ // All defaults
273
+ expect(result.index.maxDepth).toBe(10)
274
+ expect(result.search.defaultLimit).toBe(10)
275
+ })
276
+ })
277
+ })