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,483 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * MCP Server for mdcontext
5
+ *
6
+ * Exposes markdown analysis tools for Claude integration
7
+ */
8
+
9
+ import * as path from 'node:path'
10
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js'
11
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
12
+ import type { CallToolResult, Tool } from '@modelcontextprotocol/sdk/types.js'
13
+ import {
14
+ CallToolRequestSchema,
15
+ ListToolsRequestSchema,
16
+ } from '@modelcontextprotocol/sdk/types.js'
17
+ import { Effect } from 'effect'
18
+ import type { MdSection } from '../core/types.js'
19
+ import { semanticSearch } from '../embeddings/semantic-search.js'
20
+ import { buildIndex } from '../index/indexer.js'
21
+ import { parseFile } from '../parser/parser.js'
22
+ import { search } from '../search/searcher.js'
23
+ import { formatSummary, summarizeFile } from '../summarize/summarizer.js'
24
+
25
+ // Type alias for tool results - uses the SDK type
26
+
27
+ // ============================================================================
28
+ // Tool Definitions
29
+ // ============================================================================
30
+
31
+ const tools: Tool[] = [
32
+ {
33
+ name: 'md_search',
34
+ description:
35
+ 'Search markdown documents by meaning using semantic search. Returns relevant sections based on natural language queries.',
36
+ inputSchema: {
37
+ type: 'object',
38
+ properties: {
39
+ query: {
40
+ type: 'string',
41
+ description: 'Natural language search query',
42
+ },
43
+ limit: {
44
+ type: 'number',
45
+ description: 'Maximum number of results (default: 5)',
46
+ default: 5,
47
+ },
48
+ path_filter: {
49
+ type: 'string',
50
+ description:
51
+ "Glob pattern to filter files (e.g., '*.md', 'docs/**/*.md')",
52
+ },
53
+ threshold: {
54
+ type: 'number',
55
+ description: 'Minimum similarity threshold 0-1 (default: 0.5)',
56
+ default: 0.5,
57
+ },
58
+ },
59
+ required: ['query'],
60
+ },
61
+ },
62
+ {
63
+ name: 'md_context',
64
+ description:
65
+ 'Get LLM-ready context from a markdown file. Provides compressed, token-efficient summaries at various detail levels.',
66
+ inputSchema: {
67
+ type: 'object',
68
+ properties: {
69
+ path: {
70
+ type: 'string',
71
+ description: 'Path to the markdown file',
72
+ },
73
+ level: {
74
+ type: 'string',
75
+ enum: ['full', 'summary', 'brief'],
76
+ description: 'Compression level (default: summary)',
77
+ default: 'summary',
78
+ },
79
+ max_tokens: {
80
+ type: 'number',
81
+ description: 'Maximum tokens to include in output',
82
+ },
83
+ },
84
+ required: ['path'],
85
+ },
86
+ },
87
+ {
88
+ name: 'md_structure',
89
+ description:
90
+ 'Get the structure/outline of a markdown file. Shows heading hierarchy with token counts.',
91
+ inputSchema: {
92
+ type: 'object',
93
+ properties: {
94
+ path: {
95
+ type: 'string',
96
+ description: 'Path to the markdown file',
97
+ },
98
+ },
99
+ required: ['path'],
100
+ },
101
+ },
102
+ {
103
+ name: 'md_keyword_search',
104
+ description:
105
+ 'Search markdown documents by keyword search (headings, code blocks, lists, tables).',
106
+ inputSchema: {
107
+ type: 'object',
108
+ properties: {
109
+ heading: {
110
+ type: 'string',
111
+ description: 'Filter by heading pattern (regex)',
112
+ },
113
+ path_filter: {
114
+ type: 'string',
115
+ description: 'Glob pattern to filter files',
116
+ },
117
+ has_code: {
118
+ type: 'boolean',
119
+ description: 'Only sections with code blocks',
120
+ },
121
+ has_list: {
122
+ type: 'boolean',
123
+ description: 'Only sections with lists',
124
+ },
125
+ has_table: {
126
+ type: 'boolean',
127
+ description: 'Only sections with tables',
128
+ },
129
+ limit: {
130
+ type: 'number',
131
+ description: 'Maximum results (default: 20)',
132
+ default: 20,
133
+ },
134
+ },
135
+ },
136
+ },
137
+ {
138
+ name: 'md_index',
139
+ description:
140
+ 'Build or rebuild the index for a directory. Required before using search tools.',
141
+ inputSchema: {
142
+ type: 'object',
143
+ properties: {
144
+ path: {
145
+ type: 'string',
146
+ description: 'Directory to index (default: current directory)',
147
+ default: '.',
148
+ },
149
+ force: {
150
+ type: 'boolean',
151
+ description: 'Force full rebuild (default: false)',
152
+ default: false,
153
+ },
154
+ },
155
+ },
156
+ },
157
+ ]
158
+
159
+ // ============================================================================
160
+ // Tool Handlers
161
+ // ============================================================================
162
+
163
+ const handleMdSearch = async (
164
+ args: Record<string, unknown>,
165
+ rootPath: string,
166
+ ): Promise<CallToolResult> => {
167
+ const query = args.query as string
168
+ const limit = (args.limit as number) ?? 5
169
+ const pathFilter = args.path_filter as string | undefined
170
+ const threshold = (args.threshold as number) ?? 0.5
171
+
172
+ const result = await Effect.runPromise(
173
+ semanticSearch(rootPath, query, {
174
+ limit,
175
+ threshold,
176
+ pathPattern: pathFilter,
177
+ }).pipe(
178
+ Effect.catchAll((e) => Effect.succeed([{ error: e.message }] as const)),
179
+ ),
180
+ )
181
+
182
+ if (Array.isArray(result) && result.length > 0 && 'error' in result[0]) {
183
+ return {
184
+ content: [
185
+ {
186
+ type: 'text',
187
+ text: `Error: ${(result[0] as { error: string }).error}`,
188
+ },
189
+ ],
190
+ isError: true,
191
+ }
192
+ }
193
+
194
+ const formattedResults = (
195
+ result as Array<{
196
+ sectionId: string
197
+ documentPath: string
198
+ heading: string
199
+ similarity: number
200
+ }>
201
+ ).map((r, i) => {
202
+ const similarity = (r.similarity * 100).toFixed(1)
203
+ return `${i + 1}. **${r.heading}** (${similarity}% match)\n ${r.documentPath}`
204
+ })
205
+
206
+ return {
207
+ content: [
208
+ {
209
+ type: 'text',
210
+ text:
211
+ formattedResults.length > 0
212
+ ? `Found ${formattedResults.length} results for "${query}":\n\n${formattedResults.join('\n\n')}`
213
+ : `No results found for "${query}"`,
214
+ },
215
+ ],
216
+ }
217
+ }
218
+
219
+ const handleMdContext = async (
220
+ args: Record<string, unknown>,
221
+ rootPath: string,
222
+ ): Promise<CallToolResult> => {
223
+ const filePath = args.path as string
224
+ const level = (args.level as 'brief' | 'summary' | 'full') ?? 'summary'
225
+ const maxTokens = args.max_tokens as number | undefined
226
+
227
+ const resolvedPath = path.isAbsolute(filePath)
228
+ ? filePath
229
+ : path.join(rootPath, filePath)
230
+
231
+ const result = await Effect.runPromise(
232
+ summarizeFile(resolvedPath, { level, maxTokens }).pipe(
233
+ Effect.catchAll((e) => Effect.succeed({ error: e.message })),
234
+ ),
235
+ )
236
+
237
+ if ('error' in result) {
238
+ return {
239
+ content: [{ type: 'text', text: `Error: ${result.error}` }],
240
+ isError: true,
241
+ }
242
+ }
243
+
244
+ return {
245
+ content: [{ type: 'text', text: formatSummary(result) }],
246
+ }
247
+ }
248
+
249
+ const handleMdStructure = async (
250
+ args: Record<string, unknown>,
251
+ rootPath: string,
252
+ ): Promise<CallToolResult> => {
253
+ const filePath = args.path as string
254
+ const resolvedPath = path.isAbsolute(filePath)
255
+ ? filePath
256
+ : path.join(rootPath, filePath)
257
+
258
+ const result = await Effect.runPromise(
259
+ parseFile(resolvedPath).pipe(
260
+ Effect.mapError((e) => new Error(`${e._tag}: ${e.message}`)),
261
+ Effect.catchAll((e) => Effect.succeed({ error: e.message })),
262
+ ),
263
+ )
264
+
265
+ if ('error' in result) {
266
+ return {
267
+ content: [{ type: 'text', text: `Error: ${result.error}` }],
268
+ isError: true,
269
+ }
270
+ }
271
+
272
+ const formatSection = (section: MdSection, depth: number = 0): string => {
273
+ const indent = ' '.repeat(depth)
274
+ const marker = '#'.repeat(section.level)
275
+ const meta: string[] = []
276
+ if (section.metadata.hasCode) meta.push('code')
277
+ if (section.metadata.hasList) meta.push('list')
278
+ if (section.metadata.hasTable) meta.push('table')
279
+ const metaStr = meta.length > 0 ? ` [${meta.join(', ')}]` : ''
280
+
281
+ let output = `${indent}${marker} ${section.heading}${metaStr} (${section.metadata.tokenCount} tokens)\n`
282
+
283
+ for (const child of section.children) {
284
+ output += formatSection(child, depth + 1)
285
+ }
286
+
287
+ return output
288
+ }
289
+
290
+ const structure = result.sections.map((s) => formatSection(s)).join('')
291
+
292
+ return {
293
+ content: [
294
+ {
295
+ type: 'text',
296
+ text: `# ${result.title}\nPath: ${result.path}\nTotal tokens: ${result.metadata.tokenCount}\n\n${structure}`,
297
+ },
298
+ ],
299
+ }
300
+ }
301
+
302
+ const handleMdKeywordSearch = async (
303
+ args: Record<string, unknown>,
304
+ rootPath: string,
305
+ ): Promise<CallToolResult> => {
306
+ const heading = args.heading as string | undefined
307
+ const pathFilter = args.path_filter as string | undefined
308
+ const hasCode = args.has_code as boolean | undefined
309
+ const hasList = args.has_list as boolean | undefined
310
+ const hasTable = args.has_table as boolean | undefined
311
+ const limit = (args.limit as number) ?? 20
312
+
313
+ const result = await Effect.runPromise(
314
+ search(rootPath, {
315
+ heading,
316
+ pathPattern: pathFilter,
317
+ hasCode,
318
+ hasList,
319
+ hasTable,
320
+ limit,
321
+ }).pipe(
322
+ Effect.catchAll((e) => Effect.succeed([{ error: e.message }] as const)),
323
+ ),
324
+ )
325
+
326
+ if (Array.isArray(result) && result.length > 0 && 'error' in result[0]) {
327
+ return {
328
+ content: [
329
+ {
330
+ type: 'text',
331
+ text: `Error: ${(result[0] as { error: string }).error}`,
332
+ },
333
+ ],
334
+ isError: true,
335
+ }
336
+ }
337
+
338
+ const formattedResults = (
339
+ result as Array<{
340
+ section: {
341
+ heading: string
342
+ level: number
343
+ documentPath: string
344
+ tokenCount: number
345
+ hasCode: boolean
346
+ hasList: boolean
347
+ hasTable: boolean
348
+ }
349
+ }>
350
+ ).map((r, i) => {
351
+ const meta: string[] = []
352
+ if (r.section.hasCode) meta.push('code')
353
+ if (r.section.hasList) meta.push('list')
354
+ if (r.section.hasTable) meta.push('table')
355
+ const metaStr = meta.length > 0 ? ` [${meta.join(', ')}]` : ''
356
+
357
+ return `${i + 1}. **${r.section.heading}**${metaStr}\n ${r.section.documentPath} (${r.section.tokenCount} tokens)`
358
+ })
359
+
360
+ return {
361
+ content: [
362
+ {
363
+ type: 'text',
364
+ text:
365
+ formattedResults.length > 0
366
+ ? `Found ${formattedResults.length} sections:\n\n${formattedResults.join('\n\n')}`
367
+ : 'No sections found matching criteria',
368
+ },
369
+ ],
370
+ }
371
+ }
372
+
373
+ const handleMdIndex = async (
374
+ args: Record<string, unknown>,
375
+ rootPath: string,
376
+ ): Promise<CallToolResult> => {
377
+ const indexPath = (args.path as string) ?? '.'
378
+ const force = (args.force as boolean) ?? false
379
+
380
+ const resolvedPath = path.isAbsolute(indexPath)
381
+ ? indexPath
382
+ : path.join(rootPath, indexPath)
383
+
384
+ const result = await Effect.runPromise(
385
+ buildIndex(resolvedPath, { force }).pipe(
386
+ Effect.catchAll((e) => Effect.succeed({ error: e.message })),
387
+ ),
388
+ )
389
+
390
+ if ('error' in result) {
391
+ return {
392
+ content: [{ type: 'text', text: `Error: ${result.error}` }],
393
+ isError: true,
394
+ }
395
+ }
396
+
397
+ return {
398
+ content: [
399
+ {
400
+ type: 'text',
401
+ text: `Indexed ${result.documentsIndexed} documents, ${result.sectionsIndexed} sections, ${result.linksIndexed} links in ${result.duration}ms`,
402
+ },
403
+ ],
404
+ }
405
+ }
406
+
407
+ // ============================================================================
408
+ // MCP Server Setup
409
+ // ============================================================================
410
+
411
+ const createServer = (rootPath: string) => {
412
+ const server = new Server(
413
+ {
414
+ name: 'mdcontext-mcp',
415
+ version: '0.1.0',
416
+ },
417
+ {
418
+ capabilities: {
419
+ tools: {},
420
+ },
421
+ },
422
+ )
423
+
424
+ // List available tools
425
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
426
+ tools,
427
+ }))
428
+
429
+ // Handle tool calls
430
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
431
+ const { name, arguments: args } = request.params
432
+
433
+ switch (name) {
434
+ case 'md_search':
435
+ return handleMdSearch(args ?? {}, rootPath)
436
+ case 'md_context':
437
+ return handleMdContext(args ?? {}, rootPath)
438
+ case 'md_structure':
439
+ return handleMdStructure(args ?? {}, rootPath)
440
+ case 'md_keyword_search':
441
+ return handleMdKeywordSearch(args ?? {}, rootPath)
442
+ case 'md_index':
443
+ return handleMdIndex(args ?? {}, rootPath)
444
+ default:
445
+ return {
446
+ content: [{ type: 'text', text: `Unknown tool: ${name}` }],
447
+ isError: true,
448
+ }
449
+ }
450
+ })
451
+
452
+ return server
453
+ }
454
+
455
+ // ============================================================================
456
+ // Main Entry
457
+ // ============================================================================
458
+
459
+ const main = async () => {
460
+ // Use current working directory as root
461
+ const rootPath = process.cwd()
462
+
463
+ const server = createServer(rootPath)
464
+ const transport = new StdioServerTransport()
465
+
466
+ await server.connect(transport)
467
+
468
+ // Handle graceful shutdown
469
+ process.on('SIGINT', async () => {
470
+ await server.close()
471
+ process.exit(0)
472
+ })
473
+
474
+ process.on('SIGTERM', async () => {
475
+ await server.close()
476
+ process.exit(0)
477
+ })
478
+ }
479
+
480
+ main().catch((error) => {
481
+ console.error('Fatal error:', error)
482
+ process.exit(1)
483
+ })
@@ -0,0 +1 @@
1
+ export * from './parser.js'