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
|
@@ -131,6 +131,9 @@ export const statsCommand = Command.make(
|
|
|
131
131
|
if (embeddingStats.hasEmbeddings) {
|
|
132
132
|
yield* Console.log(` Vectors: ${embeddingStats.count}`)
|
|
133
133
|
yield* Console.log(` Provider: ${embeddingStats.provider}`)
|
|
134
|
+
if (embeddingStats.model) {
|
|
135
|
+
yield* Console.log(` Model: ${embeddingStats.model}`)
|
|
136
|
+
}
|
|
134
137
|
yield* Console.log(` Dimensions: ${embeddingStats.dimensions}`)
|
|
135
138
|
yield* Console.log(
|
|
136
139
|
` Cost: $${embeddingStats.totalCost.toFixed(6)}`,
|
package/src/cli/commands/tree.ts
CHANGED
|
@@ -9,9 +9,10 @@ import * as path from 'node:path'
|
|
|
9
9
|
import { Args, Command } from '@effect/cli'
|
|
10
10
|
import { Console, Effect } from 'effect'
|
|
11
11
|
import type { MdSection } from '../../core/types.js'
|
|
12
|
+
import { FileReadError, ParseError } from '../../errors/index.js'
|
|
12
13
|
import { parseFile } from '../../parser/parser.js'
|
|
13
14
|
import { jsonOption, prettyOption } from '../options.js'
|
|
14
|
-
import { formatJson,
|
|
15
|
+
import { formatJson, walkDirEffect } from '../utils.js'
|
|
15
16
|
|
|
16
17
|
export const treeCommand = Command.make(
|
|
17
18
|
'tree',
|
|
@@ -28,12 +29,32 @@ export const treeCommand = Command.make(
|
|
|
28
29
|
const resolvedPath = path.resolve(pathArg)
|
|
29
30
|
|
|
30
31
|
// Auto-detect: file or directory
|
|
31
|
-
const stat = yield* Effect.try(
|
|
32
|
+
const stat = yield* Effect.try({
|
|
33
|
+
try: () => fs.statSync(resolvedPath),
|
|
34
|
+
catch: (e) =>
|
|
35
|
+
new FileReadError({
|
|
36
|
+
path: resolvedPath,
|
|
37
|
+
message: `Cannot access path: ${e instanceof Error ? e.message : String(e)}`,
|
|
38
|
+
cause: e,
|
|
39
|
+
}),
|
|
40
|
+
})
|
|
32
41
|
|
|
33
42
|
if (stat.isFile()) {
|
|
34
43
|
// Show document outline
|
|
35
44
|
const result = yield* parseFile(resolvedPath).pipe(
|
|
36
|
-
Effect.mapError((e) =>
|
|
45
|
+
Effect.mapError((e) =>
|
|
46
|
+
e._tag === 'ParseError'
|
|
47
|
+
? new ParseError({
|
|
48
|
+
message: e.message,
|
|
49
|
+
path: resolvedPath,
|
|
50
|
+
...(e.line !== undefined && { line: e.line }),
|
|
51
|
+
...(e.column !== undefined && { column: e.column }),
|
|
52
|
+
})
|
|
53
|
+
: new FileReadError({
|
|
54
|
+
path: e.path,
|
|
55
|
+
message: e.message,
|
|
56
|
+
}),
|
|
57
|
+
),
|
|
37
58
|
)
|
|
38
59
|
|
|
39
60
|
const extractStructure = (
|
|
@@ -84,9 +105,9 @@ export const treeCommand = Command.make(
|
|
|
84
105
|
}
|
|
85
106
|
} else {
|
|
86
107
|
// Show file list
|
|
87
|
-
const files = yield*
|
|
108
|
+
const files = yield* walkDirEffect(resolvedPath)
|
|
88
109
|
|
|
89
|
-
const tree = files.sort().map((f) => ({
|
|
110
|
+
const tree = [...files].sort().map((f) => ({
|
|
90
111
|
path: f,
|
|
91
112
|
relativePath: path.relative(resolvedPath, f),
|
|
92
113
|
}))
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Configuration Layer
|
|
3
|
+
*
|
|
4
|
+
* Creates a configuration layer for use in CLI commands.
|
|
5
|
+
* Loads config with precedence: CLI flags > Environment > Config file > Defaults
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Effect, Layer } from 'effect'
|
|
9
|
+
import {
|
|
10
|
+
type ConfigProviderOptions,
|
|
11
|
+
ConfigService,
|
|
12
|
+
ConfigServiceDefault,
|
|
13
|
+
createConfigProvider,
|
|
14
|
+
type MdContextConfig,
|
|
15
|
+
} from '../config/index.js'
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Create a ConfigService layer from options.
|
|
19
|
+
*
|
|
20
|
+
* This loads configuration with the standard precedence chain:
|
|
21
|
+
* 1. CLI flags (highest priority)
|
|
22
|
+
* 2. Environment variables (MDCONTEXT_*)
|
|
23
|
+
* 3. Config file (if found)
|
|
24
|
+
* 4. Defaults
|
|
25
|
+
*
|
|
26
|
+
* @param options - Configuration provider options
|
|
27
|
+
* @returns A Layer that provides ConfigService
|
|
28
|
+
*/
|
|
29
|
+
export const makeCliConfigLayer = (
|
|
30
|
+
options: ConfigProviderOptions = {},
|
|
31
|
+
): Effect.Effect<Layer.Layer<ConfigService, never, never>, never, never> =>
|
|
32
|
+
Effect.gen(function* () {
|
|
33
|
+
// Create the config provider with precedence chain
|
|
34
|
+
const providerResult = yield* createConfigProvider(options).pipe(
|
|
35
|
+
Effect.catchAll(() =>
|
|
36
|
+
// If config loading fails, use empty provider (defaults will apply)
|
|
37
|
+
Effect.succeed(null),
|
|
38
|
+
),
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
if (!providerResult) {
|
|
42
|
+
// Fall back to default config if provider creation failed
|
|
43
|
+
return ConfigServiceDefault
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Load the config using the provider
|
|
47
|
+
const configResult = yield* Effect.gen(function* () {
|
|
48
|
+
// Import the schema to load config
|
|
49
|
+
const { MdContextConfig: MdContextConfigSchema } = yield* Effect.promise(
|
|
50
|
+
async () => import('../config/schema.js'),
|
|
51
|
+
)
|
|
52
|
+
return yield* MdContextConfigSchema
|
|
53
|
+
}).pipe(
|
|
54
|
+
Effect.withConfigProvider(providerResult),
|
|
55
|
+
Effect.catchAll(() => Effect.succeed(null)),
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
if (!configResult) {
|
|
59
|
+
// Fall back to default config if loading failed
|
|
60
|
+
return ConfigServiceDefault
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Create a layer with the loaded config
|
|
64
|
+
return Layer.succeed(ConfigService, configResult)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Create the default CLI configuration layer.
|
|
69
|
+
*
|
|
70
|
+
* This loads configuration from:
|
|
71
|
+
* - Environment variables (MDCONTEXT_*)
|
|
72
|
+
* - Config file (mdcontext.config.ts/json)
|
|
73
|
+
* - Built-in defaults
|
|
74
|
+
*
|
|
75
|
+
* No CLI flags are applied at this level - commands handle their own flag overrides.
|
|
76
|
+
*/
|
|
77
|
+
export const defaultCliConfigLayer: Effect.Effect<
|
|
78
|
+
Layer.Layer<ConfigService, never, never>,
|
|
79
|
+
never,
|
|
80
|
+
never
|
|
81
|
+
> = makeCliConfigLayer({
|
|
82
|
+
workingDir: process.cwd(),
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Synchronously create a default config layer.
|
|
87
|
+
*
|
|
88
|
+
* For use in cases where async config loading isn't possible.
|
|
89
|
+
* Uses only environment variables and defaults.
|
|
90
|
+
*/
|
|
91
|
+
export const defaultCliConfigLayerSync: Layer.Layer<
|
|
92
|
+
ConfigService,
|
|
93
|
+
never,
|
|
94
|
+
never
|
|
95
|
+
> = ConfigServiceDefault
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Get config value with CLI flag override.
|
|
99
|
+
*
|
|
100
|
+
* Helper function for commands to get a config value, preferring
|
|
101
|
+
* an explicit CLI flag value if provided.
|
|
102
|
+
*
|
|
103
|
+
* @param cliValue - Value from CLI flag (may be undefined)
|
|
104
|
+
* @param configValue - Value from config
|
|
105
|
+
* @returns The CLI value if provided, otherwise the config value
|
|
106
|
+
*/
|
|
107
|
+
export const withCliOverride = <T>(
|
|
108
|
+
cliValue: T | undefined,
|
|
109
|
+
configValue: T,
|
|
110
|
+
): T => {
|
|
111
|
+
return cliValue !== undefined ? cliValue : configValue
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Extract relevant config sections for a command.
|
|
116
|
+
*/
|
|
117
|
+
export type SearchConfigValues = {
|
|
118
|
+
defaultLimit: number
|
|
119
|
+
maxLimit: number
|
|
120
|
+
minSimilarity: number
|
|
121
|
+
includeSnippets: boolean
|
|
122
|
+
snippetLength: number
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export type IndexConfigValues = {
|
|
126
|
+
maxDepth: number
|
|
127
|
+
excludePatterns: readonly string[]
|
|
128
|
+
fileExtensions: readonly string[]
|
|
129
|
+
followSymlinks: boolean
|
|
130
|
+
indexDir: string
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export type OutputConfigValues = {
|
|
134
|
+
format: 'text' | 'json'
|
|
135
|
+
color: boolean
|
|
136
|
+
prettyJson: boolean
|
|
137
|
+
verbose: boolean
|
|
138
|
+
debug: boolean
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Extract search config from full config.
|
|
143
|
+
*/
|
|
144
|
+
export const getSearchConfig = (
|
|
145
|
+
config: MdContextConfig,
|
|
146
|
+
): SearchConfigValues => ({
|
|
147
|
+
defaultLimit: config.search.defaultLimit,
|
|
148
|
+
maxLimit: config.search.maxLimit,
|
|
149
|
+
minSimilarity: config.search.minSimilarity,
|
|
150
|
+
includeSnippets: config.search.includeSnippets,
|
|
151
|
+
snippetLength: config.search.snippetLength,
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Extract index config from full config.
|
|
156
|
+
*/
|
|
157
|
+
export const getIndexConfig = (config: MdContextConfig): IndexConfigValues => ({
|
|
158
|
+
maxDepth: config.index.maxDepth,
|
|
159
|
+
excludePatterns: config.index.excludePatterns,
|
|
160
|
+
fileExtensions: config.index.fileExtensions,
|
|
161
|
+
followSymlinks: config.index.followSymlinks,
|
|
162
|
+
indexDir: config.index.indexDir,
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Extract output config from full config.
|
|
167
|
+
*/
|
|
168
|
+
export const getOutputConfig = (
|
|
169
|
+
config: MdContextConfig,
|
|
170
|
+
): OutputConfigValues => ({
|
|
171
|
+
format: config.output.format,
|
|
172
|
+
color: config.output.color,
|
|
173
|
+
prettyJson: config.output.prettyJson,
|
|
174
|
+
verbose: config.output.verbose,
|
|
175
|
+
debug: config.output.debug,
|
|
176
|
+
})
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for CLI error handler
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
|
6
|
+
import { formatEffectCliError } from './error-handler.js'
|
|
7
|
+
|
|
8
|
+
describe('formatEffectCliError', () => {
|
|
9
|
+
let originalArgv: string[]
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
originalArgv = process.argv
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
process.argv = originalArgv
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
describe('mode validation errors with suggestions', () => {
|
|
20
|
+
it('suggests "semantic" for "semantics"', () => {
|
|
21
|
+
process.argv = ['node', 'cli', 'search', '--mode', 'semantics', 'test']
|
|
22
|
+
|
|
23
|
+
const error = {
|
|
24
|
+
_tag: 'ValidationError',
|
|
25
|
+
error: {
|
|
26
|
+
_tag: 'Paragraph',
|
|
27
|
+
value: {
|
|
28
|
+
_tag: 'Text',
|
|
29
|
+
value:
|
|
30
|
+
'Expected one of the following cases: hybrid, semantic, keyword',
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const result = formatEffectCliError(error)
|
|
36
|
+
expect(result).toContain("Did you mean '--mode semantic'?")
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('suggests "keyword" for "keywords"', () => {
|
|
40
|
+
process.argv = ['node', 'cli', 'search', '--mode', 'keywords', 'test']
|
|
41
|
+
|
|
42
|
+
const error = {
|
|
43
|
+
_tag: 'ValidationError',
|
|
44
|
+
error: {
|
|
45
|
+
_tag: 'Paragraph',
|
|
46
|
+
value: {
|
|
47
|
+
_tag: 'Text',
|
|
48
|
+
value:
|
|
49
|
+
'Expected one of the following cases: hybrid, semantic, keyword',
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const result = formatEffectCliError(error)
|
|
55
|
+
expect(result).toContain("Did you mean '--mode keyword'?")
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('suggests "hybrid" for "hybrit"', () => {
|
|
59
|
+
process.argv = ['node', 'cli', 'search', '--mode', 'hybrit', 'test']
|
|
60
|
+
|
|
61
|
+
const error = {
|
|
62
|
+
_tag: 'ValidationError',
|
|
63
|
+
error: {
|
|
64
|
+
_tag: 'Paragraph',
|
|
65
|
+
value: {
|
|
66
|
+
_tag: 'Text',
|
|
67
|
+
value:
|
|
68
|
+
'Expected one of the following cases: hybrid, semantic, keyword',
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const result = formatEffectCliError(error)
|
|
74
|
+
expect(result).toContain("Did you mean '--mode hybrid'?")
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('suggests "semantic" for "semant"', () => {
|
|
78
|
+
process.argv = ['node', 'cli', 'search', '--mode', 'semant', 'test']
|
|
79
|
+
|
|
80
|
+
const error = {
|
|
81
|
+
_tag: 'ValidationError',
|
|
82
|
+
error: {
|
|
83
|
+
_tag: 'Paragraph',
|
|
84
|
+
value: {
|
|
85
|
+
_tag: 'Text',
|
|
86
|
+
value:
|
|
87
|
+
'Expected one of the following cases: hybrid, semantic, keyword',
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const result = formatEffectCliError(error)
|
|
93
|
+
expect(result).toContain("Did you mean '--mode semantic'?")
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('suggests "keyword" for "keywordd"', () => {
|
|
97
|
+
process.argv = ['node', 'cli', 'search', '--mode', 'keywordd', 'test']
|
|
98
|
+
|
|
99
|
+
const error = {
|
|
100
|
+
_tag: 'ValidationError',
|
|
101
|
+
error: {
|
|
102
|
+
_tag: 'Paragraph',
|
|
103
|
+
value: {
|
|
104
|
+
_tag: 'Text',
|
|
105
|
+
value:
|
|
106
|
+
'Expected one of the following cases: hybrid, semantic, keyword',
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const result = formatEffectCliError(error)
|
|
112
|
+
expect(result).toContain("Did you mean '--mode keyword'?")
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it('does not suggest for typos too far off', () => {
|
|
116
|
+
process.argv = ['node', 'cli', 'search', '--mode', 'xyz', 'test']
|
|
117
|
+
|
|
118
|
+
const error = {
|
|
119
|
+
_tag: 'ValidationError',
|
|
120
|
+
error: {
|
|
121
|
+
_tag: 'Paragraph',
|
|
122
|
+
value: {
|
|
123
|
+
_tag: 'Text',
|
|
124
|
+
value:
|
|
125
|
+
'Expected one of the following cases: hybrid, semantic, keyword',
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const result = formatEffectCliError(error)
|
|
131
|
+
expect(result).not.toContain('Did you mean')
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it('handles --mode=value syntax', () => {
|
|
135
|
+
process.argv = ['node', 'cli', 'search', '--mode=semantics', 'test']
|
|
136
|
+
|
|
137
|
+
const error = {
|
|
138
|
+
_tag: 'ValidationError',
|
|
139
|
+
error: {
|
|
140
|
+
_tag: 'Paragraph',
|
|
141
|
+
value: {
|
|
142
|
+
_tag: 'Text',
|
|
143
|
+
value:
|
|
144
|
+
'Expected one of the following cases: hybrid, semantic, keyword',
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const result = formatEffectCliError(error)
|
|
150
|
+
expect(result).toContain("Did you mean '--mode semantic'?")
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
it('handles -m short flag', () => {
|
|
154
|
+
process.argv = ['node', 'cli', 'search', '-m', 'semantics', 'test']
|
|
155
|
+
|
|
156
|
+
const error = {
|
|
157
|
+
_tag: 'ValidationError',
|
|
158
|
+
error: {
|
|
159
|
+
_tag: 'Paragraph',
|
|
160
|
+
value: {
|
|
161
|
+
_tag: 'Text',
|
|
162
|
+
value:
|
|
163
|
+
'Expected one of the following cases: hybrid, semantic, keyword',
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const result = formatEffectCliError(error)
|
|
169
|
+
expect(result).toContain("Did you mean '--mode semantic'?")
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
it('does not suggest for non-mode validation errors', () => {
|
|
173
|
+
process.argv = ['node', 'cli', 'search', 'test']
|
|
174
|
+
|
|
175
|
+
const error = {
|
|
176
|
+
_tag: 'ValidationError',
|
|
177
|
+
error: {
|
|
178
|
+
_tag: 'Paragraph',
|
|
179
|
+
value: {
|
|
180
|
+
_tag: 'Text',
|
|
181
|
+
value: 'Some other validation error',
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const result = formatEffectCliError(error)
|
|
187
|
+
expect(result).toBe('Some other validation error')
|
|
188
|
+
expect(result).not.toContain('Did you mean')
|
|
189
|
+
})
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
describe('other error types', () => {
|
|
193
|
+
it('handles MissingValue errors', () => {
|
|
194
|
+
process.argv = ['node', 'cli', 'search']
|
|
195
|
+
|
|
196
|
+
const error = {
|
|
197
|
+
_tag: 'MissingValue',
|
|
198
|
+
error: {
|
|
199
|
+
_tag: 'Paragraph',
|
|
200
|
+
value: {
|
|
201
|
+
_tag: 'Text',
|
|
202
|
+
value: 'Missing required argument',
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const result = formatEffectCliError(error)
|
|
208
|
+
expect(result).toBe('Missing required argument')
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
it('handles InvalidValue errors', () => {
|
|
212
|
+
process.argv = ['node', 'cli', 'search']
|
|
213
|
+
|
|
214
|
+
const error = {
|
|
215
|
+
_tag: 'InvalidValue',
|
|
216
|
+
error: {
|
|
217
|
+
_tag: 'Paragraph',
|
|
218
|
+
value: {
|
|
219
|
+
_tag: 'Text',
|
|
220
|
+
value: 'Invalid value provided',
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const result = formatEffectCliError(error)
|
|
226
|
+
expect(result).toBe('Invalid value provided')
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
it('handles unknown error types', () => {
|
|
230
|
+
const error = { message: 'Unknown error' }
|
|
231
|
+
const result = formatEffectCliError(error)
|
|
232
|
+
expect(result).toBe('[object Object]')
|
|
233
|
+
})
|
|
234
|
+
})
|
|
235
|
+
})
|