manx_cli/cli.rs
1use clap::{Parser, Subcommand};
2use std::path::PathBuf;
3
4#[derive(Parser)]
5#[command(
6 name = "manx",
7 about = "A blazing-fast CLI documentation finder",
8 long_about = r#"🚀 Intelligent documentation finder with native RAG and AI synthesis
9
10CORE COMMANDS:
11 snippet <lib> [query] Search code snippets and examples (official + local docs)
12 search <query> Search official documentation across the web
13 doc <lib> [topic] Browse comprehensive documentation
14 get <id> Retrieve specific results by ID
15
16LOCAL RAG COMMANDS:
17 index <path> Index your documents for semantic search
18 sources list View indexed document sources
19 sources clear Clear all indexed documents
20
21EMBEDDING SYSTEM - Smart semantic search (works great out of the box):
22 embedding status View current embedding configuration
23 embedding list Show installed models
24 embedding download <model> Install neural models from HuggingFace
25 embedding test <query> Test embedding quality
26
27DEFAULT MODE (No setup required):
28 ⚡ Hash-based embeddings Built-in algorithm (0ms, offline, 0MB storage)
29 📚 Official documentation Context7 API integration
30 🔍 Keyword matching Excellent for exact phrases and terms
31
32ENHANCED MODE (Optional setup for better results):
33 🧠 Neural embeddings Install: sentence-transformers/all-MiniLM-L6-v2
34 🎯 Semantic understanding "database connection" = "data storage"
35 📊 Intent matching Superior relevance ranking
36 🔄 Easy switching manx embedding set onnx:model-name
37
38AI SYNTHESIS - Get comprehensive answers with citations (optional):
39 manx config --openai-api "sk-your-key" Enable OpenAI GPT models
40 manx config --anthropic-api "sk-ant-key" Enable Anthropic Claude models
41 manx snippet react hooks Search + AI explanation (if configured)
42
43LOCAL RAG - Search your own documents and code (optional):
44 manx index /path/to/docs Index your documentation
45 manx config --rag on Enable local document search
46 manx search "authentication" --rag Search indexed documents only
47
48QUICK START:
49 manx snippet react "state management" Works great with defaults
50 manx embedding download all-MiniLM-L6-v2 Optional: Better semantic search
51 manx config --openai-api "sk-your-key" Optional: AI synthesis
52
53Use 'manx <command> --help' for detailed options."#,
54 version = get_version_info(),
55 author,
56 arg_required_else_help = true
57)]
58pub struct Cli {
59 #[command(subcommand)]
60 pub command: Option<Commands>,
61
62 /// Show detailed debug information and API requests
63 #[arg(long, help_heading = "DEBUG OPTIONS")]
64 pub debug: bool,
65
66 /// Output JSON format (useful for scripts and automation)
67 #[arg(short = 'q', long, help_heading = "OUTPUT OPTIONS")]
68 pub quiet: bool,
69
70 /// Clear all cached documentation and start fresh
71 #[arg(long, help_heading = "CACHE OPTIONS")]
72 pub clear_cache: bool,
73
74 /// Enable automatic caching of all search results
75 #[arg(long, help_heading = "CACHE OPTIONS")]
76 pub auto_cache_on: bool,
77
78 /// Disable automatic caching (manual caching only)
79 #[arg(long, help_heading = "CACHE OPTIONS")]
80 pub auto_cache_off: bool,
81
82 /// Override API key for this session
83 #[arg(long, help_heading = "GLOBAL OPTIONS")]
84 pub api_key: Option<String>,
85
86 /// Override cache directory for this session
87 #[arg(long, help_heading = "GLOBAL OPTIONS")]
88 pub cache_dir: Option<PathBuf>,
89
90 /// Work offline using only cached results
91 #[arg(long, help_heading = "GLOBAL OPTIONS")]
92 pub offline: bool,
93}
94
95#[derive(Subcommand)]
96#[allow(clippy::large_enum_variant)]
97pub enum Commands {
98 /// 📚 Browse comprehensive documentation sections and guides
99 Doc {
100 /// Library name (examples: 'fastapi', 'react@18', 'django')
101 #[arg(value_name = "LIBRARY")]
102 library: String,
103 /// Topic to search for within documentation (optional - omit for general docs)
104 #[arg(value_name = "TOPIC", default_value = "")]
105 query: String,
106 /// Save documentation to file (auto-detects format)
107 #[arg(short = 'o', long, value_name = "FILE")]
108 output: Option<PathBuf>,
109 /// Limit number of sections shown (default: 10, use 0 for unlimited)
110 #[arg(short = 'l', long, value_name = "NUMBER")]
111 limit: Option<usize>,
112 /// Force retrieval-only mode (disable LLM synthesis even if API key configured)
113 #[arg(long)]
114 no_llm: bool,
115 /// Search locally indexed documents instead of Context7 API
116 #[arg(long)]
117 rag: bool,
118 },
119
120 /// 🔍 Search code snippets and examples with AI-powered understanding
121 ///
122 /// ENHANCED SEARCH:
123 /// • Searches official docs (Context7) + your indexed documents (RAG)
124 /// • Semantic understanding finds relevant content with different wording
125 /// • Quote prioritization: "useEffect cleanup" gets 10x higher relevance
126 /// • Optional AI synthesis provides comprehensive answers with citations
127 ///
128 /// SEMANTIC FEATURES:
129 /// • "memory leaks" finds: "memory cleanup", "performance issues", "leak prevention"
130 /// • "authentication" finds: "auth", "login", "security", "credentials"
131 /// • Version-specific: react@18, django@4.2
132 ///
133 /// AI SYNTHESIS:
134 /// • Configure: manx config --llm-api "sk-your-key"
135 /// • Get answers: manx snippet "react hooks best practices"
136 /// • Force retrieval: manx snippet react hooks --no-llm
137 ///
138 /// EXAMPLES:
139 /// manx snippet react "useEffect cleanup" # Semantic search with phrase priority
140 /// manx snippet "database pooling" --llm-api # Get AI answer with citations
141 /// manx snippet fastapi middleware --no-llm # Raw results only
142 /// manx snippet python "async functions" --rag # Search your indexed code files
143 Snippet {
144 /// Library name (examples: 'fastapi', 'react@18', 'vue@3')
145 #[arg(value_name = "LIBRARY")]
146 library: String,
147 /// Search query for specific code snippets
148 #[arg(value_name = "QUERY")]
149 query: Option<String>,
150 /// Export results to file (format auto-detected by extension: .md, .json)
151 #[arg(short = 'o', long, value_name = "FILE")]
152 output: Option<PathBuf>,
153 /// Work offline using only cached results (no network requests)
154 #[arg(long)]
155 offline: bool,
156 /// Save specific search results by number (e.g., --save 1,3,7)
157 #[arg(long, value_name = "NUMBERS")]
158 save: Option<String>,
159 /// Save all search results to file
160 #[arg(long)]
161 save_all: bool,
162 /// Export in JSON format instead of Markdown (use with --save or --save-all)
163 #[arg(long)]
164 json: bool,
165 /// Limit number of results shown (default: 10, use 0 for unlimited)
166 #[arg(short = 'l', long, value_name = "NUMBER")]
167 limit: Option<usize>,
168 /// Force retrieval-only mode (disable LLM synthesis even if API key configured)
169 #[arg(long)]
170 no_llm: bool,
171 /// Search locally indexed documents instead of Context7 API (requires: manx config --rag-enabled)
172 #[arg(long)]
173 rag: bool,
174 },
175
176 /// 🔍 Search official documentation across the web
177 ///
178 /// INTELLIGENT WEB SEARCH:
179 /// • Prioritizes official documentation sites (docs.python.org, reactjs.org, etc.)
180 /// • Uses semantic embeddings for relevance matching
181 /// • Falls back to trusted community sources with clear notification
182 /// • Optional LLM verification ensures result authenticity
183 ///
184 /// OFFICIAL-FIRST STRATEGY:
185 /// • Always searches official sources first (10x relevance boost)
186 /// • Expands to community sources only if insufficient official results
187 /// • Transparent fallback notifications: "⚠️ Expanded to community sources"
188 ///
189 /// EXAMPLES:
190 /// manx search "hydra configuration commands" # Auto-detects LLM availability
191 /// manx search "react hooks best practices" # Uses LLM if API key configured
192 /// manx search "python async await" --no-llm # Force embeddings-only mode
193 /// manx search "authentication" --rag # Search your indexed documents
194 Search {
195 /// Search query for official documentation
196 #[arg(value_name = "QUERY")]
197 query: String,
198 /// Disable LLM verification (use embeddings-only mode even if API key is configured)
199 #[arg(long)]
200 no_llm: bool,
201 /// Export results to file (format auto-detected by extension: .md, .json)
202 #[arg(short = 'o', long, value_name = "FILE")]
203 output: Option<PathBuf>,
204 /// Limit number of results shown (default: 8)
205 #[arg(short = 'l', long, value_name = "NUMBER")]
206 limit: Option<usize>,
207 /// Search locally indexed documents instead of web search (requires: manx config --rag-enabled)
208 #[arg(long)]
209 rag: bool,
210 },
211
212 /// 📥 Get specific item by ID (doc-3, section-5, etc.)
213 Get {
214 /// Item ID from previous search or doc command output
215 #[arg(value_name = "ITEM_ID")]
216 id: String,
217 /// Save retrieved item to file
218 #[arg(short = 'o', long, value_name = "FILE")]
219 output: Option<PathBuf>,
220 },
221
222 /// 🗂️ Manage local documentation cache
223 Cache {
224 #[command(subcommand)]
225 command: CacheCommands,
226 },
227
228 /// ⚙️ Configure Manx settings, API keys, and AI integration
229 Config {
230 /// Display current configuration settings
231 #[arg(long)]
232 show: bool,
233 /// Set Context7 API key (get one at context7.com)
234 #[arg(long, value_name = "KEY")]
235 api_key: Option<String>,
236 /// Set custom cache directory path
237 #[arg(long, value_name = "PATH")]
238 cache_dir: Option<PathBuf>,
239 /// Enable/disable automatic caching (values: on, off)
240 #[arg(long, value_name = "MODE")]
241 auto_cache: Option<String>,
242 /// Set cache expiration time in hours (default: 24)
243 #[arg(long, value_name = "HOURS")]
244 cache_ttl: Option<u64>,
245 /// Set maximum cache size in MB (default: 100)
246 #[arg(long, value_name = "SIZE")]
247 max_cache_size: Option<u64>,
248 /// Set OpenAI API key for GPT models
249 #[arg(long, value_name = "API_KEY")]
250 openai_api: Option<String>,
251 /// Set Anthropic API key for Claude models
252 #[arg(long, value_name = "API_KEY")]
253 anthropic_api: Option<String>,
254 /// Set Groq API key for fast inference
255 #[arg(long, value_name = "API_KEY")]
256 groq_api: Option<String>,
257 /// Set OpenRouter API key for multi-model access
258 #[arg(long, value_name = "API_KEY")]
259 openrouter_api: Option<String>,
260 /// Set HuggingFace API key for open-source models
261 #[arg(long, value_name = "API_KEY")]
262 huggingface_api: Option<String>,
263 /// Set custom endpoint URL for self-hosted models
264 #[arg(long, value_name = "URL")]
265 custom_endpoint: Option<String>,
266 /// Set preferred LLM provider (openai, anthropic, groq, openrouter, huggingface, custom, auto)
267 #[arg(long, value_name = "PROVIDER")]
268 llm_provider: Option<String>,
269 /// Set specific model name (overrides provider defaults)
270 #[arg(long, value_name = "MODEL")]
271 llm_model: Option<String>,
272 /// Legacy option - Set LLM API key (deprecated, use provider-specific options)
273 #[arg(long, value_name = "API_KEY")]
274 llm_api: Option<String>,
275 /// Enable/disable local RAG system (values: on, off)
276 #[arg(long, value_name = "MODE")]
277 rag: Option<String>,
278 /// Add custom official documentation domain (format: domain.com)
279 #[arg(long, value_name = "DOMAIN")]
280 add_official_domain: Option<String>,
281 /// Set embedding provider for RAG system (hash, onnx:model, ollama:model, openai:model, huggingface:model, custom:url)
282 #[arg(long, value_name = "PROVIDER")]
283 embedding_provider: Option<String>,
284 /// Set embedding API key for API-based providers
285 #[arg(long, value_name = "API_KEY")]
286 embedding_api_key: Option<String>,
287 /// Set embedding model path for local models
288 #[arg(long, value_name = "PATH")]
289 embedding_model_path: Option<PathBuf>,
290 /// Set embedding dimension (default: 384)
291 #[arg(long, value_name = "DIMENSION")]
292 embedding_dimension: Option<usize>,
293 },
294
295 /// 📁 Index local documents or web URLs for RAG search
296 ///
297 /// INDEXING SOURCES:
298 /// • Local files: manx index ~/docs/api.md
299 /// • Directories: manx index ~/documentation/
300 /// • Web URLs: manx index https://docs.rust-lang.org/book/ch01-01-installation.html
301 ///
302 /// SUPPORTED FORMATS:
303 /// • Documents: .md, .txt, .docx, .pdf (with security validation)
304 /// • Web content: HTML pages (auto text extraction)
305 ///
306 /// SECURITY FEATURES:
307 /// • PDF processing disabled by default (configure to enable)
308 /// • URL validation (HTTP/HTTPS only)
309 /// • Content sanitization and size limits
310 ///
311 /// EXAMPLES:
312 /// manx index ~/my-docs/ # Index directory
313 /// manx index https://docs.python.org --crawl # Deep crawl documentation site
314 /// manx index https://fastapi.tiangolo.com --crawl --max-depth 2 # Limited depth crawl
315 /// manx index api.pdf --alias "API Reference" # Index with custom alias
316 Index {
317 /// Path to document/directory or URL to index
318 #[arg(value_name = "PATH_OR_URL")]
319 path: String,
320 /// Optional alias for the indexed source
321 #[arg(long, value_name = "ALIAS")]
322 id: Option<String>,
323 /// Enable deep crawling for URLs (follows links to discover more pages)
324 #[arg(long)]
325 crawl: bool,
326 /// Maximum crawl depth for deep crawling (default: 3)
327 #[arg(long, value_name = "DEPTH")]
328 max_depth: Option<u32>,
329 /// Maximum number of pages to crawl (default: no limit)
330 #[arg(long, value_name = "PAGES")]
331 max_pages: Option<u32>,
332 },
333
334 /// 📂 Manage indexed document sources
335 Sources {
336 #[command(subcommand)]
337 command: SourceCommands,
338 },
339
340 /// 🔗 Open a specific documentation section by ID
341 Open {
342 /// Section ID from previous doc command output
343 #[arg(value_name = "SECTION_ID")]
344 id: String,
345 /// Save opened section to file
346 #[arg(short = 'o', long, value_name = "FILE")]
347 output: Option<PathBuf>,
348 },
349
350 /// 🔄 Update Manx to the latest version from GitHub
351 Update {
352 /// Check for updates without installing
353 #[arg(long)]
354 check: bool,
355 /// Force update even if already on latest version
356 #[arg(long)]
357 force: bool,
358 },
359
360 /// 🧠 Manage embedding models and providers for semantic search
361 ///
362 /// EMBEDDING PROVIDERS:
363 /// • hash: Hash-based embeddings (default, fast, lightweight)
364 /// • onnx:model: Local ONNX models (requires download)
365 /// • ollama:model: Ollama API (requires Ollama server)
366 /// • openai:model: OpenAI embeddings API (requires API key)
367 /// • huggingface:model: HuggingFace embeddings API (requires API key)
368 /// • custom:url: Custom endpoint API
369 ///
370 /// EXAMPLES:
371 /// manx embedding status # Show current provider and models
372 /// manx embedding set hash # Use hash-based (default)
373 /// manx embedding set onnx:all-MiniLM-L6-v2 # Use local ONNX model
374 /// manx embedding set ollama:nomic-embed-text # Use Ollama model
375 /// manx embedding download all-MiniLM-L6-v2 # Download ONNX model
376 /// manx embedding test "sample query" # Test current embedding setup
377 Embedding {
378 #[command(subcommand)]
379 command: EmbeddingCommands,
380 },
381
382 /// 🚀 Interactive setup wizard for configuring manx
383 ///
384 /// WHAT IT DOES:
385 /// • Guides you through initial configuration
386 /// • Sets up Context7 API for official docs
387 /// • Configures embedding models for semantic search
388 /// • Optional LLM provider setup for AI synthesis
389 ///
390 /// PERFECT FOR:
391 /// • First-time users
392 /// • Reconfiguring after updates
393 /// • Testing different providers
394 ///
395 /// EXAMPLE:
396 /// manx init # Start interactive setup wizard
397 Init,
398}
399
400#[derive(Subcommand)]
401pub enum CacheCommands {
402 /// Remove all cached documentation and free up disk space
403 Clear,
404 /// Display cache size, file count, and storage statistics
405 Stats,
406 /// Show all currently cached libraries and their sizes
407 List,
408}
409
410#[derive(Subcommand)]
411pub enum SourceCommands {
412 /// List all indexed document sources
413 List,
414 /// Add a document source to the index
415 Add {
416 /// Path to document or directory
417 path: PathBuf,
418 /// Optional alias for the source
419 #[arg(long)]
420 id: Option<String>,
421 },
422 /// Clear all indexed documents
423 Clear,
424}
425
426#[derive(Subcommand)]
427pub enum EmbeddingCommands {
428 /// Show current embedding provider status and configuration
429 Status,
430 /// Set embedding provider (hash, onnx:model, ollama:model, openai:model, huggingface:model, custom:url)
431 Set {
432 /// Provider specification
433 #[arg(value_name = "PROVIDER")]
434 provider: String,
435 /// API key for API-based providers
436 #[arg(long, value_name = "API_KEY")]
437 api_key: Option<String>,
438 /// Custom endpoint URL (for custom provider)
439 #[arg(long, value_name = "URL")]
440 endpoint: Option<String>,
441 /// Embedding dimension (default: 384)
442 #[arg(long, value_name = "DIMENSION")]
443 dimension: Option<usize>,
444 },
445 /// Download and install a local ONNX model
446 Download {
447 /// Model name to download (e.g., 'all-MiniLM-L6-v2')
448 #[arg(value_name = "MODEL_NAME")]
449 model: String,
450 /// Force redownload if model already exists
451 #[arg(long)]
452 force: bool,
453 },
454 /// List available models for download or installed models
455 List {
456 /// List available models for download instead of installed models
457 #[arg(long)]
458 available: bool,
459 },
460 /// Test current embedding setup with a sample query
461 Test {
462 /// Query text to test embeddings with
463 #[arg(value_name = "QUERY")]
464 query: String,
465 /// Show detailed embedding vector information
466 #[arg(long)]
467 verbose: bool,
468 },
469 /// Remove downloaded local models
470 Remove {
471 /// Model name to remove
472 #[arg(value_name = "MODEL_NAME")]
473 model: String,
474 },
475}
476
477impl Cli {
478 pub fn parse_args() -> Self {
479 Cli::parse()
480 }
481}
482
483fn get_version_info() -> &'static str {
484 concat!(
485 "\n",
486 "__| |__________________________________________________________________________| |__\n",
487 "__ __________________________________________________________________________ __\n",
488 " | | | | \n",
489 " | | ███ ██████ ██████ █████████ ██████ █████ █████ █████| | \n",
490 " | | ░░░██ ░░██████ ██████ ███░░░░░███ ░░██████ ░░███ ░░███ ░░███ | | \n",
491 " | | ██ ░░██ ░███░█████░███ ░███ ░███ ░███░███ ░███ ░░███ ███ | | \n",
492 " | |░░ ░░███ ░███░░███ ░███ ░███████████ ░███░░███░███ ░░█████ | | \n",
493 " | | ██░ ░███ ░░░ ░███ ░███░░░░░███ ░███ ░░██████ ███░███ | | \n",
494 " | | ██ ░███ ░███ ░███ ░███ ░███ ░░█████ ███ ░░███ | | \n",
495 " | | ██ ███ █████ █████ █████ █████ █████ ░░█████ █████ █████| | \n",
496 " | |░░ ░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ | | \n",
497 "__| |__________________________________________________________________________| |__\n",
498 "__ __________________________________________________________________________ __\n",
499 " | | | | \n",
500 "\n",
501 " v",
502 env!("CARGO_PKG_VERSION"),
503 " • blazing-fast docs finder\n"
504 )
505}