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.
Files changed (337) hide show
  1. package/.changeset/README.md +28 -0
  2. package/.changeset/config.json +11 -0
  3. package/.claude/settings.local.json +25 -0
  4. package/.github/workflows/ci.yml +83 -0
  5. package/.github/workflows/claude-code-review.yml +44 -0
  6. package/.github/workflows/claude.yml +85 -0
  7. package/.github/workflows/release.yml +113 -0
  8. package/.tldrignore +112 -0
  9. package/BACKLOG.md +338 -0
  10. package/CONTRIBUTING.md +186 -0
  11. package/NOTES/NOTES +44 -0
  12. package/README.md +434 -11
  13. package/biome.json +36 -0
  14. package/cspell.config.yaml +14 -0
  15. package/dist/chunk-23UPXDNL.js +3044 -0
  16. package/dist/chunk-2W7MO2DL.js +1366 -0
  17. package/dist/chunk-3NUAZGMA.js +1689 -0
  18. package/dist/chunk-7TOWB2XB.js +366 -0
  19. package/dist/chunk-7XOTOADQ.js +3065 -0
  20. package/dist/chunk-AH2PDM2K.js +3042 -0
  21. package/dist/chunk-BNXWSZ63.js +3742 -0
  22. package/dist/chunk-BTL5DJVU.js +3222 -0
  23. package/dist/chunk-HDHYG7E4.js +104 -0
  24. package/dist/chunk-HLR4KZBP.js +3234 -0
  25. package/dist/chunk-IP3FRFEB.js +1045 -0
  26. package/dist/chunk-KHU56VDO.js +3042 -0
  27. package/dist/chunk-KRYIFLQR.js +88 -0
  28. package/dist/chunk-LBSDNLEM.js +287 -0
  29. package/dist/chunk-MNTQ7HCP.js +2643 -0
  30. package/dist/chunk-MUJELQQ6.js +1387 -0
  31. package/dist/chunk-MXJGMSLV.js +2199 -0
  32. package/dist/chunk-N6QJGC3Z.js +2636 -0
  33. package/dist/chunk-OBELGBPM.js +1713 -0
  34. package/dist/chunk-OT7R5XTA.js +3192 -0
  35. package/dist/chunk-P7X4RA2T.js +106 -0
  36. package/dist/chunk-PIDUQNC2.js +3185 -0
  37. package/dist/chunk-POGCDIH4.js +3187 -0
  38. package/dist/chunk-PSIEOQGZ.js +3043 -0
  39. package/dist/chunk-PVRT3IHA.js +3238 -0
  40. package/dist/chunk-QNN4TT23.js +1430 -0
  41. package/dist/chunk-RE3R45RJ.js +3042 -0
  42. package/dist/chunk-S7E6TFX6.js +803 -0
  43. package/dist/chunk-SG6GLU4U.js +1378 -0
  44. package/dist/chunk-SJCDV2ST.js +274 -0
  45. package/dist/chunk-SYE5XLF3.js +104 -0
  46. package/dist/chunk-T5VLYBZD.js +103 -0
  47. package/dist/chunk-TOQB7VWU.js +3238 -0
  48. package/dist/chunk-VFNMZ4ZQ.js +3228 -0
  49. package/dist/chunk-VVTGZNBT.js +1629 -0
  50. package/dist/chunk-W7Q4RFEV.js +104 -0
  51. package/dist/chunk-XTYYVRLO.js +3190 -0
  52. package/dist/chunk-Y6MDYVJD.js +3063 -0
  53. package/dist/cli/main.d.ts +1 -0
  54. package/dist/cli/main.js +5458 -0
  55. package/dist/index.d.ts +653 -0
  56. package/dist/index.js +79 -0
  57. package/dist/mcp/server.d.ts +1 -0
  58. package/dist/mcp/server.js +472 -0
  59. package/dist/schema-BAWSG7KY.js +22 -0
  60. package/dist/schema-E3QUPL26.js +20 -0
  61. package/dist/schema-EHL7WUT6.js +20 -0
  62. package/docs/019-USAGE.md +625 -0
  63. package/docs/020-current-implementation.md +364 -0
  64. package/docs/021-DOGFOODING-FINDINGS.md +175 -0
  65. package/docs/BACKLOG.md +80 -0
  66. package/docs/CONFIG.md +1123 -0
  67. package/docs/DESIGN.md +439 -0
  68. package/docs/ERRORS.md +383 -0
  69. package/docs/PROJECT.md +88 -0
  70. package/docs/ROADMAP.md +407 -0
  71. package/docs/summarization.md +320 -0
  72. package/docs/test-links.md +9 -0
  73. package/justfile +40 -0
  74. package/package.json +74 -9
  75. package/pnpm-workspace.yaml +5 -0
  76. package/research/INDEX.md +315 -0
  77. package/research/code-review/README.md +90 -0
  78. package/research/code-review/cli-error-handling-review.md +979 -0
  79. package/research/code-review/code-review-validation-report.md +464 -0
  80. package/research/code-review/main-ts-review.md +1128 -0
  81. package/research/config-analysis/01-current-implementation.md +470 -0
  82. package/research/config-analysis/02-strategy-recommendation.md +428 -0
  83. package/research/config-analysis/03-task-candidates.md +715 -0
  84. package/research/config-analysis/033-research-configuration-management.md +828 -0
  85. package/research/config-analysis/034-research-effect-cli-config.md +1504 -0
  86. package/research/config-analysis/04-consolidated-task-candidates.md +277 -0
  87. package/research/config-docs/SUMMARY.md +357 -0
  88. package/research/config-docs/TEST-RESULTS.md +776 -0
  89. package/research/config-docs/TODO.md +542 -0
  90. package/research/config-docs/analysis.md +744 -0
  91. package/research/config-docs/fix-validation.md +502 -0
  92. package/research/config-docs/help-audit.md +264 -0
  93. package/research/config-docs/help-system-analysis.md +890 -0
  94. package/research/dogfood/consolidated-tool-evaluation.md +373 -0
  95. package/research/dogfood/strategy-a/a-synthesis.md +184 -0
  96. package/research/dogfood/strategy-a/a1-docs.md +226 -0
  97. package/research/dogfood/strategy-a/a2-amorphic.md +156 -0
  98. package/research/dogfood/strategy-a/a3-llm.md +164 -0
  99. package/research/dogfood/strategy-b/b-synthesis.md +228 -0
  100. package/research/dogfood/strategy-b/b1-architecture.md +207 -0
  101. package/research/dogfood/strategy-b/b2-gaps.md +258 -0
  102. package/research/dogfood/strategy-b/b3-workflows.md +250 -0
  103. package/research/dogfood/strategy-c/c-synthesis.md +451 -0
  104. package/research/dogfood/strategy-c/c1-explorer.md +192 -0
  105. package/research/dogfood/strategy-c/c2-diver-memory.md +145 -0
  106. package/research/dogfood/strategy-c/c3-diver-control.md +148 -0
  107. package/research/dogfood/strategy-c/c4-diver-failure.md +151 -0
  108. package/research/dogfood/strategy-c/c5-diver-execution.md +221 -0
  109. package/research/dogfood/strategy-c/c6-diver-org.md +221 -0
  110. package/research/effect-cli-error-handling.md +845 -0
  111. package/research/effect-errors-as-values.md +943 -0
  112. package/research/errors-task-analysis/00-consolidated-tasks.md +207 -0
  113. package/research/errors-task-analysis/cli-commands-analysis.md +909 -0
  114. package/research/errors-task-analysis/embeddings-analysis.md +709 -0
  115. package/research/errors-task-analysis/index-search-analysis.md +812 -0
  116. package/research/frontmatter/COMMENTS-ARE-SKIPPED.md +149 -0
  117. package/research/frontmatter/LLM-CODE-NAVIGATION.md +276 -0
  118. package/research/issue-review.md +603 -0
  119. package/research/llm-summarization/agent-cli-tools-2026.md +1082 -0
  120. package/research/llm-summarization/alternative-providers-2026.md +1428 -0
  121. package/research/llm-summarization/anthropic-2026.md +367 -0
  122. package/research/llm-summarization/claude-cli-integration.md +1706 -0
  123. package/research/llm-summarization/cli-integration-patterns.md +3155 -0
  124. package/research/llm-summarization/openai-2026.md +473 -0
  125. package/research/llm-summarization/openai-compatible-providers-2026.md +1022 -0
  126. package/research/llm-summarization/opencode-cli-integration.md +1552 -0
  127. package/research/llm-summarization/prompt-engineering-2026.md +1426 -0
  128. package/research/llm-summarization/prototype-results.md +56 -0
  129. package/research/llm-summarization/provider-switching-patterns-2026.md +2153 -0
  130. package/research/llm-summarization/typescript-llm-libraries-2026.md +2436 -0
  131. package/research/mdcontext-error-analysis.md +521 -0
  132. package/research/mdcontext-pudding/00-EXECUTIVE-SUMMARY.md +282 -0
  133. package/research/mdcontext-pudding/01-index-embed.md +956 -0
  134. package/research/mdcontext-pudding/02-search-COMMANDS.md +142 -0
  135. package/research/mdcontext-pudding/02-search-SUMMARY.md +146 -0
  136. package/research/mdcontext-pudding/02-search.md +970 -0
  137. package/research/mdcontext-pudding/03-context.md +779 -0
  138. package/research/mdcontext-pudding/04-navigation-and-analytics.md +803 -0
  139. package/research/mdcontext-pudding/04-tree.md +704 -0
  140. package/research/mdcontext-pudding/05-config.md +1038 -0
  141. package/research/mdcontext-pudding/06-links-summary.txt +87 -0
  142. package/research/mdcontext-pudding/06-links.md +679 -0
  143. package/research/mdcontext-pudding/07-stats.md +693 -0
  144. package/research/mdcontext-pudding/BUG-FIX-PLAN.md +388 -0
  145. package/research/mdcontext-pudding/P0-BUG-VALIDATION.md +167 -0
  146. package/research/mdcontext-pudding/README.md +168 -0
  147. package/research/mdcontext-pudding/TESTING-SUMMARY.md +128 -0
  148. package/research/npm_publish/011-npm-workflow-research-agent2.md +792 -0
  149. package/research/npm_publish/012-npm-workflow-research-agent1.md +530 -0
  150. package/research/npm_publish/013-npm-workflow-research-agent3.md +722 -0
  151. package/research/npm_publish/014-npm-workflow-synthesis.md +556 -0
  152. package/research/npm_publish/031-npm-workflow-task-analysis.md +134 -0
  153. package/research/research-quality-review.md +834 -0
  154. package/research/semantic-search/002-research-embedding-models.md +490 -0
  155. package/research/semantic-search/003-research-rag-alternatives.md +523 -0
  156. package/research/semantic-search/004-research-vector-search.md +841 -0
  157. package/research/semantic-search/032-research-semantic-search.md +427 -0
  158. package/research/semantic-search/embedding-text-analysis.md +156 -0
  159. package/research/semantic-search/multi-word-failure-reproduction.md +171 -0
  160. package/research/semantic-search/query-processing-analysis.md +207 -0
  161. package/research/semantic-search/root-cause-and-solution.md +114 -0
  162. package/research/semantic-search/threshold-validation-report.md +69 -0
  163. package/research/semantic-search/vector-search-analysis.md +63 -0
  164. package/research/task-management-2026/00-synthesis-recommendations.md +295 -0
  165. package/research/task-management-2026/01-ai-workflow-tools.md +416 -0
  166. package/research/task-management-2026/02-agent-framework-patterns.md +476 -0
  167. package/research/task-management-2026/03-lightweight-file-based.md +567 -0
  168. package/research/task-management-2026/04-established-tools-ai-features.md +541 -0
  169. package/research/task-management-2026/linear/01-core-features-workflow.md +771 -0
  170. package/research/task-management-2026/linear/02-api-integrations.md +930 -0
  171. package/research/task-management-2026/linear/03-ai-features.md +368 -0
  172. package/research/task-management-2026/linear/04-pricing-setup.md +205 -0
  173. package/research/task-management-2026/linear/05-usage-patterns-best-practices.md +605 -0
  174. package/research/test-path-issues.md +276 -0
  175. package/review/ALP-76/1-error-type-design.md +962 -0
  176. package/review/ALP-76/2-error-handling-patterns.md +906 -0
  177. package/review/ALP-76/3-error-presentation.md +624 -0
  178. package/review/ALP-76/4-test-coverage.md +625 -0
  179. package/review/ALP-76/5-migration-completeness.md +440 -0
  180. package/review/ALP-76/6-effect-best-practices.md +755 -0
  181. package/scripts/apply-branch-protection.sh +47 -0
  182. package/scripts/branch-protection-templates.json +79 -0
  183. package/scripts/prototype-summarization.ts +346 -0
  184. package/scripts/rebuild-hnswlib.js +58 -0
  185. package/scripts/setup-branch-protection.sh +64 -0
  186. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/active-provider.json +7 -0
  187. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/bm25.json +541 -0
  188. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/bm25.meta.json +5 -0
  189. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/config.json +8 -0
  190. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.bin +0 -0
  191. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.meta.bin +0 -0
  192. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/indexes/documents.json +60 -0
  193. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/indexes/links.json +13 -0
  194. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/indexes/sections.json +1197 -0
  195. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/configuration-management.md +99 -0
  196. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/distributed-systems.md +92 -0
  197. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/error-handling.md +78 -0
  198. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/failure-automation.md +55 -0
  199. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/job-context.md +69 -0
  200. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/process-orchestration.md +99 -0
  201. package/src/cli/argv-preprocessor.test.ts +210 -0
  202. package/src/cli/argv-preprocessor.ts +202 -0
  203. package/src/cli/cli.test.ts +627 -0
  204. package/src/cli/commands/backlinks.ts +54 -0
  205. package/src/cli/commands/config-cmd.ts +642 -0
  206. package/src/cli/commands/context.ts +285 -0
  207. package/src/cli/commands/duplicates.ts +122 -0
  208. package/src/cli/commands/embeddings.ts +529 -0
  209. package/src/cli/commands/index-cmd.ts +480 -0
  210. package/src/cli/commands/index.ts +16 -0
  211. package/src/cli/commands/links.ts +52 -0
  212. package/src/cli/commands/search.ts +1281 -0
  213. package/src/cli/commands/stats.ts +149 -0
  214. package/src/cli/commands/tree.ts +128 -0
  215. package/src/cli/config-layer.ts +176 -0
  216. package/src/cli/error-handler.test.ts +235 -0
  217. package/src/cli/error-handler.ts +655 -0
  218. package/src/cli/flag-schemas.ts +341 -0
  219. package/src/cli/help.ts +588 -0
  220. package/src/cli/index.ts +9 -0
  221. package/src/cli/main.ts +435 -0
  222. package/src/cli/options.ts +41 -0
  223. package/src/cli/shared-error-handling.ts +199 -0
  224. package/src/cli/typo-suggester.test.ts +105 -0
  225. package/src/cli/typo-suggester.ts +130 -0
  226. package/src/cli/utils.ts +259 -0
  227. package/src/config/file-provider.test.ts +320 -0
  228. package/src/config/file-provider.ts +273 -0
  229. package/src/config/index.ts +72 -0
  230. package/src/config/integration.test.ts +667 -0
  231. package/src/config/precedence.test.ts +277 -0
  232. package/src/config/precedence.ts +451 -0
  233. package/src/config/schema.test.ts +414 -0
  234. package/src/config/schema.ts +603 -0
  235. package/src/config/service.test.ts +320 -0
  236. package/src/config/service.ts +243 -0
  237. package/src/config/testing.test.ts +264 -0
  238. package/src/config/testing.ts +110 -0
  239. package/src/core/index.ts +1 -0
  240. package/src/core/types.ts +113 -0
  241. package/src/duplicates/detector.test.ts +183 -0
  242. package/src/duplicates/detector.ts +414 -0
  243. package/src/duplicates/index.ts +18 -0
  244. package/src/embeddings/embedding-namespace.test.ts +300 -0
  245. package/src/embeddings/embedding-namespace.ts +947 -0
  246. package/src/embeddings/heading-boost.test.ts +222 -0
  247. package/src/embeddings/hnsw-build-options.test.ts +198 -0
  248. package/src/embeddings/hyde.test.ts +272 -0
  249. package/src/embeddings/hyde.ts +264 -0
  250. package/src/embeddings/index.ts +10 -0
  251. package/src/embeddings/openai-provider.ts +414 -0
  252. package/src/embeddings/pricing.json +22 -0
  253. package/src/embeddings/provider-constants.ts +204 -0
  254. package/src/embeddings/provider-errors.test.ts +967 -0
  255. package/src/embeddings/provider-errors.ts +565 -0
  256. package/src/embeddings/provider-factory.test.ts +240 -0
  257. package/src/embeddings/provider-factory.ts +225 -0
  258. package/src/embeddings/provider-integration.test.ts +788 -0
  259. package/src/embeddings/query-preprocessing.test.ts +187 -0
  260. package/src/embeddings/semantic-search-threshold.test.ts +508 -0
  261. package/src/embeddings/semantic-search.ts +1270 -0
  262. package/src/embeddings/types.ts +359 -0
  263. package/src/embeddings/vector-store.ts +708 -0
  264. package/src/embeddings/voyage-provider.ts +313 -0
  265. package/src/errors/errors.test.ts +845 -0
  266. package/src/errors/index.ts +533 -0
  267. package/src/index/ignore-patterns.test.ts +354 -0
  268. package/src/index/ignore-patterns.ts +305 -0
  269. package/src/index/index.ts +4 -0
  270. package/src/index/indexer.ts +684 -0
  271. package/src/index/storage.ts +260 -0
  272. package/src/index/types.ts +147 -0
  273. package/src/index/watcher.ts +189 -0
  274. package/src/index.ts +30 -0
  275. package/src/integration/search-keyword.test.ts +678 -0
  276. package/src/mcp/server.ts +612 -0
  277. package/src/parser/index.ts +1 -0
  278. package/src/parser/parser.test.ts +291 -0
  279. package/src/parser/parser.ts +394 -0
  280. package/src/parser/section-filter.test.ts +277 -0
  281. package/src/parser/section-filter.ts +392 -0
  282. package/src/search/__tests__/hybrid-search.test.ts +650 -0
  283. package/src/search/bm25-store.ts +366 -0
  284. package/src/search/cross-encoder.test.ts +253 -0
  285. package/src/search/cross-encoder.ts +406 -0
  286. package/src/search/fuzzy-search.test.ts +419 -0
  287. package/src/search/fuzzy-search.ts +273 -0
  288. package/src/search/hybrid-search.ts +448 -0
  289. package/src/search/path-matcher.test.ts +276 -0
  290. package/src/search/path-matcher.ts +33 -0
  291. package/src/search/query-parser.test.ts +260 -0
  292. package/src/search/query-parser.ts +319 -0
  293. package/src/search/searcher.test.ts +280 -0
  294. package/src/search/searcher.ts +724 -0
  295. package/src/search/wink-bm25.d.ts +30 -0
  296. package/src/summarization/cli-providers/claude.ts +202 -0
  297. package/src/summarization/cli-providers/detection.test.ts +273 -0
  298. package/src/summarization/cli-providers/detection.ts +118 -0
  299. package/src/summarization/cli-providers/index.ts +8 -0
  300. package/src/summarization/cost.test.ts +139 -0
  301. package/src/summarization/cost.ts +102 -0
  302. package/src/summarization/error-handler.test.ts +127 -0
  303. package/src/summarization/error-handler.ts +111 -0
  304. package/src/summarization/index.ts +102 -0
  305. package/src/summarization/pipeline.test.ts +498 -0
  306. package/src/summarization/pipeline.ts +231 -0
  307. package/src/summarization/prompts.test.ts +269 -0
  308. package/src/summarization/prompts.ts +133 -0
  309. package/src/summarization/provider-factory.test.ts +396 -0
  310. package/src/summarization/provider-factory.ts +178 -0
  311. package/src/summarization/types.ts +184 -0
  312. package/src/summarize/budget-bugs.test.ts +620 -0
  313. package/src/summarize/formatters.ts +419 -0
  314. package/src/summarize/index.ts +20 -0
  315. package/src/summarize/summarizer.test.ts +275 -0
  316. package/src/summarize/summarizer.ts +597 -0
  317. package/src/summarize/verify-bugs.test.ts +238 -0
  318. package/src/types/huggingface-transformers.d.ts +66 -0
  319. package/src/utils/index.ts +1 -0
  320. package/src/utils/tokens.test.ts +142 -0
  321. package/src/utils/tokens.ts +186 -0
  322. package/tests/fixtures/cli/.mdcontext/active-provider.json +7 -0
  323. package/tests/fixtures/cli/.mdcontext/config.json +8 -0
  324. package/tests/fixtures/cli/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.bin +0 -0
  325. package/tests/fixtures/cli/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.meta.bin +0 -0
  326. package/tests/fixtures/cli/.mdcontext/indexes/documents.json +33 -0
  327. package/tests/fixtures/cli/.mdcontext/indexes/links.json +12 -0
  328. package/tests/fixtures/cli/.mdcontext/indexes/sections.json +247 -0
  329. package/tests/fixtures/cli/README.md +9 -0
  330. package/tests/fixtures/cli/api-reference.md +11 -0
  331. package/tests/fixtures/cli/getting-started.md +11 -0
  332. package/tests/integration/embed-index.test.ts +712 -0
  333. package/tests/integration/search-context.test.ts +469 -0
  334. package/tests/integration/search-semantic.test.ts +522 -0
  335. package/tsconfig.json +26 -0
  336. package/vitest.config.ts +16 -0
  337. package/vitest.setup.ts +12 -0
@@ -0,0 +1,709 @@
1
+ # Embeddings Module Error Handling Analysis
2
+
3
+ > Analysis of the embeddings module against Effect error handling best practices
4
+
5
+ ## Files Analyzed
6
+
7
+ - `/src/embeddings/openai-provider.ts`
8
+ - `/src/embeddings/semantic-search.ts`
9
+ - `/src/embeddings/types.ts`
10
+ - `/src/embeddings/vector-store.ts`
11
+
12
+ ---
13
+
14
+ ## 1. Current Error Handling Issues Found
15
+
16
+ ### Issue 1.1: Non-Effect Error Classes in openai-provider.ts
17
+
18
+ **Location**: `openai-provider.ts:24-36`
19
+
20
+ ```typescript
21
+ export class MissingApiKeyError extends Error {
22
+ constructor() {
23
+ super('OPENAI_API_KEY not set')
24
+ this.name = 'MissingApiKeyError'
25
+ }
26
+ }
27
+
28
+ export class InvalidApiKeyError extends Error {
29
+ constructor(message?: string) {
30
+ super(message ?? 'Invalid OPENAI_API_KEY')
31
+ this.name = 'InvalidApiKeyError'
32
+ }
33
+ }
34
+ ```
35
+
36
+ **Problem**: These error classes extend `Error` instead of using `Data.TaggedError`. They lack the `_tag` discriminant field required for `catchTag`/`catchTags` pattern matching.
37
+
38
+ **Priority**: HIGH
39
+
40
+ ---
41
+
42
+ ### Issue 1.2: Constructor Throws Exception Instead of Effect.fail
43
+
44
+ **Location**: `openai-provider.ts:56-60`
45
+
46
+ ```typescript
47
+ constructor(options: OpenAIProviderOptions = {}) {
48
+ const apiKey = options.apiKey ?? process.env.OPENAI_API_KEY
49
+ if (!apiKey) {
50
+ throw new MissingApiKeyError() // Throws exception!
51
+ }
52
+ // ...
53
+ }
54
+ ```
55
+
56
+ **Problem**: The constructor throws a synchronous exception instead of returning an Effect that can fail. This bypasses Effect's error tracking entirely and becomes a "defect" when wrapped in Effect operations.
57
+
58
+ **Priority**: HIGH
59
+
60
+ ---
61
+
62
+ ### Issue 1.3: Unused EmbedError Type in types.ts
63
+
64
+ **Location**: `types.ts:68-82`
65
+
66
+ ```typescript
67
+ export interface EmbedError {
68
+ readonly _tag: 'EmbedError'
69
+ readonly cause: 'RateLimit' | 'ApiKey' | 'Network' | 'Unknown'
70
+ readonly message: string
71
+ }
72
+
73
+ export const embedError = (
74
+ cause: EmbedError['cause'],
75
+ message: string,
76
+ ): EmbedError => ({
77
+ _tag: 'EmbedError',
78
+ cause,
79
+ message,
80
+ })
81
+ ```
82
+
83
+ **Problem**: A well-designed `EmbedError` type exists but is never used anywhere in the module. The actual error handling uses plain `Error` classes instead.
84
+
85
+ **Priority**: MEDIUM
86
+
87
+ ---
88
+
89
+ ### Issue 1.4: Generic Error Type in Effect Return Types
90
+
91
+ **Location**: `semantic-search.ts:73`, `semantic-search.ts:184`, etc.
92
+
93
+ ```typescript
94
+ export const estimateEmbeddingCost = (
95
+ rootPath: string,
96
+ options: { excludePatterns?: readonly string[] | undefined } = {},
97
+ ): Effect.Effect<EmbeddingEstimate, Error> => // Generic Error type
98
+ ```
99
+
100
+ **Problem**: All functions return `Effect.Effect<A, Error>` instead of specific tagged error types. This loses type safety and makes exhaustive error handling impossible at the call site.
101
+
102
+ **Priority**: HIGH
103
+
104
+ ---
105
+
106
+ ### Issue 1.5: Silent Error Swallowing with Empty catch Blocks
107
+
108
+ **Location**: `semantic-search.ts:329-332`
109
+
110
+ ```typescript
111
+ try {
112
+ fileContent = yield* Effect.promise(() =>
113
+ fs.readFile(filePath, 'utf-8'),
114
+ )
115
+ } catch {
116
+ // Skip files that can't be read
117
+ continue
118
+ }
119
+ ```
120
+
121
+ **Location**: `semantic-search.ts:532-534`
122
+
123
+ ```typescript
124
+ } catch {
125
+ resultsWithContent.push(result)
126
+ }
127
+ ```
128
+
129
+ **Problem**: Errors are silently swallowed without logging or tracking. Users have no visibility into which files failed or why.
130
+
131
+ **Priority**: MEDIUM
132
+
133
+ ---
134
+
135
+ ### Issue 1.6: Inconsistent Error Transformation
136
+
137
+ **Location**: `semantic-search.ts:366-375`
138
+
139
+ ```typescript
140
+ const result = yield* Effect.tryPromise({
141
+ try: () => provider.embed(texts),
142
+ catch: (e) => {
143
+ // Preserve InvalidApiKeyError so handleApiKeyError can catch it
144
+ if (e instanceof InvalidApiKeyError) return e
145
+ return new Error(
146
+ `Embedding failed: ${e instanceof Error ? e.message : String(e)}`,
147
+ )
148
+ },
149
+ })
150
+ ```
151
+
152
+ **Problem**: Conditional error preservation logic is fragile. Some errors are preserved (`InvalidApiKeyError`), others are converted to generic `Error`, losing type information.
153
+
154
+ **Priority**: MEDIUM
155
+
156
+ ---
157
+
158
+ ### Issue 1.7: Error Messages Embedded in Code
159
+
160
+ **Location**: `semantic-search.ts:82-84`, `semantic-search.ts:195-196`, etc.
161
+
162
+ ```typescript
163
+ return yield* Effect.fail(
164
+ new Error("Index not found. Run 'mdcontext index' first."),
165
+ )
166
+ ```
167
+
168
+ **Problem**: User-facing error messages are scattered throughout the codebase rather than being formatted at the application boundary. This violates the Effect best practice of keeping error classes as pure data.
169
+
170
+ **Priority**: LOW
171
+
172
+ ---
173
+
174
+ ### Issue 1.8: handleApiKeyError Converts to Generic Error
175
+
176
+ **Location**: `openai-provider.ts:130-165`
177
+
178
+ ```typescript
179
+ export const handleApiKeyError = <A, E>(
180
+ effect: Effect.Effect<A, E | MissingApiKeyError | InvalidApiKeyError>,
181
+ ): Effect.Effect<A, E | Error> => // Returns generic Error
182
+ effect.pipe(
183
+ Effect.catchIf(
184
+ (e): e is MissingApiKeyError => e instanceof MissingApiKeyError,
185
+ () =>
186
+ Effect.gen(function* () {
187
+ yield* Console.error('')
188
+ yield* Console.error('Error: OPENAI_API_KEY not set')
189
+ // ...
190
+ return yield* Effect.fail(new Error('Missing API key')) // Loses type
191
+ }),
192
+ ),
193
+ // ...
194
+ )
195
+ ```
196
+
197
+ **Problem**: After displaying the error message, it re-fails with a generic `Error` type, losing the original typed error. Also mixes presentation (Console.error) with error handling logic.
198
+
199
+ **Priority**: MEDIUM
200
+
201
+ ---
202
+
203
+ ### Issue 1.9: Effect.sync Used Where Effect.try Should Be
204
+
205
+ **Location**: `vector-store.ts:99-121`, `vector-store.ts:123-169`
206
+
207
+ ```typescript
208
+ add(entries: VectorEntry[]): Effect.Effect<void, Error> {
209
+ return Effect.sync(() => { // Effect.sync doesn't catch errors!
210
+ const index = this.ensureIndex()
211
+ for (const entry of entries) {
212
+ // ... operations that could throw
213
+ index.addPoint(entry.embedding as number[], idx)
214
+ }
215
+ })
216
+ }
217
+ ```
218
+
219
+ **Problem**: `Effect.sync` is used for operations that could throw (like `addPoint`). If an exception occurs, it becomes an untracked defect instead of a typed error.
220
+
221
+ **Priority**: HIGH
222
+
223
+ ---
224
+
225
+ ### Issue 1.10: JSON.parse Without Error Handling
226
+
227
+ **Location**: `vector-store.ts:234`
228
+
229
+ ```typescript
230
+ const meta = JSON.parse(metaContent) as VectorIndex
231
+ ```
232
+
233
+ **Problem**: `JSON.parse` can throw but is not wrapped in error handling. If the metadata file is corrupted, this throws an untracked exception.
234
+
235
+ **Priority**: MEDIUM
236
+
237
+ ---
238
+
239
+ ## 2. Specific Violations of Effect Best Practices
240
+
241
+ ### Violation 2.1: Not Using Data.TaggedError
242
+
243
+ **Best Practice**: "The recommended way to define errors in Effect is using `Data.TaggedError`"
244
+
245
+ **Current Code**:
246
+ ```typescript
247
+ export class MissingApiKeyError extends Error {
248
+ constructor() {
249
+ super('OPENAI_API_KEY not set')
250
+ this.name = 'MissingApiKeyError'
251
+ }
252
+ }
253
+ ```
254
+
255
+ **Violation**: Plain `Error` extension lacks `_tag` discriminant, preventing `catchTag` usage.
256
+
257
+ ---
258
+
259
+ ### Violation 2.2: Throwing in Constructors
260
+
261
+ **Best Practice**: "Effect treats errors as first-class values tracked in the type system"
262
+
263
+ **Current Code**:
264
+ ```typescript
265
+ constructor(options: OpenAIProviderOptions = {}) {
266
+ if (!apiKey) {
267
+ throw new MissingApiKeyError()
268
+ }
269
+ }
270
+ ```
271
+
272
+ **Violation**: Synchronous throws bypass Effect's error tracking. Should use factory function returning `Effect.Effect<OpenAIProvider, MissingApiKeyError>`.
273
+
274
+ ---
275
+
276
+ ### Violation 2.3: Mixing Presentation with Error Handling
277
+
278
+ **Best Practice**: "Keep error classes clean - format at the boundary"
279
+
280
+ **Current Code**:
281
+ ```typescript
282
+ Effect.catchIf(
283
+ (e): e is MissingApiKeyError => e instanceof MissingApiKeyError,
284
+ () =>
285
+ Effect.gen(function* () {
286
+ yield* Console.error('Error: OPENAI_API_KEY not set')
287
+ // ... presentation logic in error handler
288
+ }),
289
+ )
290
+ ```
291
+
292
+ **Violation**: Error handler contains `Console.error` calls. Error formatting should happen at the CLI boundary, not in domain code.
293
+
294
+ ---
295
+
296
+ ### Violation 2.4: Swallowing Errors Without Logging
297
+
298
+ **Best Practice**: "DO: Log or preserve error information"
299
+
300
+ **Current Code**:
301
+ ```typescript
302
+ } catch {
303
+ // Skip files that can't be read
304
+ continue
305
+ }
306
+ ```
307
+
308
+ **Violation**: Errors are silently ignored. Should at minimum use `Effect.logWarning` to track failures.
309
+
310
+ ---
311
+
312
+ ### Violation 2.5: Losing Error Context in Transformations
313
+
314
+ **Best Practice**: "When transforming errors, preserve the original cause"
315
+
316
+ **Current Code**:
317
+ ```typescript
318
+ return new Error(
319
+ `Embedding failed: ${e instanceof Error ? e.message : String(e)}`,
320
+ )
321
+ ```
322
+
323
+ **Violation**: Original error object is converted to string, losing stack trace and error type. Should preserve as `cause` property.
324
+
325
+ ---
326
+
327
+ ### Violation 2.6: Converting Typed Errors to Generic Error
328
+
329
+ **Best Practice**: "DO: Preserve specific error types"
330
+
331
+ **Current Code**:
332
+ ```typescript
333
+ ): Effect.Effect<A, E | Error> => // Returns generic Error
334
+ // ...
335
+ return yield* Effect.fail(new Error('Missing API key'))
336
+ ```
337
+
338
+ **Violation**: Specific `MissingApiKeyError` is transformed to generic `Error`, losing discriminated union benefits.
339
+
340
+ ---
341
+
342
+ ### Violation 2.7: Using Defects for Expected Errors (Implicit)
343
+
344
+ **Best Practice**: "Use fail for expected/recoverable errors, die for bugs/invariant violations"
345
+
346
+ **Current Code**:
347
+ ```typescript
348
+ return Effect.sync(() => {
349
+ index.addPoint(entry.embedding as number[], idx) // Can throw
350
+ })
351
+ ```
352
+
353
+ **Violation**: `Effect.sync` converts thrown errors to defects. These are expected operational errors that should be recoverable.
354
+
355
+ ---
356
+
357
+ ### Violation 2.8: Defined Error Type Not Used
358
+
359
+ **Best Practice**: "Define domain-specific error types... Create clear, tagged errors for your CLI's domain"
360
+
361
+ **Current Code**: `EmbedError` interface exists but is never used.
362
+
363
+ **Violation**: Well-designed error type is ignored in favor of generic `Error` and plain classes.
364
+
365
+ ---
366
+
367
+ ## 3. Recommended Changes with Code Examples
368
+
369
+ ### Recommendation 3.1: Convert Error Classes to Data.TaggedError
370
+
371
+ **Priority**: HIGH
372
+
373
+ ```typescript
374
+ // src/embeddings/errors.ts
375
+ import { Data } from 'effect'
376
+
377
+ export class MissingApiKeyError extends Data.TaggedError('MissingApiKeyError')<{
378
+ readonly provider: string
379
+ }> {}
380
+
381
+ export class InvalidApiKeyError extends Data.TaggedError('InvalidApiKeyError')<{
382
+ readonly provider: string
383
+ readonly details: string
384
+ }> {}
385
+
386
+ export class EmbeddingError extends Data.TaggedError('EmbeddingError')<{
387
+ readonly cause: 'RateLimit' | 'Network' | 'Unknown'
388
+ readonly message: string
389
+ readonly originalError?: unknown
390
+ }> {}
391
+
392
+ export class IndexNotFoundError extends Data.TaggedError('IndexNotFoundError')<{
393
+ readonly path: string
394
+ readonly indexType: 'document' | 'section' | 'vector'
395
+ }> {}
396
+
397
+ export class VectorStoreError extends Data.TaggedError('VectorStoreError')<{
398
+ readonly operation: 'add' | 'search' | 'save' | 'load'
399
+ readonly message: string
400
+ readonly cause?: unknown
401
+ }> {}
402
+
403
+ export class FileReadError extends Data.TaggedError('FileReadError')<{
404
+ readonly path: string
405
+ readonly cause: string
406
+ }> {}
407
+ ```
408
+
409
+ ---
410
+
411
+ ### Recommendation 3.2: Factory Function Instead of Throwing Constructor
412
+
413
+ **Priority**: HIGH
414
+
415
+ ```typescript
416
+ // src/embeddings/openai-provider.ts
417
+ import { Effect } from 'effect'
418
+ import { MissingApiKeyError, InvalidApiKeyError } from './errors.js'
419
+
420
+ export const createOpenAIProvider = (
421
+ options: OpenAIProviderOptions = {},
422
+ ): Effect.Effect<OpenAIProvider, MissingApiKeyError> =>
423
+ Effect.gen(function* () {
424
+ const apiKey = options.apiKey ?? process.env.OPENAI_API_KEY
425
+
426
+ if (!apiKey) {
427
+ return yield* Effect.fail(
428
+ new MissingApiKeyError({ provider: 'openai' })
429
+ )
430
+ }
431
+
432
+ return new OpenAIProvider(apiKey, options)
433
+ })
434
+
435
+ // Private constructor - only accessible via factory
436
+ class OpenAIProvider implements EmbeddingProvider {
437
+ private constructor(
438
+ private readonly apiKey: string,
439
+ options: OpenAIProviderOptions,
440
+ ) {
441
+ this.client = new OpenAI({ apiKey })
442
+ this.model = options.model ?? 'text-embedding-3-small'
443
+ // ...
444
+ }
445
+ }
446
+ ```
447
+
448
+ ---
449
+
450
+ ### Recommendation 3.3: Typed Error Returns with Proper Transformation
451
+
452
+ **Priority**: HIGH
453
+
454
+ ```typescript
455
+ // src/embeddings/semantic-search.ts
456
+ import {
457
+ IndexNotFoundError,
458
+ MissingApiKeyError,
459
+ EmbeddingError,
460
+ FileReadError
461
+ } from './errors.js'
462
+
463
+ type BuildEmbeddingsError =
464
+ | IndexNotFoundError
465
+ | MissingApiKeyError
466
+ | InvalidApiKeyError
467
+ | EmbeddingError
468
+
469
+ export const buildEmbeddings = (
470
+ rootPath: string,
471
+ options: BuildEmbeddingsOptions = {},
472
+ ): Effect.Effect<BuildEmbeddingsResult, BuildEmbeddingsError> =>
473
+ Effect.gen(function* () {
474
+ // ...
475
+ if (!docIndex || !sectionIndex) {
476
+ return yield* Effect.fail(
477
+ new IndexNotFoundError({
478
+ path: resolvedRoot,
479
+ indexType: 'document'
480
+ })
481
+ )
482
+ }
483
+
484
+ // Provider creation now returns Effect
485
+ const provider = options.provider ?? (yield* createOpenAIProvider())
486
+
487
+ // ...
488
+ })
489
+ ```
490
+
491
+ ---
492
+
493
+ ### Recommendation 3.4: Replace Silent Catches with Logged Failures
494
+
495
+ **Priority**: MEDIUM
496
+
497
+ ```typescript
498
+ // Instead of silent catch:
499
+ for (let fileIndex = 0; fileIndex < docPaths.length; fileIndex++) {
500
+ const docPath = docPaths[fileIndex]!
501
+ const filePath = path.join(resolvedRoot, docPath)
502
+
503
+ const fileContentResult = yield* Effect.tryPromise({
504
+ try: () => fs.readFile(filePath, 'utf-8'),
505
+ catch: (e) => new FileReadError({
506
+ path: filePath,
507
+ cause: e instanceof Error ? e.message : String(e)
508
+ })
509
+ }).pipe(
510
+ Effect.catchTag('FileReadError', (error) =>
511
+ Effect.gen(function* () {
512
+ yield* Effect.logWarning(`Skipping unreadable file: ${error.path}`)
513
+ return null // Return null to indicate skip
514
+ })
515
+ )
516
+ )
517
+
518
+ if (fileContentResult === null) continue
519
+
520
+ // Process file content...
521
+ }
522
+ ```
523
+
524
+ ---
525
+
526
+ ### Recommendation 3.5: Move Error Formatting to CLI Boundary
527
+
528
+ **Priority**: MEDIUM
529
+
530
+ ```typescript
531
+ // src/embeddings/openai-provider.ts - Keep it simple
532
+ export const handleApiKeyError = <A, E>(
533
+ effect: Effect.Effect<A, E | MissingApiKeyError | InvalidApiKeyError>,
534
+ ): Effect.Effect<A, E | MissingApiKeyError | InvalidApiKeyError> =>
535
+ effect // Just pass through - let CLI layer format
536
+
537
+ // src/cli/error-formatter.ts - Centralize formatting
538
+ export const formatEmbeddingError = (error: EmbeddingModuleError): string => {
539
+ switch (error._tag) {
540
+ case 'MissingApiKeyError':
541
+ return [
542
+ '',
543
+ 'Error: OPENAI_API_KEY not set',
544
+ '',
545
+ 'To use semantic search, set your OpenAI API key:',
546
+ ' export OPENAI_API_KEY=sk-...',
547
+ '',
548
+ 'Or add to .env file in project root.',
549
+ ].join('\n')
550
+
551
+ case 'InvalidApiKeyError':
552
+ return [
553
+ '',
554
+ 'Error: Invalid OPENAI_API_KEY',
555
+ '',
556
+ 'The provided API key was rejected by OpenAI.',
557
+ `Details: ${error.details}`,
558
+ ].join('\n')
559
+
560
+ case 'IndexNotFoundError':
561
+ return `Error: ${error.indexType} index not found. Run 'mdcontext index' first.`
562
+
563
+ // ... other cases
564
+ }
565
+ }
566
+ ```
567
+
568
+ ---
569
+
570
+ ### Recommendation 3.6: Use Effect.try for Fallible Operations
571
+
572
+ **Priority**: HIGH
573
+
574
+ ```typescript
575
+ // src/embeddings/vector-store.ts
576
+ add(entries: VectorEntry[]): Effect.Effect<void, VectorStoreError> {
577
+ return Effect.try({
578
+ try: () => {
579
+ const index = this.ensureIndex()
580
+ for (const entry of entries) {
581
+ if (this.idToIndex.has(entry.id)) continue
582
+
583
+ const idx = this.nextIndex++
584
+ if (idx >= index.getMaxElements()) {
585
+ index.resizeIndex(index.getMaxElements() * 2)
586
+ }
587
+
588
+ index.addPoint(entry.embedding as number[], idx)
589
+ this.entries.set(idx, entry)
590
+ this.idToIndex.set(entry.id, idx)
591
+ }
592
+ },
593
+ catch: (e) => new VectorStoreError({
594
+ operation: 'add',
595
+ message: e instanceof Error ? e.message : String(e),
596
+ cause: e,
597
+ })
598
+ })
599
+ }
600
+ ```
601
+
602
+ ---
603
+
604
+ ### Recommendation 3.7: Wrap JSON.parse with Error Handling
605
+
606
+ **Priority**: MEDIUM
607
+
608
+ ```typescript
609
+ // src/embeddings/vector-store.ts
610
+ load(): Effect.Effect<boolean, VectorStoreError> {
611
+ return Effect.gen(function* (this: HnswVectorStore) {
612
+ // ...
613
+ const metaContent = yield* Effect.promise(() =>
614
+ fs.readFile(metaPath, 'utf-8'),
615
+ )
616
+
617
+ const meta = yield* Effect.try({
618
+ try: () => JSON.parse(metaContent) as VectorIndex,
619
+ catch: (e) => new VectorStoreError({
620
+ operation: 'load',
621
+ message: 'Failed to parse vector metadata',
622
+ cause: e,
623
+ })
624
+ })
625
+
626
+ // ...
627
+ }.bind(this))
628
+ }
629
+ ```
630
+
631
+ ---
632
+
633
+ ### Recommendation 3.8: Update Types to Use Tagged Errors
634
+
635
+ **Priority**: MEDIUM
636
+
637
+ Replace `types.ts` `EmbedError` with proper `Data.TaggedError` or remove it in favor of the errors defined in `errors.ts`:
638
+
639
+ ```typescript
640
+ // src/embeddings/types.ts
641
+ // Remove the EmbedError interface and embedError factory
642
+ // Import from errors.ts instead:
643
+ export type {
644
+ MissingApiKeyError,
645
+ InvalidApiKeyError,
646
+ EmbeddingError,
647
+ // ...
648
+ } from './errors.js'
649
+ ```
650
+
651
+ ---
652
+
653
+ ## 4. Priority Summary
654
+
655
+ ### HIGH Priority (Address First)
656
+ | Issue | Description | Impact |
657
+ |-------|-------------|--------|
658
+ | 1.1 | Non-Effect error classes | Cannot use `catchTag`, breaks type safety |
659
+ | 1.2 | Constructor throws exception | Errors become defects, untracked |
660
+ | 1.4 | Generic `Error` return types | No exhaustive error handling |
661
+ | 1.9 | `Effect.sync` for fallible ops | Exceptions become untracked defects |
662
+
663
+ ### MEDIUM Priority
664
+ | Issue | Description | Impact |
665
+ |-------|-------------|--------|
666
+ | 1.3 | Unused `EmbedError` type | Code inconsistency |
667
+ | 1.5 | Silent error swallowing | Users blind to failures |
668
+ | 1.6 | Inconsistent transformation | Fragile error handling |
669
+ | 1.8 | `handleApiKeyError` loses type | Type safety degradation |
670
+ | 1.10 | Unhandled `JSON.parse` | Potential crash on corrupt data |
671
+
672
+ ### LOW Priority
673
+ | Issue | Description | Impact |
674
+ |-------|-------------|--------|
675
+ | 1.7 | Messages in code | Harder to maintain/localize |
676
+
677
+ ---
678
+
679
+ ## 5. Migration Strategy
680
+
681
+ ### Phase 1: Create Error Types
682
+ 1. Create `/src/embeddings/errors.ts` with `Data.TaggedError` classes
683
+ 2. Export from module index
684
+ 3. No breaking changes yet
685
+
686
+ ### Phase 2: Update OpenAI Provider
687
+ 1. Convert constructor to factory function
688
+ 2. Update error classes to use new tagged errors
689
+ 3. Remove presentation logic from `handleApiKeyError`
690
+
691
+ ### Phase 3: Update Vector Store
692
+ 1. Replace `Effect.sync` with `Effect.try`
693
+ 2. Add error handling for `JSON.parse`
694
+ 3. Use `VectorStoreError` for all operations
695
+
696
+ ### Phase 4: Update Semantic Search
697
+ 1. Update return types to use union of tagged errors
698
+ 2. Replace silent catches with logged failures
699
+ 3. Use proper error transformation with cause preservation
700
+
701
+ ### Phase 5: CLI Integration
702
+ 1. Create centralized error formatter
703
+ 2. Update CLI commands to use `catchTags` at boundary
704
+ 3. Add verbosity support for error detail levels
705
+
706
+ ---
707
+
708
+ _Analysis created: 2026-01-22_
709
+ _Based on: effect-errors-as-values.md, effect-cli-error-handling.md_