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.
- package/.changeset/config.json +9 -9
- package/.claude/settings.local.json +25 -0
- package/.github/workflows/claude-code-review.yml +44 -0
- package/.github/workflows/claude.yml +85 -0
- package/CONTRIBUTING.md +186 -0
- package/NOTES/NOTES +44 -0
- package/README.md +206 -3
- package/biome.json +1 -1
- package/dist/chunk-23UPXDNL.js +3044 -0
- package/dist/chunk-2W7MO2DL.js +1366 -0
- package/dist/chunk-3NUAZGMA.js +1689 -0
- package/dist/chunk-7TOWB2XB.js +366 -0
- package/dist/chunk-7XOTOADQ.js +3065 -0
- package/dist/chunk-AH2PDM2K.js +3042 -0
- package/dist/chunk-BNXWSZ63.js +3742 -0
- package/dist/chunk-BTL5DJVU.js +3222 -0
- package/dist/chunk-HDHYG7E4.js +104 -0
- package/dist/chunk-HLR4KZBP.js +3234 -0
- package/dist/chunk-IP3FRFEB.js +1045 -0
- package/dist/chunk-KHU56VDO.js +3042 -0
- package/dist/chunk-KRYIFLQR.js +85 -89
- package/dist/chunk-LBSDNLEM.js +287 -0
- package/dist/chunk-MNTQ7HCP.js +2643 -0
- package/dist/chunk-MUJELQQ6.js +1387 -0
- package/dist/chunk-MXJGMSLV.js +2199 -0
- package/dist/chunk-N6QJGC3Z.js +2636 -0
- package/dist/chunk-OBELGBPM.js +1713 -0
- package/dist/chunk-OT7R5XTA.js +3192 -0
- package/dist/chunk-P7X4RA2T.js +106 -0
- package/dist/chunk-PIDUQNC2.js +3185 -0
- package/dist/chunk-POGCDIH4.js +3187 -0
- package/dist/chunk-PSIEOQGZ.js +3043 -0
- package/dist/chunk-PVRT3IHA.js +3238 -0
- package/dist/chunk-QNN4TT23.js +1430 -0
- package/dist/chunk-RE3R45RJ.js +3042 -0
- package/dist/chunk-S7E6TFX6.js +718 -657
- package/dist/chunk-SG6GLU4U.js +1378 -0
- package/dist/chunk-SJCDV2ST.js +274 -0
- package/dist/chunk-SYE5XLF3.js +104 -0
- package/dist/chunk-T5VLYBZD.js +103 -0
- package/dist/chunk-TOQB7VWU.js +3238 -0
- package/dist/chunk-VFNMZ4ZQ.js +3228 -0
- package/dist/chunk-VVTGZNBT.js +1533 -1423
- package/dist/chunk-W7Q4RFEV.js +104 -0
- package/dist/chunk-XTYYVRLO.js +3190 -0
- package/dist/chunk-Y6MDYVJD.js +3063 -0
- package/dist/cli/main.js +4072 -629
- package/dist/index.d.ts +420 -33
- package/dist/index.js +8 -15
- package/dist/mcp/server.js +103 -7
- package/dist/schema-BAWSG7KY.js +22 -0
- package/dist/schema-E3QUPL26.js +20 -0
- package/dist/schema-EHL7WUT6.js +20 -0
- package/docs/019-USAGE.md +44 -5
- package/docs/020-current-implementation.md +8 -8
- package/docs/021-DOGFOODING-FINDINGS.md +1 -1
- package/docs/CONFIG.md +1123 -0
- package/docs/ERRORS.md +383 -0
- package/docs/summarization.md +320 -0
- package/justfile +40 -0
- package/package.json +39 -33
- package/research/INDEX.md +315 -0
- package/research/code-review/README.md +90 -0
- package/research/code-review/cli-error-handling-review.md +979 -0
- package/research/code-review/code-review-validation-report.md +464 -0
- package/research/code-review/main-ts-review.md +1128 -0
- package/research/config-docs/SUMMARY.md +357 -0
- package/research/config-docs/TEST-RESULTS.md +776 -0
- package/research/config-docs/TODO.md +542 -0
- package/research/config-docs/analysis.md +744 -0
- package/research/config-docs/fix-validation.md +502 -0
- package/research/config-docs/help-audit.md +264 -0
- package/research/config-docs/help-system-analysis.md +890 -0
- package/research/frontmatter/COMMENTS-ARE-SKIPPED.md +149 -0
- package/research/frontmatter/LLM-CODE-NAVIGATION.md +276 -0
- package/research/issue-review.md +603 -0
- package/research/llm-summarization/agent-cli-tools-2026.md +1082 -0
- package/research/llm-summarization/alternative-providers-2026.md +1428 -0
- package/research/llm-summarization/anthropic-2026.md +367 -0
- package/research/llm-summarization/claude-cli-integration.md +1706 -0
- package/research/llm-summarization/cli-integration-patterns.md +3155 -0
- package/research/llm-summarization/openai-2026.md +473 -0
- package/research/llm-summarization/openai-compatible-providers-2026.md +1022 -0
- package/research/llm-summarization/opencode-cli-integration.md +1552 -0
- package/research/llm-summarization/prompt-engineering-2026.md +1426 -0
- package/research/llm-summarization/prototype-results.md +56 -0
- package/research/llm-summarization/provider-switching-patterns-2026.md +2153 -0
- package/research/llm-summarization/typescript-llm-libraries-2026.md +2436 -0
- package/research/mdcontext-pudding/00-EXECUTIVE-SUMMARY.md +282 -0
- package/research/mdcontext-pudding/01-index-embed.md +956 -0
- package/research/mdcontext-pudding/02-search-COMMANDS.md +142 -0
- package/research/mdcontext-pudding/02-search-SUMMARY.md +146 -0
- package/research/mdcontext-pudding/02-search.md +970 -0
- package/research/mdcontext-pudding/03-context.md +779 -0
- package/research/mdcontext-pudding/04-navigation-and-analytics.md +803 -0
- package/research/mdcontext-pudding/04-tree.md +704 -0
- package/research/mdcontext-pudding/05-config.md +1038 -0
- package/research/mdcontext-pudding/06-links-summary.txt +87 -0
- package/research/mdcontext-pudding/06-links.md +679 -0
- package/research/mdcontext-pudding/07-stats.md +693 -0
- package/research/mdcontext-pudding/BUG-FIX-PLAN.md +388 -0
- package/research/mdcontext-pudding/P0-BUG-VALIDATION.md +167 -0
- package/research/mdcontext-pudding/README.md +168 -0
- package/research/mdcontext-pudding/TESTING-SUMMARY.md +128 -0
- package/research/research-quality-review.md +834 -0
- package/research/semantic-search/embedding-text-analysis.md +156 -0
- package/research/semantic-search/multi-word-failure-reproduction.md +171 -0
- package/research/semantic-search/query-processing-analysis.md +207 -0
- package/research/semantic-search/root-cause-and-solution.md +114 -0
- package/research/semantic-search/threshold-validation-report.md +69 -0
- package/research/semantic-search/vector-search-analysis.md +63 -0
- package/research/test-path-issues.md +276 -0
- package/review/ALP-76/1-error-type-design.md +962 -0
- package/review/ALP-76/2-error-handling-patterns.md +906 -0
- package/review/ALP-76/3-error-presentation.md +624 -0
- package/review/ALP-76/4-test-coverage.md +625 -0
- package/review/ALP-76/5-migration-completeness.md +440 -0
- package/review/ALP-76/6-effect-best-practices.md +755 -0
- package/scripts/apply-branch-protection.sh +47 -0
- package/scripts/branch-protection-templates.json +79 -0
- package/scripts/prototype-summarization.ts +346 -0
- package/scripts/rebuild-hnswlib.js +32 -37
- package/scripts/setup-branch-protection.sh +64 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/active-provider.json +7 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/bm25.json +541 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/bm25.meta.json +5 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/config.json +8 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.bin +0 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.meta.bin +0 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/indexes/documents.json +60 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/indexes/links.json +13 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/indexes/sections.json +1197 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/configuration-management.md +99 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/distributed-systems.md +92 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/error-handling.md +78 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/failure-automation.md +55 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/job-context.md +69 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/process-orchestration.md +99 -0
- package/src/cli/argv-preprocessor.test.ts +2 -2
- package/src/cli/cli.test.ts +230 -33
- package/src/cli/commands/config-cmd.ts +642 -0
- package/src/cli/commands/context.ts +97 -9
- package/src/cli/commands/duplicates.ts +122 -0
- package/src/cli/commands/embeddings.ts +529 -0
- package/src/cli/commands/index-cmd.ts +210 -30
- package/src/cli/commands/index.ts +3 -0
- package/src/cli/commands/search.ts +894 -64
- package/src/cli/commands/stats.ts +3 -0
- package/src/cli/commands/tree.ts +26 -5
- package/src/cli/config-layer.ts +176 -0
- package/src/cli/error-handler.test.ts +235 -0
- package/src/cli/error-handler.ts +655 -0
- package/src/cli/flag-schemas.ts +66 -0
- package/src/cli/help.ts +209 -7
- package/src/cli/main.ts +348 -58
- package/src/cli/options.ts +10 -0
- package/src/cli/shared-error-handling.ts +199 -0
- package/src/cli/utils.ts +150 -17
- package/src/config/file-provider.test.ts +320 -0
- package/src/config/file-provider.ts +273 -0
- package/src/config/index.ts +72 -0
- package/src/config/integration.test.ts +667 -0
- package/src/config/precedence.test.ts +277 -0
- package/src/config/precedence.ts +451 -0
- package/src/config/schema.test.ts +414 -0
- package/src/config/schema.ts +603 -0
- package/src/config/service.test.ts +320 -0
- package/src/config/service.ts +243 -0
- package/src/config/testing.test.ts +264 -0
- package/src/config/testing.ts +110 -0
- package/src/core/types.ts +6 -33
- package/src/duplicates/detector.test.ts +183 -0
- package/src/duplicates/detector.ts +414 -0
- package/src/duplicates/index.ts +18 -0
- package/src/embeddings/embedding-namespace.test.ts +300 -0
- package/src/embeddings/embedding-namespace.ts +947 -0
- package/src/embeddings/heading-boost.test.ts +222 -0
- package/src/embeddings/hnsw-build-options.test.ts +198 -0
- package/src/embeddings/hyde.test.ts +272 -0
- package/src/embeddings/hyde.ts +264 -0
- package/src/embeddings/index.ts +2 -0
- package/src/embeddings/openai-provider.ts +332 -83
- package/src/embeddings/pricing.json +22 -0
- package/src/embeddings/provider-constants.ts +204 -0
- package/src/embeddings/provider-errors.test.ts +967 -0
- package/src/embeddings/provider-errors.ts +565 -0
- package/src/embeddings/provider-factory.test.ts +240 -0
- package/src/embeddings/provider-factory.ts +225 -0
- package/src/embeddings/provider-integration.test.ts +788 -0
- package/src/embeddings/query-preprocessing.test.ts +187 -0
- package/src/embeddings/semantic-search-threshold.test.ts +508 -0
- package/src/embeddings/semantic-search.ts +780 -93
- package/src/embeddings/types.ts +293 -16
- package/src/embeddings/vector-store.ts +486 -77
- package/src/embeddings/voyage-provider.ts +313 -0
- package/src/errors/errors.test.ts +845 -0
- package/src/errors/index.ts +533 -0
- package/src/index/ignore-patterns.test.ts +354 -0
- package/src/index/ignore-patterns.ts +305 -0
- package/src/index/indexer.ts +286 -48
- package/src/index/storage.ts +94 -30
- package/src/index/types.ts +40 -2
- package/src/index/watcher.ts +67 -9
- package/src/index.ts +22 -0
- package/src/integration/search-keyword.test.ts +678 -0
- package/src/mcp/server.ts +135 -6
- package/src/parser/parser.ts +18 -19
- package/src/parser/section-filter.test.ts +277 -0
- package/src/parser/section-filter.ts +125 -3
- package/src/search/__tests__/hybrid-search.test.ts +650 -0
- package/src/search/bm25-store.ts +366 -0
- package/src/search/cross-encoder.test.ts +253 -0
- package/src/search/cross-encoder.ts +406 -0
- package/src/search/fuzzy-search.test.ts +419 -0
- package/src/search/fuzzy-search.ts +273 -0
- package/src/search/hybrid-search.ts +448 -0
- package/src/search/path-matcher.test.ts +276 -0
- package/src/search/path-matcher.ts +33 -0
- package/src/search/searcher.test.ts +99 -1
- package/src/search/searcher.ts +189 -67
- package/src/search/wink-bm25.d.ts +30 -0
- package/src/summarization/cli-providers/claude.ts +202 -0
- package/src/summarization/cli-providers/detection.test.ts +273 -0
- package/src/summarization/cli-providers/detection.ts +118 -0
- package/src/summarization/cli-providers/index.ts +8 -0
- package/src/summarization/cost.test.ts +139 -0
- package/src/summarization/cost.ts +102 -0
- package/src/summarization/error-handler.test.ts +127 -0
- package/src/summarization/error-handler.ts +111 -0
- package/src/summarization/index.ts +102 -0
- package/src/summarization/pipeline.test.ts +498 -0
- package/src/summarization/pipeline.ts +231 -0
- package/src/summarization/prompts.test.ts +269 -0
- package/src/summarization/prompts.ts +133 -0
- package/src/summarization/provider-factory.test.ts +396 -0
- package/src/summarization/provider-factory.ts +178 -0
- package/src/summarization/types.ts +184 -0
- package/src/summarize/summarizer.ts +104 -35
- package/src/types/huggingface-transformers.d.ts +66 -0
- package/tests/fixtures/cli/.mdcontext/active-provider.json +7 -0
- package/tests/fixtures/cli/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.bin +0 -0
- package/tests/fixtures/cli/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.meta.bin +0 -0
- package/tests/fixtures/cli/.mdcontext/indexes/documents.json +4 -4
- package/tests/fixtures/cli/.mdcontext/indexes/sections.json +14 -0
- package/tests/integration/embed-index.test.ts +712 -0
- package/tests/integration/search-context.test.ts +469 -0
- package/tests/integration/search-semantic.test.ts +522 -0
- package/vitest.config.ts +1 -6
- package/AGENTS.md +0 -46
- package/tests/fixtures/cli/.mdcontext/vectors.bin +0 -0
- package/tests/fixtures/cli/.mdcontext/vectors.meta.json +0 -1264
|
@@ -0,0 +1,979 @@
|
|
|
1
|
+
# CLI Error Handling Review
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-01-24
|
|
4
|
+
**Last Validated:** 2026-01-24 06:38:24 UTC
|
|
5
|
+
**Validation Commit:** `07c9e72ba01cda840046b96a1be4743a85e3d4c5`
|
|
6
|
+
**Scope:** All CLI command files and error handling infrastructure
|
|
7
|
+
**Reviewer:** Claude Sonnet 4.5
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Validation Summary
|
|
12
|
+
|
|
13
|
+
**✓ All Issues Verified**: All 11 issues remain valid and accurately documented.
|
|
14
|
+
|
|
15
|
+
### Resolution Status
|
|
16
|
+
- **✓ VALID:** 10 issues (91%)
|
|
17
|
+
- **📍 MOVED:** 1 issue (9%)
|
|
18
|
+
- **✅ RESOLVED:** 0 issues (0%)
|
|
19
|
+
|
|
20
|
+
See `/research/code-review/code-review-validation-report.md` for detailed validation results.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Executive Summary
|
|
25
|
+
|
|
26
|
+
The CLI codebase demonstrates a **well-architected error handling system** using Effect's tagged error pattern. The centralized error handler (`src/cli/error-handler.ts`) provides consistent formatting and exit codes. However, there are **inconsistencies in error propagation** between commands, with some using graceful degradation while others let errors propagate to the CLI boundary.
|
|
27
|
+
|
|
28
|
+
**Overall Grade: B+**
|
|
29
|
+
|
|
30
|
+
### Strengths
|
|
31
|
+
- Centralized error handler with comprehensive error mapping
|
|
32
|
+
- Type-safe tagged errors using Effect's Data.TaggedError pattern
|
|
33
|
+
- Consistent exit code strategy (0=success, 1=user error, 2=system error, 3=API error)
|
|
34
|
+
- Rich error context with actionable suggestions
|
|
35
|
+
- Clear separation between technical error messages and user-facing formatting
|
|
36
|
+
|
|
37
|
+
### Areas for Improvement
|
|
38
|
+
- Inconsistent error handling strategies across commands (some catch locally, some propagate)
|
|
39
|
+
- Multiple `process.exit()` calls scattered across CLI files
|
|
40
|
+
- Mixed error handling patterns (Effect.catchTags vs Effect.try)
|
|
41
|
+
- Some commands silently degrade on errors without proper logging
|
|
42
|
+
- Legacy error handling code for @effect/cli validation errors
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Error Handling Architecture
|
|
47
|
+
|
|
48
|
+
### Centralized Error Handler (`src/cli/error-handler.ts`)
|
|
49
|
+
|
|
50
|
+
**Location:** `/Users/alphab/Dev/LLM/DEV/mdcontext/worktrees/nancy-ALP-139/src/cli/error-handler.ts`
|
|
51
|
+
|
|
52
|
+
The error handler is well-designed with:
|
|
53
|
+
|
|
54
|
+
1. **Exit Code Constants** (Lines 45-52)
|
|
55
|
+
```typescript
|
|
56
|
+
export const EXIT_CODE = {
|
|
57
|
+
SUCCESS: 0,
|
|
58
|
+
USER_ERROR: 1,
|
|
59
|
+
SYSTEM_ERROR: 2,
|
|
60
|
+
API_ERROR: 3,
|
|
61
|
+
} as const
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
2. **Formatted Error Interface** (Lines 58-64)
|
|
65
|
+
- Provides structured error output with code, message, details, suggestions, and exit code
|
|
66
|
+
- Enables consistent formatting across all error types
|
|
67
|
+
|
|
68
|
+
3. **Exhaustive Error Formatting** (Lines 129-335)
|
|
69
|
+
- Uses Effect's `Match.value()` pattern matching for type-safe error handling
|
|
70
|
+
- Each error type mapped to user-friendly message with actionable suggestions
|
|
71
|
+
- Special handling for ConfigError with enhanced context
|
|
72
|
+
|
|
73
|
+
4. **Error Display Functions**
|
|
74
|
+
- `displayError()`: Standard error output to stderr
|
|
75
|
+
- `displayErrorDebug()`: Enhanced output with stack traces for debugging
|
|
76
|
+
- Clean separation of concerns
|
|
77
|
+
|
|
78
|
+
### Error Type System (`src/errors/index.ts`)
|
|
79
|
+
|
|
80
|
+
**Location:** `/Users/alphab/Dev/LLM/DEV/mdcontext/worktrees/nancy-ALP-139/src/errors/index.ts`
|
|
81
|
+
|
|
82
|
+
Excellent error taxonomy with:
|
|
83
|
+
- **Error Codes** (E1xx-E9xx): Stable, machine-readable identifiers
|
|
84
|
+
- **Tagged Errors**: Type-safe discriminated unions using Effect's Data.TaggedError
|
|
85
|
+
- **Clear Documentation**: Each error type includes purpose and usage examples
|
|
86
|
+
- **Rich Context**: Errors carry path, message, cause, and domain-specific metadata
|
|
87
|
+
|
|
88
|
+
**Error Categories:**
|
|
89
|
+
- E1xx: File system errors (FileReadError, FileWriteError, etc.)
|
|
90
|
+
- E2xx: Parse errors
|
|
91
|
+
- E3xx: API/authentication errors
|
|
92
|
+
- E4xx: Index errors
|
|
93
|
+
- E5xx: Search errors
|
|
94
|
+
- E6xx: Vector store errors
|
|
95
|
+
- E7xx: Config errors
|
|
96
|
+
- E8xx: Watch errors
|
|
97
|
+
- E9xx: CLI validation errors
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Command-by-Command Analysis
|
|
102
|
+
|
|
103
|
+
### 1. index-cmd.ts
|
|
104
|
+
|
|
105
|
+
**Location:** `/Users/alphab/Dev/LLM/DEV/mdcontext/worktrees/nancy-ALP-139/src/cli/commands/index-cmd.ts`
|
|
106
|
+
|
|
107
|
+
**Pattern:** **Graceful Degradation with Local Error Catching**
|
|
108
|
+
|
|
109
|
+
#### Good Practices ✅
|
|
110
|
+
|
|
111
|
+
1. **Strategic Error Catching** (Lines 256-276, 308-360)
|
|
112
|
+
- Uses `Effect.catchTags()` to handle expected errors gracefully
|
|
113
|
+
- Distinguishes between critical errors (index build) and optional operations (embeddings)
|
|
114
|
+
- Continues execution when embeddings fail, preserving main index functionality
|
|
115
|
+
|
|
116
|
+
2. **Context-Aware Error Handling**
|
|
117
|
+
```typescript
|
|
118
|
+
estimateEmbeddingCost(resolvedDir).pipe(
|
|
119
|
+
Effect.catchTags({
|
|
120
|
+
IndexNotFoundError: () => Effect.succeed(null),
|
|
121
|
+
FileReadError: (e) => {
|
|
122
|
+
Effect.runSync(Effect.logWarning(`Could not read index files: ${e.message}`))
|
|
123
|
+
return Effect.succeed(null)
|
|
124
|
+
},
|
|
125
|
+
// ... more error cases
|
|
126
|
+
}),
|
|
127
|
+
)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
3. **User-Friendly Degradation**
|
|
131
|
+
- Shows informative messages when embeddings fail
|
|
132
|
+
- Doesn't crash the entire operation if optional features fail
|
|
133
|
+
|
|
134
|
+
#### Issues ⚠️
|
|
135
|
+
|
|
136
|
+
1. **Inconsistent Process Exit** (Line 117) ✓ VALID
|
|
137
|
+
```typescript
|
|
138
|
+
process.on('SIGINT', () => {
|
|
139
|
+
watcher.stop()
|
|
140
|
+
console.log('\nStopped watching.')
|
|
141
|
+
process.exit(0) // Direct exit instead of Effect-based termination
|
|
142
|
+
})
|
|
143
|
+
```
|
|
144
|
+
- **Impact:** Bypasses Effect's cleanup and error handling
|
|
145
|
+
- **Recommendation:** Use Effect's interrupt mechanism
|
|
146
|
+
|
|
147
|
+
2. **Effect.runSync in Error Handlers** (Lines 261-262, 268-269, 320, 324, 332-333, 337-338, 347-348, 354-355) ✓ VALID
|
|
148
|
+
```typescript
|
|
149
|
+
Effect.runSync(Console.error(`\n${e.message}`))
|
|
150
|
+
```
|
|
151
|
+
- **Issue:** Running effects synchronously within effect chains can cause issues
|
|
152
|
+
- **Impact:** Potential for unhandled errors if Console.error fails
|
|
153
|
+
- **Recommendation:** Return effect-wrapped logging operations
|
|
154
|
+
|
|
155
|
+
3. **Duplicated Error Handling Code** (Lines 256-276 and 308-360) ✓ VALID
|
|
156
|
+
- Identical `Effect.catchTags()` blocks in two places
|
|
157
|
+
- **Impact:** Maintenance burden, potential for divergence
|
|
158
|
+
- **Recommendation:** Extract to reusable function
|
|
159
|
+
|
|
160
|
+
### 2. search.ts
|
|
161
|
+
|
|
162
|
+
**Location:** `/Users/alphab/Dev/LLM/DEV/mdcontext/worktrees/nancy-ALP-139/src/cli/commands/search.ts`
|
|
163
|
+
|
|
164
|
+
**Pattern:** **Mixed - Graceful Degradation for Missing Index, Propagation for Search Errors**
|
|
165
|
+
|
|
166
|
+
#### Good Practices ✅
|
|
167
|
+
|
|
168
|
+
1. **Early Return for Missing Index** (Lines 134-140)
|
|
169
|
+
```typescript
|
|
170
|
+
if (!indexInfo.exists && !json) {
|
|
171
|
+
yield* Console.log('No index found.')
|
|
172
|
+
yield* Console.log('')
|
|
173
|
+
yield* Console.log('Run: mdcontext index /path/to/docs')
|
|
174
|
+
yield* Console.log(' Add --embed for semantic search capabilities')
|
|
175
|
+
return
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
- User-friendly message with clear next steps
|
|
179
|
+
- Graceful handling of expected "no index" state
|
|
180
|
+
|
|
181
|
+
2. **Auto-Index Feature with Error Handling** (Lines 155-165, 359-580)
|
|
182
|
+
- Proactively creates semantic index if missing and under threshold
|
|
183
|
+
- Gracefully falls back to keyword search on embedding errors
|
|
184
|
+
- Good UX design
|
|
185
|
+
|
|
186
|
+
3. **Helpful Tips** (Lines 310-314, 349)
|
|
187
|
+
- Contextual suggestions based on current state
|
|
188
|
+
- Educates users about available features
|
|
189
|
+
|
|
190
|
+
#### Issues ⚠️
|
|
191
|
+
|
|
192
|
+
1. **Triple-Duplicated Error Handling** (Lines 368-386, 416-461, 513-559) ✓ VALID
|
|
193
|
+
- Same `Effect.catchTags()` block appears **three times** in `handleMissingEmbeddings()`
|
|
194
|
+
- **Impact:** High maintenance burden, 150+ lines of duplicated code
|
|
195
|
+
- **Recommendation:** Extract to shared utility function
|
|
196
|
+
|
|
197
|
+
2. **Silent Error Suppression** (Lines 418-421) ✓ VALID
|
|
198
|
+
```typescript
|
|
199
|
+
ApiKeyMissingError: (e) => {
|
|
200
|
+
if (!json) {
|
|
201
|
+
Effect.runSync(Console.error(`\n${e.message}`))
|
|
202
|
+
}
|
|
203
|
+
return Effect.succeed(null as BuildEmbeddingsResult | null)
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
- Errors are logged but operation continues
|
|
207
|
+
- Users may miss critical configuration issues
|
|
208
|
+
- **Recommendation:** Consider accumulating errors and displaying summary
|
|
209
|
+
|
|
210
|
+
3. **Inconsistent Error Propagation** ✓ VALID
|
|
211
|
+
- Semantic search errors propagate (Line 318: `yield* semanticSearch(...)`)
|
|
212
|
+
- Embedding errors are caught and suppressed
|
|
213
|
+
- **Impact:** Confusing for users - why do some operations fail hard, others silently?
|
|
214
|
+
|
|
215
|
+
### 3. context.ts
|
|
216
|
+
|
|
217
|
+
**Location:** `/Users/alphab/Dev/LLM/DEV/mdcontext/worktrees/nancy-ALP-139/src/cli/commands/context.ts`
|
|
218
|
+
|
|
219
|
+
**Pattern:** **Error Propagation to CLI Boundary**
|
|
220
|
+
|
|
221
|
+
#### Good Practices ✅
|
|
222
|
+
|
|
223
|
+
1. **Clean Error Propagation**
|
|
224
|
+
- Most errors are allowed to propagate naturally
|
|
225
|
+
- Minimal local error handling
|
|
226
|
+
- Consistent with Effect's error handling philosophy
|
|
227
|
+
|
|
228
|
+
2. **Explicit Validation** (Lines 74-82)
|
|
229
|
+
```typescript
|
|
230
|
+
if (fileList.length === 0) {
|
|
231
|
+
yield* Effect.fail(
|
|
232
|
+
new CliValidationError({
|
|
233
|
+
message: 'At least one file is required. Usage: mdcontext context <file> [files...]',
|
|
234
|
+
argument: 'files',
|
|
235
|
+
}),
|
|
236
|
+
)
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
- Uses typed errors for validation failures
|
|
240
|
+
- Clear, actionable error messages
|
|
241
|
+
|
|
242
|
+
3. **Error Mapping** (Lines 93-105)
|
|
243
|
+
```typescript
|
|
244
|
+
yield* parseFile(filePath).pipe(
|
|
245
|
+
Effect.mapError((e) =>
|
|
246
|
+
e._tag === 'ParseError'
|
|
247
|
+
? new ParseError({ ... })
|
|
248
|
+
: new FileReadError({ ... })
|
|
249
|
+
),
|
|
250
|
+
)
|
|
251
|
+
```
|
|
252
|
+
- Properly maps internal errors to CLI error types
|
|
253
|
+
- Preserves error context (path, line, column)
|
|
254
|
+
|
|
255
|
+
#### Issues ⚠️
|
|
256
|
+
|
|
257
|
+
1. **Continue on Error** (Line 161) 📍 MOVED (Still at line 161, verified)
|
|
258
|
+
```typescript
|
|
259
|
+
if (extractedSections.length === 0) {
|
|
260
|
+
yield* Console.error(`No sections found matching "${sectionSelector}" in ${file}`)
|
|
261
|
+
yield* Console.error('Use --sections to list available sections.')
|
|
262
|
+
continue // Continues to next file instead of failing
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
- **Issue:** Silently continues when section not found
|
|
266
|
+
- **Impact:** User may not notice if one of many files failed
|
|
267
|
+
- **Recommendation:** Collect errors and report summary, or fail fast
|
|
268
|
+
|
|
269
|
+
### 4. tree.ts
|
|
270
|
+
|
|
271
|
+
**Location:** `/Users/alphab/Dev/LLM/DEV/mdcontext/worktrees/nancy-ALP-139/src/cli/commands/tree.ts`
|
|
272
|
+
|
|
273
|
+
**Pattern:** **Error Propagation with Effect.try**
|
|
274
|
+
|
|
275
|
+
#### Good Practices ✅
|
|
276
|
+
|
|
277
|
+
1. **Effect.try for File System Operations** (Lines 32-40)
|
|
278
|
+
```typescript
|
|
279
|
+
const stat = yield* Effect.try({
|
|
280
|
+
try: () => fs.statSync(resolvedPath),
|
|
281
|
+
catch: (e) =>
|
|
282
|
+
new FileReadError({
|
|
283
|
+
path: resolvedPath,
|
|
284
|
+
message: `Cannot access path: ${e instanceof Error ? e.message : String(e)}`,
|
|
285
|
+
cause: e,
|
|
286
|
+
}),
|
|
287
|
+
})
|
|
288
|
+
```
|
|
289
|
+
- Proper error wrapping with typed errors
|
|
290
|
+
- Preserves original error as cause
|
|
291
|
+
- Good error message construction
|
|
292
|
+
|
|
293
|
+
2. **Consistent Error Mapping** (Lines 44-58)
|
|
294
|
+
- Same pattern as context.ts for parseFile errors
|
|
295
|
+
- Maps internal errors to CLI error types
|
|
296
|
+
|
|
297
|
+
#### Issues ⚠️
|
|
298
|
+
|
|
299
|
+
None identified. This is a good example of clean error handling.
|
|
300
|
+
|
|
301
|
+
### 5. links.ts & backlinks.ts
|
|
302
|
+
|
|
303
|
+
**Location:**
|
|
304
|
+
- `/Users/alphab/Dev/LLM/DEV/mdcontext/worktrees/nancy-ALP-139/src/cli/commands/links.ts`
|
|
305
|
+
- `/Users/alphab/Dev/LLM/DEV/mdcontext/worktrees/nancy-ALP-139/src/cli/commands/backlinks.ts`
|
|
306
|
+
|
|
307
|
+
**Pattern:** **Pure Error Propagation**
|
|
308
|
+
|
|
309
|
+
#### Good Practices ✅
|
|
310
|
+
|
|
311
|
+
1. **Minimal Error Handling**
|
|
312
|
+
- No try/catch blocks
|
|
313
|
+
- Errors propagate naturally
|
|
314
|
+
- Relies on centralized error handler
|
|
315
|
+
|
|
316
|
+
2. **Clean Command Implementation**
|
|
317
|
+
- Simple, focused logic
|
|
318
|
+
- No defensive error handling
|
|
319
|
+
- Trusts underlying functions to return proper errors
|
|
320
|
+
|
|
321
|
+
#### Issues ⚠️
|
|
322
|
+
|
|
323
|
+
None identified. These are excellent examples of letting errors propagate.
|
|
324
|
+
|
|
325
|
+
### 6. stats.ts
|
|
326
|
+
|
|
327
|
+
**Location:** `/Users/alphab/Dev/LLM/DEV/mdcontext/worktrees/nancy-ALP-139/src/cli/commands/stats.ts`
|
|
328
|
+
|
|
329
|
+
**Pattern:** **Graceful Degradation for Missing Index**
|
|
330
|
+
|
|
331
|
+
#### Good Practices ✅
|
|
332
|
+
|
|
333
|
+
1. **Null Check for Missing Index** (Lines 52-60)
|
|
334
|
+
```typescript
|
|
335
|
+
if (!docIndex || !sectionIndex) {
|
|
336
|
+
if (json) {
|
|
337
|
+
yield* Console.log(formatJson({ error: 'No index found' }, pretty))
|
|
338
|
+
} else {
|
|
339
|
+
yield* Console.log('No index found.')
|
|
340
|
+
yield* Console.log("Run 'mdcontext index <path>' to create an index.")
|
|
341
|
+
}
|
|
342
|
+
return
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
- Handles expected "no index" case gracefully
|
|
346
|
+
- Different output for JSON vs text mode
|
|
347
|
+
- Clear user guidance
|
|
348
|
+
|
|
349
|
+
#### Issues ⚠️
|
|
350
|
+
|
|
351
|
+
1. **No Error Propagation from loadDocumentIndex** (Line 48)
|
|
352
|
+
```typescript
|
|
353
|
+
const docIndex = yield* loadDocumentIndex(storage)
|
|
354
|
+
const sectionIndex = yield* loadSectionIndex(storage)
|
|
355
|
+
```
|
|
356
|
+
- Assumes these functions return `null` on error
|
|
357
|
+
- **Issue:** If these functions throw different errors (corruption, permission issues), they won't be caught
|
|
358
|
+
- **Recommendation:** Verify these functions handle errors internally or add error handling
|
|
359
|
+
|
|
360
|
+
### 7. config-cmd.ts
|
|
361
|
+
|
|
362
|
+
**Location:** `/Users/alphab/Dev/LLM/DEV/mdcontext/worktrees/nancy-ALP-139/src/cli/commands/config-cmd.ts`
|
|
363
|
+
|
|
364
|
+
**Pattern:** **Mixed - Effect.try for File Operations, catchTag for Config Errors**
|
|
365
|
+
|
|
366
|
+
#### Good Practices ✅
|
|
367
|
+
|
|
368
|
+
1. **Effect.try for File Operations** (Lines 249-252)
|
|
369
|
+
```typescript
|
|
370
|
+
yield* Effect.try({
|
|
371
|
+
try: () => fs.writeFileSync(filepath, content, 'utf-8'),
|
|
372
|
+
catch: (e) => new Error(`Failed to write config file: ${e}`),
|
|
373
|
+
})
|
|
374
|
+
```
|
|
375
|
+
- Wraps sync file operations properly
|
|
376
|
+
- Good error message
|
|
377
|
+
|
|
378
|
+
2. **catchTag for Expected Errors** (Lines 527-531)
|
|
379
|
+
```typescript
|
|
380
|
+
const configResult = yield* loadConfigFile(cwd).pipe(
|
|
381
|
+
Effect.catchTag('ConfigError', (e) => {
|
|
382
|
+
errors.push(e.message)
|
|
383
|
+
return Effect.succeed({ found: false, searched: [] } as const)
|
|
384
|
+
}),
|
|
385
|
+
)
|
|
386
|
+
```
|
|
387
|
+
- Catches expected config errors
|
|
388
|
+
- Accumulates errors for reporting
|
|
389
|
+
- Graceful degradation
|
|
390
|
+
|
|
391
|
+
#### Issues ⚠️
|
|
392
|
+
|
|
393
|
+
None identified. Good example of mixing patterns appropriately.
|
|
394
|
+
|
|
395
|
+
---
|
|
396
|
+
|
|
397
|
+
## Cross-Cutting Concerns
|
|
398
|
+
|
|
399
|
+
### 1. Process Exit Calls ✓ VALID
|
|
400
|
+
|
|
401
|
+
**Issue:** Direct `process.exit()` calls bypass Effect's cleanup mechanism
|
|
402
|
+
|
|
403
|
+
**Locations:** (All verified as of commit 07c9e72)
|
|
404
|
+
- `/Users/alphab/Dev/LLM/DEV/mdcontext/worktrees/nancy-ALP-139/src/cli/main.ts` (Lines 98, 128, 138, 150, 154, 181, 193, 319)
|
|
405
|
+
- `/Users/alphab/Dev/LLM/DEV/mdcontext/worktrees/nancy-ALP-139/src/cli/commands/index-cmd.ts` (Line 117)
|
|
406
|
+
- `/Users/alphab/Dev/LLM/DEV/mdcontext/worktrees/nancy-ALP-139/src/cli/help.ts` (Lines 291, 404, 427)
|
|
407
|
+
- `/Users/alphab/Dev/LLM/DEV/mdcontext/worktrees/nancy-ALP-139/src/cli/argv-preprocessor.ts` (Line 93)
|
|
408
|
+
|
|
409
|
+
**Impact:**
|
|
410
|
+
- Resource cleanup may not execute
|
|
411
|
+
- Effect finalizers are bypassed
|
|
412
|
+
- Potential for leaked resources (file handles, network connections)
|
|
413
|
+
|
|
414
|
+
**Recommendation:**
|
|
415
|
+
```typescript
|
|
416
|
+
// Instead of:
|
|
417
|
+
process.exit(1)
|
|
418
|
+
|
|
419
|
+
// Use Effect-based failure:
|
|
420
|
+
yield* Effect.fail(new CliValidationError({ message: '...' }))
|
|
421
|
+
|
|
422
|
+
// Or for success:
|
|
423
|
+
yield* Effect.succeed(void 0)
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### 2. Effect.runSync in Error Handlers ✓ VALID
|
|
427
|
+
|
|
428
|
+
**Issue:** Running effects synchronously within effect chains
|
|
429
|
+
|
|
430
|
+
**Pattern:**
|
|
431
|
+
```typescript
|
|
432
|
+
ApiKeyMissingError: (e) => {
|
|
433
|
+
Effect.runSync(Console.error(`\n${e.message}`))
|
|
434
|
+
return Effect.succeed(null)
|
|
435
|
+
}
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
**Locations:** (All verified as of commit 07c9e72)
|
|
439
|
+
- `index-cmd.ts`: Lines 261-262, 268-269, 320, 324, 332-333, 337-338, 347-348, 354-355
|
|
440
|
+
- `search.ts`: Lines 373-374, 418-419, 424-425, 433-434, 440-441, 448-449, 454-455
|
|
441
|
+
|
|
442
|
+
**Impact:**
|
|
443
|
+
- Potential for unhandled errors if Console.error fails
|
|
444
|
+
- Breaks Effect's composition model
|
|
445
|
+
- Can't be tested or mocked easily
|
|
446
|
+
|
|
447
|
+
**Recommendation:**
|
|
448
|
+
```typescript
|
|
449
|
+
// Instead of Effect.runSync:
|
|
450
|
+
ApiKeyMissingError: (e) =>
|
|
451
|
+
Console.error(`\n${e.message}`).pipe(
|
|
452
|
+
Effect.flatMap(() => Effect.succeed(null))
|
|
453
|
+
)
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
### 3. Duplicated Error Handling Code ✓ VALID
|
|
457
|
+
|
|
458
|
+
**Issue:** Same error handling blocks repeated multiple times
|
|
459
|
+
|
|
460
|
+
**Examples:** (All verified as of commit 07c9e72)
|
|
461
|
+
1. **search.ts**: `handleMissingEmbeddings()` has identical catchTags blocks 3 times at lines 368-386, 416-461, 513-559 (150+ lines)
|
|
462
|
+
2. **index-cmd.ts**: Embedding error handling duplicated 2 times at lines 256-276 and 308-360 (100+ lines)
|
|
463
|
+
|
|
464
|
+
**Impact:**
|
|
465
|
+
- Maintenance burden
|
|
466
|
+
- Risk of bugs when updating one instance but not others
|
|
467
|
+
- Code bloat
|
|
468
|
+
|
|
469
|
+
**Recommendation:**
|
|
470
|
+
```typescript
|
|
471
|
+
// Extract shared error handler:
|
|
472
|
+
const handleEmbeddingErrors = (json: boolean) => ({
|
|
473
|
+
ApiKeyMissingError: (e: ApiKeyMissingError) =>
|
|
474
|
+
Console.error(`\n${e.message}`).pipe(
|
|
475
|
+
Effect.when(() => !json),
|
|
476
|
+
Effect.flatMap(() => Effect.succeed(null as BuildEmbeddingsResult | null))
|
|
477
|
+
),
|
|
478
|
+
// ... other error handlers
|
|
479
|
+
})
|
|
480
|
+
|
|
481
|
+
// Use it:
|
|
482
|
+
buildEmbeddings(resolvedDir, { ... }).pipe(
|
|
483
|
+
Effect.catchTags(handleEmbeddingErrors(json))
|
|
484
|
+
)
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
### 4. Legacy Error Handling ✓ VALID
|
|
488
|
+
|
|
489
|
+
**Location:** `/Users/alphab/Dev/LLM/DEV/mdcontext/worktrees/nancy-ALP-139/src/cli/error-handler.ts` (Lines 446-491)
|
|
490
|
+
|
|
491
|
+
**Issue:** Code for handling @effect/cli validation errors (Verified at commit 07c9e72)
|
|
492
|
+
|
|
493
|
+
```typescript
|
|
494
|
+
export const isEffectCliValidationError = (error: unknown): boolean => {
|
|
495
|
+
// ... legacy error checking
|
|
496
|
+
}
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
**Impact:**
|
|
500
|
+
- Technical debt
|
|
501
|
+
- Suggests migration in progress
|
|
502
|
+
- Code complexity
|
|
503
|
+
|
|
504
|
+
**Recommendation:**
|
|
505
|
+
- If migration to custom error types is complete, remove this code
|
|
506
|
+
- If still needed, add comment explaining why
|
|
507
|
+
- Consider deprecation timeline
|
|
508
|
+
|
|
509
|
+
### 5. Inconsistent Error Messages ✓ VALID
|
|
510
|
+
|
|
511
|
+
**Issue:** Some commands use console.error directly, others use Console.error
|
|
512
|
+
|
|
513
|
+
**Examples:**
|
|
514
|
+
```typescript
|
|
515
|
+
// Direct console usage (bypasses Effect)
|
|
516
|
+
console.error(`Watch error: ${error.message}`) // index-cmd.ts:109
|
|
517
|
+
|
|
518
|
+
// Effect-based (proper)
|
|
519
|
+
yield* Console.error('No index found.') // stats.ts:56
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
**Impact:**
|
|
523
|
+
- Inconsistent error output
|
|
524
|
+
- Can't intercept/test direct console calls
|
|
525
|
+
- Mixed paradigms
|
|
526
|
+
|
|
527
|
+
**Recommendation:** Always use Effect's Console for consistency
|
|
528
|
+
|
|
529
|
+
---
|
|
530
|
+
|
|
531
|
+
## Error Message Quality
|
|
532
|
+
|
|
533
|
+
### Good Examples ✅
|
|
534
|
+
|
|
535
|
+
1. **Actionable Suggestions**
|
|
536
|
+
```typescript
|
|
537
|
+
suggestions: [
|
|
538
|
+
"Run 'mdcontext index' first to build the index",
|
|
539
|
+
]
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
2. **Context-Rich Messages**
|
|
543
|
+
```typescript
|
|
544
|
+
message: `Cannot read file: ${e.path}`,
|
|
545
|
+
details: e.message, // Technical details
|
|
546
|
+
suggestions: [
|
|
547
|
+
'Check that the file exists',
|
|
548
|
+
'Check file permissions',
|
|
549
|
+
]
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
3. **Progressive Disclosure**
|
|
553
|
+
- Brief message for common cases
|
|
554
|
+
- Details available with --debug flag
|
|
555
|
+
|
|
556
|
+
### Areas for Improvement ⚠️
|
|
557
|
+
|
|
558
|
+
1. **Vague Error Messages** (stats.ts:54)
|
|
559
|
+
```typescript
|
|
560
|
+
yield* Console.log(formatJson({ error: 'No index found' }, pretty))
|
|
561
|
+
```
|
|
562
|
+
- **Issue:** Generic message, no path information
|
|
563
|
+
- **Better:** `{ error: 'No index found', path: resolvedRoot, suggestion: 'Run mdcontext index' }`
|
|
564
|
+
|
|
565
|
+
2. **Missing Error Codes in Some Messages**
|
|
566
|
+
- Some errors logged without error codes
|
|
567
|
+
- Inconsistent with centralized error handler
|
|
568
|
+
|
|
569
|
+
---
|
|
570
|
+
|
|
571
|
+
## Exit Code Correctness
|
|
572
|
+
|
|
573
|
+
### ✅ Correct Usage
|
|
574
|
+
|
|
575
|
+
**Centralized Exit Codes** (error-handler.ts:45-52)
|
|
576
|
+
```typescript
|
|
577
|
+
export const EXIT_CODE = {
|
|
578
|
+
SUCCESS: 0, // Successful operation
|
|
579
|
+
USER_ERROR: 1, // Invalid arguments, missing config
|
|
580
|
+
SYSTEM_ERROR: 2, // File system, network errors
|
|
581
|
+
API_ERROR: 3, // Authentication, rate limits
|
|
582
|
+
} as const
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
**Proper Mapping:**
|
|
586
|
+
- File system errors → SYSTEM_ERROR (2)
|
|
587
|
+
- API key errors → API_ERROR (3)
|
|
588
|
+
- Validation errors → USER_ERROR (1)
|
|
589
|
+
|
|
590
|
+
### ⚠️ Issues
|
|
591
|
+
|
|
592
|
+
1. **Direct Exit Calls Without Proper Codes**
|
|
593
|
+
```typescript
|
|
594
|
+
process.exit(0) // OK for success
|
|
595
|
+
process.exit(1) // Generic error, loses context
|
|
596
|
+
```
|
|
597
|
+
- Should use centralized exit code constants
|
|
598
|
+
- Should route through error handler
|
|
599
|
+
|
|
600
|
+
2. **Main.ts Exit Strategy** (Lines 291-302)
|
|
601
|
+
```typescript
|
|
602
|
+
Effect.catchAll((error) =>
|
|
603
|
+
Effect.sync(() => {
|
|
604
|
+
if (isEffectCliValidationError(error)) {
|
|
605
|
+
const message = formatEffectCliError(error)
|
|
606
|
+
console.error(`\nError: ${message}`)
|
|
607
|
+
console.error('\nRun "mdcontext --help" for usage information.')
|
|
608
|
+
process.exit(1) // Always exit 1 for validation errors
|
|
609
|
+
}
|
|
610
|
+
throw error // Re-throw for other errors
|
|
611
|
+
}),
|
|
612
|
+
)
|
|
613
|
+
```
|
|
614
|
+
- **Issue:** Hardcoded exit code 1
|
|
615
|
+
- **Better:** Map to EXIT_CODE.USER_ERROR for clarity
|
|
616
|
+
|
|
617
|
+
---
|
|
618
|
+
|
|
619
|
+
## Unhandled Promise Rejections
|
|
620
|
+
|
|
621
|
+
### ✅ Good Practices
|
|
622
|
+
|
|
623
|
+
All async operations are properly wrapped in Effect:
|
|
624
|
+
- `Effect.promise()` for user prompts (index-cmd.ts:286, search.ts:492)
|
|
625
|
+
- `Effect.try()` for file operations (tree.ts:32, config-cmd.ts:249)
|
|
626
|
+
- No naked promises in command handlers
|
|
627
|
+
|
|
628
|
+
### ⚠️ Potential Issues
|
|
629
|
+
|
|
630
|
+
1. **SIGINT Handler** (index-cmd.ts:113-119)
|
|
631
|
+
```typescript
|
|
632
|
+
yield* Effect.async<never, never>(() => {
|
|
633
|
+
process.on('SIGINT', () => {
|
|
634
|
+
watcher.stop()
|
|
635
|
+
console.log('\nStopped watching.')
|
|
636
|
+
process.exit(0)
|
|
637
|
+
})
|
|
638
|
+
})
|
|
639
|
+
```
|
|
640
|
+
- **Issue:** If `watcher.stop()` throws, it won't be caught
|
|
641
|
+
- **Recommendation:** Wrap in try/catch or Effect.try
|
|
642
|
+
|
|
643
|
+
2. **Dynamic Import in main.ts** (Lines 309-320)
|
|
644
|
+
```typescript
|
|
645
|
+
(async () => {
|
|
646
|
+
try {
|
|
647
|
+
const configLayer = await loadConfigAsync(customConfigPath!)
|
|
648
|
+
runCli(configLayer)
|
|
649
|
+
} catch (error) {
|
|
650
|
+
console.error(`\nError: Failed to load config`)
|
|
651
|
+
if (error instanceof Error) {
|
|
652
|
+
console.error(` ${error.message}`)
|
|
653
|
+
}
|
|
654
|
+
process.exit(1)
|
|
655
|
+
}
|
|
656
|
+
})()
|
|
657
|
+
```
|
|
658
|
+
- **Issue:** IIFE async function - rejection not attached to process
|
|
659
|
+
- **Better:** Add `.catch()` or use top-level await
|
|
660
|
+
|
|
661
|
+
---
|
|
662
|
+
|
|
663
|
+
## Pattern Recommendations
|
|
664
|
+
|
|
665
|
+
### Ideal Error Handling Pattern for This Codebase
|
|
666
|
+
|
|
667
|
+
Based on the analysis, here's the recommended pattern:
|
|
668
|
+
|
|
669
|
+
```typescript
|
|
670
|
+
/**
|
|
671
|
+
* Recommended Error Handling Pattern for mdcontext CLI Commands
|
|
672
|
+
*/
|
|
673
|
+
|
|
674
|
+
// 1. Let critical errors propagate to CLI boundary
|
|
675
|
+
// The centralized error handler will format and display them
|
|
676
|
+
export const myCommand = Command.make('my-command', options, (args) =>
|
|
677
|
+
Effect.gen(function* () {
|
|
678
|
+
// Critical operation - let errors propagate
|
|
679
|
+
const result = yield* criticalOperation(args)
|
|
680
|
+
|
|
681
|
+
// ... use result
|
|
682
|
+
})
|
|
683
|
+
)
|
|
684
|
+
|
|
685
|
+
// 2. For optional operations, use graceful degradation
|
|
686
|
+
export const myCommandWithOptional = Command.make('my-command', options, (args) =>
|
|
687
|
+
Effect.gen(function* () {
|
|
688
|
+
// Critical operation
|
|
689
|
+
const result = yield* criticalOperation(args)
|
|
690
|
+
|
|
691
|
+
// Optional operation - catch and continue
|
|
692
|
+
const optional = yield* optionalOperation(args).pipe(
|
|
693
|
+
Effect.catchTags({
|
|
694
|
+
ExpectedError: (e) =>
|
|
695
|
+
Console.log(`Note: ${e.message}`).pipe(
|
|
696
|
+
Effect.map(() => null)
|
|
697
|
+
)
|
|
698
|
+
})
|
|
699
|
+
)
|
|
700
|
+
|
|
701
|
+
// Use both results
|
|
702
|
+
displayResults(result, optional)
|
|
703
|
+
})
|
|
704
|
+
)
|
|
705
|
+
|
|
706
|
+
// 3. For batch operations, accumulate errors
|
|
707
|
+
export const myBatchCommand = Command.make('my-command', options, (args) =>
|
|
708
|
+
Effect.gen(function* () {
|
|
709
|
+
const errors: Error[] = []
|
|
710
|
+
const results: Result[] = []
|
|
711
|
+
|
|
712
|
+
for (const item of args.items) {
|
|
713
|
+
const result = yield* processItem(item).pipe(
|
|
714
|
+
Effect.catchAll((e) =>
|
|
715
|
+
Effect.sync(() => {
|
|
716
|
+
errors.push(e)
|
|
717
|
+
return null
|
|
718
|
+
})
|
|
719
|
+
)
|
|
720
|
+
)
|
|
721
|
+
if (result) results.push(result)
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
// Report summary
|
|
725
|
+
if (errors.length > 0) {
|
|
726
|
+
yield* Console.log(`Completed with ${errors.length} errors:`)
|
|
727
|
+
for (const error of errors) {
|
|
728
|
+
yield* Console.error(` - ${error.message}`)
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
return results
|
|
733
|
+
})
|
|
734
|
+
)
|
|
735
|
+
|
|
736
|
+
// 4. Extract shared error handlers for reuse
|
|
737
|
+
const createEmbeddingErrorHandler = (json: boolean) => ({
|
|
738
|
+
ApiKeyMissingError: (e: ApiKeyMissingError) =>
|
|
739
|
+
Console.error(e.message).pipe(
|
|
740
|
+
Effect.when(() => !json),
|
|
741
|
+
Effect.map(() => null as BuildEmbeddingsResult | null)
|
|
742
|
+
),
|
|
743
|
+
|
|
744
|
+
ApiKeyInvalidError: (e: ApiKeyInvalidError) =>
|
|
745
|
+
Console.error(e.message).pipe(
|
|
746
|
+
Effect.when(() => !json),
|
|
747
|
+
Effect.map(() => null as BuildEmbeddingsResult | null)
|
|
748
|
+
),
|
|
749
|
+
|
|
750
|
+
// ... other handlers
|
|
751
|
+
})
|
|
752
|
+
|
|
753
|
+
// Use shared handler
|
|
754
|
+
buildEmbeddings(path, options).pipe(
|
|
755
|
+
Effect.catchTags(createEmbeddingErrorHandler(json))
|
|
756
|
+
)
|
|
757
|
+
|
|
758
|
+
// 5. Use Effect.try for all external/sync operations
|
|
759
|
+
const stat = yield* Effect.try({
|
|
760
|
+
try: () => fs.statSync(path),
|
|
761
|
+
catch: (e) => new FileReadError({
|
|
762
|
+
path,
|
|
763
|
+
message: e instanceof Error ? e.message : String(e),
|
|
764
|
+
cause: e,
|
|
765
|
+
})
|
|
766
|
+
})
|
|
767
|
+
|
|
768
|
+
// 6. Map errors to domain types
|
|
769
|
+
const document = yield* parseFile(path).pipe(
|
|
770
|
+
Effect.mapError((e) =>
|
|
771
|
+
e._tag === 'ParseError'
|
|
772
|
+
? new ParseError({ message: e.message, path, line: e.line })
|
|
773
|
+
: new FileReadError({ path: e.path, message: e.message })
|
|
774
|
+
)
|
|
775
|
+
)
|
|
776
|
+
|
|
777
|
+
// 7. Never use process.exit() in command handlers
|
|
778
|
+
// Instead, fail with typed errors:
|
|
779
|
+
yield* Effect.fail(
|
|
780
|
+
new CliValidationError({
|
|
781
|
+
message: 'Invalid argument',
|
|
782
|
+
argument: 'path',
|
|
783
|
+
expected: 'directory',
|
|
784
|
+
received: typeof args.path,
|
|
785
|
+
})
|
|
786
|
+
)
|
|
787
|
+
```
|
|
788
|
+
|
|
789
|
+
### Anti-Patterns to Avoid
|
|
790
|
+
|
|
791
|
+
```typescript
|
|
792
|
+
// ❌ DON'T: Use process.exit() in command handlers
|
|
793
|
+
process.exit(1)
|
|
794
|
+
|
|
795
|
+
// ✅ DO: Fail with typed errors
|
|
796
|
+
yield* Effect.fail(new CliValidationError({ message: '...' }))
|
|
797
|
+
|
|
798
|
+
// ❌ DON'T: Use Effect.runSync in error handlers
|
|
799
|
+
Effect.runSync(Console.error('message'))
|
|
800
|
+
|
|
801
|
+
// ✅ DO: Return effect-wrapped operations
|
|
802
|
+
Console.error('message').pipe(Effect.map(() => null))
|
|
803
|
+
|
|
804
|
+
// ❌ DON'T: Catch errors and silently continue without logging
|
|
805
|
+
.pipe(Effect.catchAll(() => Effect.succeed(null)))
|
|
806
|
+
|
|
807
|
+
// ✅ DO: Log before continuing
|
|
808
|
+
.pipe(
|
|
809
|
+
Effect.catchAll((e) =>
|
|
810
|
+
Console.log(`Note: Operation failed: ${e.message}`).pipe(
|
|
811
|
+
Effect.map(() => null)
|
|
812
|
+
)
|
|
813
|
+
)
|
|
814
|
+
)
|
|
815
|
+
|
|
816
|
+
// ❌ DON'T: Duplicate error handling code
|
|
817
|
+
// (see index-cmd.ts and search.ts for examples)
|
|
818
|
+
|
|
819
|
+
// ✅ DO: Extract to shared functions
|
|
820
|
+
const handleEmbeddingErrors = createEmbeddingErrorHandler(json)
|
|
821
|
+
|
|
822
|
+
// ❌ DON'T: Mix paradigms
|
|
823
|
+
console.error('error') // Direct console
|
|
824
|
+
yield* Console.error('error') // Effect-based
|
|
825
|
+
|
|
826
|
+
// ✅ DO: Use Effect consistently
|
|
827
|
+
yield* Console.error('error')
|
|
828
|
+
```
|
|
829
|
+
|
|
830
|
+
---
|
|
831
|
+
|
|
832
|
+
## Specific Issues with File Paths and Line Numbers
|
|
833
|
+
|
|
834
|
+
### Critical Issues
|
|
835
|
+
|
|
836
|
+
1. **index-cmd.ts:117** - Direct process.exit in SIGINT handler
|
|
837
|
+
- Priority: Medium
|
|
838
|
+
- Fix: Use Effect interrupt mechanism
|
|
839
|
+
|
|
840
|
+
2. **index-cmd.ts:262, 269, 320, etc.** - Effect.runSync in error handlers (15 occurrences)
|
|
841
|
+
- Priority: High
|
|
842
|
+
- Fix: Replace with Effect.flatMap chains
|
|
843
|
+
|
|
844
|
+
3. **search.ts:368-580** - Triple-duplicated error handling (150+ lines)
|
|
845
|
+
- Priority: High
|
|
846
|
+
- Fix: Extract to shared function
|
|
847
|
+
|
|
848
|
+
4. **main.ts:163, 175, 196, 297, 318** - Multiple process.exit calls
|
|
849
|
+
- Priority: Medium
|
|
850
|
+
- Fix: Route through error handler or use typed errors
|
|
851
|
+
|
|
852
|
+
### Medium Priority Issues
|
|
853
|
+
|
|
854
|
+
5. **context.ts:161** - Silent continue on section not found
|
|
855
|
+
- Priority: Medium
|
|
856
|
+
- Fix: Accumulate errors and report summary
|
|
857
|
+
|
|
858
|
+
6. **stats.ts:48** - No error handling for loadDocumentIndex
|
|
859
|
+
- Priority: Medium
|
|
860
|
+
- Fix: Add catchTag or verify function handles errors internally
|
|
861
|
+
|
|
862
|
+
7. **error-handler.ts:453-491** - Legacy error handling code
|
|
863
|
+
- Priority: Low
|
|
864
|
+
- Fix: Add deprecation comment or remove if no longer needed
|
|
865
|
+
|
|
866
|
+
### Low Priority Issues
|
|
867
|
+
|
|
868
|
+
8. **All commands** - Inconsistent use of console.error vs Console.error
|
|
869
|
+
- Priority: Low
|
|
870
|
+
- Fix: Standardize on Effect's Console
|
|
871
|
+
|
|
872
|
+
9. **main.ts:309** - IIFE async without top-level await
|
|
873
|
+
- Priority: Low
|
|
874
|
+
- Fix: Add .catch() handler or use top-level await (Node 14.8+)
|
|
875
|
+
|
|
876
|
+
---
|
|
877
|
+
|
|
878
|
+
## Testing Recommendations
|
|
879
|
+
|
|
880
|
+
1. **Error Path Testing**
|
|
881
|
+
- Add tests for each error type in each command
|
|
882
|
+
- Verify correct exit codes
|
|
883
|
+
- Test error message formatting
|
|
884
|
+
|
|
885
|
+
2. **Error Handler Testing**
|
|
886
|
+
- Unit tests for formatError()
|
|
887
|
+
- Verify all error tags are handled
|
|
888
|
+
- Test debug mode output
|
|
889
|
+
|
|
890
|
+
3. **Integration Tests**
|
|
891
|
+
- Test full error flow from command to exit
|
|
892
|
+
- Verify cleanup on errors
|
|
893
|
+
- Test interrupt handling
|
|
894
|
+
|
|
895
|
+
4. **Example Test Structure**
|
|
896
|
+
```typescript
|
|
897
|
+
describe('index command error handling', () => {
|
|
898
|
+
it('should exit with code 1 when index not found', async () => {
|
|
899
|
+
const result = await runCommand(['index', '/nonexistent'])
|
|
900
|
+
expect(result.exitCode).toBe(EXIT_CODE.USER_ERROR)
|
|
901
|
+
expect(result.stderr).toContain('Index not found')
|
|
902
|
+
})
|
|
903
|
+
|
|
904
|
+
it('should exit with code 2 on file system error', async () => {
|
|
905
|
+
const result = await runCommand(['index', '/no-permission'])
|
|
906
|
+
expect(result.exitCode).toBe(EXIT_CODE.SYSTEM_ERROR)
|
|
907
|
+
expect(result.stderr).toContain('permissions')
|
|
908
|
+
})
|
|
909
|
+
})
|
|
910
|
+
```
|
|
911
|
+
|
|
912
|
+
---
|
|
913
|
+
|
|
914
|
+
## Migration Path
|
|
915
|
+
|
|
916
|
+
### Phase 1: Quick Wins (1-2 days)
|
|
917
|
+
|
|
918
|
+
1. Extract duplicated error handlers to shared functions
|
|
919
|
+
- `createEmbeddingErrorHandler()` in shared utilities
|
|
920
|
+
- Reduces 250+ lines of duplication
|
|
921
|
+
|
|
922
|
+
2. Replace Effect.runSync with proper Effect chains
|
|
923
|
+
- Search and replace pattern
|
|
924
|
+
- Low risk, high value
|
|
925
|
+
|
|
926
|
+
3. Document legacy error handling
|
|
927
|
+
- Add comments explaining migration status
|
|
928
|
+
- Set deprecation timeline
|
|
929
|
+
|
|
930
|
+
### Phase 2: Consistency Improvements (2-3 days)
|
|
931
|
+
|
|
932
|
+
1. Standardize on Effect's Console throughout
|
|
933
|
+
- Replace all console.error with Console.error
|
|
934
|
+
- Consistent error output
|
|
935
|
+
|
|
936
|
+
2. Remove direct process.exit() calls
|
|
937
|
+
- Replace with typed errors where possible
|
|
938
|
+
- Use Effect.interrupt for signal handling
|
|
939
|
+
|
|
940
|
+
3. Add error accumulation to batch operations
|
|
941
|
+
- context command with multiple files
|
|
942
|
+
- Better user feedback
|
|
943
|
+
|
|
944
|
+
### Phase 3: Structural Improvements (3-5 days)
|
|
945
|
+
|
|
946
|
+
1. Refactor error handling strategy
|
|
947
|
+
- Document when to catch vs propagate
|
|
948
|
+
- Add guidelines to contributing docs
|
|
949
|
+
|
|
950
|
+
2. Improve error messages
|
|
951
|
+
- Add context to generic errors
|
|
952
|
+
- Ensure all errors have actionable suggestions
|
|
953
|
+
|
|
954
|
+
3. Add comprehensive error tests
|
|
955
|
+
- Cover all error types
|
|
956
|
+
- Verify exit codes
|
|
957
|
+
- Test error message formatting
|
|
958
|
+
|
|
959
|
+
---
|
|
960
|
+
|
|
961
|
+
## Conclusion
|
|
962
|
+
|
|
963
|
+
The mdcontext CLI has a **solid foundation** for error handling with its centralized error handler and typed error system. The main issues are:
|
|
964
|
+
|
|
965
|
+
1. **Inconsistent application** of error handling patterns across commands
|
|
966
|
+
2. **Code duplication** in error handlers (250+ lines)
|
|
967
|
+
3. **Mixed paradigms** (Effect vs direct console/process.exit)
|
|
968
|
+
4. **Graceful degradation without proper logging** in some commands
|
|
969
|
+
|
|
970
|
+
Addressing these issues will result in a **more maintainable, consistent, and user-friendly** CLI with clear error messages and proper exit codes.
|
|
971
|
+
|
|
972
|
+
**Recommended Priority:**
|
|
973
|
+
1. Extract duplicated error handlers (high impact, low effort)
|
|
974
|
+
2. Replace Effect.runSync calls (high impact, medium effort)
|
|
975
|
+
3. Document error handling strategy (medium impact, low effort)
|
|
976
|
+
4. Remove process.exit() calls (medium impact, medium effort)
|
|
977
|
+
5. Add error tests (high value, high effort)
|
|
978
|
+
|
|
979
|
+
Total estimated effort: **1-2 weeks** for complete implementation with tests.
|