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,565 @@
1
+ /**
2
+ * Provider-Specific Error Detection and Handling
3
+ *
4
+ * This module provides utilities to detect and classify errors from different
5
+ * embedding providers (Ollama, LM Studio, OpenRouter) and transform them into
6
+ * user-friendly error messages with actionable suggestions.
7
+ *
8
+ * ## Supported Providers
9
+ *
10
+ * - **Ollama**: Local daemon-based embedding (port 11434)
11
+ * - **LM Studio**: GUI-based local embedding (port 1234)
12
+ * - **OpenRouter**: API gateway for multiple providers
13
+ *
14
+ * ## Usage
15
+ *
16
+ * ```typescript
17
+ * import { detectProviderError, getProviderSuggestions } from './provider-errors.js'
18
+ *
19
+ * try {
20
+ * await client.embeddings.create(...)
21
+ * } catch (error) {
22
+ * const providerError = detectProviderError('ollama', error)
23
+ * if (providerError) {
24
+ * console.log(providerError.userMessage)
25
+ * console.log(getProviderSuggestions(providerError))
26
+ * }
27
+ * }
28
+ * ```
29
+ */
30
+
31
+ import type { EmbeddingProvider } from '../config/schema.js'
32
+ import { PROVIDER_PORTS } from './provider-constants.js'
33
+
34
+ // ============================================================================
35
+ // Provider Error Types
36
+ // ============================================================================
37
+
38
+ /**
39
+ * Types of provider-specific errors
40
+ */
41
+ export type ProviderErrorType =
42
+ // Connection errors (local providers)
43
+ | 'daemon-not-running'
44
+ | 'gui-not-running'
45
+ | 'connection-refused'
46
+ | 'connection-timeout'
47
+ // Model errors
48
+ | 'model-not-found'
49
+ | 'model-loading'
50
+ | 'model-not-ready'
51
+ // API errors (remote providers)
52
+ | 'invalid-api-key'
53
+ | 'rate-limited'
54
+ | 'quota-exceeded'
55
+ | 'model-unavailable'
56
+ // Generic
57
+ | 'network-error'
58
+ | 'unknown'
59
+
60
+ /**
61
+ * Structured provider error with context for user-friendly display
62
+ */
63
+ export interface ProviderError {
64
+ readonly type: ProviderErrorType
65
+ readonly provider: EmbeddingProvider
66
+ readonly message: string
67
+ readonly model?: string | undefined
68
+ readonly originalError: unknown
69
+ }
70
+
71
+ // ============================================================================
72
+ // Port Detection
73
+ // ============================================================================
74
+
75
+ /**
76
+ * Detect which provider an error is from based on port number.
77
+ * Uses PROVIDER_PORTS from provider-constants.ts as single source of truth.
78
+ */
79
+ const detectProviderFromPort = (
80
+ error: Error,
81
+ ): EmbeddingProvider | undefined => {
82
+ const message = error.message
83
+ for (const [provider, port] of Object.entries(PROVIDER_PORTS)) {
84
+ if (port && message.includes(String(port))) {
85
+ return provider as EmbeddingProvider
86
+ }
87
+ }
88
+ return undefined
89
+ }
90
+
91
+ // ============================================================================
92
+ // Ollama Error Detection
93
+ // ============================================================================
94
+
95
+ /**
96
+ * Detect Ollama-specific errors
97
+ */
98
+ const detectOllamaError = (error: unknown): ProviderError | null => {
99
+ if (!(error instanceof Error)) return null
100
+
101
+ const message = error.message.toLowerCase()
102
+
103
+ // Connection refused - daemon not running
104
+ if (
105
+ message.includes('econnrefused') ||
106
+ message.includes('connect econnrefused') ||
107
+ message.includes('connection refused')
108
+ ) {
109
+ // Check if it's Ollama's port
110
+ if (
111
+ error.message.includes('11434') ||
112
+ error.message.includes('localhost:11434')
113
+ ) {
114
+ return {
115
+ type: 'daemon-not-running',
116
+ provider: 'ollama',
117
+ message: 'Ollama daemon is not running',
118
+ originalError: error,
119
+ }
120
+ }
121
+ }
122
+
123
+ // Model not found
124
+ if (
125
+ message.includes('model') &&
126
+ (message.includes('not found') || message.includes('does not exist'))
127
+ ) {
128
+ const model = extractModelName(error.message)
129
+ return {
130
+ type: 'model-not-found',
131
+ provider: 'ollama',
132
+ message: model
133
+ ? `Model '${model}' is not installed in Ollama`
134
+ : 'Requested model is not installed in Ollama',
135
+ model,
136
+ originalError: error,
137
+ }
138
+ }
139
+
140
+ // Model loading/not ready
141
+ if (
142
+ message.includes('not ready') ||
143
+ message.includes('loading') ||
144
+ message.includes('initializing')
145
+ ) {
146
+ const model = extractModelName(error.message)
147
+ return {
148
+ type: 'model-loading',
149
+ provider: 'ollama',
150
+ message: model
151
+ ? `Model '${model}' is still loading`
152
+ : 'Model is still loading',
153
+ model,
154
+ originalError: error,
155
+ }
156
+ }
157
+
158
+ return null
159
+ }
160
+
161
+ // ============================================================================
162
+ // LM Studio Error Detection
163
+ // ============================================================================
164
+
165
+ /**
166
+ * Detect LM Studio-specific errors
167
+ */
168
+ const detectLMStudioError = (error: unknown): ProviderError | null => {
169
+ if (!(error instanceof Error)) return null
170
+
171
+ const message = error.message.toLowerCase()
172
+
173
+ // Connection refused - GUI not running
174
+ if (
175
+ message.includes('econnrefused') ||
176
+ message.includes('connect econnrefused') ||
177
+ message.includes('connection refused')
178
+ ) {
179
+ if (
180
+ error.message.includes('1234') ||
181
+ error.message.includes('localhost:1234')
182
+ ) {
183
+ return {
184
+ type: 'gui-not-running',
185
+ provider: 'lm-studio',
186
+ message: 'LM Studio is not running or local server is not started',
187
+ originalError: error,
188
+ }
189
+ }
190
+ }
191
+
192
+ // Model not loaded
193
+ if (
194
+ message.includes('no model') ||
195
+ message.includes('model not loaded') ||
196
+ message.includes('select a model')
197
+ ) {
198
+ return {
199
+ type: 'model-not-found',
200
+ provider: 'lm-studio',
201
+ message: 'No embedding model is loaded in LM Studio',
202
+ originalError: error,
203
+ }
204
+ }
205
+
206
+ return null
207
+ }
208
+
209
+ // ============================================================================
210
+ // OpenRouter Error Detection
211
+ // ============================================================================
212
+
213
+ /**
214
+ * Detect OpenRouter-specific errors
215
+ */
216
+ const detectOpenRouterError = (error: unknown): ProviderError | null => {
217
+ if (!(error instanceof Error)) return null
218
+
219
+ const message = error.message.toLowerCase()
220
+
221
+ // Invalid API key (401 Unauthorized)
222
+ if (
223
+ message.includes('401') ||
224
+ message.includes('unauthorized') ||
225
+ message.includes('invalid api key') ||
226
+ message.includes('invalid_api_key')
227
+ ) {
228
+ return {
229
+ type: 'invalid-api-key',
230
+ provider: 'openrouter',
231
+ message: 'Invalid or missing OpenRouter API key',
232
+ originalError: error,
233
+ }
234
+ }
235
+
236
+ // Rate limiting (429)
237
+ if (message.includes('429') || message.includes('rate limit')) {
238
+ return {
239
+ type: 'rate-limited',
240
+ provider: 'openrouter',
241
+ message: 'OpenRouter rate limit reached',
242
+ originalError: error,
243
+ }
244
+ }
245
+
246
+ // Quota exceeded
247
+ if (message.includes('quota') || message.includes('insufficient')) {
248
+ return {
249
+ type: 'quota-exceeded',
250
+ provider: 'openrouter',
251
+ message: 'OpenRouter quota exceeded',
252
+ originalError: error,
253
+ }
254
+ }
255
+
256
+ // Model unavailable
257
+ if (
258
+ message.includes('model') &&
259
+ (message.includes('not available') ||
260
+ message.includes('not found') ||
261
+ message.includes('not supported'))
262
+ ) {
263
+ const model = extractModelName(error.message)
264
+ return {
265
+ type: 'model-unavailable',
266
+ provider: 'openrouter',
267
+ message: model
268
+ ? `Model '${model}' is not available via OpenRouter`
269
+ : 'Requested model is not available via OpenRouter',
270
+ model,
271
+ originalError: error,
272
+ }
273
+ }
274
+
275
+ return null
276
+ }
277
+
278
+ // ============================================================================
279
+ // Generic Network Error Detection
280
+ // ============================================================================
281
+
282
+ /**
283
+ * Detect generic network errors
284
+ */
285
+ const detectNetworkError = (
286
+ provider: EmbeddingProvider,
287
+ error: unknown,
288
+ ): ProviderError | null => {
289
+ if (!(error instanceof Error)) return null
290
+
291
+ const message = error.message.toLowerCase()
292
+
293
+ // Connection errors
294
+ if (
295
+ message.includes('econnrefused') ||
296
+ message.includes('connection refused')
297
+ ) {
298
+ return {
299
+ type: 'connection-refused',
300
+ provider,
301
+ message: `Cannot connect to ${provider} server`,
302
+ originalError: error,
303
+ }
304
+ }
305
+
306
+ // Timeout
307
+ if (
308
+ message.includes('timeout') ||
309
+ message.includes('etimedout') ||
310
+ message.includes('timed out')
311
+ ) {
312
+ return {
313
+ type: 'connection-timeout',
314
+ provider,
315
+ message: `Connection to ${provider} timed out`,
316
+ originalError: error,
317
+ }
318
+ }
319
+
320
+ // DNS/network errors
321
+ if (
322
+ message.includes('enotfound') ||
323
+ message.includes('dns') ||
324
+ message.includes('network')
325
+ ) {
326
+ return {
327
+ type: 'network-error',
328
+ provider,
329
+ message: `Network error connecting to ${provider}`,
330
+ originalError: error,
331
+ }
332
+ }
333
+
334
+ return null
335
+ }
336
+
337
+ // ============================================================================
338
+ // Model Name Extraction
339
+ // ============================================================================
340
+
341
+ /**
342
+ * Extract model name from error message using common patterns
343
+ */
344
+ const extractModelName = (message: string): string | undefined => {
345
+ // Pattern: "model 'name'" or "model \"name\""
346
+ const quotedMatch = message.match(/model\s+['"]([^'"]+)['"]/i)
347
+ if (quotedMatch) return quotedMatch[1]
348
+
349
+ // Pattern: "model: name" or "model name"
350
+ const colonMatch = message.match(/model[:\s]+(\S+)/i)
351
+ if (colonMatch) return colonMatch[1]
352
+
353
+ return undefined
354
+ }
355
+
356
+ // ============================================================================
357
+ // Main Detection Function
358
+ // ============================================================================
359
+
360
+ /**
361
+ * Detect and classify a provider-specific error.
362
+ *
363
+ * @param provider - The embedding provider being used
364
+ * @param error - The error to analyze
365
+ * @returns Structured ProviderError or null if not a recognized provider error
366
+ */
367
+ export const detectProviderError = (
368
+ provider: EmbeddingProvider,
369
+ error: unknown,
370
+ ): ProviderError | null => {
371
+ // Try provider-specific detection first
372
+ switch (provider) {
373
+ case 'ollama':
374
+ return detectOllamaError(error) ?? detectNetworkError(provider, error)
375
+ case 'lm-studio':
376
+ return detectLMStudioError(error) ?? detectNetworkError(provider, error)
377
+ case 'openrouter':
378
+ return detectOpenRouterError(error) ?? detectNetworkError(provider, error)
379
+ case 'openai':
380
+ // OpenAI errors are handled by the SDK, fall through to network check
381
+ return detectNetworkError(provider, error)
382
+ default:
383
+ return detectNetworkError(provider, error)
384
+ }
385
+ }
386
+
387
+ /**
388
+ * Auto-detect provider from error (for cases where provider context is lost)
389
+ */
390
+ export const detectProviderFromError = (
391
+ error: unknown,
392
+ ): EmbeddingProvider | undefined => {
393
+ if (error instanceof Error) {
394
+ return detectProviderFromPort(error)
395
+ }
396
+ return undefined
397
+ }
398
+
399
+ // ============================================================================
400
+ // Suggestions
401
+ // ============================================================================
402
+
403
+ /**
404
+ * Recommended Ollama embedding models
405
+ */
406
+ export const RECOMMENDED_OLLAMA_MODELS = [
407
+ { name: 'nomic-embed-text', dims: 768, note: 'recommended, fast' },
408
+ { name: 'mxbai-embed-large', dims: 1024, note: 'higher quality' },
409
+ { name: 'bge-m3', dims: 1024, note: 'multilingual' },
410
+ ] as const
411
+
412
+ /**
413
+ * Get actionable suggestions for a provider error
414
+ */
415
+ export const getProviderSuggestions = (error: ProviderError): string[] => {
416
+ switch (error.type) {
417
+ // Ollama errors
418
+ case 'daemon-not-running':
419
+ return [
420
+ 'Start the Ollama daemon: ollama serve',
421
+ 'Install Ollama: https://ollama.com/download',
422
+ ]
423
+
424
+ // LM Studio errors
425
+ case 'gui-not-running':
426
+ return [
427
+ 'Open LM Studio application',
428
+ 'Go to Developer tab and start the local server',
429
+ 'Ensure an embedding model is loaded',
430
+ 'Note: LM Studio requires GUI - consider Ollama for automation',
431
+ ]
432
+
433
+ // Model errors
434
+ case 'model-not-found':
435
+ if (error.provider === 'ollama') {
436
+ const suggestions = [
437
+ error.model
438
+ ? `Download the model: ollama pull ${error.model}`
439
+ : 'Download an embedding model',
440
+ 'Recommended embedding models:',
441
+ ]
442
+ for (const model of RECOMMENDED_OLLAMA_MODELS) {
443
+ suggestions.push(
444
+ ` - ${model.name} (${model.dims} dims, ${model.note})`,
445
+ )
446
+ }
447
+ return suggestions
448
+ }
449
+ if (error.provider === 'lm-studio') {
450
+ return [
451
+ 'Load an embedding model in LM Studio',
452
+ 'Go to Models tab and download an embedding model',
453
+ 'Then load it in the Home tab',
454
+ ]
455
+ }
456
+ if (error.provider === 'openrouter') {
457
+ return [
458
+ 'Check available models: https://openrouter.ai/models',
459
+ 'Common embedding models: text-embedding-3-small, text-embedding-3-large',
460
+ ]
461
+ }
462
+ return ['Check that the model name is correct']
463
+
464
+ case 'model-loading':
465
+ return [
466
+ 'Wait for the model to finish loading',
467
+ error.model
468
+ ? `Or pre-load it: ollama run ${error.model}`
469
+ : 'First request may be slow while model loads',
470
+ ]
471
+
472
+ // OpenRouter API errors
473
+ case 'invalid-api-key':
474
+ return [
475
+ 'Get an API key: https://openrouter.ai/keys',
476
+ 'Set the key: export OPENROUTER_API_KEY=sk-or-...',
477
+ 'Or set: export OPENAI_API_KEY=sk-or-...',
478
+ 'Note: OpenRouter keys start with sk-or-',
479
+ ]
480
+
481
+ case 'rate-limited':
482
+ return [
483
+ 'Wait a moment and try again',
484
+ 'OpenRouter shares rate limits across all users',
485
+ 'Consider using Ollama for unlimited local inference',
486
+ 'Or use OpenAI directly for higher rate limits',
487
+ ]
488
+
489
+ case 'quota-exceeded':
490
+ return [
491
+ 'Check your OpenRouter balance: https://openrouter.ai/credits',
492
+ 'Add credits to continue using OpenRouter',
493
+ 'Or switch to a free provider like Ollama',
494
+ ]
495
+
496
+ case 'model-unavailable':
497
+ return [
498
+ 'Check available models: https://openrouter.ai/models',
499
+ 'Try: text-embedding-3-small or text-embedding-3-large',
500
+ ]
501
+
502
+ // Connection errors
503
+ case 'connection-refused':
504
+ if (error.provider === 'ollama') {
505
+ return ['Start the Ollama daemon: ollama serve']
506
+ }
507
+ if (error.provider === 'lm-studio') {
508
+ return ['Open LM Studio and start the local server']
509
+ }
510
+ return ['Check that the server is running']
511
+
512
+ case 'connection-timeout':
513
+ return [
514
+ 'Check your network connection',
515
+ 'The server may be overloaded, try again later',
516
+ 'Consider increasing timeout in config',
517
+ ]
518
+
519
+ case 'network-error':
520
+ return [
521
+ 'Check your internet connection',
522
+ 'Check if the server is reachable',
523
+ 'Try again later',
524
+ ]
525
+
526
+ default:
527
+ return ['Check the error details above']
528
+ }
529
+ }
530
+
531
+ /**
532
+ * Get a user-friendly error title for display
533
+ */
534
+ export const getProviderErrorTitle = (error: ProviderError): string => {
535
+ switch (error.type) {
536
+ case 'daemon-not-running':
537
+ return 'Ollama is not running'
538
+ case 'gui-not-running':
539
+ return 'LM Studio is not running'
540
+ case 'model-not-found':
541
+ return error.model
542
+ ? `Model '${error.model}' not found`
543
+ : 'Model not found'
544
+ case 'model-loading':
545
+ return error.model
546
+ ? `Model '${error.model}' is still loading`
547
+ : 'Model is still loading'
548
+ case 'invalid-api-key':
549
+ return 'Invalid API key'
550
+ case 'rate-limited':
551
+ return 'Rate limit exceeded'
552
+ case 'quota-exceeded':
553
+ return 'Quota exceeded'
554
+ case 'model-unavailable':
555
+ return 'Model not available'
556
+ case 'connection-refused':
557
+ return `Cannot connect to ${error.provider}`
558
+ case 'connection-timeout':
559
+ return `Connection to ${error.provider} timed out`
560
+ case 'network-error':
561
+ return 'Network error'
562
+ default:
563
+ return 'Embedding error'
564
+ }
565
+ }