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,845 @@
1
+ # Effect CLI Error Handling Research
2
+
3
+ This document explores best practices for error handling in Effect-based CLI applications, with specific recommendations for mdcontext.
4
+
5
+ ## Table of Contents
6
+
7
+ 1. [@effect/cli Error Patterns](#1-effectcli-error-patterns)
8
+ 2. [Layered Error Handling](#2-layered-error-handling)
9
+ 3. [Error Presentation](#3-error-presentation)
10
+ 4. [Real-world Examples](#4-real-world-examples)
11
+ 5. [Recommendations for mdcontext](#5-recommendations-for-mdcontext)
12
+
13
+ ---
14
+
15
+ ## 1. @effect/cli Error Patterns
16
+
17
+ ### Command Type Signature
18
+
19
+ The @effect/cli `Command` type uses three type parameters:
20
+
21
+ ```typescript
22
+ Command<R, E, A>;
23
+ ```
24
+
25
+ - **R** (Environment): Dependencies or context needed by the command's handler
26
+ - **E** (Expected Errors): Types of errors the command might produce during execution
27
+ - **A** (Arguments/Configuration): Configuration object the handler receives
28
+
29
+ ### ValidationError Types
30
+
31
+ @effect/cli provides built-in validation errors through the `ValidationError` module. These are returned when parsing fails:
32
+
33
+ | Error Type | Description |
34
+ | ------------------------ | ---------------------------------- |
35
+ | `CommandMismatch` | Command name doesn't match |
36
+ | `CorrectedFlag` | Auto-corrected misspelled flag |
37
+ | `HelpRequested` | User requested `--help` |
38
+ | `InvalidArgument` | Argument failed validation |
39
+ | `InvalidValue` | Value doesn't meet constraints |
40
+ | `MissingValue` | Required value not provided |
41
+ | `MissingFlag` | Required flag not provided |
42
+ | `MultipleValuesDetected` | Multiple values where one expected |
43
+ | `MissingSubcommand` | Subcommand required but missing |
44
+ | `UnclusteredFlag` | Cluster format issue |
45
+
46
+ Each `ValidationError` contains an `error: HelpDoc` field with formatted documentation.
47
+
48
+ ### Built-in Error Handling
49
+
50
+ The `Command.run` method handles:
51
+
52
+ 1. **Argument parsing** - Validates against command structure
53
+ 2. **Help text generation** - Automatic `--help` and `--version`
54
+ 3. **Error formatting** - User-friendly error messages
55
+
56
+ ```typescript
57
+ import { Command } from "@effect/cli";
58
+ import { NodeContext, NodeRuntime } from "@effect/platform-node";
59
+ import { Effect } from "effect";
60
+
61
+ const myCommand = Command.make(
62
+ "myapp",
63
+ {
64
+ // args and options...
65
+ },
66
+ (args) =>
67
+ Effect.gen(function* () {
68
+ // handler logic
69
+ }),
70
+ );
71
+
72
+ const cli = Command.run(myCommand, {
73
+ name: "myapp",
74
+ version: "1.0.0",
75
+ });
76
+
77
+ cli(process.argv).pipe(
78
+ Effect.provide(NodeContext.layer),
79
+ NodeRuntime.runMain, // Pretty error formatting by default
80
+ );
81
+ ```
82
+
83
+ ### Excess Arguments Protection
84
+
85
+ ```
86
+ npx tsx myapp.ts unexpected extra args
87
+ # Output: Received unknown argument: 'extra'
88
+ ```
89
+
90
+ This prevents execution of malformed commands.
91
+
92
+ ---
93
+
94
+ ## 2. Layered Error Handling
95
+
96
+ ### The Three-Layer Model
97
+
98
+ Effect applications typically organize errors into three layers:
99
+
100
+ ```
101
+ +----------------------------------+
102
+ | User-Facing Errors | CLI output, JSON responses
103
+ +----------------------------------+
104
+ |
105
+ v (transform)
106
+ +----------------------------------+
107
+ | Domain Errors | Business logic failures
108
+ +----------------------------------+
109
+ |
110
+ v (transform)
111
+ +----------------------------------+
112
+ | Infrastructure Errors | API calls, file system, DB
113
+ +----------------------------------+
114
+ ```
115
+
116
+ ### Tagged Errors with Data.TaggedError
117
+
118
+ Create type-safe, discriminated union errors:
119
+
120
+ ```typescript
121
+ import { Data, Effect, Console } from "effect";
122
+
123
+ // Domain errors
124
+ class FileNotFoundError extends Data.TaggedError("FileNotFoundError")<{
125
+ path: string;
126
+ }> {}
127
+
128
+ class ParseError extends Data.TaggedError("ParseError")<{
129
+ path: string;
130
+ message: string;
131
+ line?: number;
132
+ }> {}
133
+
134
+ class IndexNotFoundError extends Data.TaggedError("IndexNotFoundError")<{
135
+ directory: string;
136
+ }> {}
137
+
138
+ // Infrastructure errors
139
+ class ApiKeyMissingError extends Data.TaggedError("ApiKeyMissingError")<{}> {}
140
+
141
+ class ApiKeyInvalidError extends Data.TaggedError("ApiKeyInvalidError")<{
142
+ details: string;
143
+ }> {}
144
+
145
+ class NetworkError extends Data.TaggedError("NetworkError")<{
146
+ url: string;
147
+ statusCode?: number;
148
+ }> {}
149
+ ```
150
+
151
+ ### Transforming Errors Between Layers
152
+
153
+ Use `Effect.mapError` to transform errors at layer boundaries:
154
+
155
+ ```typescript
156
+ import { Effect } from "effect";
157
+
158
+ // Infrastructure layer: raw file system errors
159
+ const readFileRaw = (
160
+ path: string,
161
+ ): Effect.Effect<string, NodeJS.ErrnoException> =>
162
+ Effect.tryPromise({
163
+ try: () => fs.readFile(path, "utf-8"),
164
+ catch: (e) => e as NodeJS.ErrnoException,
165
+ });
166
+
167
+ // Domain layer: transform to domain error
168
+ const readFile = (path: string): Effect.Effect<string, FileNotFoundError> =>
169
+ readFileRaw(path).pipe(
170
+ Effect.mapError((e) => new FileNotFoundError({ path })),
171
+ );
172
+ ```
173
+
174
+ ### Catching Specific Errors with catchTag
175
+
176
+ ```typescript
177
+ import { Effect, Data } from "effect";
178
+
179
+ class HttpError extends Data.TaggedError("HttpError")<{}> {}
180
+ class ValidationError extends Data.TaggedError("ValidationError")<{
181
+ field: string;
182
+ message: string;
183
+ }> {}
184
+
185
+ const program = Effect.gen(function* () {
186
+ // ... operations that may fail
187
+ });
188
+
189
+ // Handle specific error types
190
+ const recovered = program.pipe(
191
+ Effect.catchTag("HttpError", (error) =>
192
+ Effect.succeed("Recovered from HTTP error"),
193
+ ),
194
+ Effect.catchTag("ValidationError", (error) =>
195
+ Effect.succeed(`Validation failed: ${error.message}`),
196
+ ),
197
+ );
198
+ ```
199
+
200
+ ### Handling Multiple Errors with catchTags
201
+
202
+ ```typescript
203
+ const recovered = program.pipe(
204
+ Effect.catchTags({
205
+ HttpError: (error) => Effect.succeed("HTTP recovery"),
206
+ ValidationError: (error) => Effect.succeed(`Validation: ${error.message}`),
207
+ NetworkError: (error) => Effect.succeed(`Network issue: ${error.url}`),
208
+ }),
209
+ );
210
+ ```
211
+
212
+ ### Expected Errors vs Defects
213
+
214
+ **Expected Errors (E channel):**
215
+
216
+ - Recoverable failures the caller can handle
217
+ - Validation errors, "not found", permission denied
218
+ - Tracked in the type system: `Effect<A, E, R>`
219
+
220
+ **Defects (Unexpected Errors):**
221
+
222
+ - Unrecoverable situations: bugs, invariant violations
223
+ - Use `Effect.die` or `Effect.dieMessage`
224
+ - Handle once at system boundary (logging, crash reporting)
225
+
226
+ ```typescript
227
+ // Expected error - caller can recover
228
+ const divide = (a: number, b: number): Effect.Effect<number, DivisionError> =>
229
+ b === 0
230
+ ? Effect.fail(new DivisionError({ dividend: a }))
231
+ : Effect.succeed(a / b);
232
+
233
+ // Defect - unrecoverable, terminates fiber
234
+ const assertPositive = (n: number): Effect.Effect<number, never> =>
235
+ n < 0
236
+ ? Effect.dieMessage("Invariant violated: expected positive number")
237
+ : Effect.succeed(n);
238
+ ```
239
+
240
+ ### Converting Errors to Defects
241
+
242
+ ```typescript
243
+ // Convert all failures to defects
244
+ const program = divide(1, 0).pipe(Effect.orDie);
245
+
246
+ // Convert with custom transformation
247
+ const programWithContext = divide(1, 0).pipe(
248
+ Effect.orDieWith((error) => new Error(`Critical: ${error.message}`)),
249
+ );
250
+ ```
251
+
252
+ ---
253
+
254
+ ## 3. Error Presentation
255
+
256
+ ### Terminal Output with runMain
257
+
258
+ The `runMain` function from `@effect/platform-node` provides built-in pretty error formatting:
259
+
260
+ ```typescript
261
+ import { NodeRuntime } from "@effect/platform-node";
262
+
263
+ cli(process.argv).pipe(
264
+ Effect.provide(NodeContext.layer),
265
+ NodeRuntime.runMain, // Pretty logger enabled by default
266
+ );
267
+
268
+ // Or disable pretty formatting:
269
+ NodeRuntime.runMain({ disablePrettyLogger: true });
270
+ ```
271
+
272
+ ### Custom Error Formatting
273
+
274
+ For CLI-specific error messages:
275
+
276
+ ```typescript
277
+ const formatCliError = (error: unknown): string => {
278
+ if (error && typeof error === "object") {
279
+ const err = error as Record<string, unknown>;
280
+
281
+ // Handle ValidationError from @effect/cli
282
+ if (err._tag === "ValidationError" && err.error) {
283
+ // Extract HelpDoc content
284
+ return extractHelpDocText(err.error);
285
+ }
286
+
287
+ // Handle custom tagged errors
288
+ if (err._tag && typeof err._tag === "string") {
289
+ return formatTaggedError(err);
290
+ }
291
+ }
292
+ return String(error);
293
+ };
294
+ ```
295
+
296
+ ### JSON Error Output for Machine Consumption
297
+
298
+ Support `--json` flag for structured error output:
299
+
300
+ ```typescript
301
+ interface JsonError {
302
+ error: {
303
+ code: string;
304
+ message: string;
305
+ details?: Record<string, unknown>;
306
+ };
307
+ }
308
+
309
+ const formatJsonError = (error: TaggedError): JsonError => ({
310
+ error: {
311
+ code: error._tag,
312
+ message: error.message ?? String(error),
313
+ details: { ...error }, // Include all error properties
314
+ },
315
+ });
316
+
317
+ // In command handler:
318
+ if (jsonOutput) {
319
+ yield * Console.log(JSON.stringify(formatJsonError(error)));
320
+ } else {
321
+ yield * Console.error(formatUserFriendlyError(error));
322
+ }
323
+ ```
324
+
325
+ ### Verbosity Levels
326
+
327
+ Implement `--verbose` and `--debug` flags:
328
+
329
+ ```typescript
330
+ type LogLevel = "quiet" | "normal" | "verbose" | "debug";
331
+
332
+ const logError = (error: Error, level: LogLevel): Effect.Effect<void> =>
333
+ Effect.gen(function* () {
334
+ switch (level) {
335
+ case "quiet":
336
+ // Just exit code
337
+ break;
338
+ case "normal":
339
+ yield* Console.error(error.message);
340
+ break;
341
+ case "verbose":
342
+ yield* Console.error(error.message);
343
+ yield* Console.error(` Type: ${error.constructor.name}`);
344
+ if ("path" in error) {
345
+ yield* Console.error(` Path: ${error.path}`);
346
+ }
347
+ break;
348
+ case "debug":
349
+ yield* Console.error(error.message);
350
+ yield* Console.error(error.stack ?? "");
351
+ yield* Console.error("Full error object:");
352
+ yield* Console.error(JSON.stringify(error, null, 2));
353
+ break;
354
+ }
355
+ });
356
+ ```
357
+
358
+ ### Color and ANSI Formatting
359
+
360
+ @effect/cli integrates with terminal styling:
361
+
362
+ ```typescript
363
+ // Check terminal capabilities
364
+ const supportsColor = process.stdout.isTTY;
365
+
366
+ // ANSI codes for styling
367
+ const bold = (text: string) => (supportsColor ? `\x1b[1m${text}\x1b[0m` : text);
368
+ const red = (text: string) => (supportsColor ? `\x1b[31m${text}\x1b[0m` : text);
369
+ const yellow = (text: string) =>
370
+ supportsColor ? `\x1b[33m${text}\x1b[0m` : text;
371
+
372
+ const formatError = (error: Error) => `${red(bold("Error:"))} ${error.message}`;
373
+ ```
374
+
375
+ ### Schema Error Formatting
376
+
377
+ Effect Schema provides formatters for validation errors:
378
+
379
+ ```typescript
380
+ import { Schema, ParseResult, Either } from "effect";
381
+
382
+ // Tree formatter - hierarchical view
383
+ const result = Schema.decodeUnknownEither(MySchema)(data);
384
+ if (Either.isLeft(result)) {
385
+ console.error(ParseResult.TreeFormatter.formatErrorSync(result.left));
386
+ }
387
+ /* Output:
388
+ { readonly name: string; readonly age: number }
389
+ └─ ["name"]
390
+ └─ is missing
391
+ */
392
+
393
+ // Array formatter - flat list for programmatic use
394
+ if (Either.isLeft(result)) {
395
+ const errors = ParseResult.ArrayFormatter.formatErrorSync(result.left);
396
+ // [{ _tag: 'Missing', path: ['name'], message: 'is missing' }]
397
+ }
398
+ ```
399
+
400
+ ---
401
+
402
+ ## 4. Real-world Examples
403
+
404
+ ### Example 1: File Operation with Layered Errors
405
+
406
+ ```typescript
407
+ import { Data, Effect, Console } from "effect";
408
+ import * as fs from "node:fs/promises";
409
+
410
+ // Error types
411
+ class FileReadError extends Data.TaggedError("FileReadError")<{
412
+ path: string;
413
+ cause: Error;
414
+ }> {}
415
+
416
+ class ConfigParseError extends Data.TaggedError("ConfigParseError")<{
417
+ path: string;
418
+ message: string;
419
+ }> {}
420
+
421
+ // Infrastructure layer
422
+ const readFileRaw = (path: string) =>
423
+ Effect.tryPromise({
424
+ try: () => fs.readFile(path, "utf-8"),
425
+ catch: (e) => new FileReadError({ path, cause: e as Error }),
426
+ });
427
+
428
+ // Domain layer
429
+ const parseConfig = (content: string, path: string) =>
430
+ Effect.try({
431
+ try: () => JSON.parse(content),
432
+ catch: (e) =>
433
+ new ConfigParseError({
434
+ path,
435
+ message: e instanceof Error ? e.message : "Invalid JSON",
436
+ }),
437
+ });
438
+
439
+ // Combined operation
440
+ const loadConfig = (path: string) =>
441
+ Effect.gen(function* () {
442
+ const content = yield* readFileRaw(path);
443
+ return yield* parseConfig(content, path);
444
+ });
445
+
446
+ // CLI layer - transform to user-facing messages
447
+ const loadConfigForCli = (path: string, json: boolean) =>
448
+ loadConfig(path).pipe(
449
+ Effect.catchTags({
450
+ FileReadError: (e) =>
451
+ json
452
+ ? Effect.fail({
453
+ code: "FILE_NOT_FOUND",
454
+ message: `Cannot read: ${e.path}`,
455
+ })
456
+ : Effect.gen(function* () {
457
+ yield* Console.error(`Error: Cannot read file "${e.path}"`);
458
+ yield* Console.error(` ${e.cause.message}`);
459
+ return yield* Effect.fail(new Error("File read failed"));
460
+ }),
461
+ ConfigParseError: (e) =>
462
+ json
463
+ ? Effect.fail({ code: "PARSE_ERROR", message: e.message })
464
+ : Effect.gen(function* () {
465
+ yield* Console.error(`Error: Invalid config file "${e.path}"`);
466
+ yield* Console.error(` ${e.message}`);
467
+ return yield* Effect.fail(new Error("Parse failed"));
468
+ }),
469
+ }),
470
+ );
471
+ ```
472
+
473
+ ### Example 2: API Key Validation Pattern
474
+
475
+ From a real CLI (similar to mdcontext's approach):
476
+
477
+ ```typescript
478
+ import { Console, Effect } from "effect";
479
+
480
+ class MissingApiKeyError extends Error {
481
+ readonly _tag = "MissingApiKeyError";
482
+ }
483
+
484
+ class InvalidApiKeyError extends Error {
485
+ readonly _tag = "InvalidApiKeyError";
486
+ constructor(readonly details: string) {
487
+ super("Invalid API key");
488
+ }
489
+ }
490
+
491
+ // Reusable error handler
492
+ const handleApiKeyError = <A, E>(
493
+ effect: Effect.Effect<A, E | MissingApiKeyError | InvalidApiKeyError>,
494
+ ): Effect.Effect<A, E | Error> =>
495
+ effect.pipe(
496
+ Effect.catchIf(
497
+ (e): e is MissingApiKeyError => e instanceof MissingApiKeyError,
498
+ () =>
499
+ Effect.gen(function* () {
500
+ yield* Console.error("");
501
+ yield* Console.error("Error: API key not set");
502
+ yield* Console.error("");
503
+ yield* Console.error("Set your API key:");
504
+ yield* Console.error(" export OPENAI_API_KEY=sk-...");
505
+ yield* Console.error("");
506
+ yield* Console.error("Or add to .env file.");
507
+ return yield* Effect.fail(new Error("Missing API key"));
508
+ }),
509
+ ),
510
+ Effect.catchIf(
511
+ (e): e is InvalidApiKeyError => e instanceof InvalidApiKeyError,
512
+ (e) =>
513
+ Effect.gen(function* () {
514
+ yield* Console.error("");
515
+ yield* Console.error("Error: Invalid API key");
516
+ yield* Console.error("");
517
+ yield* Console.error("The API key was rejected.");
518
+ yield* Console.error(`Details: ${e.details}`);
519
+ return yield* Effect.fail(new Error("Invalid API key"));
520
+ }),
521
+ ),
522
+ );
523
+
524
+ // Usage in command
525
+ const searchCommand = Command.make(
526
+ "search",
527
+ {
528
+ /* ... */
529
+ },
530
+ (args) =>
531
+ Effect.gen(function* () {
532
+ const results = yield* semanticSearch(args.query).pipe(handleApiKeyError);
533
+ // ...
534
+ }),
535
+ );
536
+ ```
537
+
538
+ ### Example 3: Graceful Degradation
539
+
540
+ ```typescript
541
+ const searchWithFallback = (query: string, options: SearchOptions) =>
542
+ Effect.gen(function* () {
543
+ // Try semantic search first
544
+ const semanticResult = yield* semanticSearch(query).pipe(
545
+ Effect.catchTag("MissingApiKeyError", () => Effect.succeed(null)),
546
+ Effect.catchTag("IndexNotFoundError", () => Effect.succeed(null)),
547
+ );
548
+
549
+ if (semanticResult) {
550
+ return { mode: "semantic", results: semanticResult };
551
+ }
552
+
553
+ // Fall back to keyword search
554
+ yield* Console.log("Falling back to keyword search...");
555
+ const keywordResult = yield* keywordSearch(query);
556
+ return { mode: "keyword", results: keywordResult };
557
+ });
558
+ ```
559
+
560
+ ---
561
+
562
+ ## 5. Recommendations for mdcontext
563
+
564
+ ### Current State Analysis
565
+
566
+ Based on code review, mdcontext currently:
567
+
568
+ 1. **Uses `Effect.fail(new Error(...))` directly** - Loses type safety
569
+ 2. **Has ad-hoc error handling** in `main.ts` with manual `_tag` checking
570
+ 3. **Mixes infrastructure and domain errors** - `IoError` and `ParseError` at same level
571
+ 4. **Has good API key error handling** in `openai-provider.ts` as a pattern to follow
572
+
573
+ ### Current Issues
574
+
575
+ 1. **Untyped errors**: Using `new Error()` loses discriminated union benefits
576
+
577
+ ```typescript
578
+ // Current
579
+ yield * Effect.fail(new Error("At least one file is required..."));
580
+
581
+ // Better
582
+ yield * Effect.fail(new MissingArgumentError({ argument: "files" }));
583
+ ```
584
+
585
+ 2. **Manual error formatting**: The `formatCliError` function manually inspects error structure
586
+
587
+ ```typescript
588
+ // Current - fragile, relies on internal structure
589
+ if (err._tag === "ValidationError" && err.error) {
590
+ const validationError = err.error as Record<string, unknown>;
591
+ // ...deeply nested extraction
592
+ }
593
+ ```
594
+
595
+ 3. **Inconsistent error transformation**: Some places use `Effect.mapError`, others use `Effect.catchAll`
596
+
597
+ 4. **Silent failures**: `catch (_e)` in `assembleContext` swallows errors
598
+
599
+ ```typescript
600
+ } catch (_e) {
601
+ // Skip files that can't be processed
602
+ overflow.push(sourcePath)
603
+ }
604
+ ```
605
+
606
+ ### Proposed Error Hierarchy
607
+
608
+ ```typescript
609
+ // src/errors/index.ts
610
+
611
+ import { Data } from "effect";
612
+
613
+ // Base error for all mdcontext errors
614
+ export class MdcontextError extends Data.TaggedError("MdcontextError")<{
615
+ message: string;
616
+ }> {}
617
+
618
+ // ============================================================================
619
+ // Infrastructure Errors
620
+ // ============================================================================
621
+
622
+ export class FileNotFoundError extends Data.TaggedError("FileNotFoundError")<{
623
+ path: string;
624
+ }> {}
625
+
626
+ export class FileReadError extends Data.TaggedError("FileReadError")<{
627
+ path: string;
628
+ cause: string;
629
+ }> {}
630
+
631
+ export class ApiKeyMissingError extends Data.TaggedError(
632
+ "ApiKeyMissingError",
633
+ )<{}> {}
634
+
635
+ export class ApiKeyInvalidError extends Data.TaggedError("ApiKeyInvalidError")<{
636
+ details: string;
637
+ }> {}
638
+
639
+ export class NetworkError extends Data.TaggedError("NetworkError")<{
640
+ url: string;
641
+ statusCode?: number;
642
+ message: string;
643
+ }> {}
644
+
645
+ // ============================================================================
646
+ // Domain Errors
647
+ // ============================================================================
648
+
649
+ export class ParseError extends Data.TaggedError("ParseError")<{
650
+ path: string;
651
+ message: string;
652
+ line?: number;
653
+ }> {}
654
+
655
+ export class IndexNotFoundError extends Data.TaggedError("IndexNotFoundError")<{
656
+ directory: string;
657
+ }> {}
658
+
659
+ export class IndexOutdatedError extends Data.TaggedError("IndexOutdatedError")<{
660
+ directory: string;
661
+ indexAge: number;
662
+ }> {}
663
+
664
+ export class SectionNotFoundError extends Data.TaggedError(
665
+ "SectionNotFoundError",
666
+ )<{
667
+ selector: string;
668
+ availableSections: string[];
669
+ }> {}
670
+
671
+ export class SearchError extends Data.TaggedError("SearchError")<{
672
+ query: string;
673
+ message: string;
674
+ }> {}
675
+
676
+ // ============================================================================
677
+ // CLI/User-Facing Errors
678
+ // ============================================================================
679
+
680
+ export class MissingArgumentError extends Data.TaggedError(
681
+ "MissingArgumentError",
682
+ )<{
683
+ argument: string;
684
+ usage: string;
685
+ }> {}
686
+
687
+ export class InvalidOptionError extends Data.TaggedError("InvalidOptionError")<{
688
+ option: string;
689
+ value: string;
690
+ expected: string;
691
+ }> {}
692
+ ```
693
+
694
+ ### Proposed Error Handler
695
+
696
+ ```typescript
697
+ // src/cli/error-handler.ts
698
+
699
+ import { Effect, Console } from "effect";
700
+ import type {
701
+ FileNotFoundError,
702
+ ParseError,
703
+ IndexNotFoundError,
704
+ ApiKeyMissingError,
705
+ MissingArgumentError,
706
+ } from "../errors/index.js";
707
+
708
+ type CliError =
709
+ | FileNotFoundError
710
+ | ParseError
711
+ | IndexNotFoundError
712
+ | ApiKeyMissingError
713
+ | MissingArgumentError;
714
+
715
+ interface ErrorOutput {
716
+ json: boolean;
717
+ verbose: boolean;
718
+ }
719
+
720
+ export const handleCliError =
721
+ (options: ErrorOutput) =>
722
+ <A, E extends CliError>(
723
+ effect: Effect.Effect<A, E>,
724
+ ): Effect.Effect<A, never> =>
725
+ effect.pipe(
726
+ Effect.catchTags({
727
+ FileNotFoundError: (e) => formatFileNotFound(e, options),
728
+ ParseError: (e) => formatParseError(e, options),
729
+ IndexNotFoundError: (e) => formatIndexNotFound(e, options),
730
+ ApiKeyMissingError: (e) => formatApiKeyMissing(e, options),
731
+ MissingArgumentError: (e) => formatMissingArgument(e, options),
732
+ }),
733
+ Effect.catchAll((e) => formatUnknownError(e, options)),
734
+ );
735
+
736
+ const formatFileNotFound = (error: FileNotFoundError, options: ErrorOutput) =>
737
+ Effect.gen(function* () {
738
+ if (options.json) {
739
+ yield* Console.log(
740
+ JSON.stringify({
741
+ error: { code: "FILE_NOT_FOUND", path: error.path },
742
+ }),
743
+ );
744
+ } else {
745
+ yield* Console.error(`Error: File not found: ${error.path}`);
746
+ }
747
+ return yield* Effect.die("exit");
748
+ });
749
+
750
+ // ... similar handlers for each error type
751
+ ```
752
+
753
+ ### Migration Path
754
+
755
+ **Phase 1: Define Error Types (Low Risk)**
756
+
757
+ 1. Create `src/errors/index.ts` with tagged error classes
758
+ 2. Export from main index
759
+
760
+ **Phase 2: Update Infrastructure Layer**
761
+
762
+ 1. Update `parser.ts` to use new `ParseError`/`FileReadError`
763
+ 2. Update `openai-provider.ts` to use standard tagged errors
764
+ 3. Update file system operations in indexer, storage
765
+
766
+ **Phase 3: Update Domain Layer**
767
+
768
+ 1. Add `mapError` at service boundaries
769
+ 2. Ensure consistent error transformation
770
+
771
+ **Phase 4: Update CLI Layer**
772
+
773
+ 1. Create centralized error handler
774
+ 2. Update `main.ts` to use new handler
775
+ 3. Add JSON error output support
776
+
777
+ **Phase 5: Add Verbosity Support**
778
+
779
+ 1. Add `--verbose` and `--debug` global options
780
+ 2. Adjust error output detail based on level
781
+
782
+ ### Example Refactored Command
783
+
784
+ ```typescript
785
+ // Before
786
+ export const contextCommand = Command.make(
787
+ "context",
788
+ { files: Args.file({ name: "files" }).pipe(Args.repeated) },
789
+ ({ files }) =>
790
+ Effect.gen(function* () {
791
+ if (files.length === 0) {
792
+ yield* Effect.fail(new Error("At least one file is required..."));
793
+ }
794
+ const document = yield* parseFile(filePath).pipe(
795
+ Effect.mapError((e) => new Error(`${e._tag}: ${e.message}`)),
796
+ );
797
+ // ...
798
+ }),
799
+ );
800
+
801
+ // After
802
+ export const contextCommand = Command.make(
803
+ "context",
804
+ {
805
+ files: Args.file({ name: "files" }).pipe(Args.repeated),
806
+ json: jsonOption,
807
+ verbose: verboseOption,
808
+ },
809
+ ({ files, json, verbose }) =>
810
+ Effect.gen(function* () {
811
+ if (files.length === 0) {
812
+ yield* Effect.fail(
813
+ new MissingArgumentError({
814
+ argument: "files",
815
+ usage: "mdcontext context <file> [files...]",
816
+ }),
817
+ );
818
+ }
819
+
820
+ const document = yield* parseFile(filePath);
821
+ // Errors automatically typed and handled by outer handler
822
+
823
+ // ...
824
+ }).pipe(handleCliError({ json, verbose })),
825
+ );
826
+ ```
827
+
828
+ ---
829
+
830
+ ## Sources
831
+
832
+ - [Effect Documentation - Expected Errors](https://effect.website/docs/error-management/expected-errors/)
833
+ - [Effect Documentation - Error Channel Operations](https://effect.website/docs/error-management/error-channel-operations/)
834
+ - [Effect Documentation - Unexpected Errors](https://effect.website/docs/error-management/unexpected-errors/)
835
+ - [Effect Documentation - Schema Error Formatters](https://effect.website/docs/schema/error-formatters/)
836
+ - [Effect Documentation - Logging](https://effect.website/docs/observability/logging/)
837
+ - [Effect Documentation - Cause](https://effect.website/docs/data-types/cause/)
838
+ - [@effect/cli npm package](https://www.npmjs.com/package/@effect/cli)
839
+ - [@effect/cli API - ValidationError](https://effect-ts.github.io/effect/cli/ValidationError.ts.html)
840
+ - [CLI Framework - DeepWiki](https://deepwiki.com/Effect-TS/effect/8.1-cli-framework)
841
+ - [Effect-TS Examples Repository](https://github.com/Effect-TS/examples)
842
+ - [EffectPatterns Community Repository](https://github.com/PaulJPhilp/EffectPatterns)
843
+ - [Exploring Effect in TypeScript - Tweag](https://www.tweag.io/blog/2024-11-07-typescript-effect/)
844
+ - [How to Effect TS - DTech Vision](https://dtech.vision/blog/how-to-effect-ts-best-practices/)
845
+ - [Effect Solutions - CLI Documentation](https://www.effect.solutions/cli)