aia 0.9.11 → 0.9.12

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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/.version +1 -1
  3. data/CHANGELOG.md +66 -2
  4. data/README.md +133 -4
  5. data/docs/advanced-prompting.md +721 -0
  6. data/docs/cli-reference.md +582 -0
  7. data/docs/configuration.md +347 -0
  8. data/docs/contributing.md +332 -0
  9. data/docs/directives-reference.md +490 -0
  10. data/docs/examples/index.md +277 -0
  11. data/docs/examples/mcp/index.md +479 -0
  12. data/docs/examples/prompts/analysis/index.md +78 -0
  13. data/docs/examples/prompts/automation/index.md +108 -0
  14. data/docs/examples/prompts/development/index.md +125 -0
  15. data/docs/examples/prompts/index.md +333 -0
  16. data/docs/examples/prompts/learning/index.md +127 -0
  17. data/docs/examples/prompts/writing/index.md +62 -0
  18. data/docs/examples/tools/index.md +292 -0
  19. data/docs/faq.md +414 -0
  20. data/docs/guides/available-models.md +366 -0
  21. data/docs/guides/basic-usage.md +477 -0
  22. data/docs/guides/chat.md +474 -0
  23. data/docs/guides/executable-prompts.md +417 -0
  24. data/docs/guides/first-prompt.md +454 -0
  25. data/docs/guides/getting-started.md +455 -0
  26. data/docs/guides/image-generation.md +507 -0
  27. data/docs/guides/index.md +46 -0
  28. data/docs/guides/models.md +507 -0
  29. data/docs/guides/tools.md +856 -0
  30. data/docs/index.md +173 -0
  31. data/docs/installation.md +238 -0
  32. data/docs/mcp-integration.md +612 -0
  33. data/docs/prompt_management.md +579 -0
  34. data/docs/security.md +629 -0
  35. data/docs/tools-and-mcp-examples.md +1186 -0
  36. data/docs/workflows-and-pipelines.md +563 -0
  37. data/examples/tools/mcp/github_mcp_server.json +11 -0
  38. data/examples/tools/mcp/imcp.json +7 -0
  39. data/lib/aia/chat_processor_service.rb +19 -3
  40. data/lib/aia/config/base.rb +224 -0
  41. data/lib/aia/config/cli_parser.rb +409 -0
  42. data/lib/aia/config/defaults.rb +88 -0
  43. data/lib/aia/config/file_loader.rb +131 -0
  44. data/lib/aia/config/validator.rb +184 -0
  45. data/lib/aia/config.rb +10 -860
  46. data/lib/aia/directive_processor.rb +27 -372
  47. data/lib/aia/directives/configuration.rb +114 -0
  48. data/lib/aia/directives/execution.rb +37 -0
  49. data/lib/aia/directives/models.rb +178 -0
  50. data/lib/aia/directives/registry.rb +120 -0
  51. data/lib/aia/directives/utility.rb +70 -0
  52. data/lib/aia/directives/web_and_file.rb +71 -0
  53. data/lib/aia/prompt_handler.rb +23 -3
  54. data/lib/aia/ruby_llm_adapter.rb +307 -128
  55. data/lib/aia/session.rb +27 -14
  56. data/lib/aia/utility.rb +12 -8
  57. data/lib/aia.rb +11 -2
  58. data/lib/extensions/ruby_llm/.irbrc +56 -0
  59. data/mkdocs.yml +165 -0
  60. metadata +77 -20
  61. /data/{images → docs/assets/images}/aia.png +0 -0
@@ -0,0 +1,856 @@
1
+ # Tools Integration
2
+
3
+ AIA's tools system extends AI capabilities with custom Ruby functions, enabling AI models to perform actions, access external services, and process data beyond text generation.
4
+
5
+ ## Understanding Tools
6
+
7
+ ### What are Tools?
8
+ Tools are Ruby classes that inherit from `RubyLLM::Tool` and provide specific capabilities to AI models:
9
+ - **File operations**: Read, write, analyze files
10
+ - **Web interactions**: HTTP requests, API calls, web scraping
11
+ - **Data processing**: Analysis, transformation, calculations
12
+ - **System integration**: Shell commands, external services
13
+ - **Custom logic**: Business-specific operations
14
+
15
+ ### Tool Architecture
16
+ ```ruby
17
+ class MyTool < RubyLLM::Tool
18
+ description "Brief description of what this tool does"
19
+
20
+ def tool_method(parameter1, parameter2 = nil)
21
+ # Tool implementation
22
+ "Result that gets returned to the AI"
23
+ end
24
+
25
+ private
26
+
27
+ def helper_method
28
+ # Internal helper methods
29
+ end
30
+ end
31
+ ```
32
+
33
+ ## Using Existing Tools
34
+
35
+ ### Enabling Tools
36
+ ```bash
37
+ # Use tools from a specific file
38
+ aia --tools my_tool.rb my_prompt
39
+
40
+ # Use all tools in a directory
41
+ aia --tools ./tools/ my_prompt
42
+
43
+ # Use multiple tool sources
44
+ aia --tools "./tools/,./custom_tools.rb,/shared/tools/" my_prompt
45
+ ```
46
+
47
+ ### Tool Security
48
+ ```bash
49
+ # Restrict to specific tools
50
+ aia --tools ./tools/ --allowed_tools "file_reader,calculator" my_prompt
51
+
52
+ # Block dangerous tools
53
+ aia --tools ./tools/ --rejected_tools "file_writer,system_admin" my_prompt
54
+
55
+ # Combine restrictions
56
+ aia --tools ./tools/ --allowed_tools "safe_tools" --rejected_tools "dangerous_tools" my_prompt
57
+ ```
58
+
59
+ ### Discovering Available Tools
60
+ ```bash
61
+ # List available tools
62
+ aia --tools ./tools/ tool_discovery_prompt
63
+
64
+ # Or within a prompt
65
+ //tools
66
+ ```
67
+
68
+ ## Creating Custom Tools
69
+
70
+ ### Basic Tool Structure
71
+ ```ruby
72
+ # ~/.aia/tools/file_analyzer.rb
73
+ class FileAnalyzer < RubyLLM::Tool
74
+ description "Analyzes files for structure, content, and metadata"
75
+
76
+ def analyze_file(file_path, analysis_type = "basic")
77
+ return "File not found: #{file_path}" unless File.exist?(file_path)
78
+
79
+ case analysis_type
80
+ when "basic"
81
+ basic_analysis(file_path)
82
+ when "detailed"
83
+ detailed_analysis(file_path)
84
+ when "security"
85
+ security_analysis(file_path)
86
+ else
87
+ "Unknown analysis type: #{analysis_type}"
88
+ end
89
+ end
90
+
91
+ def file_stats(file_path)
92
+ return "File not found: #{file_path}" unless File.exist?(file_path)
93
+
94
+ stat = File.stat(file_path)
95
+ {
96
+ size: stat.size,
97
+ created: stat.ctime,
98
+ modified: stat.mtime,
99
+ permissions: stat.mode.to_s(8),
100
+ type: File.directory?(file_path) ? "directory" : "file"
101
+ }.to_json
102
+ end
103
+
104
+ private
105
+
106
+ def basic_analysis(file_path)
107
+ content = File.read(file_path)
108
+ lines = content.lines.count
109
+ words = content.split.count
110
+ chars = content.length
111
+
112
+ "File: #{File.basename(file_path)}\nLines: #{lines}\nWords: #{words}\nCharacters: #{chars}"
113
+ end
114
+
115
+ def detailed_analysis(file_path)
116
+ basic = basic_analysis(file_path)
117
+ content = File.read(file_path)
118
+
119
+ # Language detection
120
+ ext = File.extname(file_path).downcase
121
+ language = detect_language(ext, content)
122
+
123
+ # Additional analysis
124
+ encoding = content.encoding.to_s
125
+ blank_lines = content.lines.count(&:strip.empty?)
126
+
127
+ "#{basic}\nLanguage: #{language}\nEncoding: #{encoding}\nBlank lines: #{blank_lines}"
128
+ end
129
+
130
+ def security_analysis(file_path)
131
+ content = File.read(file_path)
132
+ issues = []
133
+
134
+ # Check for potential security issues
135
+ issues << "Contains potential passwords" if content.match?(/password\s*=\s*["'][^"']+["']/i)
136
+ issues << "Contains API keys" if content.match?(/api[_-]?key\s*[:=]\s*["'][^"']+["']/i)
137
+ issues << "Contains hardcoded URLs" if content.match?/https?:\/\/[^\s]+/
138
+ issues << "Contains TODO/FIXME items" if content.match?/(TODO|FIXME|HACK)/i
139
+
140
+ if issues.empty?
141
+ "No obvious security issues found in #{File.basename(file_path)}"
142
+ else
143
+ "Security concerns in #{File.basename(file_path)}:\n- #{issues.join("\n- ")}"
144
+ end
145
+ end
146
+
147
+ def detect_language(ext, content)
148
+ case ext
149
+ when '.rb' then 'Ruby'
150
+ when '.py' then 'Python'
151
+ when '.js' then 'JavaScript'
152
+ when '.java' then 'Java'
153
+ when '.cpp', '.cc', '.cxx' then 'C++'
154
+ when '.c' then 'C'
155
+ when '.go' then 'Go'
156
+ when '.rs' then 'Rust'
157
+ else
158
+ # Simple heuristics based on content
159
+ return 'Ruby' if content.match?(/def\s+\w+|class\s+\w+|require ['"]/)
160
+ return 'Python' if content.match?(/def \w+\(|import \w+|from \w+ import/)
161
+ return 'JavaScript' if content.match?(/function\s+\w+|const\s+\w+|let\s+\w+/)
162
+ 'Unknown'
163
+ end
164
+ end
165
+ end
166
+ ```
167
+
168
+ ### Web Integration Tool
169
+ ```ruby
170
+ # ~/.aia/tools/web_client.rb
171
+ require 'net/http'
172
+ require 'json'
173
+ require 'uri'
174
+
175
+ class WebClient < RubyLLM::Tool
176
+ description "Performs HTTP requests and web API interactions"
177
+
178
+ def get_url(url, headers = {})
179
+ uri = URI(url)
180
+ http = Net::HTTP.new(uri.host, uri.port)
181
+ http.use_ssl = true if uri.scheme == 'https'
182
+
183
+ request = Net::HTTP::Get.new(uri)
184
+ headers.each { |key, value| request[key] = value }
185
+
186
+ response = http.request(request)
187
+
188
+ {
189
+ status: response.code,
190
+ headers: response.to_hash,
191
+ body: response.body
192
+ }.to_json
193
+ end
194
+
195
+ def post_json(url, data, headers = {})
196
+ uri = URI(url)
197
+ http = Net::HTTP.new(uri.host, uri.port)
198
+ http.use_ssl = true if uri.scheme == 'https'
199
+
200
+ request = Net::HTTP::Post.new(uri)
201
+ request['Content-Type'] = 'application/json'
202
+ headers.each { |key, value| request[key] = value }
203
+ request.body = data.to_json
204
+
205
+ response = http.request(request)
206
+
207
+ {
208
+ status: response.code,
209
+ body: response.body
210
+ }.to_json
211
+ end
212
+
213
+ def check_url_status(url)
214
+ uri = URI(url)
215
+ http = Net::HTTP.new(uri.host, uri.port)
216
+ http.use_ssl = true if uri.scheme == 'https'
217
+ http.open_timeout = 10
218
+ http.read_timeout = 10
219
+
220
+ begin
221
+ response = http.head(uri.path.empty? ? '/' : uri.path)
222
+ "#{url}: #{response.code} #{response.message}"
223
+ rescue => e
224
+ "#{url}: Error - #{e.message}"
225
+ end
226
+ end
227
+
228
+ def fetch_api_data(endpoint, api_key = nil, params = {})
229
+ uri = URI(endpoint)
230
+
231
+ # Add query parameters
232
+ unless params.empty?
233
+ uri.query = params.map { |k, v| "#{k}=#{v}" }.join('&')
234
+ end
235
+
236
+ http = Net::HTTP.new(uri.host, uri.port)
237
+ http.use_ssl = true if uri.scheme == 'https'
238
+
239
+ request = Net::HTTP::Get.new(uri)
240
+ request['Authorization'] = "Bearer #{api_key}" if api_key
241
+ request['User-Agent'] = 'AIA-Tools/1.0'
242
+
243
+ response = http.request(request)
244
+
245
+ if response.code == '200'
246
+ JSON.parse(response.body)
247
+ else
248
+ { error: "API request failed", status: response.code, message: response.body }
249
+ end
250
+ rescue JSON::ParserError
251
+ { error: "Invalid JSON response", raw_body: response.body }
252
+ rescue => e
253
+ { error: e.message }
254
+ end
255
+ end
256
+ ```
257
+
258
+ ### Data Analysis Tool
259
+ ```ruby
260
+ # ~/.aia/tools/data_analyzer.rb
261
+ require 'csv'
262
+ require 'json'
263
+
264
+ class DataAnalyzer < RubyLLM::Tool
265
+ description "Analyzes CSV data, JSON files, and performs statistical calculations"
266
+
267
+ def analyze_csv(file_path, delimiter = ',')
268
+ return "File not found: #{file_path}" unless File.exist?(file_path)
269
+
270
+ begin
271
+ data = CSV.read(file_path, headers: true, col_sep: delimiter)
272
+
273
+ analysis = {
274
+ rows: data.length,
275
+ columns: data.headers.length,
276
+ headers: data.headers,
277
+ sample_data: data.first(3).map(&:to_h),
278
+ column_types: analyze_column_types(data),
279
+ missing_values: count_missing_values(data)
280
+ }
281
+
282
+ JSON.pretty_generate(analysis)
283
+ rescue => e
284
+ "Error analyzing CSV: #{e.message}"
285
+ end
286
+ end
287
+
288
+ def calculate_statistics(file_path, column_name)
289
+ return "File not found: #{file_path}" unless File.exist?(file_path)
290
+
291
+ begin
292
+ data = CSV.read(file_path, headers: true)
293
+ values = data[column_name].compact.map(&:to_f)
294
+
295
+ return "Column not found or no numeric data" if values.empty?
296
+
297
+ stats = {
298
+ count: values.length,
299
+ mean: values.sum / values.length.to_f,
300
+ median: median(values),
301
+ min: values.min,
302
+ max: values.max,
303
+ range: values.max - values.min,
304
+ std_dev: standard_deviation(values)
305
+ }
306
+
307
+ JSON.pretty_generate(stats)
308
+ rescue => e
309
+ "Error calculating statistics: #{e.message}"
310
+ end
311
+ end
312
+
313
+ def find_correlations(file_path, columns = nil)
314
+ return "File not found: #{file_path}" unless File.exist?(file_path)
315
+
316
+ begin
317
+ data = CSV.read(file_path, headers: true)
318
+
319
+ # Get numeric columns
320
+ numeric_columns = columns || data.headers.select do |header|
321
+ data[header].compact.all? { |value| numeric?(value) }
322
+ end
323
+
324
+ correlations = {}
325
+
326
+ numeric_columns.combination(2) do |col1, col2|
327
+ values1 = data[col1].compact.map(&:to_f)
328
+ values2 = data[col2].compact.map(&:to_f)
329
+
330
+ if values1.length == values2.length && values1.length > 1
331
+ corr = correlation(values1, values2)
332
+ correlations["#{col1} vs #{col2}"] = corr.round(4)
333
+ end
334
+ end
335
+
336
+ JSON.pretty_generate(correlations)
337
+ rescue => e
338
+ "Error calculating correlations: #{e.message}"
339
+ end
340
+ end
341
+
342
+ def json_summary(file_path)
343
+ return "File not found: #{file_path}" unless File.exist?(file_path)
344
+
345
+ begin
346
+ data = JSON.parse(File.read(file_path))
347
+
348
+ summary = {
349
+ type: data.class.name,
350
+ structure: analyze_json_structure(data),
351
+ size: data.respond_to?(:length) ? data.length : 1,
352
+ keys: data.is_a?(Hash) ? data.keys : nil
353
+ }
354
+
355
+ JSON.pretty_generate(summary)
356
+ rescue JSON::ParserError => e
357
+ "Invalid JSON file: #{e.message}"
358
+ rescue => e
359
+ "Error analyzing JSON: #{e.message}"
360
+ end
361
+ end
362
+
363
+ private
364
+
365
+ def analyze_column_types(data)
366
+ types = {}
367
+ data.headers.each do |header|
368
+ sample_values = data[header].compact.first(100)
369
+
370
+ if sample_values.all? { |v| numeric?(v) }
371
+ types[header] = 'numeric'
372
+ elsif sample_values.all? { |v| date_like?(v) }
373
+ types[header] = 'date'
374
+ else
375
+ types[header] = 'text'
376
+ end
377
+ end
378
+ types
379
+ end
380
+
381
+ def count_missing_values(data)
382
+ missing = {}
383
+ data.headers.each do |header|
384
+ missing_count = data[header].count { |v| v.nil? || v.strip.empty? }
385
+ missing[header] = missing_count if missing_count > 0
386
+ end
387
+ missing
388
+ end
389
+
390
+ def numeric?(value)
391
+ Float(value) rescue false
392
+ end
393
+
394
+ def date_like?(value)
395
+ Date.parse(value) rescue false
396
+ end
397
+
398
+ def median(values)
399
+ sorted = values.sort
400
+ len = sorted.length
401
+ len.even? ? (sorted[len/2 - 1] + sorted[len/2]) / 2.0 : sorted[len/2]
402
+ end
403
+
404
+ def standard_deviation(values)
405
+ mean = values.sum / values.length.to_f
406
+ variance = values.sum { |v| (v - mean) ** 2 } / values.length.to_f
407
+ Math.sqrt(variance)
408
+ end
409
+
410
+ def correlation(x, y)
411
+ n = x.length
412
+ sum_x = x.sum
413
+ sum_y = y.sum
414
+ sum_x2 = x.sum { |v| v ** 2 }
415
+ sum_y2 = y.sum { |v| v ** 2 }
416
+ sum_xy = x.zip(y).sum { |a, b| a * b }
417
+
418
+ numerator = n * sum_xy - sum_x * sum_y
419
+ denominator = Math.sqrt((n * sum_x2 - sum_x ** 2) * (n * sum_y2 - sum_y ** 2))
420
+
421
+ denominator == 0 ? 0 : numerator / denominator
422
+ end
423
+
424
+ def analyze_json_structure(data, max_depth = 3, current_depth = 0)
425
+ return "..." if current_depth >= max_depth
426
+
427
+ case data
428
+ when Hash
429
+ sample_keys = data.keys.first(5)
430
+ structure = {}
431
+ sample_keys.each do |key|
432
+ structure[key] = analyze_json_structure(data[key], max_depth, current_depth + 1)
433
+ end
434
+ structure["..."] = "#{data.keys.length - 5} more keys" if data.keys.length > 5
435
+ structure
436
+ when Array
437
+ return [] if data.empty?
438
+ [analyze_json_structure(data.first, max_depth, current_depth + 1)]
439
+ else
440
+ data.class.name
441
+ end
442
+ end
443
+ end
444
+ ```
445
+
446
+ ## Advanced Tool Patterns
447
+
448
+ ### Tool with Configuration
449
+ ```ruby
450
+ class ConfigurableTool < RubyLLM::Tool
451
+ description "Tool that can be configured for different environments"
452
+
453
+ def initialize
454
+ super
455
+ @config = load_config
456
+ end
457
+
458
+ def process_data(input, environment = 'development')
459
+ config = @config[environment]
460
+ # Use environment-specific configuration
461
+ process_with_config(input, config)
462
+ end
463
+
464
+ private
465
+
466
+ def load_config
467
+ config_file = File.join(Dir.home, '.aia', 'tool_config.yml')
468
+ if File.exist?(config_file)
469
+ YAML.load_file(config_file)
470
+ else
471
+ default_config
472
+ end
473
+ end
474
+
475
+ def default_config
476
+ {
477
+ 'development' => { 'api_endpoint' => 'http://localhost:3000', 'timeout' => 30 },
478
+ 'production' => { 'api_endpoint' => 'https://api.example.com', 'timeout' => 10 }
479
+ }
480
+ end
481
+ end
482
+ ```
483
+
484
+ ### Tool with Caching
485
+ ```ruby
486
+ class CachedTool < RubyLLM::Tool
487
+ description "Tool with intelligent caching for expensive operations"
488
+
489
+ def expensive_operation(input)
490
+ cache_key = Digest::MD5.hexdigest(input)
491
+ cache_file = "/tmp/tool_cache_#{cache_key}.json"
492
+
493
+ if File.exist?(cache_file) && fresh_cache?(cache_file)
494
+ JSON.parse(File.read(cache_file))
495
+ else
496
+ result = perform_expensive_operation(input)
497
+ File.write(cache_file, result.to_json)
498
+ result
499
+ end
500
+ end
501
+
502
+ private
503
+
504
+ def fresh_cache?(cache_file, max_age = 3600)
505
+ (Time.now - File.mtime(cache_file)) < max_age
506
+ end
507
+
508
+ def perform_expensive_operation(input)
509
+ # Simulate expensive operation
510
+ sleep 2
511
+ { result: "Processed: #{input}", timestamp: Time.now }
512
+ end
513
+ end
514
+ ```
515
+
516
+ ### Error-Resilient Tool
517
+ ```ruby
518
+ class ResilientTool < RubyLLM::Tool
519
+ description "Tool with comprehensive error handling and recovery"
520
+
521
+ def reliable_operation(input, max_retries = 3)
522
+ attempts = 0
523
+
524
+ begin
525
+ attempts += 1
526
+ perform_operation(input)
527
+ rescue StandardError => e
528
+ if attempts <= max_retries
529
+ wait_time = 2 ** attempts # Exponential backoff
530
+ sleep wait_time
531
+ retry
532
+ else
533
+ {
534
+ error: true,
535
+ message: "Operation failed after #{max_retries} attempts",
536
+ last_error: e.message,
537
+ suggestion: "Try with simpler input or check system resources"
538
+ }.to_json
539
+ end
540
+ end
541
+ end
542
+
543
+ def safe_file_operation(file_path)
544
+ return "File path required" if file_path.nil? || file_path.empty?
545
+ return "File not found: #{file_path}" unless File.exist?(file_path)
546
+ return "Access denied: #{file_path}" unless File.readable?(file_path)
547
+
548
+ begin
549
+ File.read(file_path)
550
+ rescue => e
551
+ "Error reading file: #{e.message}"
552
+ end
553
+ end
554
+
555
+ private
556
+
557
+ def perform_operation(input)
558
+ # Simulate operation that might fail
559
+ raise "Random failure" if rand < 0.3
560
+ { success: true, data: "Processed #{input}" }
561
+ end
562
+ end
563
+ ```
564
+
565
+ ## Tool Integration in Prompts
566
+
567
+ ### Basic Tool Usage
568
+ ```markdown
569
+ # ~/.prompts/file_analysis.txt
570
+ //tools file_analyzer.rb
571
+
572
+ # File Analysis Report
573
+
574
+ Please analyze the following file:
575
+ File path: <%= file_path %>
576
+
577
+ Use the file_analyzer tool to:
578
+ 1. Get basic file statistics
579
+ 2. Perform detailed content analysis
580
+ 3. Check for security issues
581
+
582
+ Provide a comprehensive report with recommendations.
583
+ ```
584
+
585
+ ### Multi-Tool Workflows
586
+ ```markdown
587
+ # ~/.prompts/web_data_analysis.txt
588
+ //tools web_client.rb,data_analyzer.rb
589
+
590
+ # Web Data Analysis Pipeline
591
+
592
+ Data source URL: <%= api_url %>
593
+ API key: <%= api_key %>
594
+
595
+ ## Step 1: Fetch Data
596
+ Use the web_client tool to retrieve data from the API endpoint.
597
+
598
+ ## Step 2: Save and Analyze
599
+ Save the data to a temporary CSV file and use data_analyzer to:
600
+ - Generate summary statistics
601
+ - Identify data patterns
602
+ - Find correlations
603
+
604
+ ## Step 3: Generate Insights
605
+ Based on the analysis, provide actionable insights and recommendations.
606
+ ```
607
+
608
+ ### Conditional Tool Usage
609
+ ```ruby
610
+ # ~/.prompts/adaptive_analysis.txt
611
+ //ruby
612
+ input_file = '<%= input_file %>'
613
+ file_ext = File.extname(input_file).downcase
614
+
615
+ case file_ext
616
+ when '.csv'
617
+ puts "//tools data_analyzer.rb"
618
+ analysis_type = "CSV data analysis"
619
+ when '.json'
620
+ puts "//tools data_analyzer.rb"
621
+ analysis_type = "JSON structure analysis"
622
+ when '.rb', '.py', '.js'
623
+ puts "//tools file_analyzer.rb"
624
+ analysis_type = "Code analysis"
625
+ else
626
+ puts "//tools file_analyzer.rb"
627
+ analysis_type = "General file analysis"
628
+ end
629
+
630
+ puts "Selected #{analysis_type} for #{file_ext} file"
631
+ ```
632
+
633
+ Perform #{analysis_type} on: <%= input_file %>
634
+
635
+ Provide detailed insights appropriate for the file type.
636
+ ```
637
+
638
+ ## Tool Security and Best Practices
639
+
640
+ ### Security Guidelines
641
+ 1. **Input Validation**: Always validate tool inputs
642
+ 2. **File System Access**: Limit file access to safe directories
643
+ 3. **Network Requests**: Validate URLs and handle errors
644
+ 4. **Resource Limits**: Implement timeouts and size limits
645
+ 5. **Error Handling**: Never expose system details in errors
646
+
647
+ ### Performance Considerations
648
+ 1. **Caching**: Cache expensive operations appropriately
649
+ 2. **Timeouts**: Set reasonable timeouts for external calls
650
+ 3. **Memory Management**: Handle large data sets efficiently
651
+ 4. **Async Operations**: Use async patterns for I/O operations
652
+ 5. **Resource Cleanup**: Properly clean up resources
653
+
654
+ ### Testing Tools
655
+ ```ruby
656
+ # test_tool.rb
657
+ require 'minitest/autorun'
658
+ require_relative 'my_tool'
659
+
660
+ class TestMyTool < Minitest::Test
661
+ def setup
662
+ @tool = MyTool.new
663
+ end
664
+
665
+ def test_basic_functionality
666
+ result = @tool.process_data("test input")
667
+ assert_kind_of String, result
668
+ refute_empty result
669
+ end
670
+
671
+ def test_error_handling
672
+ result = @tool.process_data(nil)
673
+ assert_includes result, "error"
674
+ end
675
+ end
676
+ ```
677
+
678
+ ## Tool Distribution and Sharing
679
+
680
+ ### Tool Libraries
681
+ ```bash
682
+ # Organize tools in libraries
683
+ ~/.aia/tools/
684
+ ├── core/ # Essential tools
685
+ │ ├── file_ops.rb
686
+ │ ├── web_client.rb
687
+ │ └── data_analysis.rb
688
+ ├── development/ # Development tools
689
+ │ ├── code_analyzer.rb
690
+ │ ├── test_runner.rb
691
+ │ └── deploy_helper.rb
692
+ └── specialized/ # Domain-specific tools
693
+ ├── finance_tools.rb
694
+ ├── media_tools.rb
695
+ └── science_tools.rb
696
+ ```
697
+
698
+ ### Tool Documentation
699
+ ```ruby
700
+ class DocumentedTool < RubyLLM::Tool
701
+ description "Example of well-documented tool with usage examples"
702
+
703
+ # Processes input data with specified options
704
+ # @param input [String] The input data to process
705
+ # @param format [String] Output format: 'json', 'csv', or 'text'
706
+ # @param options [Hash] Additional processing options
707
+ # @return [String] Processed data in specified format
708
+ # @example
709
+ # process_data("sample", "json", { detailed: true })
710
+ def process_data(input, format = 'text', options = {})
711
+ # Implementation
712
+ end
713
+ end
714
+ ```
715
+
716
+ ## Troubleshooting Tools
717
+
718
+ ### Common Issues
719
+ 1. **Tool Not Found**: Check tool file paths and syntax
720
+ 2. **Permission Errors**: Verify file permissions and access rights
721
+ 3. **Missing Dependencies**: Install required gems and libraries
722
+ 4. **Method Errors**: Check method signatures and parameters
723
+ 5. **Runtime Errors**: Add proper error handling and logging
724
+
725
+ ### Debugging Tools
726
+ ```ruby
727
+ class DebuggingTool < RubyLLM::Tool
728
+ description "Tool with extensive debugging capabilities"
729
+
730
+ def debug_operation(input)
731
+ debug_log("Starting operation with input: #{input}")
732
+
733
+ begin
734
+ result = process_input(input)
735
+ debug_log("Operation successful: #{result}")
736
+ result
737
+ rescue => e
738
+ debug_log("Operation failed: #{e.message}")
739
+ debug_log("Backtrace: #{e.backtrace.first(5).join("\n")}")
740
+ raise
741
+ end
742
+ end
743
+
744
+ private
745
+
746
+ def debug_log(message)
747
+ timestamp = Time.now.strftime("%Y-%m-%d %H:%M:%S")
748
+ puts "[DEBUG #{timestamp}] #{message}" if debug_mode?
749
+ end
750
+
751
+ def debug_mode?
752
+ ENV['AIA_DEBUG'] == 'true'
753
+ end
754
+ end
755
+ ```
756
+
757
+ ## MCP Client Integration
758
+
759
+ ### Model Context Protocol (MCP) Support
760
+
761
+ AIA supports MCP clients for extended functionality through external services:
762
+
763
+ #### GitHub MCP Server
764
+ ```bash
765
+ # Install GitHub MCP server
766
+ brew install github-mcp-server
767
+
768
+ # Set required environment variable
769
+ export GITHUB_PERSONAL_ACCESS_TOKEN="your_token_here"
770
+
771
+ # Use with AIA
772
+ aia --tools examples/tools/mcp/github_mcp_server.rb --chat
773
+ ```
774
+
775
+ **Capabilities:**
776
+ - Repository analysis and management
777
+ - Issue tracking and manipulation
778
+ - Pull request automation
779
+ - Code review assistance
780
+ - Project metrics and insights
781
+
782
+ #### iMCP for macOS
783
+ ```bash
784
+ # Install iMCP (macOS only)
785
+ brew install --cask loopwork/tap/iMCP
786
+
787
+ # Use with AIA
788
+ aia --tools examples/tools/mcp/imcp.rb --chat
789
+ ```
790
+
791
+ **Capabilities:**
792
+ - Access to macOS Notes app
793
+ - Calendar integration
794
+ - Contacts management
795
+ - System information access
796
+ - File system operations
797
+
798
+ ### MCP Client Requirements
799
+
800
+ MCP clients require:
801
+ - The `ruby_llm-mcp` gem (automatically included with AIA)
802
+ - Proper MCP server installation and configuration
803
+ - Required environment variables and permissions
804
+ - Network access for external MCP servers
805
+
806
+ ## Shared Tools Collection
807
+
808
+ ### Using the Shared Tools Gem
809
+
810
+ AIA can use the [shared_tools gem](https://github.com/madbomber/shared_tools) for common functionality:
811
+
812
+ ```bash
813
+ # Access all shared tools (included with AIA)
814
+ aia --require shared_tools/ruby_llm --chat
815
+
816
+ # Access specific shared tool
817
+ aia --require shared_tools/ruby_llm/edit_file --chat
818
+
819
+ # Combine with custom tools
820
+ aia --require shared_tools/ruby_llm --tools ~/my-tools/ --chat
821
+
822
+ # Use in batch prompts
823
+ aia --require shared_tools/ruby_llm my_prompt input.txt
824
+ ```
825
+
826
+ ### Available Shared Tools
827
+
828
+ The shared_tools collection includes:
829
+ - **File Operations**: Reading, writing, editing files
830
+ - **Data Processing**: JSON/CSV manipulation, data transformation
831
+ - **Web Operations**: HTTP requests, web scraping
832
+ - **System Operations**: Process management, system information
833
+ - **Utility Functions**: String processing, date manipulation
834
+
835
+ ### ERB Integration with Shared Tools
836
+
837
+ ```ruby
838
+ # In prompt files with ERB
839
+ //ruby
840
+ require 'shared_tools/ruby_llm'
841
+ ```
842
+
843
+ Use shared tools directly within your prompts using Ruby directives.
844
+
845
+ ## Related Documentation
846
+
847
+ - [Chat Mode](chat.md) - Using tools in interactive mode
848
+ - [Advanced Prompting](../advanced-prompting.md) - Complex tool integration patterns
849
+ - [MCP Integration](../mcp-integration.md) - Model Context Protocol details
850
+ - [Configuration](../configuration.md) - Tool configuration options
851
+ - [CLI Reference](../cli-reference.md) - Tool-related command-line options
852
+ - [Examples](../examples/tools/index.md) - Real-world tool examples
853
+
854
+ ---
855
+
856
+ Tools transform AIA from a text processor into a powerful automation platform. Start with simple tools and gradually build more sophisticated capabilities as your needs grow!