exa-ai 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 (41) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +562 -0
  4. data/exe/exa-ai +95 -0
  5. data/exe/exa-ai-answer +131 -0
  6. data/exe/exa-ai-context +104 -0
  7. data/exe/exa-ai-get-contents +114 -0
  8. data/exe/exa-ai-research-get +110 -0
  9. data/exe/exa-ai-research-list +95 -0
  10. data/exe/exa-ai-research-start +175 -0
  11. data/exe/exa-ai-search +134 -0
  12. data/lib/exa/cli/base.rb +51 -0
  13. data/lib/exa/cli/error_handler.rb +98 -0
  14. data/lib/exa/cli/formatters/answer_formatter.rb +63 -0
  15. data/lib/exa/cli/formatters/contents_formatter.rb +50 -0
  16. data/lib/exa/cli/formatters/context_formatter.rb +58 -0
  17. data/lib/exa/cli/formatters/research_formatter.rb +120 -0
  18. data/lib/exa/cli/formatters/search_formatter.rb +44 -0
  19. data/lib/exa/cli/polling.rb +46 -0
  20. data/lib/exa/client.rb +132 -0
  21. data/lib/exa/connection.rb +32 -0
  22. data/lib/exa/error.rb +31 -0
  23. data/lib/exa/middleware/raise_error.rb +55 -0
  24. data/lib/exa/resources/answer.rb +20 -0
  25. data/lib/exa/resources/contents_result.rb +29 -0
  26. data/lib/exa/resources/context_result.rb +37 -0
  27. data/lib/exa/resources/find_similar_result.rb +28 -0
  28. data/lib/exa/resources/research_list.rb +18 -0
  29. data/lib/exa/resources/research_task.rb +39 -0
  30. data/lib/exa/resources/search_result.rb +30 -0
  31. data/lib/exa/services/answer.rb +23 -0
  32. data/lib/exa/services/context.rb +27 -0
  33. data/lib/exa/services/find_similar.rb +26 -0
  34. data/lib/exa/services/get_contents.rb +25 -0
  35. data/lib/exa/services/research_get.rb +30 -0
  36. data/lib/exa/services/research_list.rb +37 -0
  37. data/lib/exa/services/research_start.rb +26 -0
  38. data/lib/exa/services/search.rb +26 -0
  39. data/lib/exa/version.rb +5 -0
  40. data/lib/exa.rb +54 -0
  41. metadata +174 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ca5a6bcb0b981d51fcc93e1dc9f1e7037d1d6346e082826e1b1c003d586cce45
4
+ data.tar.gz: e598e0c91c2815a5abea958adb9f5112def140b78f13ba29872d07009c4056cc
5
+ SHA512:
6
+ metadata.gz: 4b648ebe500a28dbb8d31aa936fd0d30a09ff94c372a3f3caec463d648993ae26473fc9724aea0157395b8e78ac9a25fc3f2752c8d6fd1cc68745bb8758b0ca7
7
+ data.tar.gz: 512d79543838b4b82ba263586c9cd029f8e19f350ff9c744b85dcb5b8f4e62236b442b70d07316721bc844f6ca9ac79bac6c1b42d57d5f0ef1214c5be407818e
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Benjamin Jackson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,562 @@
1
+ # exa-ruby
2
+
3
+ Ruby client for the Exa.ai API with comprehensive command-line interface.
4
+
5
+ **Status**: Phase 9 CLI implementation complete. All commands working with full option support.
6
+
7
+ ## Installation
8
+
9
+ Add to your Gemfile:
10
+
11
+ ```ruby
12
+ gem 'exa-ai'
13
+ ```
14
+
15
+ Then run:
16
+
17
+ ```bash
18
+ bundle install
19
+ ```
20
+
21
+ Or install directly:
22
+
23
+ ```bash
24
+ gem install exa-ai
25
+ ```
26
+
27
+ ## Configuration
28
+
29
+ ### Setting Your API Key
30
+
31
+ Get your API key from [dashboard.exa.ai](https://dashboard.exa.ai).
32
+
33
+ **Option 1: Environment Variable (recommended)**
34
+
35
+ ```bash
36
+ export EXA_API_KEY="your-api-key-here"
37
+ ```
38
+
39
+ **Option 2: Ruby Code**
40
+
41
+ ```ruby
42
+ require 'exa'
43
+
44
+ Exa.configure do |config|
45
+ config.api_key = "your-api-key-here"
46
+ end
47
+
48
+ # Or pass directly to client
49
+ client = Exa::Client.new(api_key: "your-api-key-here")
50
+ ```
51
+
52
+ **Option 3: CLI Flag**
53
+
54
+ ```bash
55
+ exa-ai search "query" --api-key YOUR_API_KEY
56
+ ```
57
+
58
+ ## Ruby API Usage
59
+
60
+ ### Quick Start
61
+
62
+ ```ruby
63
+ require 'exa'
64
+
65
+ Exa.configure do |config|
66
+ config.api_key = ENV['EXA_API_KEY']
67
+ end
68
+
69
+ client = Exa::Client.new
70
+
71
+ # Search the web
72
+ results = client.search("ruby programming")
73
+ puts results.results.first["title"]
74
+
75
+ # Get an answer to a question
76
+ answer = client.answer("What are the latest trends in AI?")
77
+ puts answer.answer
78
+ puts answer.citations
79
+
80
+ # Get code context
81
+ code = client.context("React hooks")
82
+ puts code.response
83
+
84
+ # Retrieve page contents
85
+ contents = client.get_contents(["https://example.com"])
86
+ puts contents.results.first["text"]
87
+ ```
88
+
89
+ ### Search
90
+
91
+ ```ruby
92
+ client = Exa::Client.new(api_key: "your-key")
93
+
94
+ # Basic search
95
+ results = client.search("machine learning")
96
+
97
+ # With options
98
+ results = client.search("AI",
99
+ num_results: 10,
100
+ type: "neural",
101
+ include_domains: ["arxiv.org", "github.com"]
102
+ )
103
+
104
+ # Access results
105
+ results.results.each do |item|
106
+ puts item["title"]
107
+ puts item["url"]
108
+ puts item["score"]
109
+ end
110
+ ```
111
+
112
+ ### Answer
113
+
114
+ ```ruby
115
+ client = Exa::Client.new(api_key: "your-key")
116
+
117
+ # Get an answer to a question
118
+ answer = client.answer("What are the best practices for API design?")
119
+
120
+ puts answer.answer # The generated answer
121
+ puts answer.citations # Array of source citations
122
+ puts answer.cost_dollars # API cost
123
+
124
+ # With text content from sources
125
+ answer = client.answer("Latest AI breakthroughs", text: true)
126
+ puts answer.answer
127
+ answer.citations.each do |citation|
128
+ puts "#{citation["title"]} (#{citation['url']})"
129
+ end
130
+ ```
131
+
132
+ ### Context (Code Search)
133
+
134
+ ```ruby
135
+ code = client.context("authentication in Rails")
136
+
137
+ puts code.response # The code context
138
+ puts code.results_count # Number of results
139
+ puts code.cost_dollars # API cost
140
+ ```
141
+
142
+ ### Get Contents
143
+
144
+ ```ruby
145
+ contents = client.get_contents([
146
+ "https://example.com/page1",
147
+ "https://example.com/page2"
148
+ ])
149
+
150
+ contents.results.each do |content|
151
+ puts content["url"]
152
+ puts content["title"]
153
+ puts content["text"]
154
+ end
155
+ ```
156
+
157
+ ### Research (Async Tasks)
158
+
159
+ ```ruby
160
+ # Start a research task
161
+ task = client.research_start(
162
+ instructions: "Analyze recent AI breakthroughs",
163
+ model: "gpt-4"
164
+ )
165
+ puts task.research_id # Save for later polling
166
+
167
+ # Check status
168
+ status = client.research_get(task.research_id)
169
+ if status.completed?
170
+ puts status.output
171
+ end
172
+
173
+ # List all tasks
174
+ list = client.research_list(limit: 20)
175
+ list.data.each do |task|
176
+ puts "#{task.research_id}: #{task.status}"
177
+ end
178
+ ```
179
+
180
+ ## CLI Usage
181
+
182
+ ### Main Commands
183
+
184
+ ```bash
185
+ exa-ai <command> [options]
186
+
187
+ Commands:
188
+ search Search the web
189
+ answer Generate answers to questions
190
+ context Get code context from repositories
191
+ get-contents Retrieve page contents
192
+ research-start Start a research task
193
+ research-get Get research task status
194
+ research-list List research tasks
195
+ ```
196
+
197
+ ### Search Command
198
+
199
+ Search the web using Exa's neural search:
200
+
201
+ ```bash
202
+ # Basic search
203
+ exa-ai search "ruby programming"
204
+
205
+ # With options
206
+ exa-ai search "machine learning" --num-results 10 --type keyword
207
+
208
+ # Filter by domains
209
+ exa-ai search "tutorials" \
210
+ --include-domains "github.com,dev.to" \
211
+ --exclude-domains "outdated-site.com"
212
+
213
+ # Pretty output
214
+ exa-ai search "AI" --output-format pretty
215
+ ```
216
+
217
+ **Options:**
218
+ - `QUERY` - Search query (required)
219
+ - `--num-results N` - Number of results (default: 10)
220
+ - `--type TYPE` - Search type: keyword, neural, or auto (default: auto)
221
+ - `--include-domains DOMAINS` - Comma-separated domains to include
222
+ - `--exclude-domains DOMAINS` - Comma-separated domains to exclude
223
+ - `--use-autoprompt` - Use Exa's autoprompt feature
224
+ - `--output-format FORMAT` - json or pretty (default: json)
225
+ - `--api-key KEY` - API key (or set EXA_API_KEY env var)
226
+
227
+ ### Answer Command
228
+
229
+ Generate comprehensive answers to questions using Exa's answer generation feature:
230
+
231
+ ```bash
232
+ # Basic question
233
+ exa-ai answer "What is the capital of France?"
234
+
235
+ # Get answer with source citations
236
+ exa-ai answer "Latest developments in quantum computing"
237
+
238
+ # Include full text from sources
239
+ exa-ai answer "Ruby on Rails best practices" --text
240
+
241
+ # Pretty formatted output
242
+ exa-ai answer "How do I learn machine learning?" --output-format pretty
243
+ ```
244
+
245
+ **Options:**
246
+ - `QUERY` - Question to answer (required)
247
+ - `--text` - Include full text content from source pages
248
+ - `--output-format FORMAT` - json or pretty (default: json)
249
+ - `--api-key KEY` - API key (or set EXA_API_KEY env var)
250
+
251
+ **Response fields:**
252
+ - `answer` - The generated answer to your question
253
+ - `citations` - Array of source citations with URLs
254
+ - `cost_dollars` - Cost of the API request
255
+
256
+ ### Context Command (Code Search)
257
+
258
+ Find code snippets and context from open-source repositories:
259
+
260
+ ```bash
261
+ # Basic query
262
+ exa-ai context "authentication with JWT"
263
+
264
+ # With custom token allocation
265
+ exa-ai context "React hooks" --tokens-num 5000
266
+
267
+ # Text output
268
+ exa-ai context "async/await patterns" --output-format text
269
+ ```
270
+
271
+ **Options:**
272
+ - `QUERY` - Search query (required)
273
+ - `--tokens-num NUM` - Token allocation, integer or "dynamic" (default: dynamic)
274
+ - `--output-format FORMAT` - json or text (default: json)
275
+ - `--api-key KEY` - API key
276
+
277
+ ### Get-Contents Command
278
+
279
+ Retrieve the full text content from web pages:
280
+
281
+ ```bash
282
+ # Single page
283
+ exa-ai get-contents "https://example.com/article"
284
+
285
+ # Multiple pages (comma-separated)
286
+ exa-ai get-contents "https://site1.com,https://site2.com"
287
+
288
+ # With options
289
+ exa-ai get-contents "id1,id2,id3" \
290
+ --text \
291
+ --highlights \
292
+ --output-format pretty
293
+ ```
294
+
295
+ **Options:**
296
+ - `IDS` - Page IDs or URLs (required, comma-separated)
297
+ - `--text` - Include full text content
298
+ - `--highlights` - Include highlighted sections
299
+ - `--summary` - Include summary
300
+ - `--output-format FORMAT` - json or pretty (default: json)
301
+ - `--api-key KEY` - API key
302
+
303
+ ### Research Commands
304
+
305
+ Start and manage long-running research tasks:
306
+
307
+ #### research-start
308
+
309
+ ```bash
310
+ # Start a task
311
+ exa-ai research-start --instructions "Find Ruby performance tips"
312
+
313
+ # Start and wait for completion
314
+ exa-ai research-start \
315
+ --instructions "Analyze AI safety papers" \
316
+ --model gpt-4 \
317
+ --wait
318
+
319
+ # With output schema
320
+ exa-ai research-start \
321
+ --instructions "Extract key metrics" \
322
+ --output-schema '{"format":"json","fields":["metric","value"]}'
323
+ ```
324
+
325
+ **Options:**
326
+ - `--instructions TEXT` - Research instructions (required)
327
+ - `--model MODEL` - Model to use (e.g., gpt-4)
328
+ - `--output-schema SCHEMA` - JSON schema for structured output
329
+ - `--wait` - Wait for task to complete (with polling)
330
+ - `--events` - Show event log during polling
331
+ - `--output-format FORMAT` - json or pretty (default: json)
332
+ - `--api-key KEY` - API key
333
+
334
+ #### research-get
335
+
336
+ ```bash
337
+ # Check task status
338
+ exa-ai research-get abc-123
339
+
340
+ # With events
341
+ exa-ai research-get abc-123 --events
342
+
343
+ # Pretty output
344
+ exa-ai research-get abc-123 --output-format pretty
345
+ ```
346
+
347
+ **Options:**
348
+ - `RESEARCH_ID` - Task ID (required)
349
+ - `--events` - Include event log
350
+ - `--stream` - Stream results (premium feature)
351
+ - `--output-format FORMAT` - json or pretty (default: json)
352
+ - `--api-key KEY` - API key
353
+
354
+ #### research-list
355
+
356
+ ```bash
357
+ # List all tasks
358
+ exa-ai research-list
359
+
360
+ # With pagination
361
+ exa-ai research-list --limit 20
362
+
363
+ # Next page
364
+ exa-ai research-list --cursor "next_page_cursor"
365
+
366
+ # Pretty table format
367
+ exa-ai research-list --output-format pretty
368
+ ```
369
+
370
+ **Options:**
371
+ - `--cursor CURSOR` - Pagination cursor
372
+ - `--limit N` - Results per page (default: 10)
373
+ - `--output-format FORMAT` - json or pretty (default: json)
374
+ - `--api-key KEY` - API key
375
+
376
+ ### Global Options
377
+
378
+ All commands support:
379
+ - `--api-key KEY` - Override API key
380
+ - `--output-format FORMAT` - json, pretty, or text (varies by command)
381
+ - `--help, -h` - Show command help
382
+ - `exa-ai --version` - Show version
383
+ - `exa-ai --help` - Show available commands
384
+
385
+ ### Output Formats
386
+
387
+ **JSON (default)**
388
+ ```bash
389
+ exa-ai search "ruby" --output-format json
390
+ # Returns formatted JSON object
391
+ ```
392
+
393
+ **Pretty**
394
+ ```bash
395
+ exa-ai search "ruby" --output-format pretty
396
+ # Returns human-readable format with titles, URLs, scores
397
+ ```
398
+
399
+ **Text**
400
+ ```bash
401
+ exa-ai context "React" --output-format text
402
+ # Returns plain text output
403
+ ```
404
+
405
+ ## Error Handling
406
+
407
+ The CLI provides helpful error messages:
408
+
409
+ ```bash
410
+ # Missing API key
411
+ $ exa search "test"
412
+ ❌ Configuration Error
413
+
414
+ Missing API key. Set EXA_API_KEY or use --api-key
415
+
416
+ Solutions:
417
+ 1. Set the EXA_API_KEY environment variable:
418
+ export EXA_API_KEY='your-api-key'
419
+ ...
420
+
421
+ # Invalid credentials
422
+ $ exa search "test" --api-key bad-key
423
+ ❌ Authentication Error
424
+
425
+ Your API key is invalid or has expired.
426
+ ...
427
+
428
+ # Rate limited
429
+ $ exa search "test" # After many requests
430
+ ❌ Request Error
431
+
432
+ You've exceeded the rate limit. Please wait before trying again.
433
+ ```
434
+
435
+ ### Ruby API Error Handling
436
+
437
+ ```ruby
438
+ client = Exa::Client.new(api_key: "test")
439
+
440
+ begin
441
+ results = client.search("test")
442
+ rescue Exa::Unauthorized => e
443
+ puts "Invalid API key: #{e.message}"
444
+ rescue Exa::TooManyRequests => e
445
+ puts "Rate limited, please wait"
446
+ rescue Exa::ServerError => e
447
+ puts "API error: #{e.message}"
448
+ end
449
+ ```
450
+
451
+ ## Examples
452
+
453
+ ### CLI Examples
454
+
455
+ ```bash
456
+ # Find Ruby tutorials
457
+ exa search "Ruby best practices" --num-results 5
458
+
459
+ # Get an answer to a question
460
+ exa answer "What is machine learning?"
461
+
462
+ # Get code examples for async/await
463
+ exa context "async/await error handling"
464
+
465
+ # Research AI trends
466
+ exa research-start --instructions "What are latest AI trends?" --wait
467
+
468
+ # Retrieve and analyze multiple pages
469
+ exa get-contents "url1,url2,url3" --text --output-format pretty
470
+ ```
471
+
472
+ ### Ruby API Examples
473
+
474
+ ```ruby
475
+ require 'exa'
476
+
477
+ Exa.configure do |config|
478
+ config.api_key = ENV['EXA_API_KEY']
479
+ end
480
+
481
+ client = Exa::Client.new
482
+
483
+ # Search with filtering
484
+ results = client.search("kubernetes tutorial",
485
+ num_results: 20,
486
+ type: "neural",
487
+ include_domains: ["kubernetes.io", "github.com"],
488
+ use_autoprompt: true
489
+ )
490
+
491
+ results.results.each do |item|
492
+ puts "#{item['title']} (#{item['url']})"
493
+ end
494
+
495
+ # Get code context
496
+ code_result = client.context("Docker best practices", tokens_num: 5000)
497
+ puts code_result.response
498
+
499
+ # Start async research
500
+ task = client.research_start(
501
+ instructions: "Summarize recent ML papers",
502
+ model: "gpt-4"
503
+ )
504
+ puts "Task started: #{task.research_id}"
505
+
506
+ # Poll for results
507
+ loop do
508
+ status = client.research_get(task.research_id)
509
+ break if status.completed? || status.failed?
510
+ sleep 5
511
+ end
512
+
513
+ if status.completed?
514
+ puts status.output
515
+ end
516
+ ```
517
+
518
+ ## Development
519
+
520
+ ### Running Tests
521
+
522
+ ```bash
523
+ # Run all tests
524
+ bundle exec rake test
525
+
526
+ # Run specific test file
527
+ bundle exec ruby test/cli/search_test.rb
528
+
529
+ # Run with verbose output
530
+ bundle exec rake test TESTOPTS="-v"
531
+ ```
532
+
533
+ ### Building the Gem
534
+
535
+ ```bash
536
+ bundle exec rake build
537
+ bundle exec rake install
538
+ ```
539
+
540
+ ## Documentation
541
+
542
+ - [Exa API Documentation](https://docs.exa.ai)
543
+ - [API Reference](https://docs.exa.ai/reference)
544
+ - [Status Page](https://status.exa.ai)
545
+
546
+ ## Support
547
+
548
+ - **Documentation**: https://docs.exa.ai
549
+ - **API Key**: https://dashboard.exa.ai
550
+ - **Status**: https://status.exa.ai
551
+
552
+ ## License
553
+
554
+ MIT License - See LICENSE file for details
555
+
556
+ ## Changelog
557
+
558
+ See [CHANGELOG.md](CHANGELOG.md) for version history and updates.
559
+
560
+ ---
561
+
562
+ **Built with Exa.ai** - Search and discovery API for the web
data/exe/exa-ai ADDED
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "exa"
6
+ require "exa/cli/base"
7
+
8
+ # Global CLI interface for Exa API
9
+
10
+ module ExaCLI
11
+ AVAILABLE_COMMANDS = {
12
+ "search" => "Search the web",
13
+ "answer" => "Generate an answer to a question",
14
+ "context" => "Get code context from repositories",
15
+ "get-contents" => "Retrieve page contents",
16
+ "research-start" => "Start a research task",
17
+ "research-get" => "Get research task status",
18
+ "research-list" => "List research tasks"
19
+ }.freeze
20
+
21
+ def self.run
22
+ case ARGV[0]
23
+ when "--version"
24
+ puts Exa::VERSION
25
+ exit 0
26
+ when "--help", "help", "-h", nil
27
+ print_help
28
+ exit 0
29
+ when "--api-key"
30
+ puts "Use --api-key flag with specific commands"
31
+ exit 1
32
+ when "search"
33
+ exec File.expand_path("../exa-ai-search", __FILE__), *ARGV[1..]
34
+ when "answer"
35
+ exec File.expand_path("../exa-ai-answer", __FILE__), *ARGV[1..]
36
+ when "context"
37
+ exec File.expand_path("../exa-ai-context", __FILE__), *ARGV[1..]
38
+ when "get-contents"
39
+ exec File.expand_path("../exa-ai-get-contents", __FILE__), *ARGV[1..]
40
+ when "research-start"
41
+ exec File.expand_path("../exa-ai-research-start", __FILE__), *ARGV[1..]
42
+ when "research-get"
43
+ exec File.expand_path("../exa-ai-research-get", __FILE__), *ARGV[1..]
44
+ when "research-list"
45
+ exec File.expand_path("../exa-ai-research-list", __FILE__), *ARGV[1..]
46
+ else
47
+ print_error_for_command(ARGV[0])
48
+ exit 1
49
+ end
50
+ end
51
+
52
+ def self.print_help
53
+ puts "Exa CLI v#{Exa::VERSION}"
54
+ puts ""
55
+ puts "Usage: exa-ai <command> [options]"
56
+ puts ""
57
+ puts "Commands:"
58
+ AVAILABLE_COMMANDS.each do |cmd, desc|
59
+ puts " #{cmd.ljust(20)} #{desc}"
60
+ end
61
+ puts ""
62
+ puts "Global Options:"
63
+ puts " --help, -h Show this help message"
64
+ puts " --version Show version number"
65
+ puts ""
66
+ puts "Examples:"
67
+ puts " exa-ai search 'ruby programming'"
68
+ puts " exa-ai context 'React hooks' --tokens-num 5000"
69
+ puts " exa-ai research-start --instructions 'Find AI papers'"
70
+ end
71
+
72
+ def self.print_error_for_command(cmd)
73
+ return print_help if cmd.nil?
74
+
75
+ puts "Error: Unknown command '#{cmd}'"
76
+ puts ""
77
+
78
+ # Try to suggest similar commands
79
+ suggestion = suggest_command(cmd)
80
+ puts "Did you mean: #{suggestion}?" if suggestion
81
+
82
+ puts ""
83
+ puts "Run 'exa-ai --help' for usage information."
84
+ end
85
+
86
+ def self.suggest_command(input)
87
+ # Simple suggestion based on string similarity
88
+ # If first char matches and length is similar, suggest it
89
+ input_start = input[0]
90
+ candidates = AVAILABLE_COMMANDS.keys.select { |c| c[0] == input_start }
91
+ candidates.first
92
+ end
93
+ end
94
+
95
+ ExaCLI.run