mdcontext 0.0.1 → 0.1.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 (140) hide show
  1. package/.changeset/README.md +28 -0
  2. package/.changeset/config.json +11 -0
  3. package/.github/workflows/ci.yml +83 -0
  4. package/.github/workflows/release.yml +113 -0
  5. package/.tldrignore +112 -0
  6. package/AGENTS.md +46 -0
  7. package/BACKLOG.md +338 -0
  8. package/README.md +231 -11
  9. package/biome.json +36 -0
  10. package/cspell.config.yaml +14 -0
  11. package/dist/chunk-KRYIFLQR.js +92 -0
  12. package/dist/chunk-S7E6TFX6.js +742 -0
  13. package/dist/chunk-VVTGZNBT.js +1519 -0
  14. package/dist/cli/main.d.ts +1 -0
  15. package/dist/cli/main.js +2015 -0
  16. package/dist/index.d.ts +266 -0
  17. package/dist/index.js +86 -0
  18. package/dist/mcp/server.d.ts +1 -0
  19. package/dist/mcp/server.js +376 -0
  20. package/docs/019-USAGE.md +586 -0
  21. package/docs/020-current-implementation.md +364 -0
  22. package/docs/021-DOGFOODING-FINDINGS.md +175 -0
  23. package/docs/BACKLOG.md +80 -0
  24. package/docs/DESIGN.md +439 -0
  25. package/docs/PROJECT.md +88 -0
  26. package/docs/ROADMAP.md +407 -0
  27. package/docs/test-links.md +9 -0
  28. package/package.json +69 -10
  29. package/pnpm-workspace.yaml +5 -0
  30. package/research/config-analysis/01-current-implementation.md +470 -0
  31. package/research/config-analysis/02-strategy-recommendation.md +428 -0
  32. package/research/config-analysis/03-task-candidates.md +715 -0
  33. package/research/config-analysis/033-research-configuration-management.md +828 -0
  34. package/research/config-analysis/034-research-effect-cli-config.md +1504 -0
  35. package/research/config-analysis/04-consolidated-task-candidates.md +277 -0
  36. package/research/dogfood/consolidated-tool-evaluation.md +373 -0
  37. package/research/dogfood/strategy-a/a-synthesis.md +184 -0
  38. package/research/dogfood/strategy-a/a1-docs.md +226 -0
  39. package/research/dogfood/strategy-a/a2-amorphic.md +156 -0
  40. package/research/dogfood/strategy-a/a3-llm.md +164 -0
  41. package/research/dogfood/strategy-b/b-synthesis.md +228 -0
  42. package/research/dogfood/strategy-b/b1-architecture.md +207 -0
  43. package/research/dogfood/strategy-b/b2-gaps.md +258 -0
  44. package/research/dogfood/strategy-b/b3-workflows.md +250 -0
  45. package/research/dogfood/strategy-c/c-synthesis.md +451 -0
  46. package/research/dogfood/strategy-c/c1-explorer.md +192 -0
  47. package/research/dogfood/strategy-c/c2-diver-memory.md +145 -0
  48. package/research/dogfood/strategy-c/c3-diver-control.md +148 -0
  49. package/research/dogfood/strategy-c/c4-diver-failure.md +151 -0
  50. package/research/dogfood/strategy-c/c5-diver-execution.md +221 -0
  51. package/research/dogfood/strategy-c/c6-diver-org.md +221 -0
  52. package/research/effect-cli-error-handling.md +845 -0
  53. package/research/effect-errors-as-values.md +943 -0
  54. package/research/errors-task-analysis/00-consolidated-tasks.md +207 -0
  55. package/research/errors-task-analysis/cli-commands-analysis.md +909 -0
  56. package/research/errors-task-analysis/embeddings-analysis.md +709 -0
  57. package/research/errors-task-analysis/index-search-analysis.md +812 -0
  58. package/research/mdcontext-error-analysis.md +521 -0
  59. package/research/npm_publish/011-npm-workflow-research-agent2.md +792 -0
  60. package/research/npm_publish/012-npm-workflow-research-agent1.md +530 -0
  61. package/research/npm_publish/013-npm-workflow-research-agent3.md +722 -0
  62. package/research/npm_publish/014-npm-workflow-synthesis.md +556 -0
  63. package/research/npm_publish/031-npm-workflow-task-analysis.md +134 -0
  64. package/research/semantic-search/002-research-embedding-models.md +490 -0
  65. package/research/semantic-search/003-research-rag-alternatives.md +523 -0
  66. package/research/semantic-search/004-research-vector-search.md +841 -0
  67. package/research/semantic-search/032-research-semantic-search.md +427 -0
  68. package/research/task-management-2026/00-synthesis-recommendations.md +295 -0
  69. package/research/task-management-2026/01-ai-workflow-tools.md +416 -0
  70. package/research/task-management-2026/02-agent-framework-patterns.md +476 -0
  71. package/research/task-management-2026/03-lightweight-file-based.md +567 -0
  72. package/research/task-management-2026/04-established-tools-ai-features.md +541 -0
  73. package/research/task-management-2026/linear/01-core-features-workflow.md +771 -0
  74. package/research/task-management-2026/linear/02-api-integrations.md +930 -0
  75. package/research/task-management-2026/linear/03-ai-features.md +368 -0
  76. package/research/task-management-2026/linear/04-pricing-setup.md +205 -0
  77. package/research/task-management-2026/linear/05-usage-patterns-best-practices.md +605 -0
  78. package/scripts/rebuild-hnswlib.js +63 -0
  79. package/src/cli/argv-preprocessor.test.ts +210 -0
  80. package/src/cli/argv-preprocessor.ts +202 -0
  81. package/src/cli/cli.test.ts +430 -0
  82. package/src/cli/commands/backlinks.ts +54 -0
  83. package/src/cli/commands/context.ts +197 -0
  84. package/src/cli/commands/index-cmd.ts +300 -0
  85. package/src/cli/commands/index.ts +13 -0
  86. package/src/cli/commands/links.ts +52 -0
  87. package/src/cli/commands/search.ts +451 -0
  88. package/src/cli/commands/stats.ts +146 -0
  89. package/src/cli/commands/tree.ts +107 -0
  90. package/src/cli/flag-schemas.ts +275 -0
  91. package/src/cli/help.ts +386 -0
  92. package/src/cli/index.ts +9 -0
  93. package/src/cli/main.ts +145 -0
  94. package/src/cli/options.ts +31 -0
  95. package/src/cli/typo-suggester.test.ts +105 -0
  96. package/src/cli/typo-suggester.ts +130 -0
  97. package/src/cli/utils.ts +126 -0
  98. package/src/core/index.ts +1 -0
  99. package/src/core/types.ts +140 -0
  100. package/src/embeddings/index.ts +8 -0
  101. package/src/embeddings/openai-provider.ts +165 -0
  102. package/src/embeddings/semantic-search.ts +583 -0
  103. package/src/embeddings/types.ts +82 -0
  104. package/src/embeddings/vector-store.ts +299 -0
  105. package/src/index/index.ts +4 -0
  106. package/src/index/indexer.ts +446 -0
  107. package/src/index/storage.ts +196 -0
  108. package/src/index/types.ts +109 -0
  109. package/src/index/watcher.ts +131 -0
  110. package/src/index.ts +8 -0
  111. package/src/mcp/server.ts +483 -0
  112. package/src/parser/index.ts +1 -0
  113. package/src/parser/parser.test.ts +291 -0
  114. package/src/parser/parser.ts +395 -0
  115. package/src/parser/section-filter.ts +270 -0
  116. package/src/search/query-parser.test.ts +260 -0
  117. package/src/search/query-parser.ts +319 -0
  118. package/src/search/searcher.test.ts +182 -0
  119. package/src/search/searcher.ts +602 -0
  120. package/src/summarize/budget-bugs.test.ts +620 -0
  121. package/src/summarize/formatters.ts +419 -0
  122. package/src/summarize/index.ts +20 -0
  123. package/src/summarize/summarizer.test.ts +275 -0
  124. package/src/summarize/summarizer.ts +528 -0
  125. package/src/summarize/verify-bugs.test.ts +238 -0
  126. package/src/utils/index.ts +1 -0
  127. package/src/utils/tokens.test.ts +142 -0
  128. package/src/utils/tokens.ts +186 -0
  129. package/tests/fixtures/cli/.mdcontext/config.json +8 -0
  130. package/tests/fixtures/cli/.mdcontext/indexes/documents.json +33 -0
  131. package/tests/fixtures/cli/.mdcontext/indexes/links.json +12 -0
  132. package/tests/fixtures/cli/.mdcontext/indexes/sections.json +233 -0
  133. package/tests/fixtures/cli/.mdcontext/vectors.bin +0 -0
  134. package/tests/fixtures/cli/.mdcontext/vectors.meta.json +1264 -0
  135. package/tests/fixtures/cli/README.md +9 -0
  136. package/tests/fixtures/cli/api-reference.md +11 -0
  137. package/tests/fixtures/cli/getting-started.md +11 -0
  138. package/tsconfig.json +26 -0
  139. package/vitest.config.ts +21 -0
  140. package/vitest.setup.ts +12 -0
@@ -0,0 +1,210 @@
1
+ /**
2
+ * Unit tests for argv-preprocessor
3
+ */
4
+
5
+ import { describe, expect, it } from 'vitest'
6
+ import { preprocessArgvWithValidation } from './argv-preprocessor.js'
7
+
8
+ describe('preprocessArgvWithValidation', () => {
9
+ const node = '/usr/bin/node'
10
+ const script = '/path/to/mdcontext'
11
+
12
+ describe('flag reordering', () => {
13
+ it('reorders flags before positional args', () => {
14
+ const result = preprocessArgvWithValidation([
15
+ node,
16
+ script,
17
+ 'search',
18
+ 'query',
19
+ '--limit',
20
+ '5',
21
+ 'docs/',
22
+ ])
23
+ expect(result.argv).toEqual([
24
+ node,
25
+ script,
26
+ 'search',
27
+ '--limit',
28
+ '5',
29
+ 'query',
30
+ 'docs/',
31
+ ])
32
+ expect(result.error).toBeUndefined()
33
+ })
34
+
35
+ it('handles --flag=value syntax', () => {
36
+ const result = preprocessArgvWithValidation([
37
+ node,
38
+ script,
39
+ 'search',
40
+ 'query',
41
+ '--limit=5',
42
+ 'docs/',
43
+ ])
44
+ expect(result.argv).toEqual([
45
+ node,
46
+ script,
47
+ 'search',
48
+ '--limit=5',
49
+ 'query',
50
+ 'docs/',
51
+ ])
52
+ expect(result.error).toBeUndefined()
53
+ })
54
+
55
+ it('handles boolean flags', () => {
56
+ const result = preprocessArgvWithValidation([
57
+ node,
58
+ script,
59
+ 'context',
60
+ 'file.md',
61
+ '--brief',
62
+ ])
63
+ expect(result.argv).toEqual([
64
+ node,
65
+ script,
66
+ 'context',
67
+ '--brief',
68
+ 'file.md',
69
+ ])
70
+ expect(result.error).toBeUndefined()
71
+ })
72
+
73
+ it('passes through --help flag', () => {
74
+ const result = preprocessArgvWithValidation([
75
+ node,
76
+ script,
77
+ 'context',
78
+ '--help',
79
+ ])
80
+ expect(result.argv).toEqual([node, script, 'context', '--help'])
81
+ expect(result.error).toBeUndefined()
82
+ })
83
+ })
84
+
85
+ describe('unknown flag detection', () => {
86
+ it('returns error for unknown flag', () => {
87
+ const result = preprocessArgvWithValidation([
88
+ node,
89
+ script,
90
+ 'context',
91
+ '-x',
92
+ 'file.md',
93
+ ])
94
+ expect(result.error).toContain("Unknown option '-x'")
95
+ expect(result.error).toContain("'context'")
96
+ })
97
+
98
+ it('returns error for unknown long flag', () => {
99
+ const result = preprocessArgvWithValidation([
100
+ node,
101
+ script,
102
+ 'search',
103
+ '--invalid',
104
+ 'query',
105
+ ])
106
+ expect(result.error).toContain("Unknown option '--invalid'")
107
+ })
108
+
109
+ it('returns error for unknown flag with value', () => {
110
+ const result = preprocessArgvWithValidation([
111
+ node,
112
+ script,
113
+ 'context',
114
+ '--foo=bar',
115
+ 'file.md',
116
+ ])
117
+ expect(result.error).toContain("Unknown option '--foo'")
118
+ })
119
+ })
120
+
121
+ describe('typo suggestions', () => {
122
+ it('suggests --json for --jsno', () => {
123
+ const result = preprocessArgvWithValidation([
124
+ node,
125
+ script,
126
+ 'context',
127
+ '--jsno',
128
+ 'file.md',
129
+ ])
130
+ expect(result.error).toContain("Did you mean '--json'")
131
+ })
132
+
133
+ it('suggests --limit for --limt', () => {
134
+ const result = preprocessArgvWithValidation([
135
+ node,
136
+ script,
137
+ 'search',
138
+ '--limt',
139
+ '5',
140
+ 'query',
141
+ ])
142
+ expect(result.error).toContain("Did you mean '--limit'")
143
+ })
144
+
145
+ it('suggests --tokens for --toekns', () => {
146
+ const result = preprocessArgvWithValidation([
147
+ node,
148
+ script,
149
+ 'context',
150
+ '--toekns',
151
+ '100',
152
+ 'file.md',
153
+ ])
154
+ expect(result.error).toContain("Did you mean '--tokens'")
155
+ })
156
+ })
157
+
158
+ describe('edge cases', () => {
159
+ it('passes through empty args', () => {
160
+ const result = preprocessArgvWithValidation([node, script])
161
+ expect(result.argv).toEqual([node, script])
162
+ expect(result.error).toBeUndefined()
163
+ })
164
+
165
+ it('passes through unknown subcommand', () => {
166
+ const result = preprocessArgvWithValidation([
167
+ node,
168
+ script,
169
+ 'unknown',
170
+ '--flag',
171
+ ])
172
+ // Unknown subcommand should pass through (no schema)
173
+ expect(result.argv).toEqual([node, script, 'unknown', '--flag'])
174
+ expect(result.error).toBeUndefined()
175
+ })
176
+
177
+ it('handles -- end of flags marker', () => {
178
+ const result = preprocessArgvWithValidation([
179
+ node,
180
+ script,
181
+ 'context',
182
+ '--',
183
+ '--not-a-flag',
184
+ 'file.md',
185
+ ])
186
+ // After --, everything is positional
187
+ expect(result.argv).toEqual([
188
+ node,
189
+ script,
190
+ 'context',
191
+ '--not-a-flag',
192
+ 'file.md',
193
+ ])
194
+ expect(result.error).toBeUndefined()
195
+ })
196
+
197
+ it('validates alias flags', () => {
198
+ const result = preprocessArgvWithValidation([
199
+ node,
200
+ script,
201
+ 'context',
202
+ '-t',
203
+ '100',
204
+ 'file.md',
205
+ ])
206
+ expect(result.error).toBeUndefined()
207
+ expect(result.argv).toContain('-t')
208
+ })
209
+ })
210
+ })
@@ -0,0 +1,202 @@
1
+ /**
2
+ * Argv Preprocessor - Enables flexible flag positioning
3
+ *
4
+ * Effect CLI requires flags before positional args, but users expect
5
+ * to be able to do either:
6
+ * mdcontext search "query" --limit 5
7
+ * mdcontext search --limit 5 "query"
8
+ *
9
+ * This preprocessor reorders argv so flags always come first for each
10
+ * subcommand, while preserving the user's intended positional arg order.
11
+ *
12
+ * It also validates flags against per-command schemas and provides
13
+ * helpful error messages for unknown flags with typo suggestions.
14
+ */
15
+
16
+ import {
17
+ type CommandSchema,
18
+ getCommandSchema,
19
+ getValidFlags,
20
+ flagTakesValue as schemaFlagTakesValue,
21
+ } from './flag-schemas.js'
22
+ import { formatValidFlags, suggestFlag } from './typo-suggester.js'
23
+
24
+ /**
25
+ * Result of preprocessing
26
+ */
27
+ export interface PreprocessResult {
28
+ /** Processed argv (reordered) */
29
+ argv: string[]
30
+ /** Error if unknown flag detected */
31
+ error?: string
32
+ }
33
+
34
+ /**
35
+ * Check if an argument looks like a flag
36
+ */
37
+ const isFlag = (arg: string): boolean => {
38
+ return arg.startsWith('-')
39
+ }
40
+
41
+ /**
42
+ * Extract the base flag name (handles --flag=value syntax)
43
+ */
44
+ const extractFlagName = (arg: string): string => {
45
+ const eqIndex = arg.indexOf('=')
46
+ return eqIndex >= 0 ? arg.slice(0, eqIndex) : arg
47
+ }
48
+
49
+ /**
50
+ * Validate a flag against a command schema
51
+ * Returns an error message if invalid, undefined if valid
52
+ */
53
+ const validateFlag = (
54
+ flag: string,
55
+ schema: CommandSchema,
56
+ ): string | undefined => {
57
+ const flagName = extractFlagName(flag)
58
+ const validFlags = getValidFlags(schema)
59
+
60
+ if (validFlags.has(flagName)) {
61
+ return undefined // Valid flag
62
+ }
63
+
64
+ // Unknown flag - build error message with suggestion
65
+ const suggestion = suggestFlag(flagName, schema)
66
+
67
+ let errorMsg = `Unknown option '${flagName}' for '${schema.name}'`
68
+
69
+ if (suggestion) {
70
+ errorMsg += `\nDid you mean '${suggestion.flag}'?`
71
+ }
72
+
73
+ errorMsg += `\n\nValid options for '${schema.name}':\n${formatValidFlags(schema)}`
74
+
75
+ return errorMsg
76
+ }
77
+
78
+ /**
79
+ * Preprocess argv to put flags before positional arguments
80
+ *
81
+ * Transforms: ['search', 'query', '--limit', '5', 'path']
82
+ * Into: ['search', '--limit', '5', 'query', 'path']
83
+ *
84
+ * Also validates flags and returns error for unknown flags.
85
+ */
86
+ export const preprocessArgv = (argv: string[]): string[] => {
87
+ const result = preprocessArgvWithValidation(argv)
88
+
89
+ if (result.error) {
90
+ // Print error and exit
91
+ console.error(`\nError: ${result.error}`)
92
+ console.error('\nRun "mdcontext <command> --help" for usage information.')
93
+ process.exit(1)
94
+ }
95
+
96
+ return result.argv
97
+ }
98
+
99
+ /**
100
+ * Preprocess argv with validation (for testing)
101
+ */
102
+ export const preprocessArgvWithValidation = (
103
+ argv: string[],
104
+ ): PreprocessResult => {
105
+ // argv[0] = node, argv[1] = script, rest = user args
106
+ const nodeAndScript = argv.slice(0, 2)
107
+ const userArgs = argv.slice(2)
108
+
109
+ if (userArgs.length === 0) {
110
+ return { argv }
111
+ }
112
+
113
+ // Check if first arg is a subcommand (not a flag)
114
+ const firstArg = userArgs[0]
115
+ if (!firstArg || isFlag(firstArg)) {
116
+ // No subcommand, return as-is
117
+ return { argv }
118
+ }
119
+
120
+ const subcommand = firstArg
121
+ const restArgs = userArgs.slice(1)
122
+
123
+ // Get schema for this command
124
+ const schema = getCommandSchema(subcommand)
125
+
126
+ // If no schema, use legacy behavior (pass through)
127
+ if (!schema) {
128
+ return { argv }
129
+ }
130
+
131
+ // Separate flags (with their values) from positional args
132
+ const flags: string[] = []
133
+ const positionals: string[] = []
134
+
135
+ let i = 0
136
+ while (i < restArgs.length) {
137
+ const arg = restArgs[i]
138
+ if (!arg) {
139
+ i++
140
+ continue
141
+ }
142
+
143
+ if (isFlag(arg)) {
144
+ // Skip --help/-h, let Effect CLI handle it
145
+ if (arg === '--help' || arg === '-h') {
146
+ flags.push(arg)
147
+ i++
148
+ continue
149
+ }
150
+
151
+ // Skip -- (end of flags marker)
152
+ if (arg === '--') {
153
+ // Everything after -- is positional
154
+ i++
155
+ while (i < restArgs.length) {
156
+ const remaining = restArgs[i]
157
+ if (remaining) positionals.push(remaining)
158
+ i++
159
+ }
160
+ continue
161
+ }
162
+
163
+ // Validate the flag
164
+ const error = validateFlag(arg, schema)
165
+ if (error) {
166
+ return { argv, error }
167
+ }
168
+
169
+ // It's a valid flag
170
+ if (arg.includes('=')) {
171
+ // --flag=value syntax, keep as single arg
172
+ flags.push(arg)
173
+ i++
174
+ } else if (schemaFlagTakesValue(schema, arg)) {
175
+ // Flag with separate value
176
+ flags.push(arg)
177
+ i++
178
+ // Grab the value if it exists and isn't another flag
179
+ if (i < restArgs.length) {
180
+ const nextArg = restArgs[i]
181
+ if (nextArg && !isFlag(nextArg)) {
182
+ flags.push(nextArg)
183
+ i++
184
+ }
185
+ }
186
+ } else {
187
+ // Boolean flag
188
+ flags.push(arg)
189
+ i++
190
+ }
191
+ } else {
192
+ // Positional argument
193
+ positionals.push(arg)
194
+ i++
195
+ }
196
+ }
197
+
198
+ // Reconstruct: node, script, subcommand, flags, positionals
199
+ return {
200
+ argv: [...nodeAndScript, subcommand, ...flags, ...positionals],
201
+ }
202
+ }