mdcontext 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (251) hide show
  1. package/.changeset/config.json +9 -9
  2. package/.claude/settings.local.json +25 -0
  3. package/.github/workflows/claude-code-review.yml +44 -0
  4. package/.github/workflows/claude.yml +85 -0
  5. package/CONTRIBUTING.md +186 -0
  6. package/NOTES/NOTES +44 -0
  7. package/README.md +206 -3
  8. package/biome.json +1 -1
  9. package/dist/chunk-23UPXDNL.js +3044 -0
  10. package/dist/chunk-2W7MO2DL.js +1366 -0
  11. package/dist/chunk-3NUAZGMA.js +1689 -0
  12. package/dist/chunk-7TOWB2XB.js +366 -0
  13. package/dist/chunk-7XOTOADQ.js +3065 -0
  14. package/dist/chunk-AH2PDM2K.js +3042 -0
  15. package/dist/chunk-BNXWSZ63.js +3742 -0
  16. package/dist/chunk-BTL5DJVU.js +3222 -0
  17. package/dist/chunk-HDHYG7E4.js +104 -0
  18. package/dist/chunk-HLR4KZBP.js +3234 -0
  19. package/dist/chunk-IP3FRFEB.js +1045 -0
  20. package/dist/chunk-KHU56VDO.js +3042 -0
  21. package/dist/chunk-KRYIFLQR.js +85 -89
  22. package/dist/chunk-LBSDNLEM.js +287 -0
  23. package/dist/chunk-MNTQ7HCP.js +2643 -0
  24. package/dist/chunk-MUJELQQ6.js +1387 -0
  25. package/dist/chunk-MXJGMSLV.js +2199 -0
  26. package/dist/chunk-N6QJGC3Z.js +2636 -0
  27. package/dist/chunk-OBELGBPM.js +1713 -0
  28. package/dist/chunk-OT7R5XTA.js +3192 -0
  29. package/dist/chunk-P7X4RA2T.js +106 -0
  30. package/dist/chunk-PIDUQNC2.js +3185 -0
  31. package/dist/chunk-POGCDIH4.js +3187 -0
  32. package/dist/chunk-PSIEOQGZ.js +3043 -0
  33. package/dist/chunk-PVRT3IHA.js +3238 -0
  34. package/dist/chunk-QNN4TT23.js +1430 -0
  35. package/dist/chunk-RE3R45RJ.js +3042 -0
  36. package/dist/chunk-S7E6TFX6.js +718 -657
  37. package/dist/chunk-SG6GLU4U.js +1378 -0
  38. package/dist/chunk-SJCDV2ST.js +274 -0
  39. package/dist/chunk-SYE5XLF3.js +104 -0
  40. package/dist/chunk-T5VLYBZD.js +103 -0
  41. package/dist/chunk-TOQB7VWU.js +3238 -0
  42. package/dist/chunk-VFNMZ4ZQ.js +3228 -0
  43. package/dist/chunk-VVTGZNBT.js +1533 -1423
  44. package/dist/chunk-W7Q4RFEV.js +104 -0
  45. package/dist/chunk-XTYYVRLO.js +3190 -0
  46. package/dist/chunk-Y6MDYVJD.js +3063 -0
  47. package/dist/cli/main.js +4072 -629
  48. package/dist/index.d.ts +420 -33
  49. package/dist/index.js +8 -15
  50. package/dist/mcp/server.js +103 -7
  51. package/dist/schema-BAWSG7KY.js +22 -0
  52. package/dist/schema-E3QUPL26.js +20 -0
  53. package/dist/schema-EHL7WUT6.js +20 -0
  54. package/docs/019-USAGE.md +44 -5
  55. package/docs/020-current-implementation.md +8 -8
  56. package/docs/021-DOGFOODING-FINDINGS.md +1 -1
  57. package/docs/CONFIG.md +1123 -0
  58. package/docs/ERRORS.md +383 -0
  59. package/docs/summarization.md +320 -0
  60. package/justfile +40 -0
  61. package/package.json +39 -33
  62. package/research/INDEX.md +315 -0
  63. package/research/code-review/README.md +90 -0
  64. package/research/code-review/cli-error-handling-review.md +979 -0
  65. package/research/code-review/code-review-validation-report.md +464 -0
  66. package/research/code-review/main-ts-review.md +1128 -0
  67. package/research/config-docs/SUMMARY.md +357 -0
  68. package/research/config-docs/TEST-RESULTS.md +776 -0
  69. package/research/config-docs/TODO.md +542 -0
  70. package/research/config-docs/analysis.md +744 -0
  71. package/research/config-docs/fix-validation.md +502 -0
  72. package/research/config-docs/help-audit.md +264 -0
  73. package/research/config-docs/help-system-analysis.md +890 -0
  74. package/research/frontmatter/COMMENTS-ARE-SKIPPED.md +149 -0
  75. package/research/frontmatter/LLM-CODE-NAVIGATION.md +276 -0
  76. package/research/issue-review.md +603 -0
  77. package/research/llm-summarization/agent-cli-tools-2026.md +1082 -0
  78. package/research/llm-summarization/alternative-providers-2026.md +1428 -0
  79. package/research/llm-summarization/anthropic-2026.md +367 -0
  80. package/research/llm-summarization/claude-cli-integration.md +1706 -0
  81. package/research/llm-summarization/cli-integration-patterns.md +3155 -0
  82. package/research/llm-summarization/openai-2026.md +473 -0
  83. package/research/llm-summarization/openai-compatible-providers-2026.md +1022 -0
  84. package/research/llm-summarization/opencode-cli-integration.md +1552 -0
  85. package/research/llm-summarization/prompt-engineering-2026.md +1426 -0
  86. package/research/llm-summarization/prototype-results.md +56 -0
  87. package/research/llm-summarization/provider-switching-patterns-2026.md +2153 -0
  88. package/research/llm-summarization/typescript-llm-libraries-2026.md +2436 -0
  89. package/research/mdcontext-pudding/00-EXECUTIVE-SUMMARY.md +282 -0
  90. package/research/mdcontext-pudding/01-index-embed.md +956 -0
  91. package/research/mdcontext-pudding/02-search-COMMANDS.md +142 -0
  92. package/research/mdcontext-pudding/02-search-SUMMARY.md +146 -0
  93. package/research/mdcontext-pudding/02-search.md +970 -0
  94. package/research/mdcontext-pudding/03-context.md +779 -0
  95. package/research/mdcontext-pudding/04-navigation-and-analytics.md +803 -0
  96. package/research/mdcontext-pudding/04-tree.md +704 -0
  97. package/research/mdcontext-pudding/05-config.md +1038 -0
  98. package/research/mdcontext-pudding/06-links-summary.txt +87 -0
  99. package/research/mdcontext-pudding/06-links.md +679 -0
  100. package/research/mdcontext-pudding/07-stats.md +693 -0
  101. package/research/mdcontext-pudding/BUG-FIX-PLAN.md +388 -0
  102. package/research/mdcontext-pudding/P0-BUG-VALIDATION.md +167 -0
  103. package/research/mdcontext-pudding/README.md +168 -0
  104. package/research/mdcontext-pudding/TESTING-SUMMARY.md +128 -0
  105. package/research/research-quality-review.md +834 -0
  106. package/research/semantic-search/embedding-text-analysis.md +156 -0
  107. package/research/semantic-search/multi-word-failure-reproduction.md +171 -0
  108. package/research/semantic-search/query-processing-analysis.md +207 -0
  109. package/research/semantic-search/root-cause-and-solution.md +114 -0
  110. package/research/semantic-search/threshold-validation-report.md +69 -0
  111. package/research/semantic-search/vector-search-analysis.md +63 -0
  112. package/research/test-path-issues.md +276 -0
  113. package/review/ALP-76/1-error-type-design.md +962 -0
  114. package/review/ALP-76/2-error-handling-patterns.md +906 -0
  115. package/review/ALP-76/3-error-presentation.md +624 -0
  116. package/review/ALP-76/4-test-coverage.md +625 -0
  117. package/review/ALP-76/5-migration-completeness.md +440 -0
  118. package/review/ALP-76/6-effect-best-practices.md +755 -0
  119. package/scripts/apply-branch-protection.sh +47 -0
  120. package/scripts/branch-protection-templates.json +79 -0
  121. package/scripts/prototype-summarization.ts +346 -0
  122. package/scripts/rebuild-hnswlib.js +32 -37
  123. package/scripts/setup-branch-protection.sh +64 -0
  124. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/active-provider.json +7 -0
  125. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/bm25.json +541 -0
  126. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/bm25.meta.json +5 -0
  127. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/config.json +8 -0
  128. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.bin +0 -0
  129. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.meta.bin +0 -0
  130. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/indexes/documents.json +60 -0
  131. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/indexes/links.json +13 -0
  132. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/indexes/sections.json +1197 -0
  133. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/configuration-management.md +99 -0
  134. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/distributed-systems.md +92 -0
  135. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/error-handling.md +78 -0
  136. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/failure-automation.md +55 -0
  137. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/job-context.md +69 -0
  138. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/process-orchestration.md +99 -0
  139. package/src/cli/argv-preprocessor.test.ts +2 -2
  140. package/src/cli/cli.test.ts +230 -33
  141. package/src/cli/commands/config-cmd.ts +642 -0
  142. package/src/cli/commands/context.ts +97 -9
  143. package/src/cli/commands/duplicates.ts +122 -0
  144. package/src/cli/commands/embeddings.ts +529 -0
  145. package/src/cli/commands/index-cmd.ts +210 -30
  146. package/src/cli/commands/index.ts +3 -0
  147. package/src/cli/commands/search.ts +894 -64
  148. package/src/cli/commands/stats.ts +3 -0
  149. package/src/cli/commands/tree.ts +26 -5
  150. package/src/cli/config-layer.ts +176 -0
  151. package/src/cli/error-handler.test.ts +235 -0
  152. package/src/cli/error-handler.ts +655 -0
  153. package/src/cli/flag-schemas.ts +66 -0
  154. package/src/cli/help.ts +209 -7
  155. package/src/cli/main.ts +348 -58
  156. package/src/cli/options.ts +10 -0
  157. package/src/cli/shared-error-handling.ts +199 -0
  158. package/src/cli/utils.ts +150 -17
  159. package/src/config/file-provider.test.ts +320 -0
  160. package/src/config/file-provider.ts +273 -0
  161. package/src/config/index.ts +72 -0
  162. package/src/config/integration.test.ts +667 -0
  163. package/src/config/precedence.test.ts +277 -0
  164. package/src/config/precedence.ts +451 -0
  165. package/src/config/schema.test.ts +414 -0
  166. package/src/config/schema.ts +603 -0
  167. package/src/config/service.test.ts +320 -0
  168. package/src/config/service.ts +243 -0
  169. package/src/config/testing.test.ts +264 -0
  170. package/src/config/testing.ts +110 -0
  171. package/src/core/types.ts +6 -33
  172. package/src/duplicates/detector.test.ts +183 -0
  173. package/src/duplicates/detector.ts +414 -0
  174. package/src/duplicates/index.ts +18 -0
  175. package/src/embeddings/embedding-namespace.test.ts +300 -0
  176. package/src/embeddings/embedding-namespace.ts +947 -0
  177. package/src/embeddings/heading-boost.test.ts +222 -0
  178. package/src/embeddings/hnsw-build-options.test.ts +198 -0
  179. package/src/embeddings/hyde.test.ts +272 -0
  180. package/src/embeddings/hyde.ts +264 -0
  181. package/src/embeddings/index.ts +2 -0
  182. package/src/embeddings/openai-provider.ts +332 -83
  183. package/src/embeddings/pricing.json +22 -0
  184. package/src/embeddings/provider-constants.ts +204 -0
  185. package/src/embeddings/provider-errors.test.ts +967 -0
  186. package/src/embeddings/provider-errors.ts +565 -0
  187. package/src/embeddings/provider-factory.test.ts +240 -0
  188. package/src/embeddings/provider-factory.ts +225 -0
  189. package/src/embeddings/provider-integration.test.ts +788 -0
  190. package/src/embeddings/query-preprocessing.test.ts +187 -0
  191. package/src/embeddings/semantic-search-threshold.test.ts +508 -0
  192. package/src/embeddings/semantic-search.ts +780 -93
  193. package/src/embeddings/types.ts +293 -16
  194. package/src/embeddings/vector-store.ts +486 -77
  195. package/src/embeddings/voyage-provider.ts +313 -0
  196. package/src/errors/errors.test.ts +845 -0
  197. package/src/errors/index.ts +533 -0
  198. package/src/index/ignore-patterns.test.ts +354 -0
  199. package/src/index/ignore-patterns.ts +305 -0
  200. package/src/index/indexer.ts +286 -48
  201. package/src/index/storage.ts +94 -30
  202. package/src/index/types.ts +40 -2
  203. package/src/index/watcher.ts +67 -9
  204. package/src/index.ts +22 -0
  205. package/src/integration/search-keyword.test.ts +678 -0
  206. package/src/mcp/server.ts +135 -6
  207. package/src/parser/parser.ts +18 -19
  208. package/src/parser/section-filter.test.ts +277 -0
  209. package/src/parser/section-filter.ts +125 -3
  210. package/src/search/__tests__/hybrid-search.test.ts +650 -0
  211. package/src/search/bm25-store.ts +366 -0
  212. package/src/search/cross-encoder.test.ts +253 -0
  213. package/src/search/cross-encoder.ts +406 -0
  214. package/src/search/fuzzy-search.test.ts +419 -0
  215. package/src/search/fuzzy-search.ts +273 -0
  216. package/src/search/hybrid-search.ts +448 -0
  217. package/src/search/path-matcher.test.ts +276 -0
  218. package/src/search/path-matcher.ts +33 -0
  219. package/src/search/searcher.test.ts +99 -1
  220. package/src/search/searcher.ts +189 -67
  221. package/src/search/wink-bm25.d.ts +30 -0
  222. package/src/summarization/cli-providers/claude.ts +202 -0
  223. package/src/summarization/cli-providers/detection.test.ts +273 -0
  224. package/src/summarization/cli-providers/detection.ts +118 -0
  225. package/src/summarization/cli-providers/index.ts +8 -0
  226. package/src/summarization/cost.test.ts +139 -0
  227. package/src/summarization/cost.ts +102 -0
  228. package/src/summarization/error-handler.test.ts +127 -0
  229. package/src/summarization/error-handler.ts +111 -0
  230. package/src/summarization/index.ts +102 -0
  231. package/src/summarization/pipeline.test.ts +498 -0
  232. package/src/summarization/pipeline.ts +231 -0
  233. package/src/summarization/prompts.test.ts +269 -0
  234. package/src/summarization/prompts.ts +133 -0
  235. package/src/summarization/provider-factory.test.ts +396 -0
  236. package/src/summarization/provider-factory.ts +178 -0
  237. package/src/summarization/types.ts +184 -0
  238. package/src/summarize/summarizer.ts +104 -35
  239. package/src/types/huggingface-transformers.d.ts +66 -0
  240. package/tests/fixtures/cli/.mdcontext/active-provider.json +7 -0
  241. package/tests/fixtures/cli/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.bin +0 -0
  242. package/tests/fixtures/cli/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.meta.bin +0 -0
  243. package/tests/fixtures/cli/.mdcontext/indexes/documents.json +4 -4
  244. package/tests/fixtures/cli/.mdcontext/indexes/sections.json +14 -0
  245. package/tests/integration/embed-index.test.ts +712 -0
  246. package/tests/integration/search-context.test.ts +469 -0
  247. package/tests/integration/search-semantic.test.ts +522 -0
  248. package/vitest.config.ts +1 -6
  249. package/AGENTS.md +0 -46
  250. package/tests/fixtures/cli/.mdcontext/vectors.bin +0 -0
  251. package/tests/fixtures/cli/.mdcontext/vectors.meta.json +0 -1264
package/dist/index.d.ts CHANGED
@@ -1,4 +1,110 @@
1
- import { Effect } from 'effect';
1
+ import { Config, Option, Effect } from 'effect';
2
+ import * as effect_Cause from 'effect/Cause';
3
+ import * as effect_Types from 'effect/Types';
4
+
5
+ /**
6
+ * Effect Config Schema Module
7
+ *
8
+ * Defines all configuration options using Effect's Config combinators.
9
+ * This provides native integration with Effect's ConfigProvider system,
10
+ * enabling compile-time tracking of config requirements through the type system.
11
+ *
12
+ * ## Configuration Structure
13
+ *
14
+ * - index: Settings for the indexing process
15
+ * - search: Settings for search operations
16
+ * - embeddings: Settings for semantic embeddings (OpenAI)
17
+ * - output: Settings for CLI output formatting
18
+ * - paths: Settings for file paths and directories
19
+ *
20
+ * ## Usage
21
+ *
22
+ * ```typescript
23
+ * import { MdContextConfig, IndexConfig } from './config/schema.js'
24
+ * import { Effect } from 'effect'
25
+ *
26
+ * const program = Effect.gen(function* () {
27
+ * const config = yield* MdContextConfig
28
+ * console.log(`Max depth: ${config.index.maxDepth}`)
29
+ * })
30
+ * ```
31
+ */
32
+
33
+ /**
34
+ * Complete mdcontext configuration
35
+ *
36
+ * Combines all configuration sections into a single nested config.
37
+ * Use with Effect's ConfigProvider to load from various sources.
38
+ */
39
+ declare const MdContextConfig: Config.Config<{
40
+ index: {
41
+ maxDepth: number;
42
+ excludePatterns: string[] | readonly ["node_modules", ".git", "dist", "build"];
43
+ fileExtensions: string[] | readonly [".md", ".mdx"];
44
+ followSymlinks: boolean;
45
+ indexDir: string;
46
+ };
47
+ search: {
48
+ defaultLimit: number;
49
+ maxLimit: number;
50
+ minSimilarity: number;
51
+ includeSnippets: boolean;
52
+ snippetLength: number;
53
+ autoIndexThreshold: number;
54
+ };
55
+ embeddings: {
56
+ provider: "openai" | "ollama" | "lm-studio" | "openrouter" | "voyage";
57
+ baseURL: Option.Option<string>;
58
+ model: string;
59
+ dimensions: number;
60
+ batchSize: number;
61
+ maxRetries: number;
62
+ retryDelayMs: number;
63
+ timeoutMs: number;
64
+ apiKey: Option.Option<string>;
65
+ hnswM: number;
66
+ hnswEfConstruction: number;
67
+ };
68
+ summarization: {
69
+ briefTokenBudget: number;
70
+ summaryTokenBudget: number;
71
+ compressionRatio: number;
72
+ minSectionTokens: number;
73
+ maxTopics: number;
74
+ minPartialBudget: number;
75
+ };
76
+ aiSummarization: {
77
+ mode: "cli" | "api";
78
+ provider: "openai" | "claude" | "copilot" | "cline" | "aider" | "opencode" | "amp" | "deepseek" | "anthropic" | "gemini" | "qwen";
79
+ model: Option.Option<string>;
80
+ stream: boolean;
81
+ baseURL: Option.Option<string>;
82
+ apiKey: Option.Option<string>;
83
+ };
84
+ output: {
85
+ format: "text" | "json";
86
+ color: boolean;
87
+ prettyJson: boolean;
88
+ verbose: boolean;
89
+ debug: boolean;
90
+ };
91
+ paths: {
92
+ root: Option.Option<string>;
93
+ configFile: Option.Option<string>;
94
+ cacheDir: string;
95
+ };
96
+ }>;
97
+ /**
98
+ * Inferred type for the complete configuration
99
+ */
100
+ type MdContextConfig = Config.Config.Success<typeof MdContextConfig>;
101
+
102
+ /**
103
+ * Deeply partial type for MdContextConfig
104
+ */
105
+ type PartialMdContextConfig = {
106
+ [K in keyof MdContextConfig]?: Partial<MdContextConfig[K]>;
107
+ };
2
108
 
3
109
  /**
4
110
  * Core data types for mdcontext
@@ -57,6 +163,12 @@ interface MdCodeBlock {
57
163
  readonly startLine: number;
58
164
  readonly endLine: number;
59
165
  }
166
+ /**
167
+ * Parse error from markdown parsing
168
+ *
169
+ * Note: This interface is used by parser.ts. For the TaggedError version
170
+ * that works with Effect's error handling, see src/errors/index.ts ParseError.
171
+ */
60
172
  interface ParseError {
61
173
  readonly _tag: 'ParseError';
62
174
  readonly message: string;
@@ -64,19 +176,192 @@ interface ParseError {
64
176
  readonly column?: number | undefined;
65
177
  }
66
178
  declare const ParseError: (message: string, line?: number, column?: number) => ParseError;
67
- interface IoError {
68
- readonly _tag: 'IoError';
179
+
180
+ /**
181
+ * Centralized error types for mdcontext
182
+ *
183
+ * This module defines all domain errors using Effect's Data.TaggedError pattern.
184
+ * Each error has a unique `_tag` discriminant that enables:
185
+ * - Exhaustive error handling with `catchTag` / `catchTags`
186
+ * - Type-safe error composition in Effect pipelines
187
+ * - Pattern matching for user-friendly error messages at CLI boundary
188
+ *
189
+ * ## Error Message Convention
190
+ *
191
+ * The `message` field in errors should contain **technical details** from the
192
+ * underlying operation, NOT user-facing formatted messages.
193
+ *
194
+ * **Good (technical):**
195
+ * ```typescript
196
+ * new FileReadError({
197
+ * path: '/path/to/file',
198
+ * message: e.message, // e.g., "ENOENT: no such file or directory"
199
+ * cause: e,
200
+ * })
201
+ * ```
202
+ *
203
+ * **Bad (user-facing):**
204
+ * ```typescript
205
+ * new FileReadError({
206
+ * path: '/path/to/file',
207
+ * message: 'Cannot read file. Please check permissions.', // NO!
208
+ * })
209
+ * ```
210
+ *
211
+ * User-friendly messages are generated at the CLI boundary by the error handler
212
+ * in `src/cli/error-handler.ts`. This separation enables:
213
+ * - i18n/localization in the future
214
+ * - Testing error data without string matching
215
+ * - Consistent formatting in one place
216
+ *
217
+ * ## Error Taxonomy
218
+ *
219
+ * - File System: FileReadError, FileWriteError, DirectoryCreateError, DirectoryWalkError
220
+ * - Parsing: ParseError (for markdown parsing failures)
221
+ * - API: ApiKeyMissingError, ApiKeyInvalidError
222
+ * - Embeddings: EmbeddingError (rate limits, quota, network failures)
223
+ * - Index: IndexNotFoundError, IndexCorruptedError, IndexBuildError
224
+ * - Search: DocumentNotFoundError, EmbeddingsNotFoundError
225
+ * - Vector Store: VectorStoreError
226
+ * - Config: ConfigError
227
+ * - CLI: CliValidationError
228
+ *
229
+ * ## Usage
230
+ *
231
+ * ```typescript
232
+ * import { FileReadError, ApiKeyMissingError } from './errors/index.js'
233
+ * import { Effect } from 'effect'
234
+ *
235
+ * const program = Effect.gen(function* () {
236
+ * // ... operations that may fail
237
+ * }).pipe(
238
+ * Effect.catchTag('FileReadError', (e) => ...),
239
+ * Effect.catchTag('ApiKeyMissingError', (e) => ...)
240
+ * )
241
+ * ```
242
+ */
243
+ /**
244
+ * Standardized error codes for programmatic handling.
245
+ *
246
+ * Error codes enable:
247
+ * - Scripting and automation (check error codes in CI/CD)
248
+ * - Machine-readable error handling without parsing messages
249
+ * - Stable identifiers that don't change when messages are updated
250
+ *
251
+ * Naming convention: E{category}{number}
252
+ * - E1xx: File system errors
253
+ * - E2xx: Parse errors
254
+ * - E3xx: API/authentication errors
255
+ * - E4xx: Index errors
256
+ * - E5xx: Search errors
257
+ * - E6xx: Vector store errors
258
+ * - E7xx: Config errors
259
+ * - E8xx: Watch errors
260
+ * - E9xx: CLI errors
261
+ */
262
+ declare const ErrorCode: {
263
+ readonly FILE_READ: "E100";
264
+ readonly FILE_WRITE: "E101";
265
+ readonly DIRECTORY_CREATE: "E102";
266
+ readonly DIRECTORY_WALK: "E103";
267
+ readonly PARSE: "E200";
268
+ readonly API_KEY_MISSING: "E300";
269
+ readonly API_KEY_INVALID: "E301";
270
+ readonly EMBEDDING_RATE_LIMIT: "E310";
271
+ readonly EMBEDDING_QUOTA: "E311";
272
+ readonly EMBEDDING_NETWORK: "E312";
273
+ readonly EMBEDDING_MODEL: "E313";
274
+ readonly EMBEDDING_UNKNOWN: "E319";
275
+ readonly INDEX_NOT_FOUND: "E400";
276
+ readonly INDEX_CORRUPTED: "E401";
277
+ readonly INDEX_BUILD: "E402";
278
+ readonly DOCUMENT_NOT_FOUND: "E500";
279
+ readonly EMBEDDINGS_NOT_FOUND: "E501";
280
+ readonly VECTOR_STORE: "E600";
281
+ readonly DIMENSION_MISMATCH: "E601";
282
+ readonly CONFIG: "E700";
283
+ readonly WATCH: "E800";
284
+ readonly CLI_VALIDATION: "E900";
285
+ };
286
+ declare const FileReadError_base: new <A extends Record<string, any> = {}>(args: effect_Types.Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => effect_Cause.YieldableError & {
287
+ readonly _tag: "FileReadError";
288
+ } & Readonly<A>;
289
+ /**
290
+ * Error reading a file from the filesystem
291
+ */
292
+ declare class FileReadError extends FileReadError_base<{
293
+ readonly path: string;
69
294
  readonly message: string;
295
+ readonly cause?: unknown;
296
+ }> {
297
+ get code(): typeof ErrorCode.FILE_READ;
298
+ }
299
+ declare const FileWriteError_base: new <A extends Record<string, any> = {}>(args: effect_Types.Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => effect_Cause.YieldableError & {
300
+ readonly _tag: "FileWriteError";
301
+ } & Readonly<A>;
302
+ /**
303
+ * Error writing a file to the filesystem
304
+ */
305
+ declare class FileWriteError extends FileWriteError_base<{
70
306
  readonly path: string;
307
+ readonly message: string;
71
308
  readonly cause?: unknown;
309
+ }> {
310
+ get code(): typeof ErrorCode.FILE_WRITE;
72
311
  }
73
- declare const IoError: (message: string, path: string, cause?: unknown) => IoError;
74
- interface IndexError {
75
- readonly _tag: 'IndexError';
76
- readonly cause: 'DiskFull' | 'Permission' | 'Corrupted' | 'Unknown';
312
+ declare const DirectoryCreateError_base: new <A extends Record<string, any> = {}>(args: effect_Types.Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => effect_Cause.YieldableError & {
313
+ readonly _tag: "DirectoryCreateError";
314
+ } & Readonly<A>;
315
+ /**
316
+ * Error creating a directory
317
+ */
318
+ declare class DirectoryCreateError extends DirectoryCreateError_base<{
319
+ readonly path: string;
77
320
  readonly message: string;
321
+ readonly cause?: unknown;
322
+ }> {
323
+ get code(): typeof ErrorCode.DIRECTORY_CREATE;
324
+ }
325
+ declare const DirectoryWalkError_base: new <A extends Record<string, any> = {}>(args: effect_Types.Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => effect_Cause.YieldableError & {
326
+ readonly _tag: "DirectoryWalkError";
327
+ } & Readonly<A>;
328
+ /**
329
+ * Error walking/traversing a directory tree
330
+ */
331
+ declare class DirectoryWalkError extends DirectoryWalkError_base<{
332
+ readonly path: string;
333
+ readonly message: string;
334
+ readonly cause?: unknown;
335
+ }> {
336
+ get code(): typeof ErrorCode.DIRECTORY_WALK;
337
+ }
338
+ declare const IndexCorruptedError_base: new <A extends Record<string, any> = {}>(args: effect_Types.Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => effect_Cause.YieldableError & {
339
+ readonly _tag: "IndexCorruptedError";
340
+ } & Readonly<A>;
341
+ /**
342
+ * Index exists but is corrupted or invalid
343
+ */
344
+ declare class IndexCorruptedError extends IndexCorruptedError_base<{
345
+ readonly path: string;
346
+ readonly reason: 'InvalidJson' | 'VersionMismatch' | 'MissingData' | 'Unknown';
347
+ readonly details?: string;
348
+ }> {
349
+ get code(): typeof ErrorCode.INDEX_CORRUPTED;
350
+ get message(): string;
351
+ }
352
+ declare const WatchError_base: new <A extends Record<string, any> = {}>(args: effect_Types.Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => effect_Cause.YieldableError & {
353
+ readonly _tag: "WatchError";
354
+ } & Readonly<A>;
355
+ /**
356
+ * File watcher error
357
+ */
358
+ declare class WatchError extends WatchError_base<{
359
+ readonly path: string;
360
+ readonly message: string;
361
+ readonly cause?: unknown;
362
+ }> {
363
+ get code(): typeof ErrorCode.WATCH;
78
364
  }
79
- declare const IndexError: (cause: IndexError["cause"], message: string) => IndexError;
80
365
 
81
366
  /**
82
367
  * Index data types for mdcontext
@@ -128,6 +413,26 @@ interface LinkIndex {
128
413
  readonly backward: Record<string, readonly string[]>;
129
414
  readonly broken: readonly string[];
130
415
  }
416
+ /**
417
+ * Reason why a file was skipped during indexing
418
+ */
419
+ type SkipReason = 'unchanged' | 'excluded' | 'hidden' | 'not-markdown' | 'binary' | 'oversized';
420
+ /**
421
+ * Information about a skipped file
422
+ */
423
+ interface SkippedFile {
424
+ readonly path: string;
425
+ readonly reason: SkipReason;
426
+ }
427
+ /**
428
+ * Summary of skipped files by reason
429
+ */
430
+ interface SkipSummary {
431
+ readonly unchanged: number;
432
+ readonly excluded: number;
433
+ readonly hidden: number;
434
+ readonly total: number;
435
+ }
131
436
  interface IndexResult {
132
437
  readonly documentsIndexed: number;
133
438
  readonly sectionsIndexed: number;
@@ -136,9 +441,18 @@ interface IndexResult {
136
441
  readonly totalSections: number;
137
442
  readonly totalLinks: number;
138
443
  readonly duration: number;
139
- readonly errors: readonly IndexBuildError[];
444
+ /** Non-fatal file processing errors (files that couldn't be indexed) */
445
+ readonly errors: readonly FileProcessingError[];
446
+ readonly skipped: SkipSummary;
140
447
  }
141
- interface IndexBuildError {
448
+ /**
449
+ * Non-fatal error during file processing in index build.
450
+ * These are collected and reported but don't stop the build.
451
+ *
452
+ * Note: This is distinct from IndexBuildError in errors/index.ts,
453
+ * which is a TaggedError for fatal build failures.
454
+ */
455
+ interface FileProcessingError {
142
456
  readonly path: string;
143
457
  readonly message: string;
144
458
  }
@@ -158,14 +472,45 @@ declare const getIndexPaths: (rootPath: string) => {
158
472
  * Indexer service for building and updating indexes
159
473
  */
160
474
 
475
+ interface IndexProgress {
476
+ readonly current: number;
477
+ readonly total: number;
478
+ readonly filePath: string;
479
+ }
161
480
  interface IndexOptions {
481
+ readonly force?: boolean | undefined;
482
+ /** CLI/config exclude patterns (overrides ignore files) */
483
+ readonly exclude?: readonly string[] | undefined;
484
+ /** Whether to honor .gitignore (default: true) */
485
+ readonly honorGitignore?: boolean | undefined;
486
+ /** Whether to honor .mdcontextignore (default: true) */
487
+ readonly honorMdcontextignore?: boolean | undefined;
488
+ /** Callback for progress updates during file indexing */
489
+ readonly onProgress?: ((progress: IndexProgress) => void) | undefined;
490
+ }
491
+ declare const buildIndex: (rootPath: string, options?: IndexOptions) => Effect.Effect<IndexResult, DirectoryWalkError | DirectoryCreateError | FileReadError | FileWriteError | IndexCorruptedError>;
492
+ declare const getOutgoingLinks: (rootPath: string, filePath: string) => Effect.Effect<readonly string[], FileReadError | IndexCorruptedError>;
493
+ declare const getIncomingLinks: (rootPath: string, filePath: string) => Effect.Effect<readonly string[], FileReadError | IndexCorruptedError>;
494
+ declare const getBrokenLinks: (rootPath: string) => Effect.Effect<readonly string[], FileReadError | IndexCorruptedError>;
495
+ interface BuildBM25Options {
162
496
  readonly force?: boolean;
163
- readonly exclude?: readonly string[];
497
+ readonly onProgress?: (progress: {
498
+ current: number;
499
+ total: number;
500
+ }) => void;
501
+ }
502
+ interface BuildBM25Result {
503
+ readonly sectionsIndexed: number;
504
+ readonly duration: number;
164
505
  }
165
- declare const buildIndex: (rootPath: string, options?: IndexOptions) => Effect.Effect<IndexResult, Error>;
166
- declare const getOutgoingLinks: (rootPath: string, filePath: string) => Effect.Effect<readonly string[], Error>;
167
- declare const getIncomingLinks: (rootPath: string, filePath: string) => Effect.Effect<readonly string[], Error>;
168
- declare const getBrokenLinks: (rootPath: string) => Effect.Effect<readonly string[], Error>;
506
+ /**
507
+ * Build BM25 keyword index for all sections.
508
+ *
509
+ * @param rootPath - Root directory containing indexed markdown files
510
+ * @param options - Build options (force rebuild, progress callback)
511
+ * @returns Result with section count and timing
512
+ */
513
+ declare const buildBM25Index: (rootPath: string, options?: BuildBM25Options) => Effect.Effect<BuildBM25Result, FileReadError | IndexCorruptedError | FileWriteError>;
169
514
 
170
515
  /**
171
516
  * Index storage operations
@@ -177,36 +522,63 @@ interface IndexStorage {
177
522
  readonly paths: ReturnType<typeof getIndexPaths>;
178
523
  }
179
524
  declare const createStorage: (rootPath: string) => IndexStorage;
180
- declare const initializeIndex: (storage: IndexStorage) => Effect.Effect<void, Error>;
181
- declare const loadConfig: (storage: IndexStorage) => Effect.Effect<IndexConfig | null, Error>;
182
- declare const saveConfig: (storage: IndexStorage, config: IndexConfig) => Effect.Effect<void, Error>;
183
- declare const loadDocumentIndex: (storage: IndexStorage) => Effect.Effect<DocumentIndex | null, Error>;
184
- declare const saveDocumentIndex: (storage: IndexStorage, index: DocumentIndex) => Effect.Effect<void, Error>;
525
+ declare const initializeIndex: (storage: IndexStorage) => Effect.Effect<void, DirectoryCreateError | FileReadError | FileWriteError | IndexCorruptedError>;
526
+ declare const loadConfig: (storage: IndexStorage) => Effect.Effect<IndexConfig | null, FileReadError | IndexCorruptedError>;
527
+ declare const saveConfig: (storage: IndexStorage, config: IndexConfig) => Effect.Effect<void, DirectoryCreateError | FileWriteError>;
528
+ declare const loadDocumentIndex: (storage: IndexStorage) => Effect.Effect<DocumentIndex | null, FileReadError | IndexCorruptedError>;
529
+ declare const saveDocumentIndex: (storage: IndexStorage, index: DocumentIndex) => Effect.Effect<void, DirectoryCreateError | FileWriteError>;
185
530
  declare const createEmptyDocumentIndex: (rootPath: string) => DocumentIndex;
186
- declare const loadSectionIndex: (storage: IndexStorage) => Effect.Effect<SectionIndex | null, Error>;
187
- declare const saveSectionIndex: (storage: IndexStorage, index: SectionIndex) => Effect.Effect<void, Error>;
531
+ declare const loadSectionIndex: (storage: IndexStorage) => Effect.Effect<SectionIndex | null, FileReadError | IndexCorruptedError>;
532
+ declare const saveSectionIndex: (storage: IndexStorage, index: SectionIndex) => Effect.Effect<void, DirectoryCreateError | FileWriteError>;
188
533
  declare const createEmptySectionIndex: () => SectionIndex;
189
- declare const loadLinkIndex: (storage: IndexStorage) => Effect.Effect<LinkIndex | null, Error>;
190
- declare const saveLinkIndex: (storage: IndexStorage, index: LinkIndex) => Effect.Effect<void, Error>;
534
+ declare const loadLinkIndex: (storage: IndexStorage) => Effect.Effect<LinkIndex | null, FileReadError | IndexCorruptedError>;
535
+ declare const saveLinkIndex: (storage: IndexStorage, index: LinkIndex) => Effect.Effect<void, DirectoryCreateError | FileWriteError>;
191
536
  declare const createEmptyLinkIndex: () => LinkIndex;
192
- declare const indexExists: (storage: IndexStorage) => Effect.Effect<boolean, Error>;
537
+ declare const indexExists: (storage: IndexStorage) => Effect.Effect<boolean, FileReadError>;
193
538
 
194
539
  /**
195
540
  * File watcher for automatic re-indexing
541
+ *
542
+ * ## Why Not Effect Streams?
543
+ *
544
+ * We evaluated using Effect Streams (ALP-101) but decided the current approach is better:
545
+ *
546
+ * 1. **chokidar is battle-tested** - Handles OS-specific quirks (FSEvents on macOS,
547
+ * inotify on Linux, ReadDirectoryChangesW on Windows)
548
+ *
549
+ * 2. **Debouncing handles backpressure** - The 300ms debounce already batches rapid
550
+ * changes, so Stream backpressure isn't needed
551
+ *
552
+ * 3. **Simple use case** - File change → rebuild index. No complex transformations
553
+ * or compositions that would benefit from Stream operators
554
+ *
555
+ * 4. **Already Effect-based** - The setup/teardown is wrapped in Effect for proper
556
+ * error handling, and we use typed errors (WatchError, IndexBuildError)
557
+ *
558
+ * If future requirements need more sophisticated event processing (filtering by
559
+ * content type, incremental updates, event replay), reconsider Streams then.
196
560
  */
197
561
 
562
+ /**
563
+ * Union of errors that can occur during watch operations
564
+ */
565
+ type WatchDirectoryError = WatchError | DirectoryWalkError | DirectoryCreateError | FileReadError | FileWriteError | IndexCorruptedError;
198
566
  interface WatcherOptions extends IndexOptions {
199
567
  readonly debounceMs?: number;
200
568
  readonly onIndex?: (result: {
201
569
  documentsIndexed: number;
202
570
  duration: number;
203
571
  }) => void;
204
- readonly onError?: (error: Error) => void;
572
+ readonly onError?: (error: WatchError) => void;
573
+ /** Whether to honor .gitignore for file watching (default: true) */
574
+ readonly honorGitignore?: boolean;
575
+ /** Whether to honor .mdcontextignore for file watching (default: true) */
576
+ readonly honorMdcontextignore?: boolean;
205
577
  }
206
578
  interface Watcher {
207
579
  readonly stop: () => void;
208
580
  }
209
- declare const watchDirectory: (rootPath: string, options?: WatcherOptions) => Effect.Effect<Watcher, Error>;
581
+ declare const watchDirectory: (rootPath: string, options?: WatcherOptions) => Effect.Effect<Watcher, WatchDirectoryError>;
210
582
 
211
583
  /**
212
584
  * Markdown parser using remark/unified
@@ -220,12 +592,11 @@ interface ParseOptions {
220
592
  declare const parse: (content: string, options?: ParseOptions) => Effect.Effect<MdDocument, ParseError>;
221
593
  /**
222
594
  * Parse a markdown file from the filesystem
595
+ *
596
+ * @throws ParseError - File content cannot be parsed
597
+ * @throws FileReadError - File cannot be read from filesystem
223
598
  */
224
- declare const parseFile: (filePath: string) => Effect.Effect<MdDocument, ParseError | {
225
- _tag: "IoError";
226
- message: string;
227
- path: string;
228
- }>;
599
+ declare const parseFile: (filePath: string) => Effect.Effect<MdDocument, ParseError | FileReadError>;
229
600
 
230
601
  /**
231
602
  * Token counting utilities using tiktoken
@@ -263,4 +634,20 @@ declare const countWords: (text: string) => number;
263
634
  */
264
635
  declare const freeEncoder: () => void;
265
636
 
266
- export { type DocumentEntry, type DocumentIndex, type DocumentMetadata, type HeadingLevel, INDEX_DIR, INDEX_VERSION, type IndexBuildError, type IndexConfig, IndexError, type IndexOptions, type IndexResult, type IndexStorage, IoError, type LinkIndex, type LinkType, type MdCodeBlock, type MdDocument, type MdLink, type MdSection, ParseError, type ParseOptions, type SectionEntry, type SectionIndex, type SectionMetadata, type Watcher, type WatcherOptions, buildIndex, computeHash, countTokens, countTokensApprox, countWords, createEmptyDocumentIndex, createEmptyLinkIndex, createEmptySectionIndex, createStorage, freeEncoder, getBrokenLinks, getIncomingLinks, getIndexPaths, getOutgoingLinks, indexExists, initializeIndex, loadConfig, loadDocumentIndex, loadLinkIndex, loadSectionIndex, parse, parseFile, saveConfig, saveDocumentIndex, saveLinkIndex, saveSectionIndex, watchDirectory };
637
+ /**
638
+ * Type-safe configuration helper for mdcontext.config.ts files.
639
+ *
640
+ * @example
641
+ * ```typescript
642
+ * import { defineConfig } from 'mdcontext'
643
+ *
644
+ * export default defineConfig({
645
+ * index: {
646
+ * maxDepth: 5,
647
+ * },
648
+ * })
649
+ * ```
650
+ */
651
+ declare const defineConfig: <T extends PartialMdContextConfig>(config: T) => T;
652
+
653
+ export { type BuildBM25Options, type BuildBM25Result, type DocumentEntry, type DocumentIndex, type DocumentMetadata, type FileProcessingError, type HeadingLevel, INDEX_DIR, INDEX_VERSION, type IndexConfig, type IndexOptions, type IndexProgress, type IndexResult, type IndexStorage, type LinkIndex, type LinkType, type MdCodeBlock, type MdDocument, type MdLink, type MdSection, ParseError, type ParseOptions, type PartialMdContextConfig, type SectionEntry, type SectionIndex, type SectionMetadata, type SkipReason, type SkipSummary, type SkippedFile, type WatchDirectoryError, type Watcher, type WatcherOptions, buildBM25Index, buildIndex, computeHash, countTokens, countTokensApprox, countWords, createEmptyDocumentIndex, createEmptyLinkIndex, createEmptySectionIndex, createStorage, defineConfig, freeEncoder, getBrokenLinks, getIncomingLinks, getIndexPaths, getOutgoingLinks, indexExists, initializeIndex, loadConfig, loadDocumentIndex, loadLinkIndex, loadSectionIndex, parse, parseFile, saveConfig, saveDocumentIndex, saveLinkIndex, saveSectionIndex, watchDirectory };
package/dist/index.js CHANGED
@@ -1,9 +1,10 @@
1
1
  import {
2
2
  watchDirectory
3
- } from "./chunk-KRYIFLQR.js";
3
+ } from "./chunk-SYE5XLF3.js";
4
4
  import {
5
5
  INDEX_DIR,
6
6
  INDEX_VERSION,
7
+ buildBM25Index,
7
8
  buildIndex,
8
9
  computeHash,
9
10
  countTokens,
@@ -30,7 +31,7 @@ import {
30
31
  saveDocumentIndex,
31
32
  saveLinkIndex,
32
33
  saveSectionIndex
33
- } from "./chunk-S7E6TFX6.js";
34
+ } from "./chunk-MUJELQQ6.js";
34
35
 
35
36
  // src/core/types.ts
36
37
  var ParseError = (message, line, column) => ({
@@ -39,23 +40,14 @@ var ParseError = (message, line, column) => ({
39
40
  line,
40
41
  column
41
42
  });
42
- var IoError = (message, path, cause) => ({
43
- _tag: "IoError",
44
- message,
45
- path,
46
- cause
47
- });
48
- var IndexError = (cause, message) => ({
49
- _tag: "IndexError",
50
- cause,
51
- message
52
- });
43
+
44
+ // src/index.ts
45
+ var defineConfig = (config) => config;
53
46
  export {
54
47
  INDEX_DIR,
55
48
  INDEX_VERSION,
56
- IndexError,
57
- IoError,
58
49
  ParseError,
50
+ buildBM25Index,
59
51
  buildIndex,
60
52
  computeHash,
61
53
  countTokens,
@@ -65,6 +57,7 @@ export {
65
57
  createEmptyLinkIndex,
66
58
  createEmptySectionIndex,
67
59
  createStorage,
60
+ defineConfig,
68
61
  freeEncoder,
69
62
  getBrokenLinks,
70
63
  getIncomingLinks,