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,275 @@
1
+ /**
2
+ * Per-Command Flag Schemas
3
+ *
4
+ * Defines the valid flags for each CLI command to enable:
5
+ * - Unknown flag detection
6
+ * - Typo suggestions
7
+ * - Accurate flag/value parsing in the preprocessor
8
+ */
9
+
10
+ /**
11
+ * Type of flag
12
+ */
13
+ export type FlagType = 'boolean' | 'string'
14
+
15
+ /**
16
+ * Flag specification
17
+ */
18
+ export interface FlagSpec {
19
+ /** Flag name without leading dashes (e.g., 'json') */
20
+ name: string
21
+ /** Flag type: boolean (no value) or string (takes value) */
22
+ type: FlagType
23
+ /** Short alias without leading dash (e.g., 'n' for --limit) */
24
+ alias?: string
25
+ /** Description for error messages */
26
+ description?: string
27
+ }
28
+
29
+ /**
30
+ * Command flag schema
31
+ */
32
+ export interface CommandSchema {
33
+ /** Command name */
34
+ name: string
35
+ /** Valid flags for this command */
36
+ flags: FlagSpec[]
37
+ }
38
+
39
+ // ============================================================================
40
+ // Shared Flags (used by multiple commands)
41
+ // ============================================================================
42
+
43
+ const jsonFlag: FlagSpec = {
44
+ name: 'json',
45
+ type: 'boolean',
46
+ description: 'Output as JSON',
47
+ }
48
+
49
+ const prettyFlag: FlagSpec = {
50
+ name: 'pretty',
51
+ type: 'boolean',
52
+ description: 'Pretty-print JSON output',
53
+ }
54
+
55
+ const forceFlag: FlagSpec = {
56
+ name: 'force',
57
+ type: 'boolean',
58
+ description: 'Force full rebuild',
59
+ }
60
+
61
+ const rootFlag: FlagSpec = {
62
+ name: 'root',
63
+ type: 'string',
64
+ alias: 'r',
65
+ description: 'Root directory',
66
+ }
67
+
68
+ // ============================================================================
69
+ // Per-Command Schemas
70
+ // ============================================================================
71
+
72
+ export const indexSchema: CommandSchema = {
73
+ name: 'index',
74
+ flags: [
75
+ {
76
+ name: 'embed',
77
+ type: 'boolean',
78
+ alias: 'e',
79
+ description: 'Build semantic embeddings',
80
+ },
81
+ {
82
+ name: 'no-embed',
83
+ type: 'boolean',
84
+ description: 'Skip semantic search prompt',
85
+ },
86
+ {
87
+ name: 'watch',
88
+ type: 'boolean',
89
+ alias: 'w',
90
+ description: 'Watch for changes',
91
+ },
92
+ forceFlag,
93
+ jsonFlag,
94
+ prettyFlag,
95
+ ],
96
+ }
97
+
98
+ export const searchSchema: CommandSchema = {
99
+ name: 'search',
100
+ flags: [
101
+ {
102
+ name: 'keyword',
103
+ type: 'boolean',
104
+ alias: 'k',
105
+ description: 'Force keyword search',
106
+ },
107
+ {
108
+ name: 'heading-only',
109
+ type: 'boolean',
110
+ alias: 'H',
111
+ description: 'Search headings only',
112
+ },
113
+ {
114
+ name: 'mode',
115
+ type: 'string',
116
+ alias: 'm',
117
+ description: 'Force search mode (semantic or keyword)',
118
+ },
119
+ {
120
+ name: 'limit',
121
+ type: 'string',
122
+ alias: 'n',
123
+ description: 'Maximum results',
124
+ },
125
+ { name: 'threshold', type: 'string', description: 'Similarity threshold' },
126
+ {
127
+ name: 'context',
128
+ type: 'string',
129
+ alias: 'C',
130
+ description: 'Lines of context around matches (like grep -C)',
131
+ },
132
+ {
133
+ name: 'before-context',
134
+ type: 'string',
135
+ alias: 'B',
136
+ description: 'Lines of context before matches (like grep -B)',
137
+ },
138
+ {
139
+ name: 'after-context',
140
+ type: 'string',
141
+ alias: 'A',
142
+ description: 'Lines of context after matches (like grep -A)',
143
+ },
144
+ jsonFlag,
145
+ prettyFlag,
146
+ ],
147
+ }
148
+
149
+ export const contextSchema: CommandSchema = {
150
+ name: 'context',
151
+ flags: [
152
+ { name: 'tokens', type: 'string', alias: 't', description: 'Token budget' },
153
+ { name: 'brief', type: 'boolean', description: 'Minimal output' },
154
+ { name: 'full', type: 'boolean', description: 'Include full content' },
155
+ {
156
+ name: 'section',
157
+ type: 'string',
158
+ alias: 'S',
159
+ description: 'Filter by section name, number, or glob pattern',
160
+ },
161
+ {
162
+ name: 'sections',
163
+ type: 'boolean',
164
+ description: 'List available sections',
165
+ },
166
+ {
167
+ name: 'shallow',
168
+ type: 'boolean',
169
+ description: 'Exclude nested subsections when filtering',
170
+ },
171
+ jsonFlag,
172
+ prettyFlag,
173
+ ],
174
+ }
175
+
176
+ export const treeSchema: CommandSchema = {
177
+ name: 'tree',
178
+ flags: [jsonFlag, prettyFlag],
179
+ }
180
+
181
+ export const linksSchema: CommandSchema = {
182
+ name: 'links',
183
+ flags: [rootFlag, jsonFlag, prettyFlag],
184
+ }
185
+
186
+ export const backlinksSchema: CommandSchema = {
187
+ name: 'backlinks',
188
+ flags: [rootFlag, jsonFlag, prettyFlag],
189
+ }
190
+
191
+ export const statsSchema: CommandSchema = {
192
+ name: 'stats',
193
+ flags: [jsonFlag, prettyFlag],
194
+ }
195
+
196
+ // ============================================================================
197
+ // Schema Registry
198
+ // ============================================================================
199
+
200
+ /**
201
+ * All command schemas indexed by command name
202
+ */
203
+ export const commandSchemas: Record<string, CommandSchema> = {
204
+ index: indexSchema,
205
+ search: searchSchema,
206
+ context: contextSchema,
207
+ tree: treeSchema,
208
+ links: linksSchema,
209
+ backlinks: backlinksSchema,
210
+ stats: statsSchema,
211
+ }
212
+
213
+ /**
214
+ * Get schema for a command
215
+ */
216
+ export const getCommandSchema = (
217
+ commandName: string,
218
+ ): CommandSchema | undefined => {
219
+ return commandSchemas[commandName]
220
+ }
221
+
222
+ /**
223
+ * Get all valid flag names for a command (both long and short forms)
224
+ */
225
+ export const getValidFlags = (schema: CommandSchema): Set<string> => {
226
+ const flags = new Set<string>()
227
+ for (const spec of schema.flags) {
228
+ flags.add(`--${spec.name}`)
229
+ if (spec.alias) {
230
+ flags.add(`-${spec.alias}`)
231
+ }
232
+ }
233
+ return flags
234
+ }
235
+
236
+ /**
237
+ * Check if a flag takes a value for a given command
238
+ */
239
+ export const flagTakesValue = (
240
+ schema: CommandSchema,
241
+ flag: string,
242
+ ): boolean => {
243
+ // Handle --flag=value syntax
244
+ if (flag.includes('=')) {
245
+ return false // Value is already embedded
246
+ }
247
+
248
+ for (const spec of schema.flags) {
249
+ if (
250
+ flag === `--${spec.name}` ||
251
+ (spec.alias && flag === `-${spec.alias}`)
252
+ ) {
253
+ return spec.type === 'string'
254
+ }
255
+ }
256
+ return false
257
+ }
258
+
259
+ /**
260
+ * Find the canonical name for a flag (for suggestions)
261
+ */
262
+ export const getCanonicalFlagName = (
263
+ schema: CommandSchema,
264
+ flag: string,
265
+ ): string | undefined => {
266
+ for (const spec of schema.flags) {
267
+ if (
268
+ flag === `--${spec.name}` ||
269
+ (spec.alias && flag === `-${spec.alias}`)
270
+ ) {
271
+ return `--${spec.name}`
272
+ }
273
+ }
274
+ return undefined
275
+ }
@@ -0,0 +1,386 @@
1
+ /**
2
+ * Custom help system for mdcontext CLI
3
+ *
4
+ * Provides beautiful, useful help output that matches the quality of
5
+ * professional CLI tools like git and gh.
6
+ */
7
+
8
+ // ============================================================================
9
+ // Types
10
+ // ============================================================================
11
+
12
+ interface CommandHelp {
13
+ description: string
14
+ usage: string
15
+ examples: string[]
16
+ options: { name: string; description: string }[]
17
+ notes?: string[]
18
+ }
19
+
20
+ // ============================================================================
21
+ // Help Content
22
+ // ============================================================================
23
+
24
+ export const helpContent: Record<string, CommandHelp> = {
25
+ index: {
26
+ description: 'Index markdown files for fast searching',
27
+ usage: 'mdcontext index [path] [options]',
28
+ examples: [
29
+ 'mdcontext index # Index current directory',
30
+ 'mdcontext index docs/ # Index specific directory',
31
+ 'mdcontext index --embed # Include semantic embeddings',
32
+ 'mdcontext index --watch # Watch for file changes',
33
+ 'mdcontext index --embed --watch # Full setup with live updates',
34
+ 'mdcontext index --force # Rebuild from scratch',
35
+ ],
36
+ options: [
37
+ {
38
+ name: '-e, --embed',
39
+ description: 'Build semantic embeddings (enables AI-powered search)',
40
+ },
41
+ {
42
+ name: '--no-embed',
43
+ description: 'Skip the prompt to enable semantic search',
44
+ },
45
+ {
46
+ name: '-w, --watch',
47
+ description: 'Watch for changes and re-index automatically',
48
+ },
49
+ { name: '--force', description: 'Rebuild from scratch, ignoring cache' },
50
+ { name: '--json', description: 'Output results as JSON' },
51
+ { name: '--pretty', description: 'Pretty-print JSON output' },
52
+ ],
53
+ notes: [
54
+ 'After indexing, prompts to enable semantic search (use --no-embed to skip).',
55
+ 'Embedding requires OPENAI_API_KEY environment variable.',
56
+ 'Index is stored in .mdcontext/ directory.',
57
+ ],
58
+ },
59
+ search: {
60
+ description: 'Search markdown content by meaning or heading pattern',
61
+ usage: 'mdcontext search [options] <query> [path]',
62
+ examples: [
63
+ 'mdcontext search "auth" # Simple term search',
64
+ 'mdcontext search "auth AND deploy" # Both terms required',
65
+ 'mdcontext search "error OR bug" # Either term matches',
66
+ 'mdcontext search "impl NOT test" # Exclude "test"',
67
+ 'mdcontext search "auth AND (error OR bug)" # Grouped expressions',
68
+ 'mdcontext search \'"exact phrase"\' # Exact phrase match',
69
+ 'mdcontext search \'"context resumption" AND drift\' # Phrase + boolean',
70
+ 'mdcontext search -H "API.*" # Regex on headings only',
71
+ 'mdcontext search --mode keyword "auth" # Force keyword mode',
72
+ 'mdcontext search --mode semantic "auth" # Force semantic mode',
73
+ 'mdcontext search -n 5 "setup" # Limit to 5 results',
74
+ 'mdcontext search "config" docs/ # Search in specific directory',
75
+ '',
76
+ '# Context lines (like grep):',
77
+ 'mdcontext search "checkpoint" -C 3 # 3 lines before AND after',
78
+ 'mdcontext search "error" -B 2 -A 5 # 2 before, 5 after',
79
+ ],
80
+ options: [
81
+ {
82
+ name: '-k, --keyword',
83
+ description: 'Force keyword search (content text match)',
84
+ },
85
+ {
86
+ name: '-H, --heading-only',
87
+ description: 'Search headings only (not content)',
88
+ },
89
+ {
90
+ name: '-m, --mode <mode>',
91
+ description: 'Force search mode: semantic or keyword',
92
+ },
93
+ {
94
+ name: '-n, --limit <n>',
95
+ description: 'Maximum number of results (default: 10)',
96
+ },
97
+ {
98
+ name: '-C <n>',
99
+ description: 'Show N context lines before AND after each match',
100
+ },
101
+ {
102
+ name: '-B <n>',
103
+ description: 'Show N context lines before each match',
104
+ },
105
+ {
106
+ name: '-A <n>',
107
+ description: 'Show N context lines after each match',
108
+ },
109
+ {
110
+ name: '--threshold <n>',
111
+ description:
112
+ 'Similarity threshold 0-1 for semantic search (default: 0.5)',
113
+ },
114
+ { name: '--json', description: 'Output results as JSON' },
115
+ { name: '--pretty', description: 'Pretty-print JSON output' },
116
+ ],
117
+ notes: [
118
+ 'Auto-detects mode: semantic if embeddings exist, keyword otherwise.',
119
+ 'Boolean operators: AND, OR, NOT (case-insensitive).',
120
+ 'Quoted phrases match exactly: "context resumption".',
121
+ 'Regex patterns (e.g., "API.*") always use keyword search.',
122
+ 'Run "mdcontext index --embed" first for semantic search.',
123
+ ],
124
+ },
125
+ context: {
126
+ description: 'Get LLM-ready summary of markdown files',
127
+ usage: 'mdcontext context [options] <files>...',
128
+ examples: [
129
+ 'mdcontext context README.md # Summarize single file',
130
+ 'mdcontext context *.md # Summarize all markdown files',
131
+ 'mdcontext context -t 1000 *.md # Fit within 1000 token budget',
132
+ 'mdcontext context --brief *.md # Minimal output (headings only)',
133
+ 'mdcontext context --full doc.md # Include full content',
134
+ 'mdcontext context *.md | pbcopy # Copy to clipboard (macOS)',
135
+ '',
136
+ '# Section filtering:',
137
+ 'mdcontext context doc.md --sections # List available sections',
138
+ 'mdcontext context doc.md --section "Setup" # Extract by section name',
139
+ 'mdcontext context doc.md --section "2.1" # Extract by section number',
140
+ 'mdcontext context doc.md --section "API*" # Glob pattern matching',
141
+ 'mdcontext context doc.md --section "Config" --shallow # Top-level only',
142
+ ],
143
+ options: [
144
+ {
145
+ name: '-t, --tokens <n>',
146
+ description: 'Token budget for output (default: 2000)',
147
+ },
148
+ {
149
+ name: '--brief',
150
+ description: 'Minimal output (headings and key points only)',
151
+ },
152
+ {
153
+ name: '--full',
154
+ description: 'Include full content (no summarization)',
155
+ },
156
+ {
157
+ name: '--section <name>',
158
+ description:
159
+ 'Extract specific section by name, number, or glob pattern',
160
+ },
161
+ {
162
+ name: '--sections',
163
+ description: 'List available sections with numbers and token counts',
164
+ },
165
+ {
166
+ name: '--shallow',
167
+ description: 'Exclude nested subsections when using --section',
168
+ },
169
+ { name: '--json', description: 'Output as JSON' },
170
+ { name: '--pretty', description: 'Pretty-print JSON output' },
171
+ ],
172
+ notes: [
173
+ 'Token budget controls how much content is included.',
174
+ 'Lower tokens = more aggressive summarization.',
175
+ 'Output is formatted for direct use in LLM prompts.',
176
+ 'Use --sections to discover section names before filtering.',
177
+ ],
178
+ },
179
+ tree: {
180
+ description: 'Show file tree or document outline',
181
+ usage: 'mdcontext tree [path] [options]',
182
+ examples: [
183
+ 'mdcontext tree # List markdown files in current dir',
184
+ 'mdcontext tree docs/ # List files in specific directory',
185
+ 'mdcontext tree README.md # Show document outline (headings)',
186
+ 'mdcontext tree doc.md --json # Outline as JSON',
187
+ ],
188
+ options: [
189
+ { name: '--json', description: 'Output as JSON' },
190
+ { name: '--pretty', description: 'Pretty-print JSON output' },
191
+ ],
192
+ notes: [
193
+ 'Pass a directory to list markdown files.',
194
+ 'Pass a file to show its heading structure with token counts.',
195
+ ],
196
+ },
197
+ links: {
198
+ description: 'Show what a file links to (outgoing links)',
199
+ usage: 'mdcontext links <file> [options]',
200
+ examples: [
201
+ 'mdcontext links README.md # Show outgoing links',
202
+ 'mdcontext links doc.md --json # Output as JSON',
203
+ 'mdcontext links doc.md -r docs/ # Resolve links relative to docs/',
204
+ ],
205
+ options: [
206
+ {
207
+ name: '-r, --root <dir>',
208
+ description: 'Root directory for resolving relative links',
209
+ },
210
+ { name: '--json', description: 'Output as JSON' },
211
+ { name: '--pretty', description: 'Pretty-print JSON output' },
212
+ ],
213
+ },
214
+ backlinks: {
215
+ description: 'Show what links to a file (incoming links)',
216
+ usage: 'mdcontext backlinks <file> [options]',
217
+ examples: [
218
+ 'mdcontext backlinks api.md # What links to api.md?',
219
+ 'mdcontext backlinks doc.md --json # Output as JSON',
220
+ 'mdcontext backlinks doc.md -r ./ # Resolve from current directory',
221
+ ],
222
+ options: [
223
+ {
224
+ name: '-r, --root <dir>',
225
+ description: 'Root directory for resolving relative links',
226
+ },
227
+ { name: '--json', description: 'Output as JSON' },
228
+ { name: '--pretty', description: 'Pretty-print JSON output' },
229
+ ],
230
+ notes: ['Requires index to exist. Run "mdcontext index" first.'],
231
+ },
232
+ stats: {
233
+ description: 'Show index statistics',
234
+ usage: 'mdcontext stats [path] [options]',
235
+ examples: [
236
+ 'mdcontext stats # Show stats for current directory',
237
+ 'mdcontext stats docs/ # Show stats for specific directory',
238
+ 'mdcontext stats --json # Output as JSON',
239
+ ],
240
+ options: [
241
+ { name: '--json', description: 'Output as JSON' },
242
+ { name: '--pretty', description: 'Pretty-print JSON output' },
243
+ ],
244
+ notes: ['Shows embedding count, dimensions, and cost if embeddings exist.'],
245
+ },
246
+ }
247
+
248
+ // ============================================================================
249
+ // Help Rendering
250
+ // ============================================================================
251
+
252
+ /**
253
+ * Render beautiful subcommand help
254
+ */
255
+ export const showSubcommandHelp = (command: string): void => {
256
+ const help = helpContent[command]
257
+ if (!help) {
258
+ console.log(`Unknown command: ${command}`)
259
+ console.log('Run "mdcontext --help" for available commands.')
260
+ process.exit(1)
261
+ }
262
+
263
+ // Header
264
+ console.log(`\n\x1b[1mmdcontext ${command}\x1b[0m - ${help.description}`)
265
+
266
+ // Usage
267
+ console.log(`\n\x1b[33mUSAGE\x1b[0m`)
268
+ console.log(` ${help.usage}`)
269
+
270
+ // Examples
271
+ console.log(`\n\x1b[33mEXAMPLES\x1b[0m`)
272
+ for (const example of help.examples) {
273
+ console.log(` ${example}`)
274
+ }
275
+
276
+ // Options
277
+ console.log(`\n\x1b[33mOPTIONS\x1b[0m`)
278
+ for (const opt of help.options) {
279
+ // Pad option name to 24 chars for alignment
280
+ const paddedName = opt.name.padEnd(24)
281
+ console.log(` ${paddedName}${opt.description}`)
282
+ }
283
+
284
+ // Notes (if any)
285
+ if (help.notes && help.notes.length > 0) {
286
+ console.log(`\n\x1b[33mNOTES\x1b[0m`)
287
+ for (const note of help.notes) {
288
+ console.log(` ${note}`)
289
+ }
290
+ }
291
+
292
+ console.log('')
293
+ }
294
+
295
+ /**
296
+ * Custom help output for main command - beautiful and useful
297
+ */
298
+ export const showMainHelp = (): void => {
299
+ const help = `
300
+ \x1b[1mmdcontext\x1b[0m - Token-efficient markdown analysis for LLMs
301
+
302
+ \x1b[33mCOMMANDS\x1b[0m
303
+ index [path] Index markdown files (default: .)
304
+ search <query> [path] Search by meaning or structure
305
+ context <files>... Get LLM-ready summary
306
+ tree [path] Show files or document outline
307
+ links <file> Show outgoing links
308
+ backlinks <file> Show incoming links
309
+ stats [path] Index statistics
310
+
311
+ \x1b[33mEXAMPLES\x1b[0m
312
+ mdcontext tree # List all markdown files
313
+ mdcontext tree README.md # Show document outline
314
+ mdcontext index # Index current directory
315
+ mdcontext index --embed # Index with semantic embeddings
316
+ mdcontext search "auth" # Simple term search
317
+ mdcontext search "auth AND deploy" # Boolean AND (both required)
318
+ mdcontext search "error OR bug" # Boolean OR (either matches)
319
+ mdcontext search '"exact phrase"' # Quoted phrase (exact match)
320
+ mdcontext search "how to deploy" # Semantic search (if embeddings exist)
321
+ mdcontext context README.md # Summarize a file
322
+ mdcontext context *.md -t 2000 # Multi-file with token budget
323
+
324
+ \x1b[33mWORKFLOWS\x1b[0m
325
+ \x1b[2m# Quick context for LLM\x1b[0m
326
+ mdcontext context README.md docs/*.md | pbcopy
327
+
328
+ \x1b[2m# Find relevant documentation\x1b[0m
329
+ mdcontext search "error handling"
330
+
331
+ \x1b[2m# Complex queries with boolean operators\x1b[0m
332
+ mdcontext search "auth AND (error OR exception) NOT test"
333
+
334
+ \x1b[2m# Explore a new codebase\x1b[0m
335
+ mdcontext tree && mdcontext stats
336
+
337
+ \x1b[2m# Build semantic search\x1b[0m
338
+ mdcontext index --embed && mdcontext search "authentication flow"
339
+
340
+ \x1b[33mGLOBAL OPTIONS\x1b[0m
341
+ --json Output as JSON
342
+ --pretty Pretty-print JSON
343
+ --help, -h Show help
344
+ --version, -v Show version
345
+
346
+ Run \x1b[36mmdcontext <command> --help\x1b[0m for command-specific options.
347
+ `
348
+ console.log(help)
349
+ }
350
+
351
+ // ============================================================================
352
+ // Help Detection
353
+ // ============================================================================
354
+
355
+ /**
356
+ * Check for subcommand help pattern: mdcontext <cmd> --help or mdcontext <cmd> -h
357
+ * Returns true if help was shown and we should exit
358
+ */
359
+ export const checkSubcommandHelp = (): boolean => {
360
+ const args = process.argv.slice(2)
361
+ if (args.length < 2) return false
362
+
363
+ const command = args[0]
364
+ const hasHelpFlag = args.includes('--help') || args.includes('-h')
365
+
366
+ if (hasHelpFlag && command && helpContent[command]) {
367
+ showSubcommandHelp(command)
368
+ process.exit(0)
369
+ }
370
+
371
+ return false
372
+ }
373
+
374
+ /**
375
+ * Check if we should show main help
376
+ */
377
+ export const shouldShowMainHelp = (): boolean => {
378
+ const args = process.argv.slice(2)
379
+ const showHelp =
380
+ args.length === 0 ||
381
+ args.includes('--help') ||
382
+ args.includes('-h') ||
383
+ (args.length === 1 && args[0] === 'help')
384
+
385
+ return showHelp && !args.some((a) => !a.startsWith('-') && a !== 'help')
386
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * CLI module barrel export
3
+ *
4
+ * Re-exports CLI utilities for programmatic use.
5
+ * The CLI entry point is in main.ts.
6
+ */
7
+
8
+ // Currently no public exports - CLI is run directly via main.ts
9
+ // Future: export shared utilities, command builders, etc.